Skip to content

enhancement(doctor): セルフインストール投影の鮮度を検出できない — ブランチ前進後 build 前に着地済み修正が無音で無効化される #2851

Description

@j5ik2o

エレベーターピッチ

ブランチ前進のたびに手作業の再ビルドを覚えていなくても、実行される自分自身が古いことに気づきたい
自己開発リポジトリで Amadeus を回す開発者・エージェント 向けの、
Amadeus セルフインストール鮮度チェック というプロダクトは、
ローカルの読み取り専用診断面 です。
これは 正本 packages/framework/core/ と実行面 .claude/ などの投影ツリーの乖離を、実行前に loud に検出 ができ、
CI の隔離2回ビルド再現性検査 とは違って、
ローカルの実行直前に、着地済み修正が実際に効いているかを本人へ返す こと が備わっている。

背景・対象範囲

dist/ とセルフインストール面(.claude/ / .codex/ など)は source-only 境界により未追跡のローカル生成物である。したがってブランチを前進させても、bun run build を実行するまで実行面は前進前のコードのまま残る。

このとき、その worktree で動くエージェントは古いツリーのツールを実行し続けるが、それを知らせる loud なローカル信号が無い。対象範囲は amadeus-plugin.tsstatus / doctor が提供するローカル読み取り専用診断面と、packages/framework/core/tools/<harness>/tools/ の投影鮮度。CI 側の再現性検査・source-only:check は対象外(後述のとおり別の面を守っている)。

根拠・実測証拠

観測環境: macOS 26.5.0 / bun 1.3.13 / ハーネス Claude Code / worktree マネジメント用セッション
対象リビジョン: 前進前 c909b6130 → 前進後 da3f19e9forigin/main へ fast-forward、自分のコミット 0・15コミット取り込み)

fast-forward 直後、再ビルド前の実測:

  1. 正本と実行面のツール差分 — packages/framework/core/tools/*.ts.claude/tools/*.tscmp で全件突き合わせ、130件中3件が差分amadeus-harness.ts / amadeus-plugin.ts / amadeus-stage-stats.ts)。それぞれ [plugin-harness-dir-token/bolt-1/fix-2790-plugin-harness-dir-token] resolve {{HARNESS_DIR}} at the plugin staging seed #2811 / feat(stage-stats): CG ウィンドウの観測可能区間と帰属不能残余の遡及集計 (#2695) #2809 の着地先を含む。

  2. 実行面が古いことの直接証拠 — リポジトリ外 scratch から .claude/tools/amadeus-plugin.ts を in-process import して stagingHarnessDirOf を呼ぶと TypeError: mod.stagingHarnessDirOf is not a function。同シンボルは正本 packages/framework/core/tools/amadeus-plugin.ts:659 に export 済み(seedBytesForHarness:669stagingEntryState:681)。つまり [plugin-harness-dir-token/bolt-1/fix-2790-plugin-harness-dir-token] resolve {{HARNESS_DIR}} at the plugin staging seed #2811 の修正は実行時に一切効いていなかった。

  3. 症状 — 合成済み .claude/plugins/pr-convergence/stages/pr-convergence.md が修正前の出力のまま。正本の 5 箇所の {{HARNESS_DIR}} に対し、4 箇所はハーネス名が脱落した bun plugins/pr-convergence/tools/...(本来 .claude/plugins/...)、1 箇所(:180)は生トークン bun {{HARNESS_DIR}}/tools/amadeus-sensor.ts が残存。後者は fix(pr-convergence): plugin センサー手動発火の HARNESS_DIR リテラル化を修正 #2798 が修正対象とした行そのもの。

  4. 読み取り専用診断面の応答 — bun .claude/tools/amadeus-plugin.ts statusPlugins: 2 installed, 2 composed, revision 0(exit 0)を返すのみでドリフトを報告しない。ハンドラは amadeus-plugin.ts:1382-1387installed / composed / revision の3値を返すだけで、バイト比較を一切行わない。

  5. 再ビルド後 — bun run build(exit 0)後に再測定し、ツール差分 0/134{{HARNESS_DIR}} 残存 0、5 パスすべて .claude/... へ正しく解決、追跡ファイルへの変更なし(git status --porcelain 空)。bun run source-only:check は前後とも clean(exit 0)。

機序

doctor のプラグイン節にはステール検知が実在する(amadeus-plugin.ts:1352stagingEntryState(staging, source) !== "identical"staledrift 行)。ただしその比較対象は plugin staging (<harness>/.amadeus-plugin-src/<name>) と authoring source (plugins/<name>) であり、packages/framework/core/tools/<harness>/tools/ の投影鮮度は対象外である。したがって今回ステールだった core tools 3件は、この検知の守備範囲の外にある。

さらに構造的な要因として、検知器自身が投影ツリーから読み込まれる。stagingEntryState は source を seedBytesForHarness を通して読むため(:681 以降)、投影面の当該関数が古い世代であれば「古い変換 vs 古い出力」を比較して identical と判定しうる。すなわちドリフト検知器がドリフトした面の一部であるという自己参照の死角が成立しうる。

未特定として明記する: 再ビルド前に doctor を実行しなかったため、「doctor が実際に沈黙した」ことは観測していない。確定しているのは (i) doctor のプラグイン節が core tools を比較対象に含めないこと(静的読解)、(ii) status が沈黙したこと(実測)の 2 点である。自己参照の死角は上記コード構造からの推論であり、決定的再現は未実施。

期待結果・完了条件

ローカルの読み取り専用診断で、投影ツリーが正本から遅れていることを loud に返せること。

  • 完了条件1: 正本 packages/framework/core/ に対し投影ツリーが遅れている場合、ローカル実行の読み取り専用コマンドが drift として報告し、非ゼロ exit または明示の drift 行で可視化する。
  • 完了条件2: 検知は投影面のコードに依存せず判定できる(自己参照の死角を作らない)。手段は設計段で決める。
  • 完了条件3: 落ちる実証 — 投影ツリーのツール1件を意図的に旧世代へ差し替えた状態で検知が実際に赤くなることを示す。
  • 完了条件4: 正常状態(build 直後)で誤検知しないことを、対象コーパスへの実述語適用で示す。

影響・価値

影響は「着地済みの修正が効いていないまま作業が進む」こと。今回は #2811 / #2798 / #2809 の3修正が実行時に無効なまま、合成済み plugin prose が誤ったパスを保持していた。ルート直下に plugins/ が存在する自己開発リポジトリでは bun plugins/... が偶然解決するため、症状が表面化しにくい。

価値は、bun run build の実行忘れという運用依存を、機械が検出する信号へ置き換えること。project.md の Mandated「正本変更後は bun run build を実行し…」は人間・エージェント側の義務として書かれており、履行漏れを検出する機構は存在しない。

代替案と非採用理由

  • CI の隔離2回ビルド再現性検査に任せる — 非採用。守っている面が違う。CI はクリーン checkout からの独立ビルド結果と正本の性質を検証するもので、ローカル worktree の投影が古いまま実行されている状態は検出しない。実際、今回 source-only:check はステール状態でも clean(exit 0)だった。
  • 運用ルール(ブランチ前進のたびに build を挟む)で対処する — 非採用。今回の見落としがまさにその運用の履行漏れであり、同じ手段の反復は再発を防がない。org.md の検証劇場の禁止と同じ理由で、人間の記憶に依存する保証は保証ではない。
  • status にバイト比較を足す — 部分的に妥当だが単独では不十分。status の現契約は件数の報告(:1382-1387)であり、そこへ判定を足しても検知器が投影面に載る問題(自己参照の死角)は解決しない。手段の選定は設計段へ委ねる。

関連 Issue・PR・intent

重複検索: gh issue list --state allplugin status stale drift / self-install stale build 反映 / staging compose drift の3クエリで実施し、open な重複なし。open PR も stale|drift|self-install|promote|plugin status で 0 件。

初期分類

  • 種別: enhancement。issue-type-decision の判定順に従い、(3) の bug ではない — 投影ツリーの鮮度を検出すると約束した契約は存在せず、status は件数報告という自身の契約どおりに動作している。履行義務は project.md の Mandated として人間・エージェント側に課されており、実行可能な成果物が合意済み契約に違反しているわけではない。(4) に該当し、新しい検査を追加する提案である。
  • 優先度: P2(重要だが急がない)。回避策として bun run build の実行が確立しており、CI 側は別面で守られている。

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2重要だが急がないenhancementNew feature or requestin-progressintent 起動中

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions