diff --git a/docs/working/TASK-0896/INDEX.md b/docs/working/TASK-0896/INDEX.md new file mode 100644 index 00000000..8dbf98af --- /dev/null +++ b/docs/working/TASK-0896/INDEX.md @@ -0,0 +1,27 @@ +# TASK-0896 INDEX + +> Issue: [#896](https://github.com/s977043/plangate/issues/896)(P1)refactor(ai-loop): 検証ロジックの共通契約層化 — 定数/hash/snapshot三つ組照合の重複解消 +> 関連 EPIC: #870(#873/#874 前の基盤整理)/ Mode: **high-risk**(人間 C-3 必須・非 HO) + +## 現在フェーズ + +**C-1 PASS → C-2 完了(R-001〜R-010 確定反映済み)→ C-3 Human 承認待ち**(2026-07-22) + +## ファイル一覧 + +| ファイル | 状態 | +|---------|------| +| [pbi-input.md](./pbi-input.md) | ✅ PR #898 merge 済み(作成前レビュー完了) | +| [plan.md](./plan.md) | ✅ B-3 生成済み | +| [todo.md](./todo.md) | ✅ B-3 生成済み | +| [test-cases.md](./test-cases.md) | ✅ B-3 生成済み | +| [current-state.md](./current-state.md) | ✅ | +| [decision-log.jsonl](./decision-log.jsonl) | ✅ 初期化済み | +| [review-self.md](./review-self.md) | ✅ C-1 PASS(WARN 1)+ 簡易 C-1 PASS | +| [review-external.md](./review-external.md) | ✅ C-2 2 レーン完了(R-001〜R-010 集約・裁定済み) | +| approvals/c3.json | ⏳ C-3(Human) | + +## C-3 で人間が確定する論点 + +1. **#873(P0)との実装順**: (a) #896 逐次先行 or (b) #873 と並行(c3_contract 先行 merge → #873 rebase) +2. EPIC #870 本体への位置づけ追記コメントの要否 diff --git a/docs/working/TASK-0896/approvals/c3.json b/docs/working/TASK-0896/approvals/c3.json new file mode 100644 index 00000000..f645ecbd --- /dev/null +++ b/docs/working/TASK-0896/approvals/c3.json @@ -0,0 +1,12 @@ +{ + "task_id": "TASK-0896", + "phase": "C-3", + "c3_status": "APPROVED", + "approved_by": "s977043@users.noreply.github.com", + "approved_at": "2026-07-22T05:25:11Z", + "plan_hash": "sha256:8cb2d72c2fa4128cf9b2b3f717c3c1444eb284a79eb1a5a7509bff1682a72877", + "source": "cli", + "_approved_by_source": "git-config", + "_approver_identity_unverified": true, + "_note": "Generated by `plangate approve` (TASK-0128). Human presence verified via L1-L4; identity (approved_by) is git-config derived and NOT cryptographically verified." +} diff --git a/docs/working/TASK-0896/current-state.md b/docs/working/TASK-0896/current-state.md new file mode 100644 index 00000000..c91258de --- /dev/null +++ b/docs/working/TASK-0896/current-state.md @@ -0,0 +1,10 @@ +# current-state — TASK-0896 + +- **フェーズ**: C-2 確定反映済み(R-001〜R-010)→ 次 = **C-3 Human 承認待ち** +- **branch**: `docs/task-0896-plan`(origin/main 53879e1 から分岐) +- **Mode**: high-risk / lite_eligible=false / autonomous APPROVE 不可(人間 C-3 必須) +- **Metrics**: 変更ファイル実数 9(見積 9・比率 1.0・採用)。重複 5 点の実在を行番号で裏取り済み +- **設計確定**: c3_contract.py 新設(論点 1 案 A・REQUIRED_KEYS 系含む)/ 三つ組照合は strictness 引数で非対称保存(論点 2 案 A・reviewer 集合非対称も保存)/ 返り値 = 理由文字列リスト + 順序契約(論点 3) +- **C-1**: PASS(WARN 1)/ **C-2**: 2 レーン完了(A=Codex major 4→採用 3・不採用 1〔経路違い〕、B=整合 9/9 照合 OK・minor 2 info 4 全採用)→ 1 回確定反映済み・簡易 C-1 PASS +- **次アクション**: 人間 C-3(論点 1: #873 との実装順 / 論点 2: EPIC #870 追記コメント要否)→ APPROVED なら `bin/plangate approve TASK-0896`(c3.json 発行)→ exec +- **ブロッカー**: C-3 Human 判断待ち diff --git a/docs/working/TASK-0896/decision-log.jsonl b/docs/working/TASK-0896/decision-log.jsonl new file mode 100644 index 00000000..3a9906f3 --- /dev/null +++ b/docs/working/TASK-0896/decision-log.jsonl @@ -0,0 +1,7 @@ +{"ts":"2026-07-22T10:00:00+09:00","phase":"B-1","decision":"確認質問なしで進行","rationale":"pbi-input(PR #898 レビュー済み)で Unknowns 2 件は解決済み・唯一の open 論点(#873 実装順)は C-3 で人間確定と明記済み","actor":"agent"} +{"ts":"2026-07-22T10:05:00+09:00","phase":"B-1.5","decision":"事前メトリクス検証: 実数 8 / 見積 9 = 0.89 で採用・Mode high-risk 維持","rationale":"重複 5 点を行番号実測で裏取り。<1 倍の Mode 下げ候補は承認境界の受理器 touch(セキュリティ関連)のため安全側で不採用","actor":"agent"} +{"ts":"2026-07-22T10:10:00+09:00","phase":"B-2","decision":"論点1=c3_contract.py 新設(案A) / 論点2=三つ組照合コア共通化+strict_keys 引数で非対称保存(案A) / 論点3=理由文字列リスト契約(pbi 確定の追認)","rationale":"中立配置で AC-6 の I/O 層分離を module 構造で表現。arbiter 余剰許容/c3prime strict の #889 R2 由来非対称は挙動不変のため保存必須","actor":"agent"} +{"ts":"2026-07-22T10:15:00+09:00","phase":"B-3","decision":"plan.md / todo.md / test-cases.md 同時生成完了","rationale":"コミット戦略 = 1 コミット 1 種類の 4 コミット(定数→hash→trio→sync)・各コミットで 4 系テスト green 維持","actor":"agent"} +{"ts":"2026-07-22T12:40:00+09:00","phase":"C-1","decision":"PASS(WARN 1 = T-11 粒度・exec 時サブ分割)","rationale":"17 項目全チェック・FAIL 0","actor":"agent"} +{"ts":"2026-07-22T13:10:00+09:00","phase":"C-2","decision":"2 レーン完了: A=Codex 設計妥当性 major 4(採用 3/不採用 1)・B=コードベース整合 9/9 照合 OK minor 2 info 4 全採用","rationale":"R-002(C1-VERDICT マーカー)は経路違いで不採用 — c3-prime evidence マーカーは ai-loop run 経路用・本タスクは human C-3 経路(前例 TASK-0872 grep 0 件で受理)","actor":"agent"} +{"ts":"2026-07-22T13:25:00+09:00","phase":"C-2 確定反映","decision":"R-001/R-003〜R-010 を plan/todo/test-cases へ 1 回確定反映・簡易 C-1 PASS","rationale":"主要変更: REQUIRED_KEYS 系の共通化対象化 / 理由リスト順序契約 + 代表文言回帰 / 純粋性 I/O 封じテスト / 残置列挙の完全化(reviewers 2 者・source_sha 照合)/ ta-55 CI 経路追加(実数 8→9)","actor":"agent"} diff --git a/docs/working/TASK-0896/plan.md b/docs/working/TASK-0896/plan.md new file mode 100644 index 00000000..9b7d91fc --- /dev/null +++ b/docs/working/TASK-0896/plan.md @@ -0,0 +1,172 @@ +# EXECUTION PLAN — TASK-0896 + +> Issue: [#896](https://github.com/s977043/plangate/issues/896)(P1 / refactor)/ 関連 EPIC: #870(#873/#874 前の基盤整理) +> 入力: [`pbi-input.md`](./pbi-input.md)(PR #898 でレビュー済み・2026-07-22 実測裏取り) +> 作成: 2026-07-22(main 53879e1 で再実測済み) + +## Goal + +c3-prime 契約の検証規則(契約定数 / sha256・canonical JSON hash / reviewer snapshot 三つ組照合)を `scripts/ai-loop/c3_contract.py` 単一モジュールへ集約し、arbiter.py / plan_package.py / c3prime_verify.py の 3 消費者が import 参照する構造にする。**機能変更ゼロ・振る舞い不変**(判定結果 = decision / exit code / reject 不変)で、#873 delivery.py / #874 run_evidence.py が最初から共通層を import できる状態を作る。 + +## Constraints / Non-goals + +- **Non-goals**(issue verbatim): arbiter `plan_package_check`(入力ブロック検証)と c3prime_verify(record 検証)の**全統合**(premature abstraction 回避・共通化は定数 / hash / 三つ組照合の 3 点に限定)/ `c3-prime-contract.md` の規則変更(契約は不変)/ 機能追加・挙動変更 +- **不変条件**: 偽造 record 群(test_c3prime_verify の手 mutate 14 系)の reject 不変(AC-7)/ 既存テスト 4 系全 green(AC-4: test_arbiter 247 / test_plan_package 30 / test_c3prime_verify 12 / run-tests 411)/ arbiter は共通層から **I/O なし関数のみ** import(AC-6) +- **非 HO**: 全対象は `scripts/ai-loop/**` + `scripts/sync-plugin-plangate.sh`(scripts/ 直下・hooks でない)。Hardening Override 9 カテゴリに非該当を確認済み +- **exec 経路**: EH-1 が `scripts/` 編集に plan+TASK 文脈を要求 → `PLANGATE_HOOK_TASK=TASK-0896` セッションでメイン直実装が正規(worker 委譲不可) +- **既存の検証強度非対称は保存する**(下記論点 2): arbiter = snapshot 余剰キー許容(欠落・空値のみ検査)/ c3prime_verify = ちょうど 5 キー strict(#889 R2 medium 由来)。共通化で強度を「揃える」と挙動変更になるため、strictness は引数で保持 + +## Approach Overview + +### 論点 1: 共通層の配置 — **案 A 採用(c3_contract.py 新設)** + +| 案 | 内容 | 評価 | +|----|------|------| +| **A: `scripts/ai-loop/c3_contract.py` 新設** | 契約定数(REQUIRED_KEYS 系含む・Refs: R-001)+ I/O なし純関数(`canonical_hash` / `check_snapshot_trio`)+ I/O あり(`sha256_of_file`)を層分離して新設。3 消費者が import | ✅ 「契約の正本」を producer(plan_package)にも consumer(c3prime_verify)にも寄せない中立配置。AC-6 の I/O 層分離が module 構造で表現できる。#873/#874 が最初から import 可能 | +| B: plan_package.py へ集約 | check_evidence 前例の延長で既存 producer に定数・関数を寄せ、新モジュールを作らない | ❌ arbiter が producer モジュールを import する形になり「arbiter は入力 dict のみに依存」の設計と衝突(plan_package はファイル I/O が主責務で、I/O なし関数だけを選んで import する規律が module 境界で保証されない)。契約定数の正本が producer 側に置かれ責務が歪む | +| C: 共通化せず drift 検出テストのみ | 各実装に定数値同一 assert のテストを足し、実装は分散のまま | ❌ issue が共通化を明示要求。drift の検出はできても構造原因(改版時の多重修正)が残り、#873/#874 で消費者が 4〜5 に増える問題を解決しない | + +### 論点 2: 三つ組照合の共通化範囲 — **「5 キー整合 + 三つ組一致」のコアのみ・strictness 引数で非対称保存** + +| 案 | 内容 | 評価 | +|----|------|------| +| **A: コア共通化 + `strict_keys` 引数** | `check_snapshot_trio(container, reviewers, strict_keys)` が「snapshot 5 キーの存在(strict 時はちょうど 5 キー)/ 空値 / plan_hash・source_sha・plan_package_hash のトップレベル一致」を検査し**理由文字列リスト(空 = OK)**を返す。verdict 語彙・reviewer 独立性・AUTO_APPROVED 整合・**reviewers ちょうど 2 者(model_a/model_b・余剰 reject。Refs: R-005)**は c3prime_verify 固有で残置。呼び出し側の分岐制御(arbiter = tuple 部分成功 / c3prime = 即時 reject)も各自に残す | ✅ 実測した非対称(arbiter L514 = 欠落・空値のみ / c3prime L163 = set 一致 + L165 空値)を挙動不変で保存。**reviewer 集合も同様の意図的非対称(arbiter = 余剰 reviewer 許容 L510 / c3prime = 拒否 L158)= 保存対象**(Refs: R-005)。契約 AC-5 の核心規則(同一 hash を観た)は単一実装になる | +| B: strict へ完全統一 | 両経路とも「ちょうど 5 キー」に統一 | ❌ arbiter 側で今まで通過していた余剰キー付き入力が BLOCKED に変わる = 挙動変更(Non-goal 違反)。受理側だけ strict の現状は #889 R2 の意図的設計 | +| C: 三つ組照合は共通化しない(定数 + hash のみ) | 最小リスク構成 | ❌ AC-3 が「arbiter / c3prime_verify の両経路が同一実装を使う」を明示要求。三つ組照合こそ契約 AC-5 の核心で drift 許容度が最も低い | + +### 論点 3: 返り値契約(pbi-input Step 0 確定事項の追認) + +共通関数は**理由文字列リスト(空 = OK)を返し、判定・終端制御は呼び出し側**。arbiter はリスト非空なら `integrity_ok=False` + reason = 先頭(既存 tuple 契約へ変換)/ c3prime_verify はリスト非空なら `_fail(先頭)`。既存テストはメッセージ非検証(実測: test_c3prime_verify のメッセージ assert 0 件・test_arbiter は decision / priority 接頭のみで pp_reason 内部文言非 assert)のため、理由文言は共通関数側で単一化してよく、**判定結果ベースで振る舞い不変を確認**する。 + +**理由リストの順序・文言契約(Refs: R-004)**: 先頭要素が外部(arbiter reason / c3prime stderr)へ出るため、理由リストの**生成順序を検査順(キー集合 → 空値 → 三つ組不一致)で契約固定**し、test_c3_contract.py で順序を assert する。加えて外部可視文言の代表例(snapshot キー不一致 / 三つ組不一致の各 1 本)を回帰テストで固定し、以後の共通層改版で silent に変わらないようにする。 + +### コミット戦略(refactoring-guidance 準拠・1 コミット 1 種類) + +(a) 定数集約 → (b) hash ヘルパー統合 → (c) 三つ組照合コア共通化 → (d) sync 列挙、の 4 コミット。各コミットで 4 系テスト green を維持し、差分検出時は該当コミットのみ局所 revert 可能にする。 + +## Metrics Evidence(#351 事前メトリクス検証) + +| 項目 | AI 見積もり(pbi-input) | 実数 | 比率 | 判定 | +|------|------------------------|------|------|------| +| 変更ファイル数 | 9 | **9**(新設 2: c3_contract.py + test_c3_contract.py / 変更 3: arbiter.py / plan_package.py / c3prime_verify.py / sync 1: sync-plugin-plangate.sh / sync 再生成 2: plugin/.../c3_contract.py + test_c3_contract.py / CI 実行経路 1: tests/extras/ta-55 へ 1 行追記〔Refs: R-010〕。既存 python テスト変更 0 — test_c3prime_verify.py の定数参照はコメントのみ実測) | 1.0 | 採用 | +| AC 件数 | 8 | 8(issue verbatim) | 1.0 | 採用 | +| 重複実在(5 点) | 5 点 | **5 点全実在を行番号で確認**: 定数 = plan_package L27,82-83 + c3prime L28,32-33 / snapshot キー = arbiter L486 + c3prime L34 + plan_package 組み立て側 / file sha256 = plan_package L137 + c3prime L46 / canonical hash 式 = plan_package L148-153 + c3prime L151-152(式・separators まで同一。Refs: R-007) / 三つ組照合 = arbiter L495-528 + c3prime L155-171(**別実装・強度非対称あり**) | 1.0 | 採用 | + +**取得コマンド**: `grep -n "VALID_DECISIONS\|SNAPSHOT_KEYS\|def _sha256\|json.dumps" scripts/ai-loop/{arbiter,plan_package,c3prime_verify}.py`(2026-07-22 main 53879e1) + +**判定理由**: 比率 1.0(1〜3 倍の範囲内)で採用。c3prime_verify = **承認境界の受理器**touch はセキュリティ関連(mode-classification 例外「セキュリティ関連 → 最低中」+ 定性「受理器の検証強度」)であり、pbi-input 確定の **high-risk を維持**。(C-2 確定反映 R-010 で実数 8→9: ta-55 への CI 実行経路 1 行追記を追加) + +**追加実測(sync / bundled)**: bundled 展開先(plugin/plangate/skills/ai-loop-cycle/scripts/)は既に 8 本列挙済み。sync 列挙は copy for リスト(L308)+ delete 保護 case(L318)の **2 箇所**へ c3_contract.py / test_c3_contract.py を追加。ta-30 TC-07 は `>= 2` の下限判定のため**期待値変更不要**(10 本になっても PASS)。ta-30 TC-08(bundled test_arbiter.py 自立実行)は arbiter が c3_contract を import しても同 dir に sync されるため成立 — Step 4 で実測確認。 + +## Work Breakdown (Steps) + +1. **Step 0: ベースライン確立 + API 契約固定** + - Output: 4 系テスト全 green の実測記録(`evidence/test-runs/step0-baseline.log`: test_arbiter 247 / test_plan_package 30 / test_c3prime_verify 12 / run-tests 411)+ c3_contract.py の公開 API docstring(定数一覧 / `canonical_hash(obj) -> str` / `check_snapshot_trio(container, reviewers, strict_keys) -> list[str]` / `sha256_of_file(path) -> str` の層区分明記) + - Owner: agent / Risk: 低 + - 🚩 チェックポイント: ベースライン数値が AC-4 の期待値と一致しているか(不一致なら即停止し原因調査 — 別 PBI の回帰を巻き込まない) + - rollback: 不要(読取・記録のみ) +2. **Step 1: 定数集約(コミット a)** + - Output: `c3_contract.py` 新設 — 定数は **ARTIFACTS / VALID_DECISIONS / VALID_VERDICTS / SNAPSHOT_KEYS + REQUIRED_KEYS 系(record 用 REQUIRED_KEYS・OPTIONAL_KEYS〔c3prime L36-43〕/ 入力ブロック用 PLAN_PACKAGE_REQUIRED_KEYS〔arbiter L478〕。重複はないが AC-1 verbatim の単一モジュール定義に含める。Refs: R-001)** + 3 消費者の定数 import 置換(arbiter は SNAPSHOT_REQUIRED_KEYS = c3_contract.SNAPSHOT_KEYS の別名参照可・値 byte 同一を assert する移行テスト付き)+ `test_c3_contract.py` 新設(定数値の契約固定テスト) + - **import 解決の確定(Refs: R-008)**: arbiter.py 本体に sys.path 操作は追加しない — CLI 直実行時は sys.path[0]=script dir で同 dir import が解決し、test 経由は test_arbiter.py L15 の既存 insert で解決する + - Owner: agent / Risk: 低 + - 🚩: 4 系テスト不変 green + 定数値 byte 同一 assert PASS + - rollback: `git revert <コミット a>`(単独 revert 可能な独立コミット) +3. **Step 2: hash ヘルパー統合(コミット b)** + - Output: `sha256_of_file` / `canonical_hash` を c3_contract.py へ追加し、plan_package.py `_sha256_of` L137 / c3prime_verify.py `_sha256` L46 / 両者の canonical JSON hash 式(json.dumps sort_keys + separators)を置換。test_c3_contract.py に境界値テスト追加(空 dict / キー順序非依存 / 1 byte 改変検出) + - Owner: agent / Risk: 中 + - 🚩: TC-09 系(1 byte 改変検出)+ 冪等テスト不変 PASS + - rollback: `git revert <コミット b>` +4. **Step 3: 三つ組照合コア共通化(コミット c)** + - Output: `check_snapshot_trio(container, reviewers, strict_keys)` を c3_contract.py へ追加(理由文字列リスト返却・I/O なし・**生成順序 = 検査順で契約固定 + 代表文言回帰テスト**。Refs: R-004)。arbiter `plan_package_check` は snapshot 検査部をこれで置換し tuple 変換(リスト非空 → `(True, False, 先頭)`)、c3prime_verify は strict_keys=True で呼び `_fail(先頭)`。残置(Refs: R-005/R-006): c3prime 側 = verdict 語彙・evidence_ref 独立性・AUTO_APPROVED 整合・**reviewers ちょうど 2 者検査(L158)** / arbiter 側 = plan_package 構造検査(PLAN_PACKAGE_REQUIRED_KEYS L503)・**source_sha vs target_sha 照合(L523-527・check_snapshot_trio は target_sha を受けない)** + - Owner: agent / Risk: 高(承認境界の核心規則) + - 🚩: snapshot 不一致 → BLOCKED(arbiter)/ reject(c3prime)の判定結果が両経路不変 + 偽造 14 パターン reject 不変 + **非対称保存の負側テスト**(余剰キー付き snapshot: arbiter = 通過 / c3prime = reject を test_c3_contract.py で両側固定) + - rollback: `git revert <コミット c>` +5. **Step 4: sync 配布整合 + CI 実行経路(コミット d)** + - Output: sync-plugin-plangate.sh の copy for リスト(L308 付近)+ delete 保護 case(L318 付近)へ c3_contract.py / test_c3_contract.py 追加 → `sh scripts/sync-plugin-plangate.sh` 実行 → plugin 再生成。**tests/extras/ta-55 へ `python3 scripts/ai-loop/test_c3_contract.py` 実行 1 行を追記**(新設テストの CI 実行経路・非 HO。Refs: R-010) + - **順序制約(Refs: R-009)**: コミット a〜c の途中で sync を実行しない(逆転禁止 — copy リスト未追加のまま新 arbiter.py だけが同期されると bundled 側 c3_contract 欠落で ta-30 TC-08 が FAIL する) + - Owner: agent / Risk: 低 + - 🚩: sync 2 回目 no-op(`git diff --quiet -- plugin/plangate/`)+ ta-30 実測 PASS(TC-07 scripts >= 2 / TC-08 bundled 自立 / TC-09 up-to-date skip) + - rollback: `git revert <コミット d>` + sync 再実行 +6. **Step 5: 検証総括 + 敵対レビュー** + - Output: 4 系テスト + run-tests 全 green の最終実測(`evidence/test-runs/`)→ **複数エージェント敵対レビュー 1 ラウンド以上**(受理器 touch のため。観点: 共通化で検証強度が weakening していないか / strict_keys 既定値の fail-open 余地 / import 失敗時の fail-closed)→ disposition 記録(AC-8) + - Owner: agent / Risk: 中 + - 🚩: 敵対レビューで major 以上が出た場合は是正 → 再レビュー(収束まで) + - rollback: 指摘是正コミットは各 Step の revert 単位に従う +7. **Step 6: 🚩 PR 作成 → C-4(Human)** + - Owner: human(マージ)/ Risk: - + +## Files / Components to Touch + +新設 2: `scripts/ai-loop/c3_contract.py` / `scripts/ai-loop/test_c3_contract.py` +変更 3: `scripts/ai-loop/arbiter.py` / `scripts/ai-loop/plan_package.py` / `scripts/ai-loop/c3prime_verify.py` +sync 1: `scripts/sync-plugin-plangate.sh` +sync 再生成 2: `plugin/plangate/skills/ai-loop-cycle/scripts/c3_contract.py` / 同 `test_c3_contract.py`(+ 既存 8 本の byte 同一維持) +CI 実行経路 1: `tests/extras/ta-55-c3prime-accept.sh`(test_c3_contract.py 実行 1 行追記。Refs: R-010) + +## Testing Strategy + +- Unit: `python3 scripts/ai-loop/test_c3_contract.py`(新設: 定数契約固定〔REQUIRED_KEYS 系含む〕/ hash 境界値 / trio 理由リスト境界値 + **順序 assert + 代表文言回帰**〔R-004〕/ strict・lenient 非対称の両側固定 / **I/O 封じ純粋性テスト**〔R-003: builtins.open 等を封じて実行〕)+ 既存 3 系不変 +- Integration: 既存 test_c3prime_verify.py(偽造 14 パターン = 受理器統合テスト・**残置**、pbi-input Unknowns 確定どおり test_c3_contract へ移さない)+ test_arbiter.py(plan_package_check 経路) +- E2E: ta-30(bundled 自立)+ ta-55(c3-prime 受理チェーン + test_c3_contract 実行追記)+ run-tests 既存 411 全 green(R-010 追記分のテスト数加算は許容・既存期待値の変更はゼロ) +- Edge cases: 余剰キー付き snapshot(非対称の両側)/ 空 reviewers / トップレベル値 None / canonical hash のキー順序非依存 / 1 byte 改変検出 +- Verification Automation: `python3 scripts/ai-loop/test_c3_contract.py && python3 scripts/ai-loop/test_arbiter.py && python3 scripts/ai-loop/test_plan_package.py && python3 scripts/ai-loop/test_c3prime_verify.py && sh tests/run-tests.sh` + +## Loop Scope + +単一 PBI(TASK-0896)の exec 内: 各コミット単位の「置換 → 4 系テスト検証 → 失敗時は該当コミット revert」の反復。#873/#874 へは跨がない。 + +## Stop Condition + +変更が Files to Touch 内 / Verification Automation 全 PASS / AC-1〜8 充足 / 偽造 14 パターン reject 不変の実測記録あり / 敵対レビュー disposition 記録済み。 + +## Resume Condition + +stop 後の再開は、原因・修正方針・検証手順を本 plan に追記し Replan 判定を通す。 + +## Replan Triggers + +- 変更ファイル数 > 14(= 想定 9 + 5) +- 同一検証コマンドの連続失敗 3 回 +- 同一ファイルへの修正反復 3 回 +- plan 外ディレクトリへの波及 1 件(`scripts/ai-loop/` / `scripts/sync-plugin-plangate.sh` / `plugin/plangate/skills/ai-loop-cycle/scripts/` / `docs/working/TASK-0896/` 以外) +- 既存テストの期待値変更が必要になった時点(= 振る舞い不変の前提崩壊 → 即 Replan。期待値を書き換えて通すことは禁止) +- AC / Verification コマンドの変更検知時 + +## Revert Policy + +停止時、Scope 外へ波及した変更のみを対象パス限定で `git restore -- `。コミットは 1 コミット 1 種類のため `git revert ` で局所巻き戻し可。ブランケットな `git stash` は使わない。 + +Loop Attempts:(exec 中に追記) +- attempt: / changed: / verification: / result: / next decision: + +## Risks & Mitigations + +| リスク | 検証手段 | Fallback | +|--------|---------|----------| +| 共通化で受理器の検証強度が意図せず変わる | Step 0 ベースライン + 偽造 14 パターン + 411 テストを各コミットで実測。非対称保存の負側テストを新規固定 | 該当コミットを局所 revert | +| 三つ組照合の非対称(arbiter 余剰許容 / c3prime strict)を「統一」してしまう | 論点 2 で strict_keys 引数保存を設計確定。負側テストで両側固定 | strict へ寄った場合 arbiter 既存テストが検出(余剰キー入力の decision 変化) | +| #896 が #873(P0)着手を遅延させる | **C-3 論点 1**(人間確定): #896 先行 or #873 と並行。並行時の重複は c3prime_verify の import 行のみ | 並行選択時は c3_contract 先行 merge → #873 が rebase | +| ta-30 bundled 自立が import 追加で壊れる | Step 4 で ta-30 実測(消費者側の既存 `sys.path.insert` パターン維持・c3_contract 側に I/O・path 操作を持ち込まない) | import fallback は消費者側 sys.path パターンで担保 | +| メッセージ文言の silent 変化 | 既存テストが decision / exit code のみ検証である実測に基づき、判定結果ベースで不変確認(論点 3)。文言は共通関数由来に単一化されることを PR 説明に明記 | 理由コード enum + 呼び出し側マッピング(V2 候補) | +| 理由リスト先頭要素の外部露出で文言・優先順が silent に変わる | R-004 対応: 生成順序を検査順で契約固定 + test_c3_contract.py で順序 assert + 代表文言回帰テスト | 順序変更が必要になったら契約改版として明示コミット | + +## Questions / Unknowns + +- ~~check_snapshot_trio の返り値契約~~ → 確定(論点 3・理由文字列リスト) +- ~~test_c3_contract.py の粒度~~ → 確定(純関数境界値のみ・偽造 14 は test_c3prime_verify 残置) +- **C-3 論点 1(人間確定・Open)**: EPIC #870 実装順への位置づけ — (a) #873 前に #896 逐次先行(P0 遅延を許容)or (b) #873 と並行(別セッション・c3_contract 先行 merge で rebase)。pbi-input Notes verbatim のとおり決定権 = 人間 +- **C-3 論点 2(Open)**: EPIC #870 本体 issue へ「#873 前提整理として #896」を追記する issue コメントの要否(位置づけ正式化の記録先) + +## Mode判定 + +**モード**: high-risk + +**判定根拠**: +- 変更ファイル数: 9 → high(6-15) +- 受入基準数: 8 → high(6-10) +- 変更種別: 承認境界の受理器(c3prime_verify)touch を含むリファクタ → セキュリティ関連「最低中」を上回る +- リスク: 検証強度の silent 変化リスク → 高 +- **最終判定**: high-risk(pbi-input 確定値を維持。Metrics 比率 1.0 = 範囲内で採用) + +**lite_eligible**: false(high-risk・承認境界の受理器 touch・新規モジュール設計あり) +**autonomous APPROVE**: 不可(working-context「C-3 Autonomous APPROVE 判定マトリクス」の Mode = high-risk → ❌ 人間 C-3 必須) diff --git a/docs/working/TASK-0896/review-external.md b/docs/working/TASK-0896/review-external.md new file mode 100644 index 00000000..ffd0276a --- /dev/null +++ b/docs/working/TASK-0896/review-external.md @@ -0,0 +1,46 @@ +# C-2 外部レビュー — TASK-0896(追記専用集約) + +> 実施: 2026-07-22 / 2 レーン並列(§7-bis 責務契約準拠) +> レーン A = Codex(設計妥当性: plan/todo/test-cases/pbi-input のみ・実装コード原則非読) +> レーン B = 独立 subagent(コードベース整合: 既存パターン 9 項目の一次ソース全数照合) + +## レーン B 総括 + +plan の「既存コード実態」主張は **9 項目中 9 項目で一次ソース整合**(critical/major = 0)。特に: 非対称の実態は「両者とも空値検査あり・差はキー集合 strict のみ」で plan 記述は正確 / 既存テストは reason 内部文言非 assert(priority 接頭のみ)で「判定結果ベースの不変確認」戦略は成立 / sync 列挙 2 箇所で他の列挙経路なし(install 系は find ベース・CI は glob・全数確認済み)/ import DAG は循環なし。 + +## 指摘一覧(R-NNN・追記専用) + +| R-NNN | レーン | severity | 指摘 | 裁定 | 根拠 | +|-------|--------|----------|------|------|------| +| R-001 | A | major | AC-1 の「REQUIRED_KEYS 系」が共通層集約の対象に含まれていない(Step 1 は 4 定数のみ・TC-1/TC-2 も非対象) | **採用** | 実測: REQUIRED_KEYS 系は c3prime_verify L36-43(record 用 REQUIRED/OPTIONAL/ALLOWED)と arbiter L478(入力ブロック用 PLAN_PACKAGE_REQUIRED_KEYS)で**重複はしていない**が、AC-1 verbatim は「単一モジュール定義」を要求 → c3_contract へ移設し import 参照に統一(additive・挙動不変) | +| R-002 | A | major | review-self.md に c3-prime 契約の `C1-VERDICT:` マーカー + plan hash がなく C-3' presence gate が fail-closed になる | **不採用** | 経路違い。c3-prime evidence マーカーは **ai-loop run(C-3' arbiter 裁定経路)**の機械判定用。本 plan 正式化は human C-3(`bin/plangate approve` → c3.json)経路で、validate の legacy 経路は evidence マーカーを要求しない。前例: TASK-0872 の review-self.md もマーカーなしで human C-3 受理(grep 実測 0 件)。将来本 TASK を ai-loop run に載せる場合はマーカー付与が必要(info として記録) | +| R-003 | A | major | AC-3「I/O なし純関数」を失敗させる検証手段が TC に定義されていない | **採用** | TC-5 を拡張: builtins.open / Path.read_bytes を封じた状態で check_snapshot_trio / canonical_hash を実行して成功する monkeypatch テスト + arbiter が sha256_of_file を参照しない静的検査(TC-10 と統合) | +| R-004 | A | major | 理由リストの順序・文言が契約化も回帰検証もされておらず、先頭要素を外部へ出す設計と「振る舞い不変」が整合しない | **採用** | 論点 3 を拡張: 理由リストの生成順序を検査順(キー集合 → 空値 → 三つ組不一致)で契約固定し test_c3_contract.py で順序 assert。外部可視文言(arbiter reason / c3prime stderr の代表例)を回帰テストで固定 | +| R-005 | B | minor | 残置リストに「reviewers ちょうど 2 者」検査(c3prime L158・#889 R2)が漏れ | **採用** | 論点 2 / Step 3 の残置列挙へ追記。**reviewer 集合の strict/lenient(arbiter=余剰 reviewer 許容 L510 / c3prime=拒否 L158)も snapshot キーと同様の意図的非対称 = 保存対象**と明記 | +| R-006 | B | minor | arbiter 残置のうち source_sha vs target_sha 照合(L523-527)が Step 3 に明記なし・切断面が曖昧 | **採用** | Step 3 Output に残置 1 行追記 | +| R-007 | B | info | canonical hash の行番号表記ゆれ(plan「L146-152」/ 実体 L148-153) | **採用** | Metrics Evidence の行番号修正 | +| R-008 | B | info | arbiter.py 本体に sys.path.insert パターンは存在しない(test 側/c3prime 側のみ) | **採用** | Step 1 に確定方針を明記: arbiter は CLI 直実行時 sys.path[0]=script dir で同 dir import が解決するため **arbiter 自体への sys.path 操作追加は不要**(test 経由は test_arbiter.py L15 の既存 insert で解決) | +| R-009 | B | info | sync 実行を Step 4 まで遅らせる順序は必須(a〜c 途中で sync すると bundled 側 c3_contract 欠落で ta-30 TC-08 FAIL) | **採用** | Step 4 に「コミット a〜c の途中で sync を実行しない(逆転禁止)」を明記 | +| R-010 | B | info(AC 候補返送) | 新設 test_c3_contract.py が CI 自動実行経路に乗らない(test.yml は run-tests.sh のみ・extras に source tree python テスト直実行なし = 既存 4 系と同型の pre-existing 構造) | **採用(最小対応)** | 既存構造と同型のため AC 追加はしないが、bundled 側は ta-30 TC-08 と同型で担保可能 → Step 4 に「ta-30 の bundled 自立実行対象へ test_c3_contract.py を追加検討(1 行)」…ではなく最小確定: **tests/extras/ta-55 へ `python3 scripts/ai-loop/test_c3_contract.py` 実行 1 行を追記**(tests/ は非 HO)。Files to Touch +1(実数 9・Replan 閾値 13 内) | + +## 集計 + +- レーン A: major 4(採用 3 / 不採用 1)・スコープ整合とリスク 3 点セットは「指摘なし」明示 +- レーン B: critical/major 0・minor 2・info 4(全採用)・照合 OK 9/9 +- **確定反映対象**: R-001 / R-003 / R-004 / R-005 / R-006 / R-007 / R-008 / R-009 / R-010(1 回確定反映・Refs 付きコミット) +- 反映後: 簡易 C-1 再実行 → 人間 C-3(c3.json 発行は確定反映の後 = EH-3 整合順序) + +## 監査表(追記専用) + +| R-NNN | status | reflected_in (commit) | notes | +|-------|--------|----------------------|-------| +| R-001 | 採用 | f6bf1a9 | AC-1 verbatim 準拠・REQUIRED_KEYS 系 3 定数を c3_contract へ | +| R-002 | 不採用 | — | 経路違い(human C-3 経路・前例 TASK-0872) | +| R-003 | 採用 | f6bf1a9 | TC-5/TC-10 拡張 | +| R-004 | 採用 | f6bf1a9 | 理由リスト順序契約 + 代表文言回帰固定 | +| R-005 | 採用 | f6bf1a9 | reviewer 集合非対称の保存を明記 | +| R-006 | 採用 | f6bf1a9 | source_sha/target_sha 残置明記 | +| R-007 | 採用 | f6bf1a9 | 行番号修正 | +| R-008 | 採用 | f6bf1a9 | arbiter sys.path 不要の確定 | +| R-009 | 採用 | f6bf1a9 | sync 順序逆転禁止 | +| R-010 | 採用 | f6bf1a9 | ta-55 に 1 行追記・実数 9 へ更新 | diff --git a/docs/working/TASK-0896/review-self.md b/docs/working/TASK-0896/review-self.md new file mode 100644 index 00000000..d7e6a549 --- /dev/null +++ b/docs/working/TASK-0896/review-self.md @@ -0,0 +1,55 @@ +# C-1 セルフレビュー — TASK-0896 + +> 実施: 2026-07-22 / 対象: plan.md / todo.md / test-cases.md(B-3 生成物) +> 判定: **PASS**(WARN 1 件・FAIL 0 件) + +## Plan チェック(7 項目) + +| # | 項目 | 判定 | 根拠 | +|---|------|------|------| +| C1-PLAN-01 | 受入基準網羅性 | PASS | AC-1〜8(issue verbatim)すべてが Work Breakdown の Step 1〜5 と test-cases.md の TC-1〜12 に写像(マッピング表で全数確認。AC-6 は TC-10 静的検査 + 回帰テストで機械化) | +| C1-PLAN-02 | Unknowns 処理 | PASS | pbi-input の Unknowns 2 件は確定済みを追認(論点 3 / test 粒度)。未確定の 2 件(#873 実装順 / EPIC 追記コメント)は **C-3 論点として人間へ明示送付**し推測で確定していない | +| C1-PLAN-03 | スコープ制御 | PASS | Non-goals(issue verbatim 3 件: 全統合しない / 契約不変 / 機能変更ゼロ)+ 不変条件(偽造 14 reject / 4 系 green / 非対称保存)を Constraints に明記。Replan Triggers に機械値(ファイル数 >13 / 既存テスト期待値変更 = 即 Replan)あり | +| C1-PLAN-04 | テスト戦略 | PASS | Unit / Integration / E2E / Edge / Verification Automation すべて具体コマンド。既存テストがメッセージ非検証である実測(assert 0 件)に基づき「判定結果ベースの不変確認」を明示 | +| C1-PLAN-05 | Work Breakdown Output | PASS | Step 0〜6 全てに Output / Owner / Risk / 🚩 / rollback。Output の無い Step なし | +| C1-PLAN-06 | 依存関係 | PASS | コミット a→b→c→d 直列(各コミット green 維持)。C-3 APPROVED 前に exec 開始しない構造を todo ⚠️ 節に明示 | +| C1-PLAN-07 | 動作検証自動化 | PASS | `python3 test_c3_contract.py && test_arbiter.py && test_plan_package.py && test_c3prime_verify.py && sh tests/run-tests.sh` を Stop Condition と連動 | + +## ToDo チェック(5 項目) + +| # | 項目 | 判定 | 根拠 | +|---|------|------|------| +| C1-TODO-01 | タスク粒度 | **WARN** | T-11(arbiter + c3prime の両置換 + 偽造 14 検証)と T-15(敵対レビュー)は 2-5 分粒度を超える見込み。T-11 は exec 時に「arbiter 置換 → 検証」「c3prime 置換 → 検証」の 2 サブへ分割する前提を注記。T-15 は subagent 派遣 1 アクション + disposition で構造上分割不能 | +| C1-TODO-02 | depends_on 設定 | PASS | 全 17 タスクに depends_on 記載。循環なし・直列制約がコミット戦略と一致 | +| C1-TODO-03 | チェックポイント設定 | PASS | 全タスクに 🚩。Human ゲート(C-3 / C-4)明示 | +| C1-TODO-04 | Iron Law 遵守 | PASS | main 直接 push なし(PR 前提)/ 非 HO 確認済み(HO 適用タスクなし)/ exec は C-3 APPROVED 後のみ / L-0〜V-4 は conductor 制御で todo に含めない | +| C1-TODO-05 | 完了条件 | PASS | 各実装タスクに「4 系テスト green」の機械確認条件 + rollback 記載(high-risk 必須要件充足) | + +## TestCases チェック(3 項目) + +| # | 項目 | 判定 | 根拠 | +|---|------|------|------| +| C1-TC-01 | 受入基準との紐付き | PASS | AC-1〜8 → TC-1〜12 全数マッピング(表で確認) | +| C1-TC-02 | Edge case 網羅 | PASS | TC-6(strict/lenient 非対称の両側固定 = 本 PBI 最重要 edge)+ TC-E1〜E3(空 reviewers / None 一致 / bundled 自立) | +| C1-TC-03 | 自動化可否 | PASS | TC-12(敵対レビュー記録)のみ性質上手動(記録検査)、他 11 件は全て自動実行可能なコマンド付き | + +## 指摘事項まとめ + +- WARN-1(C1-TODO-01): T-11 の粒度超過 → exec 時に 2 サブ分割(対応方針記載済み・plan 修正不要) + +**総合判定: PASS**(軽微 WARN 1 件のみ・FAIL なし)。C-2 外部レビュー(2 レーン)へ進む。 + +--- + +## 簡易 C-1 再実行(C-2 確定反映後 / 2026-07-22) + +R-001/R-003〜R-010 反映後の plan/todo/test-cases を再確認: + +| 項目 | 判定 | 根拠 | +|------|------|------| +| 受入基準網羅性 | PASS | AC-1 の REQUIRED_KEYS 系が Step 1 / TC-1 / TC-2 に写像された(R-001 是正で AC-1 完全網羅) | +| スコープ制御 | PASS | R-010 の ta-55 追記は CI 実行経路の最小追加(+1 ファイル・Replan 閾値 14 内・非 HO)。Non-goals 逸脱なし | +| テスト戦略 | PASS | 純粋性検証(R-003)・順序契約/代表文言回帰(R-004)が TC-5 に追加され「振る舞い不変」の検証手段が閉じた | +| 整合性 | PASS | Metrics 実数 9 / 比率 1.0 / Replan 閾値 14 / Files to Touch 9 で全記述一致(stale 記述の残存を grep で全数確認済み) | + +**総合判定: PASS** — C-3(Human)へ。 diff --git a/docs/working/TASK-0896/status.md b/docs/working/TASK-0896/status.md new file mode 100644 index 00000000..161a2dbf --- /dev/null +++ b/docs/working/TASK-0896/status.md @@ -0,0 +1,30 @@ +# status — TASK-0896 + +> Issue: [#896](https://github.com/s977043/plangate/issues/896) / Mode: high-risk / branch: `docs/task-0896-plan` + +## フェーズ履歴 + +| 日時 | フェーズ | 結果 | +|------|---------|------| +| 2026-07-22 10:00 | B(B-1→B-3) | plan/todo/test-cases 生成(確認質問なし・メトリクス実測 8→C-2 後 9) | +| 2026-07-22 12:40 | C-1 | PASS(WARN 1 = T-11 粒度) | +| 2026-07-22 13:10 | C-2 | 2 レーン完了(A=Codex 設計妥当性 major 4 / B=コードベース整合 9/9 照合 OK) | +| 2026-07-22 13:25 | C-2 確定反映 | R-001/R-003〜R-010 採用反映(f6bf1a9)・R-002 不採用(経路違い)・簡易 C-1 PASS | +| 2026-07-22 13:40 | C-3 | **APPROVED**(下記) | + +## C-3 Gate: APPROVED + +- 判定: **APPROVE**(Human・AskUserQuestion 回答 verbatim: 「APPROVE」) +- 論点 1(#873 実装順): **並行**(「並行 (Recommended)」— #896 exec は本セッション継続、#873 plan は別セッション。c3_contract 先行 merge → #873 が rebase) +- 論点 2(EPIC #870 追記): **投稿する**(AI が下書き投稿・論点 1 の決定内容を反映) +- c3.json 発行: Human が `bin/plangate approve TASK-0896` を plan branch 上で実行(発行は確定反映 f6bf1a9 の後 = EH-3 整合順序) + +## 残タスク + +- [ ] c3.json 発行(Human・`bin/plangate approve TASK-0896`) +- [ ] c3.json commit + push → PR 作成(plan 正式化 PR・1 PR 完結方式) +- [ ] C-4 マージ後: exec 開始(todo.md T-1〜T-17・本セッション or `PLANGATE_HOOK_TASK=TASK-0896` 再起動セッション) + +## 参照 + +- plan/todo/test-cases: 本ディレクトリ / C-2 集約: [review-external.md](./review-external.md) diff --git a/docs/working/TASK-0896/test-cases.md b/docs/working/TASK-0896/test-cases.md new file mode 100644 index 00000000..7649d624 --- /dev/null +++ b/docs/working/TASK-0896/test-cases.md @@ -0,0 +1,110 @@ +# TEST CASES — TASK-0896 + +> plan: [`plan.md`](./plan.md) / 受入基準: pbi-input.md(issue #896 verbatim・8 項目) + +## 受入基準 → テストケース マッピング + +| 受入基準 | テストケースID | 種別 | +|---------|--------------|------| +| AC-1: 契約定数が単一モジュール定義・3 消費者が import 参照 | TC-1, TC-2 | Unit + 静的検査 | +| AC-2: sha256 / canonical JSON hash が単一実装 | TC-3, TC-4 | Unit + 静的検査 | +| AC-3: 三つ組照合が I/O なし共通純関数・両経路同一実装 | TC-5(I/O 封じ純粋性・順序契約含む), TC-6, TC-7 | Unit + 静的検査 | +| AC-4: 既存テスト全 green(振る舞い不変) | TC-8 | Integration | +| AC-5: sync 列挙追加・sync 2 回目 no-op・ta-30 自立 PASS | TC-9 | E2E | +| AC-6: arbiter が I/O あり関数を import / call しない | TC-10 | 静的検査 | +| AC-7: 偽造 record 群の reject 不変 | TC-11 | Integration | +| AC-8: 敵対レビュー 1 ラウンド以上の disposition 記録 | TC-12 | 手動(記録検査) | + +## テストケース一覧 + +### TC-1: 契約定数の単一定義と値固定 +- 前提条件: コミット a 適用後 +- 入力: `python3 scripts/ai-loop/test_c3_contract.py` +- 期待出力: ARTIFACTS(6 要素)/ VALID_DECISIONS(3 値)/ VALID_VERDICTS(2 値)/ SNAPSHOT_KEYS(5 キー)+ **REQUIRED_KEYS 系(record 用 REQUIRED_KEYS・OPTIONAL_KEYS / PLAN_PACKAGE_REQUIRED_KEYS。Refs: R-001)**の値が現行実装と byte 同一で PASS +- 種別: Unit + +### TC-2: 3 消費者のローカル定数定義が消えている +- 前提条件: コミット a 適用後 +- 入力: `grep -n "^ARTIFACTS = \|^VALID_DECISIONS = \|^VALID_VERDICTS = \|^SNAPSHOT_KEYS = \|^SNAPSHOT_REQUIRED_KEYS = (\|^REQUIRED_KEYS = (\|^OPTIONAL_KEYS = (\|^PLAN_PACKAGE_REQUIRED_KEYS = (" scripts/ai-loop/{arbiter,plan_package,c3prime_verify}.py` +- 期待出力: タプルリテラルの重複定義 0 件(c3_contract からの import / 別名代入のみ) +- 種別: 静的検査 + +### TC-3: hash ヘルパーの境界値 +- 前提条件: コミット b 適用後 +- 入力: `canonical_hash({})` / キー順序を変えた同値 dict ×2 / `sha256_of_file` に 1 byte 違いのファイル 2 つ +- 期待出力: 空 dict でも決定論的 / キー順序非依存で同一 hash / 1 byte 改変で hash 相違(TC-09 系の改変検出が成立) +- 種別: Unit + +### TC-4: hash 実装の重複が消えている +- 前提条件: コミット b 適用後 +- 入力: `grep -n "def _sha256\|hashlib.sha256" scripts/ai-loop/{plan_package,c3prime_verify}.py` +- 期待出力: ローカル hash 実装 0 件(c3_contract 経由のみ) +- 種別: 静的検査 + +### TC-5: check_snapshot_trio の理由リスト契約(順序 + 純粋性含む) +- 前提条件: コミット c 適用後 +- 入力: 正常 snapshot(5 キー・三つ組一致)/ キー欠落 / 空値 / 三つ組不一致(plan_hash・source_sha・plan_package_hash 各不一致)/ reviewers 非 dict / **複合異常(キー集合異常 + 空値 + 三つ組不一致を同時に含む入力)** / **builtins.open・Path.read_bytes を monkeypatch で封じた状態での実行** +- 期待出力: 正常 = 空リスト / 異常 = 非空の理由文字列リスト(例外を投げない)/ **複合異常の理由リストが契約順序(キー集合 → 空値 → 三つ組不一致)で並ぶ(Refs: R-004)** / **I/O 封じ下でも成功 = 純粋性の失敗検証(Refs: R-003)** / **代表文言 2 本(キー不一致・三つ組不一致)が回帰固定と一致** +- 種別: Unit + +### TC-6: strict / lenient 非対称の両側固定(Edge・最重要) +- 前提条件: コミット c 適用後 +- 入力: 余剰キー付き snapshot(6 キー目を追加)を strict_keys=True / False で照合 +- 期待出力: strict=True → 理由リスト非空(c3prime 経路 = reject 維持)/ strict=False → 空リスト(arbiter 経路 = 通過維持)。**#889 R2 由来の非対称が保存されている** +- 種別: Unit + +### TC-7: 両経路の判定結果不変 +- 前提条件: コミット c 適用後 +- 入力: snapshot 不一致入力を arbiter(plan_package_check)と c3prime_verify に投入 +- 期待出力: arbiter = `(True, False, reason)` → BLOCKED priority 1.65 / c3prime_verify = exit 非 0 の reject(既存テストの decision / exit code 期待と一致) +- 種別: Integration + +### TC-8: 既存 4 系テスト全 green(AC-4) +- 前提条件: 各コミット後 + 最終 +- 入力: `python3 scripts/ai-loop/test_arbiter.py && python3 scripts/ai-loop/test_plan_package.py && python3 scripts/ai-loop/test_c3prime_verify.py && sh tests/run-tests.sh` +- 期待出力: test_arbiter 247 / test_plan_package 30 / test_c3prime_verify 12 / run-tests 411 — **期待値変更ゼロ**で全 PASS +- 種別: Integration + +### TC-9: sync 配布整合(AC-5) +- 前提条件: コミット d 適用後 +- 入力: `sh scripts/sync-plugin-plangate.sh`(2 回)→ `git diff --quiet -- plugin/plangate/` → ta-30 実行 → ta-55 実行 +- 期待出力: 1 回目で c3_contract.py / test_c3_contract.py が plugin へ配布・2 回目 no-op(diff 空)/ ta-30 TC-07(scripts >= 2)・TC-08(bundled test_arbiter 自立 PASS)・TC-09(installed:0)PASS / **ta-55 が test_c3_contract.py を実行し PASS(CI 実行経路。Refs: R-010)** +- 種別: E2E + +### TC-10: arbiter の I/O なし import 制約(AC-6) +- 前提条件: コミット c 適用後 +- 入力: `grep -n "sha256_of_file" scripts/ai-loop/arbiter.py` +- 期待出力: 0 件(arbiter が import / call するのは定数・canonical_hash・check_snapshot_trio のみ)。test_c3_contract.py にも同制約の回帰テスト(arbiter モジュールの属性参照検査)を置く +- 種別: 静的検査 + +### TC-11: 偽造 record 群 reject 不変(AC-7) +- 前提条件: 最終形 +- 入力: `python3 scripts/ai-loop/test_c3prime_verify.py`(手 mutate 偽造系: c3_status 混入 / 未知キー / 必須欠落 / source_sha 不整合 / evidence 偽造 / task_id 非束縛 / reviewer 独立性違反 / AUTO_APPROVED 改竄 ほか 14 パターン) +- 期待出力: 全パターン reject(12 テスト PASS・変更ゼロ) +- 種別: Integration + +### TC-12: 敵対レビュー disposition(AC-8) +- 前提条件: T-15 完了 +- 入力: `docs/working/TASK-0896/evidence/` の敵対レビュー記録 +- 期待出力: 1 ラウンド以上・指摘ごとの採否 + 根拠が記録されている(major 以上は是正済み) +- 種別: 手動(記録検査) + +## エッジケース + +### TC-E1: reviewers が空 dict / None +- 前提条件: コミット c 適用後 +- 入力: `check_snapshot_trio(container, {}, strict_keys=...)` / reviewers=None +- 期待出力: 非空理由リスト(例外でなく fail-closed の理由返却) +- 種別: Unit + +### TC-E2: container 側トップレベル値が None / 欠落 +- 前提条件: コミット c 適用後 +- 入力: container の plan_hash が None のまま snapshot は値あり +- 期待出力: 不一致として非空理由リスト(None == None の偶然一致で通過しない — 両側 None ケースも理由リスト非空か既存挙動と同一かを実装時に既存テストで確認し、挙動を変えない) +- 種別: Unit / Edge + +### TC-E3: bundled 展開先での import 自立 +- 前提条件: コミット d 適用後 +- 入力: ta-30 TC-08(展開先 tmp dir で `python3 test_arbiter.py`) +- 期待出力: 同 dir の c3_contract.py を sys.path 既存パターンで解決し PASS(リポジトリ外で自立) +- 種別: E2E / Edge diff --git a/docs/working/TASK-0896/todo.md b/docs/working/TASK-0896/todo.md new file mode 100644 index 00000000..b4d2ae88 --- /dev/null +++ b/docs/working/TASK-0896/todo.md @@ -0,0 +1,72 @@ +# EXECUTION TODO — TASK-0896 + +> plan: [`plan.md`](./plan.md) / Mode: high-risk(rollback 必須) +> L-0〜V-4・PR 作成は workflow-conductor が自動制御するため含めない + +## 🤖 Agentタスク + +### 準備フェーズ + +- [ ] 🚩 T-1: Scope/受入基準(AC-1〜8)を再掲し、Files to Touch 8 ファイルに作業範囲を固定する [Owner: agent] [depends_on: -] [files: -] + - rollback: 不要(読取のみ) +- [ ] 🚩 T-2: ベースライン実測 — 4 系テスト(test_arbiter 247 / test_plan_package 30 / test_c3prime_verify 12 / run-tests 411)を実行し `evidence/test-runs/step0-baseline.log` に記録。期待値と不一致なら即停止 [Owner: agent] [depends_on: T-1] [files: docs/working/TASK-0896/evidence/test-runs/step0-baseline.log] + - rollback: 不要(読取・記録のみ) + +### 実装フェーズ Step 1: 定数集約(コミット a) + +- [ ] 🚩 T-3: `c3_contract.py` 新設 — API docstring(層区分明記)+ 契約定数(ARTIFACTS / VALID_DECISIONS / VALID_VERDICTS / SNAPSHOT_KEYS + REQUIRED_KEYS 系〔record 用 REQUIRED_KEYS・OPTIONAL_KEYS / 入力ブロック用 PLAN_PACKAGE_REQUIRED_KEYS〕Refs: R-001)のみ定義。arbiter への sys.path 操作は追加しない(Refs: R-008) [Owner: agent] [depends_on: T-2] [files: scripts/ai-loop/c3_contract.py] + - rollback: ファイル削除(`git rm`) +- [ ] 🚩 T-4: `test_c3_contract.py` 新設 — 定数値の契約固定テスト(既存 3 ファイルの値と byte 同一 assert を含む移行テスト)を書き、RED→GREEN 確認 [Owner: agent] [depends_on: T-3] [files: scripts/ai-loop/test_c3_contract.py] + - rollback: ファイル削除 +- [ ] 🚩 T-5: 3 消費者の定数 import 置換(arbiter.py / plan_package.py / c3prime_verify.py・ローカル定義削除)→ 4 系テスト green 確認 → コミット a [Owner: agent] [depends_on: T-4] [files: scripts/ai-loop/arbiter.py, scripts/ai-loop/plan_package.py, scripts/ai-loop/c3prime_verify.py] + - rollback: `git revert <コミット a>` + +### 実装フェーズ Step 2: hash 統合(コミット b) + +- [ ] 🚩 T-6: test_c3_contract.py に hash 境界値テスト追加(空 dict / キー順序非依存 / 1 byte 改変検出)→ RED 確認 [Owner: agent] [depends_on: T-5] [files: scripts/ai-loop/test_c3_contract.py] + - rollback: `git revert <コミット b>` +- [ ] 🚩 T-7: `sha256_of_file` / `canonical_hash` を c3_contract.py に実装 → GREEN 確認 [Owner: agent] [depends_on: T-6] [files: scripts/ai-loop/c3_contract.py] + - rollback: `git revert <コミット b>` +- [ ] 🚩 T-8: plan_package.py `_sha256_of` / c3prime_verify.py `_sha256` / 両者の canonical hash 式を import 置換 → 4 系テスト green → コミット b [Owner: agent] [depends_on: T-7] [files: scripts/ai-loop/plan_package.py, scripts/ai-loop/c3prime_verify.py] + - rollback: `git revert <コミット b>` + +### 実装フェーズ Step 3: 三つ組照合コア(コミット c) + +- [ ] 🚩 T-9: test_c3_contract.py に trio 境界値テスト追加(キー欠落 / 空値 / 型不一致 / 三つ組不一致 / **余剰キーの strict・lenient 両側固定** / **理由リストの順序 assert + 代表文言回帰**〔Refs: R-004〕/ **I/O 封じ純粋性テスト**〔Refs: R-003〕)→ RED 確認 [Owner: agent] [depends_on: T-8] [files: scripts/ai-loop/test_c3_contract.py] + - rollback: `git revert <コミット c>` +- [ ] 🚩 T-10: `check_snapshot_trio(container, reviewers, strict_keys)` を c3_contract.py に実装(理由文字列リスト返却・I/O なし)→ GREEN 確認 [Owner: agent] [depends_on: T-9] [files: scripts/ai-loop/c3_contract.py] + - rollback: `git revert <コミット c>` +- [ ] 🚩 T-11: arbiter `plan_package_check` の snapshot 検査部を置換(tuple 変換・strict_keys=False)+ c3prime_verify 置換(strict_keys=True・`_fail(先頭)`)。残置 = c3prime 側: verdict 語彙 / 独立性 / AUTO_APPROVED 整合 / **reviewers ちょうど 2 者**〔R-005〕、arbiter 側: PLAN_PACKAGE 構造検査 / **source_sha vs target_sha 照合**〔R-006〕→ 4 系テスト + 偽造 14 パターン reject 不変を確認 → コミット c(exec 時は「arbiter 置換→検証」「c3prime 置換→検証」の 2 サブに分割・C-1 WARN-1 対応) [Owner: agent] [depends_on: T-10] [files: scripts/ai-loop/arbiter.py, scripts/ai-loop/c3prime_verify.py] + - rollback: `git revert <コミット c>` + +### 実装フェーズ Step 4: sync 整合(コミット d) + +- [ ] 🚩 T-12: sync-plugin-plangate.sh の copy for リスト + delete 保護 case へ c3_contract.py / test_c3_contract.py 追加 + **ta-55 へ `python3 scripts/ai-loop/test_c3_contract.py` 実行 1 行追記**〔R-010〕→ `sh scripts/sync-plugin-plangate.sh` 実行(**a〜c 途中での sync 実行禁止**〔R-009〕) [Owner: agent] [depends_on: T-11] [files: scripts/sync-plugin-plangate.sh, tests/extras/ta-55-c3prime-accept.sh, plugin/plangate/skills/ai-loop-cycle/scripts/] + - rollback: `git revert <コミット d>` + sync 再実行 +- [ ] 🚩 T-13: sync 2 回目 no-op(`git diff --quiet -- plugin/plangate/`)+ ta-30 実測 PASS(TC-07/08/09)→ コミット d [Owner: agent] [depends_on: T-12] [files: -] + - rollback: 不要(検証のみ) + +### 検証フェーズ + +- [ ] 🚩 T-14: Verification Automation 全実行(test_c3_contract + 既存 3 系 + run-tests 411)→ `evidence/test-runs/` に記録 [Owner: agent] [depends_on: T-13] [files: docs/working/TASK-0896/evidence/test-runs/] + - rollback: 不要 +- [ ] 🚩 T-15: 敵対レビュー 1 ラウンド以上(複数エージェント・観点: 検証強度 weakening / strict_keys fail-open / import 失敗 fail-closed)→ disposition 記録(AC-8)。major 以上は是正 → 再レビュー [Owner: agent] [depends_on: T-14] [files: docs/working/TASK-0896/evidence/] + - rollback: 是正コミットは各 Step の revert 単位に従う +- [ ] 🚩 T-16: AC-1〜8 の全確認(test-cases.md 突合) [Owner: agent] [depends_on: T-15] [files: -] + - rollback: 不要 + +### 完了フェーズ + +- [ ] 🚩 T-17: status.md / current-state.md 最終更新 [Owner: agent] [depends_on: T-16] [files: docs/working/TASK-0896/status.md, docs/working/TASK-0896/current-state.md] + - rollback: 不要 + +## 👤 Humanタスク + +- [ ] C-3: Plan/ToDo/Test Cases の人間レビュー(exec 前ゲート・**論点 1: #873 との実装順の確定** / 論点 2: EPIC #870 への追記コメント要否) [Owner: human] +- [ ] C-4: PR レビュー・承認(GitHub 上) [Owner: human] + +## ⚠️ 依存関係 + +- Agent 実装(T-3 以降)→ Human C-3 APPROVED(c3.json)後に exec 開始 +- PR 作成 → Human C-4 承認後にマージ +- コミット a→b→c→d は直列(各コミットで green 維持が前提)