Repository navigation
v0.16.0
[0.16.0] - 2026-07-29
Highlight: Skill 配布 MVP を「入れられる」から「運用できる」へ引き上げたリリース。 導入先 Agent の対応状況を manifest の申告だけでなく CommandMate 側の実測で裏付ける互換 matrix(#1246)、どの worktree に何が入っていて導入時のままかを横断確認する監査 dashboard と receipt からの reindex(#1248、DB migration v48)、Skill 導入を review 可能な commit / draft PR に載せる専用 git workflow(#1247)を追加した。あわせて、uninstall した Skill を再 install すると exit 0 で「Installed」と報告しながら 1 バイトも書かれない journal replay の欠陥(#1552)を修正し、対になる uninstall 経路の同型欠陥も同時に塞いだ。
Added
-
Agent 別 discovery 互換 matrix・support evidence・reload guidance を追加 (#1246): Agent 対応状況が manifest の申告 4 値(
native/commandmate_runtime/unsupported/unknown)+自由記述のevidenceしか無く、CommandMate 側が実測した結果を保持する場所も、それを申告と突き合わせる仕組みも無かった。install は #1460 以降.agents/skills/<id>と.claude/skills/<id>の両方へ byte-identical に配置するが、どの Agent がどちらの root を読むかはコード上どこにも表現されていなかった(SKILL_INSTALL_ROOT_PREFIXESと slash loader 3 本のcliTools上書きに暗黙分散していた)ため、片方だけ編集しても「install 済みなのに見えない」形でしか露見しなかった。(1)src/lib/skills/compatibility-matrix.tsを新設し、2026-07-26 の隔離環境実測(#1513 G4)を 発見(discovery)と呼出(slash command 露出)の 2 軸で記録した。Codex CLI 0.145.0 は発見のみ成立するため単一の native/unsupported 値では表現できない、というのが 2 軸にした理由そのものである。証跡の強さ(機械的 / Agent の自己申告 / 実測なし)、実測 version・計測日・証跡 URL・既知制約・reload 手順を各行に持ち、CLI_TOOL_IDS全 7 件を網羅する(未計測 Agent は行を省かずunknown+ skip 理由。行が無いと「言及が無い」が「たぶん大丈夫」と読まれるため)。support 値は discovery 軸だけで決まり、Codex の palette 非露出は known limitation として併記する(呼出できないことは「動かない」ではない)。(2)compatibility.tsのreconcileAgentSupport()が申告を実測で下方向にのみ制限する(capSupportByMeasurement())。実測が弱ければ実測値を表示してRESTRICTED、強ければ申告のままSTALE_DECLARATION(package が何を対応と主張するかを決めるのは提供元であるため引き上げない)、実測が無ければUNVERIFIED。これが「evidence 無しに native と表示しない」受入条件の実装である。staleness はnowを明示引数で受け取り、180 日超で経過日数つき警告を出す。(3)CompatibilityEvidenceを追加し、詳細画面の Agent 対応欄と install 前サマリ(UX-01)へ、申告・実測 2 軸・version/計測日/証跡・制約・reload 手順・未計測の skip 理由を表示する。(4) matrix の discovery root と CommandMate 自身の slash loader の整合をtests/unit/lib/skills/agent-discovery-regression.test.tsで固定した(両 root へ byte-identical に install した fixture を実際にloadSkills()/loadAgentsSkills()/loadCodexSkills()に通し、filterCommandsByCliTool()まで含めて検証。legacy.codex/skillsの回帰と二重表示防止も含む)。matrix→loader の一方向のみ主張する(.agents/skillsが antigravity にも供給される #1504 は CommandMate によるコマンド注入であって Agent の native discovery ではないため evidence にしない)。(5) 実 CLI の再計測はCM_SKILL_DISCOVERY_PROBE=1指定時のみ走る opt-in プローブとし、<cli> --versionの差分検出に限定した(対話 TUI の自動操作は無関係な global 設定を書き換える事故の実績があるため行わない)。CLI 未導入は failure でも compatible でもなくunknown+ skip 理由として記録し、その規則自体は CLI の無い CI でも常時テストされる。(6)docs/reference/skill-agent-compatibility.mdを新設し、doc の表が code の matrix から乖離しないようテストで固定した。変異注入(matrix の root 差し替え/loader のcliTools差し替え)で両方向とも実際に赤くなることを確認済み。 -
Skill 導入監査履歴と適用状態 dashboard を追加 (#1248): 「どの worktree に何が入っていて、それが導入時のままか」を横断確認する手段が無く、DB 記録だけを真実とすると手動変更や DB 再作成でずれ、filesystem 走査だけでは actor・失敗・取得元を追えなかった。(A)
status-scanner.tsを新設し、全登録 worktree を横断してinstalled/modified/missing/unmanaged/update_availableを算出する。走査対象 root は receipt のinstall_rootsを正とし、install root 集合(.agents/skills/.claude/skills、#1460)の全 root を見る — 片側 root だけを走査すると.claude/skills側の削除・改変を「健全」と誤報告するため、この画面の存在意義そのものが失われる(install_rootsを持たない旧 receipt は単一 root として読むので half-missing にはならない)。判定は既存assessSkillUninstallの per-file 照合を再利用するので symlink 非追従・走査上限・receipt の自己 fold-in がそのまま効く。走査は同期 I/O のため promise pool は並列化に寄与しないと判断し、上限+worktree 毎の event loop yield+短期 TTL cache で保護した上で、打ち切りをtruncatedとして黙らず報告する。(B)reindex.tsを新設し、receipt からskill_installationsを再構築する。DB 全削除後も復旧でき、復元後の root 集合は receipt のinstall_rootsと一致する。receipt 不在/破損/他 Skill 宛のディレクトリは index せず理由付きで報告し(勝手に index すると裏付けの無い provenance を主張することになる)、payload にも append-only log にも一切書かない。(C)operation-audit.tsの読み取りを拡張し、worktree 省略で横断、operation/result/期間で絞り込み、(recordedAt, id)複合 cursor で改ページできるようにした(同一 ms の tie でも重複・欠落なし)。(D) DB migration v48 でskill_operationsにfrom_version/to_versionと横断 feed 用 index 2 本を追加(ALTER TABLEなので既存行は NULL 遷移で読める。append-only trigger は維持)。遷移は journal から導出するため既存の書き込み経路に変更は無い。(E) API 3 route(GET /api/skills/installations/GET /api/skills/operations/POST /api/skills/reindex)と/skills/installed画面、commandmate skill reindexを追加。Catalog 不達は 500 にせずcatalogAvailable:falseとして更新判定のみ無効化し(走査結果は receipt 由来なので依然有効)、走査失敗を空一覧に退化させず、result=faildのような打ち間違いは 400 で拒否する(黙って全件返すと「失敗ゼロ」に見えるため)。監査は書き込み時 redaction 済みのため署名付き URL や絶対 path は保存されず、応答にも machine-absolute path を含まない。単一 root 走査・receipt 無視の 2 種の変異注入でテストが実際に赤くなることを確認済み。 -
Skill 導入専用の branch・scoped commit・draft PR workflow を追加 (#1247): Skill を install しても、その導入を review 可能な形(commit / PR)に載せる手段が無かった。
git add .や既存 index を使う実装は無関係な作業中ファイル(secret を含みうる)を巻き込むため、index を入力として一切使わない設計にした。(A)src/lib/skills/git-workflow.tsを新設。stage する pathspec は receipt のinstall_rootsから導出する(#1460 以降 install は.agents/skills/<id>と.claude/skills/<id>の両方へ byte-identical に配置されるため、.agents/skillsだけを pathspec にすると PR に導入内容の半分しか載らない。旧 receipt は単一 root として読む後方互換も維持)。stage 後にgit diff --cached --name-status -zを読み直し、owned root 外のパスと receipt inventory に無い追加/変更を fail closed で拒否する(削除は旧版の残骸が消えるケースなので root 内であれば許容)。(B) branch 確定は Install Plan 生成より前に行うpreparephase に分離した。plan は生成時の branch/HEAD に束縛されるため(#1233)、plan 生成後に checkout するとSKILL_PLAN_STALEが例外ではなく既定の結末になる。dedicated_branchは現在 HEAD からskills/install-<id>-v<version>を作って switch し(HEAD 起点なので working tree のファイルは 1 バイトも動かない)、稼働中の Agent session がある worktree では拒否する。開始条件は「既存 staged 変更が無いこと」で、unstaged/untracked の作業中ファイルは巻き込まずそのまま残す。(C) push は force 禁止・default branch 拒否・remote 未設定は install 前のprepare時点で拒否。gh呼び出しはsrc/lib/skills/pull-request-service.tsに閉じ込め、argv 配列のみ(shell 無し)で draft PR を作る。PR 本文には能力・期待効果・risk・宣言 permissions・scripts・提供元・source commit・artifact SHA-256・検証結果・変更ファイル一覧・Agent 互換性を載せ、diff 本文は載せない。machine-absolute path や token は publisher 由来の自由文を含め field 単位で除去する。(D) API はPOST /api/worktrees/[id]/skills/[skillId]/git-workflow(prepare / apply)。apply は branch でも path でもなく server 発行 token だけを受け取り、branch/paths/commitMessage/force等の client 指定は明示的に 400 で拒否する。install 済み・commit 済み・push 済み・PR 未作成が別々の状態として区別でき、apply は再試行しても二重 commit / 二重 PR にならない。(E) CLI を薄い client として配線:commandmate skill install <id> --git <current|dedicated> [--push] [--pr]。--gitに既定値は無く(commit 先は利用者が明示する対象)、--push/--pr単独はエラー、--dry-runでは branch を作らない。承認後に prepare → plan を破棄して再生成 → install → apply の順で進む。実 git リポジトリ(unit)と実 bare remote への実 push(integration)で検証し、単一 root pathspec化・inventory 検証の削除・clean-index 前提の削除・再 plan の省略という 4 種の変異注入でテストが実際に赤くなることを確認済み。 -
Skill 配布 MVP の実ブラウザ E2E(Catalog 閲覧・install/uninstall)を追加 (#1242): 承認 UI の検証が component test(
fetchモック+React tree)止まりで、「実際のページで、実際の viewport で、利用者が何を見て承認しているか」を確認する自動テストが無かった。tests/e2e/skills-catalog.spec.tsとtests/e2e/skills-install.spec.tsを追加し、隔離構成(port 3177・専用 DB・空の非 git scan root)の実ブラウザで固定した: (1) target の repository/branch と install root 両方(#1460)が承認前に提示される、(2) permissions・requirements・scripts・per-file diff・stats が preview に出る、(3) high-risk は承諾チェックまで apply request がブラウザから出ない(request log による negative 検証。「押せなかった」ではなく「送っていない」を主張する)、(4) blocker つき plan が「何も書かれていない+何が阻んでいるか」として描画される、(5) Catalog 取得失敗が空 Catalog に退化せず stale が stale として出る、(6) 390px mobile で承認ボタンが column 内に収まり click でき、横スクロールが発生しない。Catalog と書き込み route はpage.routeで browser 側 stub(E2E サーバは上流 Catalog へ到達できない)。server 側の fail-closed と on-disk allowlist は既存tests/integration/skills-mvp-*.test.tsの担当で、範囲が重ならないよう分けてある。あわせて「manifest が言及しない Agent の互換 view を生成しない」不変条件をcompatibility.test.tsに追加(未計測をunsupportedと表示すれば行っていない計測の主張、commandmate_runtimeと表示すれば Phase 1 に無い機能の主張になる)。変異注入で非空振りを確認済み。 -
orchestrate-monitor に per-poll 状態ログと完了フック配線を追加 (#1533): 監視ループを回しても「総ポーリング数・状態分類の分布・完了判定の根拠」がログに残らず、#1513 G2(誤報 0 の実運用証明)の証拠採取ができなかった。さらに
count_commits/count_uncommittedはスタブ(常に 0)のままで外から供給する手段が無く、verify-completion.shがcommits=0 && uncommitted=0を「タスク未送信」と読む STARTED ガードにより COMPLETE 分岐が実運用で一度も発火しない(完走した worker まで NOT_STARTED と記録される)状態だった。(A)--verboseを追加し、1 ポーリング 1 行の固定フォーマットmonitor[<wid>]: poll <N> -> <STATE> started= streak= commits= uncommitted= verdict=を出力する。verdict だけでなく判定に渡した入力を並べるので「なぜ COMPLETE にならないか」が読める。opt-in で、既定の stdout は 1 バイトも変わらない(変更前のmonitor.shと 5 fixture で stdout を実 diff して同一を確認し、テストでも既定出力を byte 単位で pin した)。(B)--hooks <file>/MONITOR_HOOKSenv で任意のファイルをスタブ定義の後に source し、定義された関数だけを上書きする(未指定時はスタブのまま、片方だけ定義したファイルでももう片方はスタブのまま)。指定したファイルが存在しない場合は黙ってスタブに落ちず exit 2 で失敗する。実運用でそのまま使える参考実装hooks-git.shを同梱(worktree-id をgit worktree list --porcelainから実 checkout に解決しgit log --oneline <base>..HEAD/git status --porcelainで数える。base ref が解決できなければ起動時に stderr へ警告)。(C) SKILL.md に A/B と #1513 G2 の証拠採取レシピ(どのオプションで回せば 4 項目が 1 本のログに揃うか、各項目の取り出しコマンド付き)を追記した。判定ロジック(classify-state.sh/verify-completion.sh/ 介入条件)は不変、bash 3.2 互換と #1527 の決定論的テスト設計も維持。「フック有りで COMPLETE 到達/無しで到達不能」を両方向で固定し、5 種の変異注入(既定 verbose ON・フックを先に source・--hooks無視・poll 行の書式変更・参考フックの commit 数 0 固定)でテストが実際に赤くなることを確認済み。
Fixed
- uninstall した Skill を再 install すると exit 0 のままファイルが1つも書かれない問題を修正 (#1552): CLI は per-request の idempotency key を送らないため、journal は key を binding(actor / operation / worktreeId / skillId / version / plan hash)から導出する。uninstall するとその入力はすべて元の値に戻る(install root が消えるので
currentTreeHashも、receipt が決定的なのでreceiptDigestも初回と一致する)ため、次の install は初回とまったく同じ key を導出し、beginSkillOperationが初回の SUCCEEDED entry を replay として返していた。結果、CLI はInstalled …と表示して exit 0 で終わるのに 1 バイトも書かれず、skill statusはinstalled:falseを返す(成功報告と status が矛盾する)。replay 応答は index 由来で、uninstall 済みなのでinstall: null→ CLI が plan 側の単一 root にフォールバックし、成功メッセージの install root が 2 個から 1 個に減るのが唯一の外形的な兆候だった。journal retention は 7 日なので、一度 uninstall した Skill は当該 entry が消えるまで再 install できない。uninstall/route.tsにも同じ欠陥があることを実測で確認した(uninstall plan の binding は snapshotId を含まず全項目が決定的なので、install → uninstall → install → uninstall の 2 回目の uninstall が初回の記録を replay し、Removed …と報告しながらファイルを 1 つも消さない)。対策としてsrc/lib/skills/operation-replay.tsを新設し、filesystem commit を主張する entry に限り、その主張が今も worktree に対して真かを replay 前に照合する: install は primary root の receipt が実在し digest が記録と一致すること、uninstall はその receipt が存在しないこと。偽になった entry は supersede(削除して新規 operation を開始)し、両 route の client-key 分岐とbeginSkillOperationの両方に適用した。commit 前の entry(PREPARING / rollback 済み failure)は on-disk の主張を持たないため従来どおり in-progress / failed として replay され、PREPARING は predicate に関わらず supersede しない(実行中の operation の crash recovery 記録を落とさないため)。「同一リクエストのネットワーク再送は 1 回しか実行されない」という replay 本来の意図は既存テストごと維持されている。Issue 本文は根本原因を install route の client-key 分岐(323-328 行)と記述しているが、CLI はそもそも key を送らないためその分岐は通らず、実際の発生点はbegun.replayed側(374-376 行)である(不具合時に journal entry が新規作成されないという本文の観察とも一致する)。対策案 2(uninstall 成功時に対になる install entry を無効化)は採らなかった: uninstall route は install plan の hash を持たないため対の key を導出できず journal 全走査が必要になるうえ、手動削除で payload が消えた場合を救えないため、on-disk 照合が両方を同時に扱える。install route のみ・uninstall route のみ・guard を常時 false にする 3 種の変異注入で、追加した回帰テストと既存 replay テストがそれぞれ実際に赤くなることを確認済み。 - Skill ドキュメントの手動 rollback 手順が install root を1つしか消しておらず、実行すると復旧できない状態になる問題を修正 (#1242):
docs/user-guide/skills.mdが #1460 以前の単一 root 前提(.agents/skillsのみ)のまま残っていた。§4-2 の手順どおりrm -rf <worktree>/.agents/skills/<id>だけを実行すると、.claude/skills/<id>が残るため Claude Code からは Skill が見え続ける一方、再 install は残った側の destination が既存であるためSKILL_INSTALL_DESTINATION_EXISTS(409)で拒否されるという、利用者が自力で抜けられない状態になっていた。両 root を消す手順へ訂正し、正確な root 一覧を receipt のinstall_rootsで確認する方法を併記した。あわせて support matrix(install 先・変更範囲の保証・残留物の掃除・committed_reconcilingからの復旧・worktree 再作成後の復旧)を両 root へ訂正し、UI 導線を「未接続」・導入済み一覧を「未提供」と書いたまま §3-1 の記述と自己矛盾していた箇所を #1431 / #1440 の実態へ、Agent 対応状況を 2026-07-26 の実機実測(Claude Code 2.1.220 は.claude/skillsを読み.agents/skillsは読まない/Codex CLI 0.145.0 は.agents/skillsを読むが slash command としては露出しない/Gemini・OpenCode・vibe-local は未計測)へ更新した。docs/qa/skills-mvp-uat-report.mdも第2回判定として、人手検証3件のうち実機ブラウザ UAT を自動 e2e で充足・実 Agent discovery を実測済みへ降格し、残る保留を初見参加者 UX 調査1件に絞った。 - orchestrate-monitor の
monitor-resend.test.tsが壁時計 timeout に判定を委ねていた問題を修正 (#1527): ループが COMPLETE 以外で終わらないため、テストはspawnSyncの 2.5 秒 timeout で監視ループを kill し、その時点までに何ポーリング進めたかで判定していた。マシン負荷次第で 2 回目のポーリングに届かず「resend budget spentを含むこと」が低頻度で落ちる一方、否定的アサーション2件(expect(tmuxCalls).toEqual([]))はループが0回でも無条件で PASS する偽の緑だった。(1)monitor.shに--max-polls N(既定 0 = 従来どおり全ワーカー COMPLETE まで継続)を追加し、ループが内側から決定論的に exit 0 できるようにした。停止条件のみの追加で状態判定・介入条件は不変、bash 3.2 互換も維持。(2) テスト側は--interval 0+--idle-threshold 1+ ケースごとに必要なポーリング回数を定数化し、launcher shim の呼び出し回数・exit 0・--max-polls到達ログを検証してからでないと空を主張しない gate を通す。ループを1回で打ち切る/必ず介入する変異を注入すると当該テストが実際に赤くなることを確認済み。実行時間も 12.5s → 2.8s に短縮した。