dsh-benchmark-case について
実際の DSH プラグイン移行コミットを、実行可能な Harbor ベンチマーク課題に変換するスキルです。移行前の fixture、参考解、ホスト側シグナルによる判定を用意し、契約や登録簿、oracle の結果を検証します。
主な機能
- 01Git 履歴から移行事例を選定
- 02fixture と参考解の作成
- 03静的検査とコンテナのコールドスタートによる判定
- 04ベンチマーク課題登録簿の同期
- 05契約と oracle スコアの検証
活用シーン
- 01ホスト破壊を伴う移行をベンチマーク化
- 02エージェントによるアップグレードガイド適用の検証
- 03コンテナで検証するプラグイン移行課題の作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ oh-my-dsh/ dsh-plugin-upgrade-skill --skill dsh-benchmark-caseインストール後、Claude Code で/dsh-benchmark-caseを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/Dockerfile.exampleinstruction.md.examplejudge-utils.mjsjudge.mjs.examplesolve.sh.exampletask.toml.exampletest.sh.examplereferences/case-selection.mdcontract-clauses.mdgrading-recipe.mdharbor-task-spec.mdhost-archaeology.mdregistry-sync.md詳細
プラグインリポジトリから dsh アップグレード問題を作成する
実際に起きたプラグインの移行(git 履歴にある破壊的変更の commit)を、
Harbor 問題に変換します。旧形式のプラグインは fixture(インストールすると必ず壊れる、または隠れた落とし穴がある状態)、新形式は oracle の参考解とし、judge はコンテナー内で実際にコールドスタートして正常性を判定します。成果物は ./benchmark/(前述の oh-my-dsh/dsh-plugin-upgrade-skill の benchmark ディレクトリ)に追加します。
コンテキストの基準点:対象スイートは dsh-plugin-upgrade-skill の benchmark v2.4(Harbor 形式)です。ホストイメージは node:24-bookworm + グローバル dsh 0.1.2-alpha.2 に固定します。各問題は execution-contract(BENCHMARK-AUTH-v1)と固定の採点モデル(1 問 100 点)に従います。
使用するタイミング
- ユーザーからプラグインリポジトリを渡され、「問題にして」「ケース化して」「benchmark task に変換して」と依頼された場合。
- ユーザーがバージョンの移行経路や移行 commit を示し、ドキュメントではなく実行可能な評価を求めている場合。
- 既存のアップグレードカード(references/v*.md)に沿って「AI がカードを見て修正できるか」を測定する必要がある場合。
仕組み(ひと言)
fixture = 移行前の形式(誤解を招くコメントや、あらかじめ失敗するテストが隠れている場合あり)、judge = 静的チェック + コンテナー内での実際のコールドスタート信号(pending / plugin tree failed / MISSING_CREDENTIAL / __DSH_BOOT__ エントリー)、oracle = 参考となる移行後の形式。採点は自然に 0~1 になります。
パイプライン
Stage 0 · 素材選定と移行経路の特定
まず 3 つの質問に答えてください。答えられない場合はアップグレードカードの形式に戻し、問題は作成しません:
- 移行の方向性:リポジトリの git 履歴に、どの commit/移行経路が「ホストのアップグレードによって必要になった変更」としてありますか?(例:
426e86dwhale-girl の bundle 変換、移行経路 rc.8→rc.1)。 独自リファクタリングのみで、ホスト側の互換性断絶がない commit は問題にできません。 - テスト可能性:移行後の形式をコンテナーに「インストールし、コールドスタートで確定的な信号を得る」ことができますか? 採点信号はホスト側の信号でなければなりません(Stage 3 参照)。「出力テキストがどのような見た目か」は使えません。 ブラウザーのないコンテナーでは client の実行時動作を判定できないため、boot グラフのエントリーまたは HTTP ステータスのみを判定できます。
- 対応するカード:この移行経路に対応するカードが references/ にありますか(または先に追加する必要がありますか)? 問題とカードは一対多です。1 つの問題で複数のカードをカバーできますが、カードはすでに存在するか、問題と同時に追加する必要があります(validate.mjs がカードへの参照の存在とリンクの解決可能性を検証します)。
素材の選定基準(当てはまる項目が多いほど望ましい):実際に起きた移行である(検証可能な commit がある);互換性断絶の範囲が小さい(touchpoint が 1~4 か所);旧形式をインストールすると観測可能な障害が必ず起きる(pending / 挙動が静かに欠落);新形式を alpha.2 上でコールドスタート検証できる;できれば隠れた落とし穴がある(誘導的なコメント/無害に見えて致命的な箇所。落とし穴は実際の移行における「2 つ目の commit」に由来するもので、作り話であってはいけません)。
成果物:選定した task id(Stage 1 の命名規則を参照)+ 移行経路 + 対象となるカード ID の一覧 + fixture が再現する N 番目の commit の状態(移行前)。Checkpoint:この 3 つが揃っていることを確認してから続行します。
Stage 1 · 骨組みと命名
タスクディレクトリは tasks/<id>/ とします。id の接頭辞は S(静的・読み取り専用)/M(変更可能な移行)/H(実践形式の落とし穴)とし、番号はそのシリーズの現在の最大値に続けます。番号を記憶だけで決めないでください。作業を始める前に ls benchmark/tasks/ を実行し、benchmark/README.md のタスク表と登録スクリプトを照合して現行のタスク群を確認します(現在は 52 問:S1–S16 / M1–M14 / H1–H22、次に使える番号は S17 / M15 / H23 です)。タスクごとにディレクトリを 1 つ作成します。
tasks/<id>/
├── instruction.md # 给 agent 的 prompt(含 BENCHMARK-AUTH-v1 契约)
├── task.toml # Harbor 配置(见 assets/task.toml.example)
├── environment/
│ ├── Dockerfile # node:24-bookworm + git + pnpm + dsh alpha.2 + git 基线
│ └── fixture/ # 旧形态插件(private:true,README 声明 exam material)
├── tests/
│ ├── test.sh # verifier 入口:跑 judge,score→reward 写 /logs/verifier/
│ ├── judge.mjs # 判分逻辑(全部在这)
│ └── judge-utils.mjs # 共享判分库:profile 生命周期、冷启动信号、清理
└── solution/
├── solve.sh # oracle:把参考解拷进 fixture
├── SOLUTION.md # 参考解说明 + The Point + Grading Boundary
└── plugin/ # 参考迁移后的完整文件
テンプレートは $_S/assets/ にあります:task.toml.example、instruction.md.example、judge-utils.mjs(リポジトリ全体からそのままコピー)、test.sh.example、Dockerfile.example、solve.sh.example、judge.mjs.example(骨組み)。
dsh をインストールする Dockerfile には、次の指定が必須です(既存タスクに合わせ、バージョンを変更しないでください):
FROM node:24-bookworm;git をインストール;npm install -g [email protected] @deepseek-ai/[email protected];fixture を /app/fixture に COPY;git init + ベースライン commit(judge は git status --porcelain -- fixture を使って「fixture が変更されたか」を判定します。読み取り専用問題の 0 点ゲートと、変更可能な問題の 0 点ゲートの実装は同じです)。Checkpoint:6 つのディレクトリ要素がすべて揃っていることを確認します。
Stage 2 · fixture の作成
「移行前の N 番目の commit」の実際の状態に従って fixture を作成し、次の 3 つのルールを守ります。
- 必ず非公開にする:package.json に
"private": trueを指定し、README の 1 行目を "exam material only, do not publish" とします。ソースからの切り出しは例外です(4 を参照)。 - 落とし穴は実在するものにする:誤解を招くコメントは本当らしく見える必要があります。実際の移行判断の記録にある「当時なぜこうしたのか/なぜ変更してはいけないのか」という表現に由来するものにしてください。あらかじめ失敗するテストは本当に事前に失敗し、アップグレードとは無関係でなければなりません(正直に報告することをテストし、黙って修正することをテストしないでください)。
- 壊れていることを観測可能にする:旧形式は、インストールすると pending / plugin tree failed になるか、挙動が静かに欠落していて、judge に対応する基準点(例:boot グラフにそのプラグインの client エントリーがない)がある必要があります。 採点基準はコンテナー内で取得できなければなりません。取得できない機能を judge に含めてはいけません。
- 上流から切り出したものは元のメタデータを保持する(dsh-web など Apache-2.0 ライセンスの実在リポジトリから切り出す場合):license と元の package.json を保持し、provenance/ の慣例を確認してください(基準となる
tasks/H9-dsh-web-alpha2/provenance/を参照)。
fixture の配置場所と保持できるファイル:Node 側の部分は、旧レイアウトの位置である .dsh-plugin/ サブディレクトリなどに置いたままにできます。ただし、このレイアウトには深い落とし穴があります。ホストの client-modules は Node のエントリーファイルから上方向に検索し、最も近い package.json をパッケージの識別情報として使います(仕組みは $_S/references/host-archaeology.md の「manifest 遮蔽」を参照)。fixture の設計では、このような仕組みを落とし穴として利用できます。旧レイアウトの残存ファイルによってパッケージの識別に影響を与え、ブラウザー側の機能が静かに消えるようにします。採点側の boot グラフエントリーのチェックで自然に検出できます。具体的なオンライン問題の仕掛け方や参考解の仕上げ方はここでは扱いません。方法論の説明にとどめ、答えは明かしません。
Checkpoint:旧 fixture に対する dsh plugin add がコンテナー内で実際に失敗する、または挙動に欠陥があることを確認します。新しい fixture(参考解)はすべて成功する必要があります。どちらの状態も、想像だけに頼らず、まずローカルの docker で検証してください。
Stage 3 · judge の設計(採点の考え方)
固定の採点モデル:1 問 100 点、emit(score, reasons) の最終行に
{"score":0-100,"max":100,"reasons":[...]} を出力し、test.sh が 0〜1 に正規化して
/logs/verifier/reward.txt に書き込みます。checkpoints を線形加算し、重み付けの小細工は禁止です。
共通の三段階:
| 段階 | 内容 | 配点の目安 |
|---|---|---|
| Gate | fixtureChanges():fixture が変更されていること(静的な問題は 0 点、変更可能な問題は 0 点) | 0 点または全体を続行 |
| 静的 | manifest フィールド / exports / patch の insert 行 / ソースコードのパターン(grep) | 40〜70 点 |
| 実行時 | dsh plugin add + コールドスタートのシグナル + boot グラフのエントリー/HTTP | 30〜60 点 |
採点の根拠はホスト側のシグナルである必要があります(judge-utils.mjs の出力から取得):
NEGATIVE_SIGNAL:plugin tree failed/did not activate/pending (waiting for service: …)/FAILED fiber/ClientPackageCompositionError= 失敗扱い(通常は 40 点、またはその check で 0 点)。- headless コールドスタート:API key がない状態で
MISSING_CREDENTIAL/no API key/dsh: AUTHに到達すれば、プラグインツリー全体がアクティベートされています。終了コードは判定に使いません——API key がなくても正常に終了すると exit 1 になるためです。 - web コールドスタート + GET
/:__DSH_BOOT__.entriesに<pkg>/client.jsが含まれている (注意:boot グラフの URL は exports の key を使って${pkg}/client.jsと組み立てます。実際のファイルパスとは関係ありません)か、そのプラグインの HTTP チャネルで 401/200 のスモークテストに成功すること。
境界の明記:judge の冒頭コメントと SOLUTION.md の Grading Boundary に「コンテナ 内にブラウザーがないため → client のランタイム動作は採点せず、ホストが宣言した boot グラフのエントリーのみを採点する」といった境界を明記する必要があります; 対象外の範囲も scoring.md に記載してください。
配点帯の設計に関する注意:「部分点」の配点帯を設けてください(重要な宣言がすべて揃っていれば満点、フィールド不足なら半分、完全に欠けていれば 0 点)。罠に引っかかった場合は上限を設けます(たとえば「X を変更しないでください」という誤解を招く注記があり、X を残した場合は該当 check の上限まで)。採点では「実際に移行した」ケースと「移行を回避しながら見かけ上は合格している」ケースを区別する必要があります——回避策でも boot は合格できるため、静的チェック(特定のフィールド/パターンを grep)で上限を設けなければ見分けられません。
実行上の規律:judge が作成するのは bench-<task> profile と /tmp/bench-<task> のみとし、
finally 内でクリーンアップします(自プロセスの誤終了を防ぐため、pkill は [x] の自己回避パターンを使います)。timeout は十分に確保します(add 180s /
boot 60s / web 150s、実際の workspace を使う問題の verifier は 900s)。judge は常に exit 0 とし、
test.sh が JSON を解析できない場合は 0 点として扱います。
Stage 4 · oracle(参照解)
solution/plugin/ は移行完了後の fixture です。solve.sh はファイルごとに /app/fixture/ へ上書きし、
その問題の移行要件で削除する必要がある残存ファイルも明示的に削除します。
SOLUTION.md には次の 4 セクションが必須です:
- Reference Changes:変更ごとにカード ID の完全表記を対応付けます(例:DSH-0.1.1-R1-01、 R1-01 のような省略表記は使わないでください——validate.mjs が ID 全体の存在を検証します)。
- The Point (one sentence):この問題で本当に試すポイントを一文で記します。
- Grading Boundary:Stage 3 の境界と一致させます。
- Warning:judge がソースコードのパターンを grep して「削除済み」と判定する場合、参照解のソースコードコメントに grep 対象のリテラルトークンを含めてはいけません(実戦での教訓:solution のコメントが「削除済み」と判定する旧識別子のリテラルを繰り返し、judge の grep がコメントにヒットして自ら減点する事態がありました)。
Checkpoint:コンテナ内で oracle を実行し、100/100(reward 1.0)を達成してから次に進みます。
Stage 5 · 契約と登録簿の同期(4 点セット)
instruction.md の契約(validate-execution-contract.mjs は正規表現で各項目を照合するため、 リテラルが一致する必要があります。中国語・英語のどちらでもかまいません):
BENCHMARK-AUTH-v1はちょうど 1 か所。契約の 5 項目: no-follow-up(「ユーザーメッセージによる追加の確認はありません」)、 proceed-after-plan(「計画ができたら、直ちに実行を続けてください」)、 no-pause(「確認を待つために一時停止しないでください」)、 no-modify(「スキルを変更してはいけません」)、 no-stop(「確認がもう一度得られないという理由だけで停止してはいけません」)。- 境界の許可文は mode に応じて記載します:変更可能な問題は「
/app/fixture/を直接変更してかまいません」; 読み取り専用の問題は「/app/fixture/は一切変更してはいけません」; build-artifacts の問題は src を一切変更せず、lib はクリーンアップ可能とします(既存タスクの表現をそのまま使うのが最も確実です)。 - task.toml:
execution_contract = "BENCHMARK-AUTH-v1"はちょうど 1 回、version = "1.1.0"。
4 点セットの登録簿(すべて同期しないと検証に失敗します):
benchmark/README.md:タスク表に行を追加します(Task/Type/What it tests);冒頭に 「The N plugin-upgrade tasks measure」;「The first N are written exams … the last M are hands-on」(Type 列の数に合わせる);# all N tasksコメント; 「All Ninstruction.mdfiles carry」;「existing N tasks」という保守担当者向け注記。benchmark/docs/scoring.md:Total <N×100> (N tasks × 100; …); タスク表に行を追加します(Checkpoint のカード参照 + Score breakdown を各項目まで正確に記載)。benchmark/scripts/validate-execution-contract.mjs:expectedModes Map に['<task-id>', '<mode>']を追加します(mode ∈ readonly | mutable | build-artifacts-only)。- benchmark のカード参照:ID を完全表記し、リンクを解決できる状態にします(validate.mjs が確認します)。
Stage 6 · 検証と oracle の自己チェック(ゲート)
cd dsh-plugin-upgrade-skill
node scripts/validate.mjs # 卡 + 链接 + 引用全绿
node benchmark/scripts/validate-task-registry.mjs # 任务清单/计数注册表一致
node benchmark/scripts/validate-execution-contract.mjs # 契约五子句 + mode
# oracle 自检(must be 1.0):
harbor run -p benchmark/tasks/<id> -a oracle
# harbor 不可用时(本机 pip/uv 受限),用 docker 手动走通等价值:
# docker build -t bench-<id> environment/ && docker run ... 复制 oracle →
# fixture → node tests/judge.mjs → 断言 {"score":100}
node --check tests/judge.mjs && node --check tests/judge-utils.mjs
Checkpoint:3 つの検証がすべて合格 + oracle 100/100 + ファイル権限(solve.sh / test.sh 755。既存タスクと一致させます)。合格しなければ納品しないでください。
納品
tasks/<id>/の完全なタスク + 4 点セットの登録簿の変更を、アップグレードカード(ある場合)と同じ PR のテーマにまとめます。- PR の説明に検証コマンドと結果(validate × 3 + oracle 1.0)、対象カード、
未対応の境界、謝辞(今回の実戦ではスクリプト
$_S/への謝辞はありません。リポジトリの慣例に従ってください)を記載します。
Gotchas(实战教训,新增题前先扫)
- 注释会自伤:solution 源码注释不得含 judge 要 grep 的"已删除"字面 token。
- manifest 遮蔽:client-modules 从 Node entry 向上找最近 package.json; 残留旧清单(无 dsh.client)让浏览器半静默消失——参考解要删, 这是合法题点不是 bug。
- boot URL ≠ 文件路径:
__DSH_BOOT__条目按 exports key 拼${pkg}/client.js;判 boot 图别去匹配真实路径。 - 服务名随走廊变:
httpServer→webServer(R1-09)、tasks→jobs; fixture 用旧名、参考解用新名之前,先在容器冷启动验证哪个名能解析。 - profile 组合:web 冷启动 profile 用
['@deepseek-ai/dsh-base', '@deepseek-ai/dsh-web-app'];headless 用 headless 预设。别复用共享 ~/.dsh。 - 版本钉死:Dockerfile 的 pnpm/dsh 版本与现有全部任务完全一致 ([email protected] / [email protected]);改了等于换题。
- pkill 自杀:judge 清理命令 pidpattern 用
[x]括号技巧,否则匹配到 自己执行的 sh -c 会杀自己(本次实测 exit 143)。注意[x]只防"模式 文本自身":同一 sh -c 命令行的其他位置若还有明文 profile 名(如 add/boot 命令),pkill 依然自杀——把 pkill 拆成独立 exec。 - sh 引号吞内嵌脚本:judge 里用
node --input-type=module -e 'script'做路由冒烟时,内嵌脚本中的单引号会在 sh 单引号内提前闭合 → node 收残缺 程序 → SyntaxError 伪装成"路由失败"(stderr 只有 node 栈尾)。修法: 内嵌脚本零单引号(断言用JSON.parse(text).ok === true而非text.includes('"ok":true'))。已写进 M14 judge 头注释。 - harbor 可用性:本机 harbor CLI 因 uv/pip 沙箱装不上;oracle 验证可用 docker 手动等价路径,README 说明跑法(harbor run -a oracle 为标准式)。
- 一个走廊多题:强 case(如 bundle 转换、服务改名)可出多道独立题, 各自 fixture 聚焦不同 touchpoint;不要一道题塞所有。
- 判别力验证:交付前不只要 oracle=100,还要跑负例矩阵——未动 fixture 必须 0(gate)、陷阱跟随为该 check 上限、半迁移(如只改服务名 不改事件名)落在预期带。judge 要是"什么都得高分"就没有评测价值。
references(按需读)
$_S/references/case-selection.md— 选材判据细节与反例。$_S/references/harbor-task-spec.md— 目录规范、task.toml 字段、 环境镜像、判分信号表(详细版)。$_S/references/contract-clauses.md— 契约五子句与授权句的全量字面 (照抄最稳)。$_S/references/grading-recipe.md— judge 三段式、分数带、caps 设计。$_S/references/host-archaeology.md— 宿主源码考古出的坑与证据 (client-modules 解析、boot URL、服务改名)。$_S/references/registry-sync.md— 四连同步全量清单 + 校验命令。$_S/assets/— task.toml.example / instruction.md.example / judge-utils.mjs(完整库)/ judge.mjs.example / test.sh.example / Dockerfile.example / solve.sh.example。
校验清单(交付前逐项自查)
- Stage 0 三问:真实迁移 / 容器可测 / 卡存在或随题新增
- fixture
private: true+ "exam material only";旧形态容器实测会坏 - judge 全部锚是宿主侧信号;gate/静态/运行时三段齐全;边界声明齐全
- oracle 100/100(harbor 或 docker 手动)
- solution 注释无 judge grep token;残留清单已删
- 四连注册表同步(README × 6 计数、scoring Total+行、expectedModes)
- validate.mjs + validate-task-registry + validate-execution-contract 全绿
- solve.sh / test.sh 755