Skip to content

fix: operator-only VC timeout の occurrence と実適用 budget を root review artifact に保持する (#2897) - #2901

Merged
squne121 merged 3 commits into
mainfrom
worktree-issue-2897-operator-only-vc-timeout-occurrence-budget-identity
Oct 3, 2026
Merged

squne121 merged 3 commits into
mainfrom
worktree-issue-2897-operator-only-vc-timeout-occurrence-budget-identity

Conversation

@squne121

@squne121 squne121 commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner

Summary

operator-only VC timeout が発生したとき、どの occurrence がどの実適用予算で timeout したかを root review artifact から後で特定できるようにします(Issue #2897)。新しい estimator や ledger は作らず、既存 result が持つ timeout_provenance / execution_key_hash / dedup 情報 / canonical occurrence index を、readiness 変換・review merge・durable artifact の各境界で落とさず運ぶだけの変更です。

  • contract_readiness_check.py: 既存 result item の occurrence index、execution_key_hash、dedup 情報、timeout_provenance(5 項目に限定)を errors[].source_payload へ allowlist で引き継ぎ、diagnostic_report.canonical_plan_digest と pre-filter の results_count を readiness result の top level に載せます(not_computed の場合は digest は null のまま補完しません)。
  • check_issue_contract.py: human_judgment の timeout errors から TIMEOUT_DIAGNOSTICS_V1(最大 16 件、超過は truncated_count / total_timeout_occurrences、直列化 16 KiB 以下)を投影し、REVIEW_ISSUE_RESULT_V1 の additive な optional field timeout_diagnostics として載せます。binding を検証できない occurrence は attribution: unknown と列挙 reason code を記録し、別 VC や再計算値で補完しません。
  • run_root_review_pipeline.py: production 変更なし(test-only integration point)。merged review result は child stdout 経由でそのまま transport の semantic_result と full review artifact になるため、配線は不要と判明しました。

structured_blockers に deterministic blocker は追加せず、failure_class / operator-only route / Compact V2 wire / estimator / budget 計算は変更していません。baseline_vc_preflight.py も変更していません。

受け入れ条件の達成状況

  • AC1: 達成。readiness が bounded な provenance と canonical occurrence index を引き継ぎ、merge 後の診断に body SHA・occurrence index・block-relative line・command_hash が残ることをテストで確認しました。blocker は捏造せず、failure_class も従来どおりです。
  • AC2: 達成。同一 command_hash・同一 line・同一 AC の別 fenced block のうち timeout した方の canonical index にのみ帰属します。dedup replay と実 execution を区別し、digest 欠落・不一致、provenance 欠落、execution key 欠落、dedup 参照不正、index 範囲外は unknown と reason code になり、新しい gate にはなりません。body SHA 不一致は従来どおり fail-closed で診断も生成しません。
  • AC3: 達成。一時 readiness artifact 削除後に、verified transport artifact の readback と full review artifact の双方から同じ診断を取得できます。snapshot 取得後に history を変更しても保存値は snapshot 時点の予算のままで、transport は timeout: false / exit_code: 0 のままです。
  • AC4: 達成。20 件の timeout でも 16 件に切り詰め truncated_count を保持し capture_failure になりません。診断に raw command / stdout / env / runner_env_delta / minimal_context は含まれません。通常成功・非 timeout human_judgment・outer 側 timeout・Compact V2 wire は変化しません。

検証コマンド結果

Issue の Verification Commands 7 件(node id は Issue と完全一致)は実装前(本番スクリプトを stash した状態)に fail、実装後に全件 pass しました。

$ uv run --locked pytest .claude/skills/issue-contract-review/tests/test_readiness_timeout_provenance_passthrough.py::test_readiness_passes_bounded_provenance_and_occurrence_index  -> pass
$ uv run --locked pytest .claude/skills/review-issue/tests/test_timeout_diagnostic_projection.py::test_inner_timeout_preserves_bounded_identity_without_blocker  -> pass
$ uv run --locked pytest .claude/skills/review-issue/tests/test_timeout_diagnostic_projection.py::test_same_hash_same_line_other_block_only_timed_out_occurrence_attributed  -> pass
$ uv run --locked pytest .claude/skills/review-issue/tests/test_timeout_diagnostic_projection.py::test_mismatch_or_missing_binding_records_unknown_without_new_gate  -> pass
$ uv run --locked pytest .claude/skills/issue-refinement-loop/tests/test_root_review_timeout_diagnostics.py::test_production_path_diagnostic_survives_cleanup_in_transport_and_full_artifact  -> pass
$ uv run --locked pytest .claude/skills/issue-refinement-loop/tests/test_root_review_timeout_diagnostics.py::test_snapshot_budget_unchanged_after_history_mutation  -> pass
$ uv run --locked pytest .claude/skills/issue-refinement-loop/tests/test_root_review_timeout_diagnostics.py::test_no_leak_and_non_timeout_routes_unchanged  -> pass

テストは本番の変換経路(fake は baseline_vc_preflight.run_command() の戻り値と、子 process 起動を同じ script の main() の in-process 呼び出しへ置き換える adapter のみ)を通します。完成済みの timeout_provenance や merged result を直接注入してはいません。unknown 系のネガティブケースだけは、実 artifact から binding 項目を削除または改ざんして劣化 producer を再現します。

Allowed Paths 遵守

変更は Issue の Allowed Paths 6 件のみです(本番 2 件 + 新規テスト 3 件、run_root_review_pipeline.py は変更なし)。

Checks

  • pnpm typecheck / pnpm lint / pnpm test(108 files, 1943 tests)/ pnpm build: すべて pass
  • uv run --locked pytest .claude/skills/gemini-cli-headless-delegation/tests/test_model_routing.py: 24 passed
  • .claude/skills/issue-contract-review/tests + .claude/skills/review-issue/tests + .claude/skills/issue-contract-review/scripts/tests: 1392 passed, 1 skipped(実装後の本番変更込みで実行)
  • .claude/skills/issue-refinement-loop/tests: 新規テスト含め pass。test_command_registry.py::TestAuthorityTransportPrivilegedExecutorRealSubprocessDispatch の 3 件は main(本変更なし)でも同様に fail する既存の環境依存の失敗で、本変更とは無関係です。
  • .github/ci/python-test-plan.json は 3 つの tests ディレクトリを既に登録済みのため追加登録は不要です。

OWNER REQUEST_CHANGES への対応(https://github.com/squne121/loop-protocol/pull/2901#issuecomment-5974155871)

  • P1: timeout_diagnostics 単体の 16 KiB ではなく、実 writer と同じ json.dumps + 末尾 newline + utf-8 の merged result 全体の byte 数が STDOUT_CAP(65,536)以下になるよう、optional diagnostic 側のみを縮小します(occurrences を減らし truncated_count / total_timeout_occurrences を整合)。header すら入らない場合は optional field を省略し、capture_failure にはしません。verdict / failure_class / blockers は不変です。
  • P2: enum-like 値の判定は型確認(str)を先に行い、list / dict でも TypeError にせず既存の unknown 劣化に流します(producer / consumer の両境界)。
  • P2: dedup replay の source は、自身の binding(plan digest / index range / provenance / execution key)を検証できた executed occurrence のみ登録し、replay は execution key と budget provenance の一致も必要です。不正な source を参照する replay は dedup_binding_invalid になります。
  • 回帰テストは既存の 3 test ファイル内に追加し、境界ケース(cap 直下 / cap ちょうど / header 省略)、malformed enum、source 不正 dedup、正常 dedup の positive control を固定しています。

Runtime Verification Evidence

AC3 は動作検証 AC(decision: immediate)です。現在の PR head 8b3853a4318594f7f8a8ed18f1fa5b7952fe77a4 に束縛して生成した証跡ログ artifacts/runtime-verification-AC3-20261003T230514Z.log を inline 引用します(SKIP / fallback を PASS として扱っていません)。

# Runtime Verification AC3 (Issue #2897) 20261003T230514Z
# worktree: /home/squne/projects/LOOP_PROTOCOL/.claude/worktrees/issue-2897-operator-only-vc-timeout-occurrence-budget-identity
# head: 8b3853a4318594f7f8a8ed18f1fa5b7952fe77a4 (clean working tree: 0 changes)
$ uv run --locked pytest .claude/skills/issue-refinement-loop/tests/test_root_review_timeout_diagnostics.py::test_production_path_diagnostic_survives_cleanup_in_transport_and_full_artifact -v
.claude/skills/issue-refinement-loop/tests/test_root_review_timeout_diagnostics.py::test_production_path_diagnostic_survives_cleanup_in_transport_and_full_artifact PASSED [100%]
============================== 1 passed in 0.33s ===============================
exit=0
$ uv run --locked pytest .claude/skills/issue-refinement-loop/tests/test_root_review_timeout_diagnostics.py::test_snapshot_budget_unchanged_after_history_mutation -v
.claude/skills/issue-refinement-loop/tests/test_root_review_timeout_diagnostics.py::test_snapshot_budget_unchanged_after_history_mutation PASSED [100%]
============================== 1 passed in 0.48s ===============================
exit=0
$ uv run --locked pytest .claude/skills/issue-refinement-loop/tests/test_root_review_timeout_diagnostics.py::test_large_review_result_with_timeouts_is_not_turned_into_capture_failure -v
.claude/skills/issue-refinement-loop/tests/test_root_review_timeout_diagnostics.py::test_large_review_result_with_timeouts_is_not_turned_into_capture_failure PASSED [100%]
============================== 1 passed in 0.65s ===============================
exit=0

Schema Change Applicability

  • decision: not_schema_change
  • reason: REVIEW_ISSUE_RESULT_V1 は additionalProperties: true で、optional field timeout_diagnostics を追加するだけのため schema ファイルは変更していません。
    (スキーマ変更の適用可否を判定するセクションです)

Schema Consumer Inventory

N/A
reason: schema ファイルの変更がないため。consumer(reviewer_transport.validate_semantic_result_schema() / compact_review_result)が optional field を受理することは新規テストで確認済みです。
(スキーマ変更がある場合の consumer 一覧を記載するセクションです)

Safety Claim Matrix

本変更は診断情報の保持のみで、権限・隔離・hook・secret の境界は変更しません。

Claim Implemented? Not controlled Evidence Follow-up
診断に raw command / env / stdout / stderr を含めない 実装済み - test_no_leak_and_non_timeout_routes_unchanged(キー集合と禁止文字列の検査) -
診断の直列化サイズを 16 KiB 以下に制限する 実装済み - 同テスト(20 件の timeout を 16 件に切り詰め) -
診断欠落や unknown を新しい workflow gate にしない 実装済み - test_mismatch_or_missing_binding_records_unknown_without_new_gate -

(safety-sensitive な変更の有無を判定するセクションです)

Notes

IMPLEMENTATION_SCOPE_COVERAGE_V1:
  schema_version: "IMPLEMENTATION_SCOPE_COVERAGE_V1"
  issue_number: 2897
  issue_body_sha256: "sha256:ff14d7ebc20f74cdd9254c99c0431ae29dd1ab491b002fa3a38d015bfbd9f7d2"
  normalized_scope_manifest_sha256: "sha256:021f17d7a2fa1651e011624d183714d04492630baed1ac7e195c658c1749178e"
  pr_head_sha: "1b8159a6b8f5371907e3c8a179fcb4a2537adbe0"
  scope_manifest: {"acceptance_criteria": ["readiness producer が既存 result の timeout_provenance・execution_key_hash・dedup 情報・canonical occurrence index を bounded な allowlist として source_payload へ引き継ぎ、synthetic inner command-level timeout を readiness human_judgment として merge した結果が、body SHA、occurrence index、block-relative line、command_hash を含む bounded な診断を保持する。structured_blockers に deterministic blocker を捏造せず、failure_class / operator-only route は従来どおり。", "一時 readiness artifact の削除後に、verified transport artifact の canonical readback と full review artifact の双方から、同じ bounded diagnostic(canonical VC plan digest と、当該 result に適用された既存 timeout_provenance の bounded 項目を該当 occurrence に結合したもの)が取得できる。immutable snapshot 取得後に history が変化しても保存値は変わらない。duration_ms や現在の再計算値から budget を復元しない。provenance / binding が欠落する場合は推測せず unknown + reason code を記録する。transport timeout: false / exit_code: 0 の inner timeout を outer timeout に誤分類しない。", "同一 command_hash・同一 block-relative line・同一 AC を持つ別 fenced block の 2 occurrence のうち片方だけが timeout したケースで、診断は timeout した occurrence の canonical index にのみ関連付く。実 execution source と dedup replay occurrence を区別する。判別不能時は診断内で attribution: unknown + bounded reason code とし、別 VC へ誤帰属させない。unknown になる条件と reason code(列挙)は次のとおり: plan_digest_missing(readiness トップレベルの digest が null)、plan_digest_mismatch(error が持つ digest と readiness トップレベルの digest が不一致)、provenance_missing(timeout_provenance 欠落)、execution_key_missing(execution_key_hash 欠落)、dedup_binding_invalid(dedup replay の source_result_index が results_count 以上、または参照先が不整合)、occurrence_index_out_of_range(occurrence_index が results_count 以上)。merge 段はこれらを readiness result 内の値同士の比較だけで判定し、review result の parsed_vc_commands(別 parser 由来で index 整合が保証されない)は参照しない。body SHA 不一致は既存の fail-closed(merge_readiness_into_review_result() が ValueError で merge を拒否する挙動)を変更せず、診断も生成しない。この欠落は新しい workflow 停止理由にならず、既存の timeout による operator route は維持される。", "診断は上記の数値上限を満たし(17 件以上の timeout occurrence を持つ入力でも上限内で truncated_count を保持し、transport capture_failure にならない)、raw secret / env / stdout / stderr / raw command / minimal_context / runner_env_delta を含まない。通常成功・非 timeout human_judgment・outer transport failure の既存 route・compact V2 wire・body-SHA mismatch 等の既存 integrity 判定は変わらず、診断 field を持たない legacy payload / 通常成功に新しい必須 gate を追加しない(focused regression tests)。"], "allowed_paths": [".claude/skills/issue-contract-review/scripts/contract_readiness_check.py", ".claude/skills/issue-contract-review/tests/test_readiness_timeout_provenance_passthrough.py", ".claude/skills/issue-refinement-loop/scripts/run_root_review_pipeline.py", ".claude/skills/issue-refinement-loop/tests/test_root_review_timeout_diagnostics.py", ".claude/skills/review-issue/scripts/check_issue_contract.py", ".claude/skills/review-issue/tests/test_timeout_diagnostic_projection.py"], "change_kind": "workflow", "goal_ref": "operator-only VC timeout の根拠となる occurrence と実適用予算を root review artifact から追跡可能にする(#2860 の独立 follow-up)", "in_scope": ["bounded の数値上限: timeout_diagnostics.occurrences は最大 16 件(超過分は件数のみ truncated_count と合計 total_timeout_occurrences で保持)、各 field は型制約(hash は sha256: 付き hex 文字列、budget は整数、source / attribution / reason_code は列挙値)、診断全体の直列化サイズは 16 KiB 以下とする(reviewer_transport.STDOUT_CAP = 65,536 bytes を十分下回る。超過すると capture_failure で診断自体を失うため)。", "check_issue_contract.py(review merge): human_judgment の timeout errors[] から bounded な timeout diagnostic(pinned body SHA、canonical plan digest、occurrence index、block-relative line、command_hash、必要最小限の execution identity、適用された budget provenance、attribution status と reason code)を投影し、REVIEW_ISSUE_RESULT_V1 の additive な optional field timeout_diagnostics(自身の schema_version: \"TIMEOUT_DIAGNOSTICS_V1\" を持つ)として review result に載せる。この新規 optional key の追加は Stop Condition の対象ではない(additionalProperties: true)。structured_blockers に deterministic blocker を捏造せず、failure_class は従来どおり。", "contract_readiness_check.py(readiness producer): map_preflight_result_to_errors() が、既存 result item の次の項目だけを allowlist 的に errors[].source_payload へ引き継ぐ。(a) canonical occurrence index(error filtering 前の results 配列内の位置)、(b) block-relative line(補助表示として座標系を明記)、(c) command_hash、(d) execution_key_hash、(e) dedup replay か実 execution source かの区別(dedup.source_result_index)、(f) timeout_provenance の bounded な数値・既知 source 項目、(g) canonical VC plan digest と error filtering 前の results 件数 results_count(readiness producer が既存 preflight payload の diagnostic_report.canonical_plan_digest と results の長さを読み、readiness result のトップレベルに引き継ぐ。--expected-plan-digest は子 baseline_vc_preflight.py の argv であり readiness CLI の入力ではないため source にしない。merge 段で body や現在の history から再計算しない)。early-return / static 経路で diagnostic_report が not_computed の場合、digest は欠落(null)として引き継ぎ、補完しない。baseline_vc_preflight.py の既存 result だけで足りない場合に限り scope delta で判断する(本 Issue では変更しない)。", "focused tests(下記 Allowed Paths の 3 件)。production の変換経路(execution boundary の bounded fake → real result builder → real readiness conversion → real review merge → 既存 artifact writer → temporary workspace cleanup → persisted bytes / verified readback)を通す。完成済み timeout_provenance を直接 fixture に注入して downstream merge だけを検証するテストは本 Issue の検証として認めない。fake にしてよいのは実時間待機等の末端 execution boundary のみ。許可する seam は次の 2 種に限る。(1) baseline_vc_preflight.run_command() の戻り値(timeout は既存 sentinel exit_code == -1 かつ stderr == \"timeout\")の差し替え。(2) process 起動 adapter の in-process 化: production は run_checker_pipeline_once() が run_check_issue_contract() / run_contract_readiness_check() / run_merge_readiness() を subprocess で起動し、readiness は baseline_vc_preflight.py を子 process で起動し、transport は run-checker-attempt を子 process で起動する。テストはこれらの「起動 mechanics」だけを、同じ実 script の main() を in-process で呼び stdout / exit code を捕捉する adapter に差し替えてよい(子 process 内の run_command() 差し替えを成立させるため)。result builder・readiness 変換・review merge・transport artifact builder と verify_artifact / readback・artifact writer は実物でなければならず、完成済み timeout_provenance や merged result を直接注入してはならない。上記 seam で表現できず baseline_vc_preflight.py への test hook 追加が必要と判明した場合は Stop Conditions に従う。", "run_root_review_pipeline.py(root pipeline): merged review result(timeout_diagnostics を含む)は child stdout に出力され、verified transport が semantic_result として verbatim に保存し、full review artifact も同じ dict から作られる。したがって診断は一時 readiness artifact の削除前、かつ semantic_result の serialize 前に merged result へ結合済みであること。本 script の production 変更は、plan digest / occurrence binding を readiness 実行と merge 実行へ受け渡す配線(run_contract_readiness_check() / run_merge_readiness() 周辺)が必要な場合に限る。配線が不要と判明した場合は production 変更なしの test-only integration point とし、その旨を PR に記載する。", "帰属不能時の挙動: 識別・binding が欠落または不一致の場合、診断内で attribution: unknown と bounded reason code を記録する。別 VC や現在再計算した budget で補完しない。", "識別子: 「line + command_hash」を occurrence の一意識別子としない。error filtering 前の canonical result 順の index を主識別子とし、line(block-relative)は補助表示とする。実 execution source と dedup replay occurrence を混同しない。"], "schema_version": "IMPLEMENTATION_SCOPE_MANIFEST_V1"}

…artifact に保持する (#2897)

readiness 変換・review merge・root pipeline の 3 境界で落ちていた timeout の
identity を、既存 result の値だけを運ぶ bounded な診断として保持する。

- contract_readiness_check.py: 既存 result item の occurrence index・
  execution_key_hash・dedup 情報・timeout_provenance を allowlist で
  source_payload に引き継ぎ、canonical plan digest と results_count を
  readiness result の top level に載せる
- check_issue_contract.py: human_judgment の timeout errors から
  TIMEOUT_DIAGNOSTICS_V1 (最大 16 件、16KiB 以下) を投影し、
  REVIEW_ISSUE_RESULT_V1 の optional field timeout_diagnostics として載せる
- run_root_review_pipeline.py は production 変更なし (merged result が
  そのまま transport の semantic_result と full artifact になる)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Preview URL

Preview removed (PR #2901 closed).

@squne121

squne121 commented Oct 3, 2026

Copy link
Copy Markdown
Owner Author

判定:REQUEST_CHANGES

方向性は妥当ですが、マージ前に直したい点が3件あります。 最も重要なのは、診断を保存するための変更が、逆にレビュー結果の転送失敗を起こし得ることです。残り2件も、セキュリティ機構の追加ではなく、診断情報が不完全でもレビュー処理を壊さず、分からないものを正しく unknown にする修正で対処できます。

確認中に HEAD が更新されたため、最終的には ef0acf2e94e424e8fc3ed349f81c90706192c548 を対象に確認しました。PR は OPEN / Draft です。初回確認した 1b8159a… から今回の timeout 関連の本番変更は変わっておらず、以下の指摘は最新 HEAD にも残っています。

優先度 | 指摘 | 主な影響 -- | -- | -- P1 | 診断単体の16 KiB制限では、レビュー結果全体の64 KiB制限を守れない | 診断追加によって capture_failure を発生させ、結果を失う可能性 P2 | 列挙値の型不正で TypeError が発生し、unknown に劣化しない | 任意の診断メタデータがレビュー処理の新しい障害原因になる P2 | dedup の参照元が不正でも replay 側を binding_verified にできる | 「どの実行の、どの予算か」を検証済みとして誤表示する

以下の再現は、取得コードの該当純粋関数を転記した局所検証と合成入力によるものです。実 Issue での障害を観測したという意味ではなく、リポジトリ全体の pytest/完全な本番経路は、この環境では再実行していません。

1. P1:診断追加で、元は転送できたレビュー結果が capture_failure になる

対象:
.claude/skills/review-issue/scripts/check_issue_contract.py
build_timeout_diagnostics() のサイズ調整と、merge_readiness_into_review_result() での添付処理。

実装は timeout_diagnostics 単体を最大16件・16 KiB以下にしています。しかし、その直後に送られるのは診断だけではなく、parsed_vc_commands、blocking_issues、その他の情報を含む merged review result 全体です。診断側の制限処理には、その既存部分のサイズが入っていません。

一方、root pipeline の child は print(json.dumps(merged)) で全体を stdout に出し、transport は stdout が 65,536 bytesを超えると capture_failure にします。このため「診断が16 KiB以下」から「転送失敗を起こさない」は導けません。

局所検証の結果

本番と同じ json.dumps() と末尾改行を使い、既存レビュー結果を大きめにした合成入力で確認しました。

診断追加前のレビュー stdout       59,338 bytes
timeout_diagnostics 単体          10,348 bytes
診断追加後のレビュー stdout       69,711 bytes
transport の上限                 65,536 bytes

診断単体は上限内なのに、追加前は通る結果が追加後には上限超過します。 この入力はサイズ境界を検証するための合成レビュー JSON で、実際の parser がこの値を出したことまで確認したものではありません。

しかも既存 transport では、deterministic backend でも capture_failure は再試行対象です。したがって、診断の追加が原因で結果を失うだけでなく、不要な再実行へ進む可能性があります。診断追加の目的と逆方向の回帰です。

推奨修正

最終的な送信 JSON 全体の残り容量に合わせて、任意の診断部分を縮めてください。

既存の verdict、failure_class、blocker を保持したまま、診断の occurrences を減らし、truncated_count を更新する形が適切です。診断のヘッダーすら収まらない境界では、任意フィールドの省略規則を用意する方が、レビュー全体を転送失敗にするより有用です。JSON の生バイト列を途中で切ったり、根拠なく transport 上限だけを引き上げたりする修正は勧めません。

Python 公式仕様でも、json.dumps() は文字列を返し、ensure_ascii や separators により出力表現が変わります。容量判定は、実際の送信設定で直列化・エンコードした全体に対して行う必要があります。Python documentation

回帰テストは、現在の「20件の短い timeout」だけでなく、追加前は64 KiB未満、素直に診断を足すと64 KiB超過するケースを既存の production-path test に加えるのが効果的です。PR 本文の「20件でも capture_failure にならない」という確認だけでは、この境界をカバーできていません。

2. P2:診断の型不正が、unknown ではなく例外になる

対象:
contract_readiness_check.py::_bounded_timeout_provenance()
check_issue_contract.py::_timeout_bounded_provenance()
check_issue_contract.py::build_timeout_diagnostics()

producer と consumer の両方に、次の形の判定があります。

if source not in _TIMEOUT_PROVENANCE_SOURCES:    return None

source が文字列か確認する前に、frozenset の membership 判定をしています。consumer の execution_source にも同じ問題があります。

正常な fixture の診断メタデータだけを変えると、局所検証では次の結果になりました。

timeout_provenance.source = []   → TypeError: unhashable type: 'list'
timeout_provenance.source = {}   → TypeError: unhashable type: 'dict'
execution_source = []           → TypeError: unhashable type: 'list'
execution_source = {}           → TypeError: unhashable type: 'dict'

これは CPython の実装とも整合します。集合の membership 判定は対象のハッシュを求めるため、list/dict は単に「許可値ではない」と評価されるのではなく、例外になります。Python 3.12.3 の Objects/setobject.c も確認しました。GitHub

現在の正常 producer がこれらの型を生成する証拠はありません。 ただし本 PR は、不完全な provenance/binding を unknown に劣化させ、診断を新しい workflow gate にしないことを明示的に約束しています。その境界で例外を送出するのは、今回追加した処理の不具合です。

推奨修正

型を先に判定するだけで足ります。

if not isinstance(source, str) or source not in _TIMEOUT_PROVENANCE_SOURCES:    return None

execution_source にも同様の判定を入れ、既存の unknown 分岐へ流してください。producer 側だけ直すのではなく、プロセス/ファイル境界を越えた入力を扱う consumer 側も直す必要があります。

ここで必要なのは、不正入力を理由に停止する新しい validator ではありません。 また、処理全体を大きな except Exception で囲む必要もありません。列挙値の境界だけを直し、list/dict を入れても「merge が完了する」「従来の failure_class と route を保つ」「診断は unknown」を確認すれば十分です。

3. P2:参照元が unknown なのに、dedup replay が「検証済み」になる

対象:
check_issue_contract.py::build_timeout_diagnostics() の executed_key_by_index 構築、および _timeout_unknown_reason()。

現在、参照元の索引は、おおむね次の3条件だけで登録されます。

occurrence_index が形式上有効
execution_key_hash が形式上有効
execution_source == "executed"

参照元自身の plan digest や provenance が有効かを確認する前に登録しています。 replay 側の判定は、その索引に同じ execution key があるかを見るため、不正な参照元でも判定を通せます。

局所検証の結果

正常な2件の fixture に対し、実行元である occurrence 0 の plan digest だけを top-level と不一致にしました。occurrence 1 は occurrence 0 の dedup replay のままです。

occurrence 0 / executed
  attribution: unknown
  reason_code: plan_digest_mismatch

occurrence 1 / dedup_replay → source_result_index: 0
attribution: attributed
reason_code: binding_verified

参照元は「この plan の実行だと確認できない」のに、そこを参照した replay は「確認できた」ことになります。

さらに、同じ実行元を指す fixture で、実行元の timeout を300秒、replay 側を150秒にしても、両方が binding_verified になりました。少なくとも現在の検証処理は、参照関係に結び付いた予算の矛盾を検出していません。

これは攻撃対策の話ではありません。「どの実行にどの予算が適用されたかを後から特定する」という、本 PR の情報品質の中心部分です。既存 baseline 実装でも、dedup 情報は source execution を特定するためのものとして扱われています。

推奨修正

参照元の索引を、単なる index → execution_key ではなく、この readiness result 内で整合を確認できた実行元だけの索引にしてください。

既存の値だけを使い、参照元の plan digest、index、実行種別、execution key、provenance を確認したうえで replay を照合すれば足ります。参照元が不正・欠落、または予算情報が矛盾する場合は、replay 側を既存の dedup_binding_invalid 等で unknown に落とします。

履歴の再読込、予算の再計算、新しい実行 ID、永続 ledger は不要です。 また、この不整合を理由にレビューを止める必要もありません。

維持したい設計と、広げなくてよい範囲

今回の問題は、設計全体をやり直す理由にはなりません。

readiness producer で情報を引き継ぎ、既存の durable artifact に載せる方針は維持すべきです。 enumerate() を未フィルタの results に適用して canonical occurrence index を保持する変更は、エラー配列内の位置と元の実行位置を混同しないために適切です。

現在の履歴から予算を再計算しないことも正しいです。 関連 PR #2382 は immutable history snapshot を複数 consumer へ渡す設計を導入しています。今回もその実行時点の値を運ぶだけに留めるべきで、diagnostics の修正に estimator の再設計を混ぜる必要はありません。

Compact V2 wire と operator-only route を変更しない判断も維持すべきです。 関連 PR #2398 では、wire の値を増やす旧案を撤回し、root が verified な failure_class を読む構成になっています。今回の診断の問題を直すために、その routing 契約を再び広げる必要はありません。

また、Issue #2845 には別経路の transport 容量不整合が既に記録されています。ただし対象は contract snapshot の materialize 処理です。今回の root review stdout と同種の設計上の注意点ではあっても、#2845 の全面解決を #2901 のマージ条件にする必要はありません。

文書上の軽微な改善として、PR 本文の「schema ファイルが変わらないので consumer inventory は N/A」は、もう少し正確にできます。additionalProperties: true により新フィールドをスキーマが受理することと、転送サイズまで含めて consumer が問題なく扱えることは別です。「schema ファイルは不変、producer 出力には任意フィールド追加、確認した consumer はこの経路」程度の追記で十分であり、新しい承認手続きは不要です。 JSON Schema

検証状況と修正の進め方

PR 差分、新規3テスト、Issue #2897 の本文・OWNER コメント、関連 PR #2382/#2398、Issue #2845、readiness・merge・transport の実装を照合しました。既存テストが確認している通常経路を否定するものではなく、今回の指摘は主にサイズ境界と、壊れた診断情報を受けた際の未検証部分です。

CI については、旧 HEAD 1b8159a… の python-test と ci-verdict-summary の成功を確認しました。一方、最新 HEAD の ci-verdict-summary は取得時点で0件だったため、旧 HEAD の成功を最新 HEAD の成功として扱ってはいません。これは最新 CI の失敗を意味するものではありません。

再現用のスクリプト、実行結果、検証の限界を記した README をまとめました。

pr2901_adversarial_probes.zip

実 checkout の該当関数を AST で抽出して検証するモードも含めています。

python3 adversarial_probe.py --repo /absolute/path/to/loop-protocol

推奨する修正順は、送信全体の容量調整 → 列挙値の型チェック → dedup 参照元の整合確認です。その後、既存の production-path tests に今回の境界入力を追加して確認するのが最小で有効な進め方です。

「診断のために作業を止めない」という狙いは支持します。その狙いを実装上も成立させる、この3点に絞って修正するのが妥当です。

## 判定:REQUEST_CHANGES

方向性は妥当ですが、マージ前に直したい点が3件あります。 最も重要なのは、診断を保存するための変更が、逆にレビュー結果の転送失敗を起こし得ることです。残り2件も、セキュリティ機構の追加ではなく、診断情報が不完全でもレビュー処理を壊さず、分からないものを正しく unknown にする修正で対処できます。

確認中に HEAD が更新されたため、最終的には ef0acf2e94e424e8fc3ed349f81c90706192c548 を対象に確認しました。PR は OPEN / Draft です。初回確認した 1b8159a… から今回の timeout 関連の本番変更は変わっておらず、以下の指摘は最新 HEAD にも残っています。

優先度 指摘 主な影響
P1 診断単体の16 KiB制限では、レビュー結果全体の64 KiB制限を守れない 診断追加によって capture_failure を発生させ、結果を失う可能性
P2 列挙値の型不正で TypeError が発生し、unknown に劣化しない 任意の診断メタデータがレビュー処理の新しい障害原因になる
P2 dedup の参照元が不正でも replay 側を binding_verified にできる 「どの実行の、どの予算か」を検証済みとして誤表示する

以下の再現は、取得コードの該当純粋関数を転記した局所検証と合成入力によるものです。実 Issue での障害を観測したという意味ではなく、リポジトリ全体の pytest/完全な本番経路は、この環境では再実行していません。

1. P1:診断追加で、元は転送できたレビュー結果が capture_failure になる

対象:
.claude/skills/review-issue/scripts/check_issue_contract.py
build_timeout_diagnostics() のサイズ調整と、merge_readiness_into_review_result() での添付処理。

実装は timeout_diagnostics 単体を最大16件・16 KiB以下にしています。しかし、その直後に送られるのは診断だけではなく、parsed_vc_commands、blocking_issues、その他の情報を含む merged review result 全体です。診断側の制限処理には、その既存部分のサイズが入っていません。

一方、root pipeline の child は print(json.dumps(merged)) で全体を stdout に出し、transport は stdout が 65,536 bytesを超えると capture_failure にします。このため「診断が16 KiB以下」から「転送失敗を起こさない」は導けません。

局所検証の結果

本番と同じ json.dumps() と末尾改行を使い、既存レビュー結果を大きめにした合成入力で確認しました。

診断追加前のレビュー stdout       59,338 bytes
timeout_diagnostics 単体          10,348 bytes
診断追加後のレビュー stdout       69,711 bytes
transport の上限                 65,536 bytes

診断単体は上限内なのに、追加前は通る結果が追加後には上限超過します。 この入力はサイズ境界を検証するための合成レビュー JSON で、実際の parser がこの値を出したことまで確認したものではありません。

しかも既存 transport では、deterministic backend でも capture_failure は再試行対象です。したがって、診断の追加が原因で結果を失うだけでなく、不要な再実行へ進む可能性があります。診断追加の目的と逆方向の回帰です。

推奨修正

最終的な送信 JSON 全体の残り容量に合わせて、任意の診断部分を縮めてください。

既存の verdict、failure_class、blocker を保持したまま、診断の occurrences を減らし、truncated_count を更新する形が適切です。診断のヘッダーすら収まらない境界では、任意フィールドの省略規則を用意する方が、レビュー全体を転送失敗にするより有用です。JSON の生バイト列を途中で切ったり、根拠なく transport 上限だけを引き上げたりする修正は勧めません。

Python 公式仕様でも、json.dumps() は文字列を返し、ensure_ascii や separators により出力表現が変わります。容量判定は、実際の送信設定で直列化・エンコードした全体に対して行う必要があります。[Python documentation](https://docs.python.org/3.12/library/json.html)

回帰テストは、現在の「20件の短い timeout」だけでなく、追加前は64 KiB未満、素直に診断を足すと64 KiB超過するケースを既存の production-path test に加えるのが効果的です。PR 本文の「20件でも capture_failure にならない」という確認だけでは、この境界をカバーできていません。

2. P2:診断の型不正が、unknown ではなく例外になる

対象:
contract_readiness_check.py::_bounded_timeout_provenance()
check_issue_contract.py::_timeout_bounded_provenance()
check_issue_contract.py::build_timeout_diagnostics()

producer と consumer の両方に、次の形の判定があります。

if source not in _TIMEOUT_PROVENANCE_SOURCES:
    return None

source が文字列か確認する前に、frozenset の membership 判定をしています。consumer の execution_source にも同じ問題があります。

正常な fixture の診断メタデータだけを変えると、局所検証では次の結果になりました。

timeout_provenance.source = []   → TypeError: unhashable type: 'list'
timeout_provenance.source = {}   → TypeError: unhashable type: 'dict'
execution_source = []           → TypeError: unhashable type: 'list'
execution_source = {}           → TypeError: unhashable type: 'dict'

これは CPython の実装とも整合します。集合の membership 判定は対象のハッシュを求めるため、list/dict は単に「許可値ではない」と評価されるのではなく、例外になります。Python 3.12.3 の Objects/setobject.c も確認しました。[GitHub](https://raw.githubusercontent.com/python/cpython/v3.12.3/Objects/setobject.c)

現在の正常 producer がこれらの型を生成する証拠はありません。 ただし本 PR は、不完全な provenance/binding を unknown に劣化させ、診断を新しい workflow gate にしないことを明示的に約束しています。その境界で例外を送出するのは、今回追加した処理の不具合です。

推奨修正

型を先に判定するだけで足ります。

if not isinstance(source, str) or source not in _TIMEOUT_PROVENANCE_SOURCES:
    return None

execution_source にも同様の判定を入れ、既存の unknown 分岐へ流してください。producer 側だけ直すのではなく、プロセス/ファイル境界を越えた入力を扱う consumer 側も直す必要があります。

ここで必要なのは、不正入力を理由に停止する新しい validator ではありません。 また、処理全体を大きな except Exception で囲む必要もありません。列挙値の境界だけを直し、list/dict を入れても「merge が完了する」「従来の failure_class と route を保つ」「診断は unknown」を確認すれば十分です。

3. P2:参照元が unknown なのに、dedup replay が「検証済み」になる

対象:
check_issue_contract.py::build_timeout_diagnostics() の executed_key_by_index 構築、および _timeout_unknown_reason()。

現在、参照元の索引は、おおむね次の3条件だけで登録されます。

occurrence_index が形式上有効
execution_key_hash が形式上有効
execution_source == "executed"

参照元自身の plan digest や provenance が有効かを確認する前に登録しています。 replay 側の判定は、その索引に同じ execution key があるかを見るため、不正な参照元でも判定を通せます。

局所検証の結果

正常な2件の fixture に対し、実行元である occurrence 0 の plan digest だけを top-level と不一致にしました。occurrence 1 は occurrence 0 の dedup replay のままです。

occurrence 0 / executed
  attribution: unknown
  reason_code: plan_digest_mismatch

occurrence 1 / dedup_replay → source_result_index: 0
  attribution: attributed
  reason_code: binding_verified

参照元は「この plan の実行だと確認できない」のに、そこを参照した replay は「確認できた」ことになります。

さらに、同じ実行元を指す fixture で、実行元の timeout を300秒、replay 側を150秒にしても、両方が binding_verified になりました。少なくとも現在の検証処理は、参照関係に結び付いた予算の矛盾を検出していません。

これは攻撃対策の話ではありません。「どの実行にどの予算が適用されたかを後から特定する」という、本 PR の情報品質の中心部分です。既存 baseline 実装でも、dedup 情報は source execution を特定するためのものとして扱われています。

推奨修正

参照元の索引を、単なる index → execution_key ではなく、この readiness result 内で整合を確認できた実行元だけの索引にしてください。

既存の値だけを使い、参照元の plan digest、index、実行種別、execution key、provenance を確認したうえで replay を照合すれば足ります。参照元が不正・欠落、または予算情報が矛盾する場合は、replay 側を既存の dedup_binding_invalid 等で unknown に落とします。

履歴の再読込、予算の再計算、新しい実行 ID、永続 ledger は不要です。 また、この不整合を理由にレビューを止める必要もありません。

維持したい設計と、広げなくてよい範囲

今回の問題は、設計全体をやり直す理由にはなりません。

readiness producer で情報を引き継ぎ、既存の durable artifact に載せる方針は維持すべきです。 enumerate() を未フィルタの results に適用して canonical occurrence index を保持する変更は、エラー配列内の位置と元の実行位置を混同しないために適切です。

現在の履歴から予算を再計算しないことも正しいです。 関連 PR #2382 は immutable history snapshot を複数 consumer へ渡す設計を導入しています。今回もその実行時点の値を運ぶだけに留めるべきで、diagnostics の修正に estimator の再設計を混ぜる必要はありません。

Compact V2 wire と operator-only route を変更しない判断も維持すべきです。 関連 PR #2398 では、wire の値を増やす旧案を撤回し、root が verified な failure_class を読む構成になっています。今回の診断の問題を直すために、その routing 契約を再び広げる必要はありません。

また、Issue #2845 には別経路の transport 容量不整合が既に記録されています。ただし対象は contract snapshot の materialize 処理です。今回の root review stdout と同種の設計上の注意点ではあっても、#2845 の全面解決を #2901 のマージ条件にする必要はありません。

文書上の軽微な改善として、PR 本文の「schema ファイルが変わらないので consumer inventory は N/A」は、もう少し正確にできます。additionalProperties: true により新フィールドをスキーマが受理することと、転送サイズまで含めて consumer が問題なく扱えることは別です。「schema ファイルは不変、producer 出力には任意フィールド追加、確認した consumer はこの経路」程度の追記で十分であり、新しい承認手続きは不要です。 [JSON Schema](https://json-schema.org/understanding-json-schema/reference/object)

検証状況と修正の進め方

PR 差分、新規3テスト、Issue #2897 の本文・OWNER コメント、関連 PR #2382/#2398、Issue #2845、readiness・merge・transport の実装を照合しました。既存テストが確認している通常経路を否定するものではなく、今回の指摘は主にサイズ境界と、壊れた診断情報を受けた際の未検証部分です。

CI については、旧 HEAD 1b8159a… の python-test と ci-verdict-summary の成功を確認しました。一方、最新 HEAD の ci-verdict-summary は取得時点で0件だったため、旧 HEAD の成功を最新 HEAD の成功として扱ってはいません。これは最新 CI の失敗を意味するものではありません。

再現用のスクリプト、実行結果、検証の限界を記した README をまとめました。

pr2901_adversarial_probes.zip局所再現コードと検証結果をダウンロード

実 checkout の該当関数を AST で抽出して検証するモードも含めています。

python3 adversarial_probe.py --repo /absolute/path/to/loop-protocol

推奨する修正順は、送信全体の容量調整 → 列挙値の型チェック → dedup 参照元の整合確認です。その後、既存の production-path tests に今回の境界入力を追加して確認するのが最小で有効な進め方です。

「診断のために作業を止めない」という狙いは支持します。その狙いを実装上も成立させる、この3点に絞って修正するのが妥当です。

@squne121

squne121 commented Oct 3, 2026

Copy link
Copy Markdown
Owner Author

GitHub live state を確認しました。指定コメントは OWNER による REQUEST_CHANGES で、対象 HEAD は現在も ef0acf2e94e424e8fc3ed349f81c90706192c548。main は 0ab0946e5dc28336cee08470f176487f68a077f7、PR は OPEN / Draft、main 比 ahead 2 / behind 0、最新 CI run は成功しています。前回 transcript の merge-ready 相当の終了結果は今回の OWNER feedback より前の証跡なので、再利用せず fresh re-review が必要です。貼り付けたテキスト(1)

推奨は 新規 PR を作らず #2901 を resume し、3 finding を apply_pr_review_fix_delta 相当で修正 → fresh test-runner → fresh pr-reviewer → current-head CI / terminal gate です。そのまま貼り付けられる形にしています。

repo=squne121/loop-protocol

対象:

あなたは既に fresh interactive Claude Code の Auto mode で起動済みです。
新しい root Claude Code session を起動せず、この session を継続してください。

目的

PR #2901 を current repository の canonical impl-review-loop に従って再開し、

fresh state/intake → existing PR resume → OWNER REQUEST_CHANGES fix_delta → independent verification → fresh PR review → fix/re-review loop → current-head CI / mergeability 確認

まで自律実行し、人間が merge 判断できる直前の canonical terminal state まで完遂してください。

これは明示的な implementation / repair work order です。

今回の OWNER comment に記載された 3 finding の修正、必要な focused regression test、既存 PR/branch/worktree の更新、fresh verification、fresh PR review、通常の review/fix iteration、current-head CI 確認を明示的に承認します。

ただし:

  • 実 merge はしない
  • auto-merge を有効化しない
  • merge queue に投入しない
  • force push / history rewrite はしない
  • foreign worktree / branch / 他 task の dirty work を変更・削除しない

0. Prompt 作成時点の live state — 実行時には必ず fresh revalidate

この prompt 作成時点で確認済み:

  • PR fix: operator-only VC timeout の occurrence と実適用 budget を root review artifact に保持する (#2897) #2901: OPEN / Draft
  • current PR HEAD:
    ef0acf2e94e424e8fc3ed349f81c90706192c548
  • current main:
    0ab0946e5dc28336cee08470f176487f68a077f7
  • PR は current main に対して:
    • ahead: 2
    • behind: 0
  • changed files は現在 5 件:
    • .claude/skills/issue-contract-review/scripts/contract_readiness_check.py
    • .claude/skills/issue-contract-review/tests/test_readiness_timeout_provenance_passthrough.py
    • .claude/skills/issue-refinement-loop/tests/test_root_review_timeout_diagnostics.py
    • .claude/skills/review-issue/scripts/check_issue_contract.py
    • .claude/skills/review-issue/tests/test_timeout_diagnostic_projection.py
  • .claude/skills/issue-refinement-loop/scripts/run_root_review_pipeline.py は PR diff にない
  • Issue 実装: operator-only VC timeout の occurrence・budget identity を root review artifact に保持する #2897 は OPEN
  • OWNER comment 5974155871 は author_association: OWNER
  • OWNER comment の確認対象 HEAD は現在の PR HEAD と同じ ef0acf2e...
  • OWNER comment より後の trusted corrective comment は、この prompt 作成時点では検出されていない
  • inline review thread / submitted GitHub review はこの時点ではなし
  • HEAD ef0acf2e... の最新 ci workflow run は completed / success

これらは advisory snapshot にすぎません。
開始時および各 evidence binding の直前に live GitHub / repository を fresh 取得し、drift があれば current state を正本にしてください。

1. 最初に current canonical contract を読む

実作業前に current main / current repository から少なくとも以下を読んでください。

  • .claude/skills/impl-review-loop/SKILL.md
  • impl-review-loop が要求する current steps/ / references
  • .claude/skills/impl-review-loop/steps/preparation.md
  • .claude/skills/impl-review-loop/steps/step-1-implementation.md
  • .claude/skills/impl-review-loop/steps/step-2-verification.md
  • .claude/skills/impl-review-loop/steps/step-4-pr-review.md
  • .claude/skills/impl-review-loop/steps/step-5-feedback-and-termination.md
  • .claude/skills/impl-review-loop/steps/step-5-mergeability-handling.md
  • .claude/skills/impl-review-loop/steps/context-protocol-and-guardrails.md
  • .claude/agents/implementation-worker.md
  • .claude/agents/test-runner.md
  • .claude/agents/pr-reviewer.md
  • Issue 実装: operator-only VC timeout の occurrence・budget identity を root review artifact に保持する #2897 の current body / AC / VC / Allowed Paths / Stop Conditions
  • OWNER comment 5974155871 全文

過去 session の保存済み APPROVE、test verdict、Step 5 approved、CI green、mergeability、dispatch seq 等を current authorization / current-head evidence として再利用しないでください。

今回の OWNER REQUEST_CHANGES が、それらより新しい corrective authority です。

2. Fresh intake / landing disposition

current canonical intake / landing-disposition producer を使用して current state を判定してください。

PR #2901 が current Issue #2897 の exact linked open/draft candidate として正しく認識される場合、期待する経路は概念的には:

existing_pr_resume → resume_existing_pr

です。

ただし prose から決め打ちせず、current producer の canonical result をそのまま消費してください。

重要:

3. OWNER REQUEST_CHANGES を current fix_delta として扱う

OWNER comment:
#2901 (comment)

を全文読み、以下 3 件を merge-blocking fix_delta として扱ってください。

current .claude/agents/implementation-worker.md が引き続き
IMPLEMENTATION_WORKER_REQUEST_V2 / apply_pr_review_fix_delta
を正規 repair mode としている場合は、その current contract に従って implementation-worker へ委譲してください。

古い field shape をこの prompt から盲目的にコピーせず、current agent contract を読んで materialize してください。

Finding A — P1: timeout_diagnostics 単体の 16 KiB 制限では transport の full stdout cap を保証できない

問題:

  • 現実に transport が受け取るのは timeout_diagnostics 単体ではなく merged review result 全体
  • child が実際に使用する serialization / encoding / trailing newline を含む final stdout bytes が STDOUT_CAP を超えれば capture_failure
  • 「diagnostic <= 16 KiB」だけでは、diagnostic 追加前に cap 未満だった result を cap 超過へ押し上げる可能性がある

必要な修正方針:

  • transport cap を安易に引き上げない
  • JSON byte stream を途中切断しない
    -既存 verdict / failure_class / blockers 等の routing-critical semantics を削らない
  • 実際に送信する merged result 全体の serialized byte size を基準にする
  • optional diagnostic 側を available budget に合わせて縮小する
  • occurrences を bounded に減らし、truncated_count / total_timeout_occurrences を整合させる
  • diagnostic header 自体を含める余地がない場合は、current contract と互換な optional-field omission を優先し、review result 全体を capture_failure にしない
  • actual writer と同じ json.dumps options / encoding / newline semantics で計算する
  • unrelated transport redesign、ledger、new gate は追加しない

focused regression では少なくとも:

  1. diagnostic 追加前 full stdout は cap 未満
  2. naive に diagnostic を全部付けると cap 超過
  3. production fix 後は cap 以下
  4. routing-critical base result は保持
  5. diagnostic は正しく truncate / omit
  6. capture_failure にならない

という境界ケースを production-path test に固定してください。

Finding B — P2: enum-like value の unhashable 型で TypeError を起こさない

対象候補:

  • contract_readiness_check.py::_bounded_timeout_provenance()
  • check_issue_contract.py::_timeout_bounded_provenance()
  • check_issue_contract.py::build_timeout_diagnostics()
  • execution_source の membership 判定

必要な性質:

  • frozenset / set membership より先に型を確認する
  • list / dict 等が来ても例外にしない
  • producer 境界だけでなく consumer 境界でも防御する
  • malformed optional diagnostic metadata は、新しい workflow blocker / new validator にしない
  • existing unknown degradation semantics に流す
  • existing failure_class / operator-only route を維持する
  • broad except Exception で隠さない

少なくとも list / dict 等の malformed source / execution_source を入れ、

  • merge が例外なく完了する
  • attribution は unknown
  • current bounded reason code を使う
  • route / failure_class は従来通り
    を regression test にしてください。

新しい reason code が本当に必要かは current Issue contract を基準に判断し、既存列挙で表現可能なら増やさないでください。

Finding C — P2: invalid executed source を参照する dedup replay を binding_verified にしない

問題:

  • replay の source execution が自身では plan digest / provenance 等の binding 検証に失敗して unknown なのに、
    occurrence_index + execution_key + execution_source=executed
    だけで source index に登録すると replay が binding_verified になり得る
  • source / replay の適用 budget provenance が矛盾しても current logic では見逃し得る

必要な修正方針:

  • replay source index には、この readiness result 内で source occurrence 自身の binding integrity を確認できた executed occurrence だけを登録する
  • source occurrence について current contract が要求する:
    • canonical plan digest
    • occurrence index / range
    • execution source
    • execution key
    • timeout provenance
      を既存値だけで検証する
  • replay は source execution key と整合することを確認する
  • producer contract 上 source/replay 間で同一であるべき bounded budget/provenance identity に矛盾があれば attribution しない
  • source が unknown / malformed / inconsistent の場合、replay も dedup_binding_invalid 等 current contract の bounded unknown reason へ落とす
  • history 再読込、budget 再計算、新しい execution ID、ledger は追加しない

regression では少なくとも:

  1. source occurrence の plan digest mismatch → source unknown
  2. その source を参照する replay も attributed にしない
  3. source/replay の budget provenance が producer semantics 上矛盾 → replay unknown
  4. 正常 dedup replay は従来どおり attributed
    を固定してください。

4. 修正範囲を広げない

今回の主眼は OWNER comment の 3 finding です。

維持するもの:

  • readiness producer で既存 identity/provenance を運ぶ設計
  • canonical occurrence index
  • history から budget を再計算しない設計
  • Compact V2 wire
  • existing operator-only route / failure_class
  • optional diagnostic であり routing authority にしないこと
  • baseline_vc_preflight.py を変更しない方針
  • run_root_review_pipeline.py は配線が本当に不要なら production change なしのまま

原則として Issue #2897 current Allowed Paths 内で最小修正してください。

OWNER comment にある #2845 は類似の transport-size concern ですが別経路です。
#2845 全面解決を #2901 の merge 条件にしないでください。

5. SubAgent の使い方

必要な implementation / verification / review は current impl-review-loop の SubAgent へ委譲してください。

少なくとも想定:

  • implementation-worker
  • test-runner
  • pr-reviewer

必要なら current workflow が指定する他 agent も使って構いません。

重要:

  • SubAgent に親会話の暗黙 context を期待しない
  • task / target PR / linked Issue / exact HEAD / trusted OWNER comment / current authority / constraints / expected output を message に具体値で渡す
  • fork_turns: none 等 current contract を守る
  • nested delegation を勝手に許可しない
  • SubAgent dispatch だけで完了扱いしない

Common Completion Protocol を厳守してください。

各 SubAgent について:

  1. final result を実際に取得する
  2. list_agents 等 current canonical mechanism で canonical task の terminal completed を確認する
  3. final result と terminal completion の両方が揃うまで downstream step に進まない

timeout / mailbox update / partial report / background dispatch は completion ではありません。

6. Implementation worker の repair execution

current agent contract が許すなら、既存 PR repair として implementation-worker の
apply_pr_review_fix_delta
相当を使ってください。

worker には少なくとも:

worker の self-report は advisory です。
返却後、root/control-plane は live PR head / changed paths / branch / Issue binding を独立に再確認してください。

7. Verification

修正 commit / push 後、新しい PR HEAD に旧 verification evidence を流用しないでください。

current Issue #2897 の live body から:

  • body SHA
  • literal Verification Commands
  • baseline-derived AC label
  • command hashes

を current impl-review-loop の canonical parse-only metadata route で fresh materialize し、test-runner に渡してください。

Issue #2897 の current literal VC をすべて fresh 実行すること。

加えて OWNER findings 用の focused regression tests も worker / appropriate test lane で実行してください。

特に:

  • near-STDOUT_CAP full-result regression
  • list/dict malformed enum regression
  • invalid source → dedup replay unknown regression
  • valid dedup replay positive control
    を確認してください。

AC3 Runtime Verification が current contract 上引き続き immediate / applicable なら、新しい HEAD に束縛した evidence を再生成してください。

前回 PR body にある
head: 3d75aac9 (+ uncommitted working tree)
の古い runtime log header を current-head evidence として再利用しないでください。

PR body の Runtime Verification Evidence / Checks / scope coverage 等は、current workflow が要求する範囲で新 HEAD の事実へ更新してください。

8. Step 4 fresh PR review

test-runner の:

  • final report
  • terminal completed

の双方を確認した後だけ Step 4 へ進んでください。

current adjudicate_vc_result.py step4-adjudicate 等の canonical gate を current docs どおり使用し、stored PASS の不正流用や手組み state による bypass をしないでください。

その後 pr-reviewer を 新しい current HEAD に対して fresh 起動してください。

reviewer には OWNER comment の 3 finding が本当に閉じたかを明示的に再確認させてください。

特に adversarial に:

  • full serialized merged result の cap safety
  • malformed optional metadata で exception が出ないか
  • invalid source の dedup replay が誤って verified にならないか
  • fix のために routing semantics / Compact V2 / failure_class を変えていないか
  • regression test が synthetic happy path だけでなく failure class を固定しているか
    を確認させてください。

pr-reviewer の self-reported mergeability は authority にせず、mergeability は control-plane が live GitHub から独立取得してください。

9. REQUEST_CHANGES / CI failure は自動修復する

fresh pr-reviewer が REQUEST_CHANGES を返した場合:

  • current canonical Step 5 routing で continue_loop
  • blockers を次 iteration の fix_delta として implementation-worker に渡す
  • 修正
  • fresh verification
  • fresh review
    を bounded loop 内で自律実行してください。

通常の:

  • test failure
  • lint/typecheck failure
  • current-head CI failure
  • reviewer finding
  • stale reviewer head
  • repairable branch-behind
    は、人間へ即停止する理由ではありません。

current workflow が許す canonical recovery を行ってください。

HEAD が変わったら、head-bound evidence を fresh に取り直してください。

10. CI / mergeability

修正後 HEAD の GitHub Actions を current repository policy どおり評価してください。

古い ef0acf2e... の success は、新しい HEAD の CI evidence として扱わないでください。

required CI を current head で確認し、必要なら canonical wait/retry mechanism を使ってください。

Step 5 では live GitHub から同一時点に近い state として:

  • current PR head SHA
  • mergeable
  • mergeStateStatus
  • current main SHA
    を取得してください。

main drift が発生した場合は current canonical mergeability / reconciliation runbook に従い、必要な evidence だけを再取得してください。

force push はしないでください。

11. 再発防止 Issue

今回の 3 finding 自体は #2901 の in-scope fix として解決してください。
これを別 Issue に逃がさないでください。

ただし作業中に 独立した recurring defect / systemic failure class が実際に再現・確認された場合は、再発防止 Issue の起票まで明示的に承認します。

起票前に必ず:

  1. current OPEN Issues を dedupe_key / root cause / owner / failure class / affected path で検索
  2. trusted related Issues / PRs を確認
  3. OPEN duplicate があれば再利用し、新規作成しない
  4. CLOSED Issue を勝手に reopen しない
  5. 実装: contract snapshot の vc_preflight_classifications transport 上限(16,000 文字)と readiness の VC 上限(40 件)の不整合を解消し、VC が 14 件以上の Issue でも snapshot を materialize できるようにする #2845 等、既に別経路を ownership している Issue と区別する
  6. 単なる security/harness 一般論では起票しない
  7. 実際に再現した failure class と evidence を持つこと
  8. fix: operator-only VC timeout の occurrence と実適用 budget を root review artifact に保持する (#2897) #2901 に混ぜるべきでない理由を明示する

新規起票が必要なら current repository の:

  • issue-refinement-loop/references/follow-up-materialization.md
  • issue-creator
  • create-issue
    等の current canonical workflow を読み、それに従ってください。

issue-creator を SubAgent として使った場合も、final result と terminal completion の両方を確認してください。

この prompt は、上記条件を満たす再発防止 Issue の canonical 起票を明示的に承認します。
ただし deterministic workflow 自身がなお human_judgment_required を返す場合は、その authority を迂回しないでください。

過去 session で報告された
TestAuthorityTransportPrivilegedExecutorRealSubprocessDispatch
系の環境依存 failure は、今回 fresh に再現し root cause が確認できない限り、それだけを根拠に新規 Issue を作らないでください。

12. 明示的に承認する作業

以下は、この作業目的と current repository contract の範囲内で、人間の iteration ごとの追加承認なしに実行して構いません。

  • live GitHub / repository state の取得
  • current Skill / Agent / SSOT の読了
  • canonical intake / landing disposition / root transition
  • existing PR / worktree / branch の再利用
  • OWNER REQUEST_CHANGES 3件の fix_delta 実装
  • Allowed Paths 内のコード・テスト変更
  • commit / push
  • current PR fix: operator-only VC timeout の occurrence と実適用 budget を root review artifact に保持する (#2897) #2901 の更新
  • current workflow が要求する PR body 更新
  • local tests / VC / runtime verification
  • SubAgent dispatch
  • SubAgent completion 待ちと final result 取得
  • fresh adversarial review
  • bounded review/fix/re-review iteration
  • required CI の確認・通常の再実行
  • non-destructive branch update / reconciliation
  • duplicate search
  • 条件を満たす再発防止 Issue の canonical 起票

ただしこれは Claude Code permission bypass の承認ではありません。
--dangerously-skip-permissions / bypassPermissions 等を要求・変更しないでください。

13. 停止 / escalation 境界

以下は停止または canonical escalation としてください。

  • destructive operation が必要
  • secret / credential の取得・変更・公開が必要
  • force push / history rewrite が必要
  • foreign dirty work を変更しなければ進めない
  • current Issue の Allowed Paths 外 production change が不可避
  • baseline_vc_preflight.py の production change が不可避
  • fixed schema / routing contract の変更が不可避で current Issue が許可していない
  • external service / privilege escalation が新たに必要
  • evidence integrity を維持できない
  • irreconcilable scope conflict
  • actual Git conflict を current canonical runbook でも解消不能
  • canonical iteration 上限到達
  • current deterministic authority が human_judgment_required を返す
  • current trusted human instruction が明示停止を要求する

通常の test failure / review finding / CI failure は停止理由ではありません。

14. 完了条件

current PR HEAD に対し fresh に以下を確認してください。

  • OWNER comment 5974155871 の P1 / P2 / P2 が全て実質的に解消
  • Issue 実装: operator-only VC timeout の occurrence・budget identity を root review artifact に保持する #2897 current AC を満たす
  • Allowed Paths compliance
  • literal Issue VC の fresh PASS
  • applicable Runtime Verification の current-head evidence
  • full serialized review result の transport-size regression PASS
  • malformed diagnostic metadata が graceful unknown へ劣化
  • invalid dedup source が replay を falsely verified にしない
  • required CI current-head PASS
  • fresh pr-reviewer verdict APPROVE
  • blockers: []
  • reviewed head == live current PR head
  • unresolved actionable feedback / review thread がない
  • current live mergeability が canonical terminal condition を満たす
  • Step 5 canonical terminal gate が approved 相当
  • follow-up issue が必要なら created / reused / skipped が確定

ここまで到達したら merge / auto-merge を行わず停止してください。

Draft → Ready の変更については current impl-review-loop の canonical terminal contract に従ってください。
単に「merge 直前に見えるから」という理由で独自判断して Ready 化しないでください。

15. 最終報告

最後に、具体値付きで簡潔に報告してください。

  • final main HEAD
  • Issue 実装: operator-only VC timeout の occurrence・budget identity を root review artifact に保持する #2897 live state / body SHA
  • OWNER comment URL
  • landing disposition / resume route
  • 使用した existing worktree / branch
  • PR fix: operator-only VC timeout の occurrence と実適用 budget を root review artifact に保持する (#2897) #2901 URL
  • initial repair HEAD
  • final PR HEAD SHA
  • review/fix iteration 数
  • P1 full-result cap finding の修正内容と regression evidence
  • P2 enum type finding の修正内容と regression evidence
  • P2 dedup source finding の修正内容と regression evidence
  • changed files
  • Allowed Paths compliance
  • Issue VC 結果
  • Runtime Verification 結果 / evidence
  • focused regression 結果
  • required CI 結果
  • pr-reviewer final verdict
  • reviewer blockers / warnings
  • live mergeability / mergeStateStatus
  • unresolved review threads / actionable feedback
  • 使用した全 SubAgent
  • 各 SubAgent の final result と terminal completion を確認したこと
  • follow-up Issue: created / reused / skipped(番号と理由)
  • unresolved blocker
  • canonical merge-ready / draft_pr_ready 相当 state に到達したか
  • merge / auto-merge / merge queue は実行していないこと

調査や提案だけで終了せず、current canonical workflow が許す限り
repair → test → independent verification → fresh review → fix → re-test → CI → terminal gate
まで実行してください。

ポイントは、今回の3件を「追加hardening」ではなく、#2897 の診断を non-blocking / truthful に保つための直接的な fix_delta と明示したことです。これなら impl-review-loop の通常 REQUEST_CHANGES → continue_loop に素直に乗せられます。

…urce 検証を追加 (#2897)

- P1: merged result 全体 (実 writer と同じ serialize) の STDOUT_CAP に合わせて optional diagnostic のみを縮小/省略
- P2: source / execution_source の list/dict で TypeError を出さず unknown に劣化 (producer / consumer 両側)
- P2: binding 検証済み executed source のみを dedup source として登録し、replay の budget provenance 矛盾は unknown

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@squne121
squne121 marked this pull request as ready for review October 3, 2026 23:21
@squne121
squne121 merged commit f5a1388 into main Oct 3, 2026
65 of 68 checks passed
@squne121
squne121 deleted the worktree-issue-2897-operator-only-vc-timeout-occurrence-budget-identity branch October 3, 2026 23:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

実装: operator-only VC timeout の occurrence・budget identity を root review artifact に保持する

1 participant