feat(cli-fix-push-gate): Phase B の 4 軸ゲートと判定コアの lib 化 (WP-17 2b) - #351
Conversation
Phase B (CI 側の無人 fix push) が同じ scope 検証を必要とするため、cli-pr-monitor に 閉じていた判定コアを lib 化する。挙動は不変。 抽出根拠は ADR-044 層 1 の「3+ crate 重複」ではなく **判定の同一性**。同一 ADR (ADR-054) の防御がローカル経路と CI 経路に分岐すると、片方だけ緩んだ時点で injection 防御が無効化される。重複数ではなくこの drift リスクを根拠に、2 呼び手の時点で抽出する (lib-docs-policy が ADR-035 の path 基準を単一実装へ集約したのと同じ理由)。 lib に置いたもの (純粋な文字列処理のみ): - normalize_path / allowlist_from_paths / parse_changed_files / find_out_of_scope - ALWAYS_ALLOWED (.takt/review-diff.txt) 呼び出し側に残したもの: diff の取得 (jj / git)、mode 判定 (enforce/observe)、 kill-switch、ログ出力。lib は「変更ファイル集合が許可集合に収まるか」だけを答える。 allowlist の入力を Finding 型ではなくパス文字列の iterator にしたのは、findings の 表現が経路ごとに異なるため (ローカル = lib_report_formatter::Finding、CI = JSON)。 これにより lib は依存ゼロを保てる。 テストも移設し、lib 側に 11 件 (境界値の網羅: 空 summary / 空白行 / tab 区切り / 空白を含むパス / 空 allowlist)。cli-pr-monitor 側には Finding→パスの glue と mode/統合テストのみ残す。tab 区切り拒否のテストを新規追加した — git diff --name-status は tab 区切りで、CI 経路が正規化を怠ると fail-closed に倒れることを固定するため。 検証: cargo clippy -D warnings 緑、cli-pr-monitor 260 件 + lib-scope-guard 11 件 pass。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
後続 PR (WP-17 PR 2b) の fix push 直前ゲート `cli-fix-push-gate` が kill-switch を 含む全軸を 1 回で評価できるようにするため、cli-autonomy-gate 内の decision.rs / sources.rs を lib へ移す。挙動は不変 (テスト 21 件は PR 1 と同数を維持)。 **本 PR の時点で 2 つ目の呼び手はまだ存在しない。** ADR-044 層 1 は「2 つ目の使用例が 出た時点で extract」と定めるが、後から昇格すると kill-switch 判定が一時的に 2 箇所へ 分岐する期間ができる。その期間を作らないための**明示的な前倒し判断**であり、 層 1 の厳密な充足ではない。この位置づけは各 module doc にも記載した (pre-push review の simplicity facet が「存在しない 2nd caller を根拠にしている」と 指摘し、未来形へ言い換える fix が入った。指摘は妥当で、4 箇所すべてが修正済み)。 exe 連鎖 (`cli-autonomy-gate && cli-fix-push-gate`) を採らなかった理由: workflow 側で `&&` を書き忘れると kill-switch を通り越して fix push が走る。 「1 つでも欠けたら停止」の fail-closed 合成が呼び手のミスで壊れる経路を残さないため、 ライブラリ共有にして fix push ゲートが構造的に kill-switch を含む形にする。 cli-autonomy-gate は CLI 面 (引数解析・loud 出力・exit コード・telemetry) だけを持つ 薄い exe になり、汎用の kill-switch 判定 (pnpm autonomy-status / 単体 drill) を担う。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ADR-052 自動実行可クラスの 4 軸を 1 回の呼び出しで AND 評価する決定論ゲート。 GitHub Actions の Phase B job が push 直前に呼び、非ゼロなら push を止めて Phase A 相当 (分析コメントのみ) へ degrade する。 4 軸と根拠: - kill-switch (ADR-066 / ADR-052 原則 5): --config + env AUTONOMY_ENABLED - target 軸 (ADR-052 原則 2): --branch が claude/ prefix であること - 内容軸 (ADR-052 原則 2 / ADR-035): fix diff が docs-only であること - scope (ADR-054): 変更ファイルが findings 由来 allowlist に収まること 各軸の基準は既存の単一実装 (lib-autonomy-policy / lib-docs-policy / lib-scope-guard) を借り、本 crate 固有のロジックは AND 合成と判定順序だけに留めた。基準の再実装は ADR-035 / ADR-054 が防ごうとした drift の再生産にあたるため。 判定順は kill-switch → ブランチ → 空 diff → 内容軸 → scope。空 diff を内容軸より先に 見るのは、is_docs_only_summary が空入力へ false を返す仕様で、そのままだと「変更なし」が 「docs-only ではない」と誤報告されるため (drill 6 で実確認)。 findings の出所に関する契約を inputs.rs の doc に明記した: findings は fix を書いた エージェント自身ではなく、先行する読み取り専用の分析 step の出力でなければならない。 同一エージェントが両方を出すと scope guard が自己申告の追認になり ADR-054 の防御が 成立しない (ローカル経路で review facet と fix step が別エージェントなのと同じ分離)。 loud 出力は deny 理由 1 つに絞らず 4 軸すべての状態を出す。drill 3 (非 claude ブランチ) では autonomy=allowed branch=not-isolated content=docs-only scope=in-scope と出て、 ブランチだけが原因と 1 行で読める。 引数は 4 つとも必須。省略で軸が無検査になる fail-open を作らないため、どれか 1 つでも 落とすと exit 2 になることをテストで固定した。 テスト 22 件 + 実 exe drill 7 シナリオ (全軸 OK → exit 0、kill-switch / 非 claude ブランチ / scope violation / code 変更 / 空 diff / rename fail-closed → 全て exit 1)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
先行 3 コミット (lib-scope-guard / lib-autonomy-policy 抽出、cli-fix-push-gate 追加) は incident 前に「抽出だけの PR」として書かれており、module doc が呼び手を「計画中・本 diff の時点では未実装」と説明していた。本 PR は抽出と最初の呼び手を同一 PR に入れるため (ADR-069 § 決定 3-1)、その前提が成立しない。文言を現実に合わせる。 ## 時制修正 (4 ファイル) - `lib-autonomy-policy/src/lib.rs`: 呼び手 2 件 (`cli-autonomy-gate` / `cli-fix-push-gate`) を実在として記載。「前倒しで lib 化した (ADR-044 層 1 の厳密な充足ではない)」という 但し書きは、2 呼び手が同一 PR に揃った今は不要なので落とし、層 1 充足と書く。 - `lib-scope-guard/src/lib.rs`: 同上。「CI 側の呼び手はまだ実装されていない」を削除。 lib 化の根拠 (重複数ではなく判定の同一性) は ADR-054 の防御に関わるので残す。 - `cli-autonomy-gate/src/main.rs`: `cli-fix-push-gate` を実在として参照。 - `cli-pr-monitor/src/stages/scope_guard.rs`: 「将来 CI 経路が追加された際に」を現在形へ。 ## chain 宣言の精緻化 (ADR-069 初回 dogfood) 計画書の「2b の chain 宣言」を、ADR-069 § 決定 1 の 3 要件に照らして具体化した: - **未消費は 1 つだけ**であることを明示。`lib-scope-guard` / `lib-autonomy-policy` は どちらも呼び手 2 件が本 PR の diff 内に揃っており missing-consumer ではない。 宣言が要るのは `cli-fix-push-gate` の workflow 呼び手のみ。 - **名前一致要件**を満たすため、2c の実体 (未 land コミット lqxzpvuw) と照合して step 名を 実名 `Gate fix push (deterministic, 4-axis AND)` に修正した (旧記載 `Gate fix push` は 前方一致にすぎず、ADR-069 の「矛盾する宣言は降格根拠にならない」に触れうる)。 exe パスと 4 引数も実体と突き合わせ済み。 宣言付き先頭 PR が missing-consumer で REJECT されないことが ADR-069 試験運用の decision trigger (a) の初回実測になる。結果は ADR-069 の判断基準へ記帳する。 ## 計画書の状態行 2a を完了 (PR #350) に、2b を実施中に更新。この更新自体が計画書を本 PR の diff に載せ、 ADR-069 § 決定 1「置き場所 = diff 内の計画文書のみ」を満たす手段でもある。 検証: cargo test --workspace 1945 件 pass、clippy --workspace --all-targets -D warnings 緑、 pnpm lint:docs / lint:md 0 error。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthrough自律性ポリシーと scope 判定を共有ライブラリへ分離した。新しい Changesfix push ゲート基盤
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant CLI as cli-fix-push-gate
participant Inputs as 入力ファイル
participant Policy as lib-autonomy-policy
participant Scope as lib-scope-guard
participant Telemetry as lib-telemetry
CLI->>Inputs: findings JSON と diff summary を読み込む
CLI->>Policy: 設定・環境値を評価する
CLI->>Scope: diff と allowlist を解析する
CLI->>CLI: 4 軸のゲート判定を実行する
CLI->>Telemetry: deny 理由コードを記録する
CLI-->>CLI: 結果を出力して終了する
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🤖 PR Monitor 分析 (GitHub Actions バックストップ)
Applicable Findings (Critical / High / Major)該当なし (レビュー未着のため) Applicable Findings (Medium 以下)該当なし Filtered (not applicable)該当なし Diff 概要
次のアクション
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/cli-fix-push-gate/src/checks.rs (1)
127-158: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
evaluateとdescribe_axesが同じ判定を別々に計算しています。
main.rsのreport()はdescribe_axes(facts)とevaluate(facts)を両方呼び出します。両関数はそれぞれ独立にlib_scope_guard::parse_changed_files、lib_docs_policy::is_docs_only_summary、lib_scope_guard::find_out_of_scopeを実行しており、1 回の gate 呼び出しでこれらの処理が最大 2 回走ります。処理コスト自体は小さいですが、同じ判定ロジックが 2 か所に分散しているため、将来
evaluateの分岐条件を変えたときにdescribe_axesの更新を忘れると、出力される 4 軸のログと実際の deny 理由が矛盾する恐れがあります。evaluateの結果 (parse 済みchangedやdocs_onlyフラグなど) をdescribe_axesに渡す、または両方を 1 回のパスで計算する構造に見直すことを検討してください。🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/cli-fix-push-gate/src/checks.rs` around lines 127 - 158, Unify the shared decision computation used by evaluate and describe_axes so parsing changed files, determining docs-only status, and finding out-of-scope files occur once per gate evaluation. Update the relevant evaluate/report flow to pass or reuse the computed results, and make describe_axes derive its four-axis output from that shared result instead of recalculating the checks independently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/harness-improvement-plan.md`:
- Around line 182-185: 2b の chain 宣言を、2c の実際の workflow
契約値に更新してください。docs/harness-improvement-plan.md の chain 記述で、fix job、step
名、cli-fix-push-gate の実行パス、および
--branch/--config/--diff-summary-file/--findings-file の指定を
.github/workflows/pr-monitor.yml と完全に一致させ、lib-scope-guard と lib-autonomy-policy
の既存呼び手の記述は変更しないでください。
---
Nitpick comments:
In `@src/cli-fix-push-gate/src/checks.rs`:
- Around line 127-158: Unify the shared decision computation used by evaluate
and describe_axes so parsing changed files, determining docs-only status, and
finding out-of-scope files occur once per gate evaluation. Update the relevant
evaluate/report flow to pass or reuse the computed results, and make
describe_axes derive its four-axis output from that shared result instead of
recalculating the checks independently.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 4fd399be-7f91-47df-8f1c-3f34c7b4d451
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (17)
Cargo.tomldocs/harness-improvement-plan.mdpackage.jsonsrc/cli-autonomy-gate/Cargo.tomlsrc/cli-autonomy-gate/src/main.rssrc/cli-fix-push-gate/Cargo.tomlsrc/cli-fix-push-gate/src/checks.rssrc/cli-fix-push-gate/src/inputs.rssrc/cli-fix-push-gate/src/main.rssrc/cli-pr-monitor/Cargo.tomlsrc/cli-pr-monitor/src/stages/scope_guard.rssrc/lib-autonomy-policy/Cargo.tomlsrc/lib-autonomy-policy/src/decision.rssrc/lib-autonomy-policy/src/lib.rssrc/lib-autonomy-policy/src/sources.rssrc/lib-scope-guard/Cargo.tomlsrc/lib-scope-guard/src/lib.rs
| **2b の chain 宣言**(ADR-069 準拠。2b PR の diff にこの計画書が含まれることで有効になる): | ||
|
|
||
| - **未消費なのは 1 つだけ**: 2b が導入する `cli-fix-push-gate`(crate `src/cli-fix-push-gate`、bin 同名)の **workflow 呼び手**。これは**後続 PR 2c** の `.github/workflows/pr-monitor.yml` の `fix` job、step 名 `Gate fix push (deterministic, 4-axis AND)` が `master-ref/target/release/cli-fix-push-gate` を `--branch` / `--config` / `--diff-summary-file` / `--findings-file` 付きで実行する形で land する(2c の実体で step 名・パス・引数を照合済み)。 | ||
| - **lib 2 件の呼び手は 2b 自身の diff 内に揃っている**(未消費ではない): `lib-scope-guard` → `cli-pr-monitor::stages::scope_guard`(既存)+ `cli-fix-push-gate`(本 PR)。`lib-autonomy-policy` → `cli-autonomy-gate`(既存)+ `cli-fix-push-gate`(本 PR)。ADR-069 § 決定 3-1「抽出と最初の呼び手の間で切らない」に従い、incident の初回分割が分離したこの境界を同一 PR に戻してある。 |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 期待結果: chain 宣言と workflow の step 名、パス、引数が一致する。
rg -n -C 8 \
'Gate fix push \(deterministic, 4-axis AND\)|cli-fix-push-gate|--branch|--config|--diff-summary-file|--findings-file' \
.github/workflows/pr-monitor.ymlRepository: aloekun/claude-code-hook-test
Length of output: 167
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== git status/stat =="
git status --short
git diff --stat
echo "== files with relevant names =="
git ls-files | rg '(^docs/harness-improvement-plan\.md$|\.github/workflows/pr-monitor\.yml$|cli-fix-push-gate|pr-monitor)' || true
echo "== docs/harness-improvement-plan.md relevant section =="
if [ -f docs/harness-improvement-plan.md ]; then
nl -ba docs/harness-improvement-plan.md | sed -n '160,205p'
fi
echo "== workflow target if present =="
if [ -f .github/workflows/pr-monitor.yml ]; then
nl -ba .github/workflows/pr-monitor.yml | sed -n '1,260p' | rg -n -C 8 'cli-fix-push-gate|--branch|--config|--diff-summary-file|--findings-file|Gate fix push|fix[[:space:]]*:' || true
else
echo ".github/workflows/pr-monitor.yml not present"
fiRepository: aloekun/claude-code-hook-test
Length of output: 2033
🏁 Script executed:
#!/bin/bash
set -u
echo "== docs/harness-improvement-plan.md lines 160-210 =="
awk 'NR>=160 && NR<=210 { printf "%5d %s\n", NR, $0 }' docs/harness-improvement-plan.md || true
echo "== pr-monitor workflow matching lines =="
awk '
/cli-fix-push-gate|--branch|--config|--diff-summary-file|--findings-file|Gate fix push|fix[[:space:]]*:/ || /Gate fix push \(deterministic/
{ s=$0; for(i=1;i<=4;i++) {if(getline line) s=s"\n"line} print NR": "s }
' .github/workflows/pr-monitor.yml || true
echo "== pr-monitor workflow context =="
awk 'NR>=1 && NR<=280 { printf "%5d %s\n", NR, $0 }' .github/workflows/pr-monitor.ymlRepository: aloekun/claude-code-hook-test
Length of output: 39279
2c の workflow に一致する chain 宣言に更新してください。
.github/workflows/pr-monitor.yml には fix job、step 名 Gate fix push (deterministic, 4-axis AND)、cli-fix-push-gate の実行パス、または --branch / --config / --diff-summary-file / --findings-file がありません。ADR-069 の chain 判定が参照する契約値が workflow と一致するように、2c とこの宣言を同じ値で揃えてください。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/harness-improvement-plan.md` around lines 182 - 185, 2b の chain 宣言を、2c
の実際の workflow 契約値に更新してください。docs/harness-improvement-plan.md の chain 記述で、fix
job、step 名、cli-fix-push-gate の実行パス、および
--branch/--config/--diff-summary-file/--findings-file の指定を
.github/workflows/pr-monitor.yml と完全に一致させ、lib-scope-guard と lib-autonomy-policy
の既存呼び手の記述は変更しないでください。
🤖 PR Monitor 分析 (GitHub Actions バックストップ)
Applicable Findings (Critical / High / Major)該当なし Applicable Findings (Medium 以下)
Filtered (not applicable)該当なし 次のアクション
|
Resolved findings: - [Minor] docs/harness-improvement-plan.md:185 2c の workflow に一致する chain 宣言に更新してください。
CodeRabbit 指摘「2c の workflow に一致する chain 宣言に更新してください」への対応。 ## 経緯 — takt fix が真の記述を偽に書き換えた 指摘を受けた post-pr-review の fix step は、宣言の「2c の実体で step 名・パス・引数を 照合済み」を「実装済みの実体と照合済みではない」へ書き換えて auto-push した。これは **事実に反する**。2c の実装はローカルの未 land コミットとして存在し、step 名 `Gate fix push (deterministic, 4-axis AND)`・exe パス・4 引数はいずれも実体と照合済み である (照合は 2b 着手時に実施)。 一方 CodeRabbit の懸念自体は正当だった: 本 PR の diff だけを見るレビュアーには、その 照合の主張を検証する手段がない。fix 後の記述は「偽だが検証可能」、修正前の記述は 「真だが検証不能」で、どちらも宣言として不十分だった。 ## 修正内容 主張を検証可能性で 2 つに分ける: - **diff 内で照合できるもの**: 引数 4 種と exe 名 (`main.rs` の `parse_args` / `USAGE`)。 ADR-069 § 決定 1 の名前一致要件は、この部分で本 diff 内に閉じて満たされる。 - **diff 外の主張**: step 名と exe パス。2c の未 land 実装と照合済みであることを述べつつ、 **本 PR の diff だけでは検証できない主張である**と明示し、名前一致の最終確認は 2c の diff レビューで行うと書く。 真である事実を落とさず、レビュアーが何を確認できて何を確認できないかを判別できる形に した。宣言の強度は落ちない — ADR-069 が要求する名前一致は diff 内で閉じている。 なお本件は「fix step の出力を実測検証する」(#309 / ADR-068) が docs 領域でも必要である ことの実例になった。WP-17 完了後の feedback 採否で扱う。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* ci(pr-monitor): Phase B (claude/ ブランチ限定の無人 fix push) を追加 (WP-17 PR 2) pr-monitor.yml に fix job を追加し、Actions variable AUTONOMY_ENABLED = 'true' の ときだけ claude/ ブランチ PR の docs 指摘を無人で修正・push する。既定 OFF。 ## contents: write 昇格に対する代替担保 Phase A の安全担保の主体は contents: read (push が 403 で決定論的に失敗) だった。 Phase B ではこれが使えないため 4 点で代替する: 1. fix agent に Bash / gh / git を一切与えない。push を実行できるのは workflow step だけで、LLM は push の主体になれない (Phase A で「コメント投稿はエージェント外の 後続 step」としたのと同じ形) 2. agent の書き込み先を pr/docs/** に scope する (許可ツール自体をパス制限) 3. push 直前に cli-fix-push-gate が 4 軸 AND を決定論判定し、非ゼロなら push しない 4. ゲート exe と autonomy-config.toml を master ref から調達する 4 が信頼境界の要。PR ブランチの checkout に含まれるゲートのソースや config を使うと、 自律 actor 自身が自分の制限を書き換えて自己解除できる (ADR-054 と同型)。master を 別ディレクトリへ checkout し、そこで cargo build したバイナリと config だけを使う。 ## findings と fix の agent 分離 scope guard の allowlist を fix agent 自身に作らせると自己申告の追認になるため、 read-only の findings agent を先行させて allowlist を確定させる。ローカル経路で review facet と fix step が別エージェントなのと同じ分離を CI でも保つ。 ## degrade 経路 早期打ち切り (fork / 非 OPEN / 非 claude ブランチ)、findings ゼロ、ゲート deny の いずれでも run を失敗させず、[FIX_PUSH_DENY] を出して Phase A 相当 (分析コメントのみ) に落ちる。gate step は continue-on-error で outcome を分岐に使う。 ## 副次的な安全性 GITHUB_TOKEN による push は新たな workflow run を発火させない (GitHub 仕様)。 Phase B → pr-monitor 再起動の無限ループが構造的に起きない。 ## 実装上の注意点 (レビュー観点) - findings agent の出力パスはリポジトリルート起点を要求する。`pr/` プレフィックスを 付けると全件が scope 違反になるため prompt で明示した - git の --name-status は tab 区切りだが lib-scope-guard は空白区切りを期待するため 最初の tab のみ空白へ正規化する。rename は "R100" status となりパーサが fail-closed で弾く - findings_json が skip されると count が空文字列になる。`!= '0'` だけでは通過するため outcome の成功を明示的に AND した 検証: js-yaml でパースし job/step/条件チェーンを確認。実走スモークは Actions variable 設定と claude/ テストブランチが要るためユーザー操作後に実施する (ADR-067 § 残課題)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(autonomy): repo 側フラグを有効化 (WP-17 PR 2) 呼び手 (pr-monitor.yml の Phase B fix job) が実装されたため、autonomy-config.toml の [autonomy] enabled を true へ倒す。リポジトリとして自律 fix push を許可する意思表示。 **これだけでは Phase B は動かない。** 判定は repo config と外部フラグの AND で、 GitHub Actions 側は Actions variable AUTONOMY_ENABLED = 'true' が別途必要。 それは admin (リポジトリ所有者) だけが設定できるため、実際の有効化タイミングは 引き続き人間が握る。本コミットのマージによる挙動の変化はゼロ。 検証 (pnpm autonomy-status): repo_config=enabled / AUTONOMY_ENABLED=unset で reason=external-unset の deny。AND 合成が設計どおり効いていることを実測した。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(adr): ADR-067 (Phase B 無人 fix push) 起票 + ADR-052 stale 訂正 (WP-17 PR 2) Phase B の設計判断を永続成果物へ移管し、計画書 (ephemeral) の PR 2 節を ADR 参照 + 残作業のみへ縮約する (順位 117 の知識移管 3 ステップ)。 ADR-067 が記録する主な決定: - agent を push の主体にしない。Phase A で「コメント投稿はエージェント外の後続 step」と した形を push でも踏襲する。contents: write を持つのは job だが、行使できるのは 決定論的 step だけで、prompt injection が成立しても push 先・内容は変えられない - findings agent と fix agent の分離。fix を書く agent 自身に findings を出させると scope guard の allowlist が自己申告になり ADR-054 の防御が成立しない - ゲート exe と config を master ref から調達。PR ブランチ側を使うと自律 actor が 自分の制限を書き換えて自己解除できる。ADR-066 § 決定 3 は config だけを想定していたが、 ゲート実行ファイル自体も同じ信頼境界に属することを本 ADR で明示した - 自動化範囲は docs 指摘の修正に限る。Tier 3 cleanup は機械判定できず分類不能 = ゲート必須へ倒れる (ADR-052 原則 3)。意図的に狭い保守的な出発点 - degrade は失敗ではない。Phase B は Phase A の上乗せで、上乗せ不成立を CI 失敗に すると本物の失敗と区別できなくなる (ADR-065 の判断と同じ論理) - 無限ループは GITHUB_TOKEN push が run を発火させない仕様に依存している。token 種別を 変える際はこの防御が消えることを明記した § 2 外部 SaaS 事実の永続化 (計画書の必須要件): 2026-08-02 に公式 docs で再確認し 両事実とも維持を確認して ADR へ移管した。 - public リポジトリ + standard runner の Actions は無料・無制限 (分数枠は private のみ) - claude-code-action は claude_code_oauth_token (claude setup-token 生成、Pro/Max) 対応 本リポジトリは public のため cargo build を含む job 追加でも Actions 課金は発生しないが、 Max 枠は agent 2 本分消費する点を欠点として記録した。 ADR-052 の stale 記述訂正: - 実装スコープ節の「gate.rs の docs-only 判定を lib へ切り出す (現状 pub(crate))」は ADR-057 の副産物として完了済み。取り消し線 + 訂正注記を付けた - 同節冒頭に「呼び手は WP-17 で実装済み (ADR-066 / ADR-067)」の追記を入れ、 「呼び手が無いので分類関数は見送る」という起票時判断が現状と食い違わないようにした 検証: cargo test --workspace 1936 件 pass (失敗 0)、cargo clippy --workspace -D warnings 緑、pnpm lint:docs / lint:md (120 ファイル) 0 error。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(wp17): ADR-069/068 に 2b の実測を記帳 + workflow 構文検証 (WP-17 2c) 2c の land にあたり、2b で得られた 2 件の実測を永続記録へ移す。あわせて計画書の 状態行を更新する。 ## ADR-069 § 実測 1: chain 宣言の初回 dogfood (trigger (a) 充足) PR #351 で、宣言付き先頭 PR が missing-consumer で REJECT されないことを実測した。 simplicity review は宣言を字面で受け取らず、決定 1 の 3 要件を個別に照合したうえで (diff 内の計画書である / 後続 PR と step 名を具体名で指名 / 引数 4 種が main.rs の parse_args と一致) non-blocking warning へ降格した。「名前一致」要件が実際に検査される ことを確認できた。 ## ADR-069 § 実測 2: 宣言の「検証可能性」という欠落要件 同 PR で、決定 1 の 3 要件では捉えられない問題が出た。当初の宣言「2c の実体で照合済み」 は**真**だが、PR の diff だけを見るレビュアーには**検証できない主張**だった。決定 1 の 名前一致要件は暗黙に「diff 内で照合できること」を前提にしている。後続 PR の実体にしか 存在しない名前を含める場合は、その部分が検証不能である旨を宣言自身が明示するのが正しい。 実測 1 件のため要件化はせず、本採用時の判断材料として記録に留める。 ## ADR-068 § 観測: docs-only fix の事実性は無検査 上記 2 の往復で、post-PR の fix step が真の記述を偽へ書き換えて auto-push した。このとき 決定論層はすべて設計どおり動作している — scope guard は編集**範囲**を、docs-only 判定は 変更**クラス**を、後退検知は量的後退を見るが、**内容の真偽はどの層も見ていない**。 コードではコンパイル・テストが事実性の代理検査になるが docs には代理が無い。 観測 1 件で機構は足さない (最小処置原則)。再検討の条件を 2 つ明記した。うち 1 つは Phase B の無人 fix push (本 PR で live になる) で同種の誤りが出たとき — Phase B の対象は docs 指摘に限られるため、この穴は Phase B の主要リスク面と重なる。ADR-067 の bounded lifetime 観測時に併せて確認する。 ## workflow 構文検証 (計画書 § 2c 手順 3) js-yaml で pr-monitor.yml をパースし、fix job の構造を機械的に確認した: - jobs: analyze, fix / triggers: pull_request_review, issue_comment, workflow_dispatch - fix.if: `vars.AUTONOMY_ENABLED == 'true'` (kill-switch 第 1 層) - fix.permissions: contents: write, pull-requests: read - 12 step。step 10 `Gate fix push (deterministic, 4-axis AND)` は continue-on-error、 step 11 が `Push fix (workflow-authored, not agent-authored)` step 10 の名称が 2b の chain 宣言と完全一致していることも確認した (ADR-069 決定 1 の 名前一致要件を、後続 PR 側から閉じたことになる)。 検証: cargo test --workspace 1945 件 pass、pnpm lint:docs / lint:md 0 error。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * ci(pr-monitor): degrade 理由を gate 到達有無で出し分ける (WP-17 2c) pre-push simplicity review の非ブロッキング指摘への対応。 `Report degrade to Phase A` の guard `steps.gate.outcome != 'success'` は 2 つの異なる 事象で真になる: 1. gate が走って push を拒否した (outcome = failure) 2. gate に到達しなかった (findings 0 件 / 前段 step の skip・失敗。outcome = '' or 'skipped') 旧実装はどちらでも「直前の gate step の [FIX_PUSH_DENY] 行を参照してください」と出して いたため、2 のケースで**存在しない行を案内**していた。 この workflow の run log は ADR-067 bounded lifetime の観測チャネルそのもの (自律 fix push 3〜5 回で判定する)。degrade 理由が不正確だと、判定材料として読むときに「gate が拒否した」 と「そもそも到達していない」を取り違える。両者は意味が全く違う — 前者は 4 軸判定が働いた 証拠だが、後者は判定に至っていないだけである。 skip された step の outcome は '' と 'skipped' のどちらもあり得るため、「gate が走った」 ことを示す値 (failure) だけを明示的に判定し、それ以外は未到達として扱う (fail-safe な 分岐方向)。 検証: js-yaml パースで 12 step の構造が維持されていることを確認。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * ci(pr-monitor): findings agent の未使用ツール権限を削る (WP-17 2c) pre-push simplicity review の非ブロッキング指摘への対応。 findings agent の `--allowedTools` に `Bash(gh pr diff:*)` と `Read(master-ref/CLAUDE.md)` が入っていたが、本 agent のプロンプトはどちらも参照しない (指摘は comments / reviews の 2 エンドポイントから取得し、`file` の実在確認に `pr/docs/**` を読むだけ)。Phase A の analyze job の allowedTools からの写しと見られる。 **本 agent の出力は scope guard (ADR-054) の allowlist を確定させる**ため、余剰権限は 「allowlist を作る主体」の攻撃面をそのまま広げる。findings と fix を別 agent に分けた のは自己申告の追認を避けるためで、その分離の価値は findings 側の権限が絞られている ことに依存する。本 PR 自身の主題 (最小権限・信頼境界) に照らして落とす。 ## 検証スクリプトを 1 本追加した理由 説明コメントを `claude_args: |` の中に書きかけて気付いたが、**リテラルブロック内の `#` 行は YAML コメントではなく引数文字列**になる。混入すると agent に不正なフラグが渡る。 目視では気付きにくいため、js-yaml でパースして claude_args の全行が `--` 始まりか検査 するスクリプトを scratchpad に置き、本コミットで実行して 3 agent 分すべて緑を確認した (説明はブロック外の YAML コメントへ移動済み)。 検証: js-yaml パースで 12 step 構造が維持されていること、claude_args 全 12 行が フラグ形式であることを確認。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * ci(pr-monitor): reviewer 権限解決 step を env 経由化し失敗を可視化 (WP-17 2c) 前ラウンドの fix が追加した `Resolve reviewer repository permission` step への follow-up。 指摘の趣旨 (author_association では write を含意しない) の対応自体は妥当だが、実装に 2 点の弱さがあったため補強する。 ## (a) reviewer 名を env 経由へ `${{ github.event.review.user.login }}` を bash の run ブロックへ直接展開していた。 GitHub の username は英数字とハイフンのみでシェル注入は成立しないため実害は無いが、 同じ job の他 step は SEC-NEW-pr-monitor-yml-L512 対策で env indirection に統一済み であり、ここだけ例外にすると「安全性が値の文字種を読者が知っていることに依存する」 状態が残る。GITHUB_REPOSITORY も組み込み env を使い、run ブロックから式展開を無くした。 ## (b) API 失敗を無音にしない `collaborators/{user}/permission` は push 権限を要求する endpoint のため、 GITHUB_TOKEN の permissions 次第では 403 になり得る (reviewer が collaborator で なければ 404)。旧実装は `set -e` も無く失敗時に PERMISSION が空のまま step 成功と なっていた。 空 permission は fix job の actor gate で不一致になり Phase B は起動しない = fail-closed なので方向は正しい。しかし **「権限が足りない reviewer だった」と「API 自体が呼べない」 は原因が全く違う**。後者なら pull_request_review 経路の Phase B が恒久的に死ぬが、 run log には何も出ないため気付けない。ADR-064 が排除した「silent success」と同型の穴。 成功時 `[PHASE_B_ACTOR]`、失敗時 `[PHASE_B_ACTOR_UNRESOLVED]` + stderr を出力し、 どちらの経路を通ったか run log から判別できるようにした。判定自体は従来どおり fail-closed のまま。 なお本 endpoint が GITHUB_TOKEN で実際に呼べるかは静的には確定できない。マージ前 スモーク (計画書 § 2c 段 0.5) の観測項目として扱う — 呼べなければ上記 marker が出る。 検証: js-yaml パースで 12 step 構造と claude_args 全行のフラグ形式を確認。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(review): apply CodeRabbit fixes for #352 Resolved findings: - [Major] .github/workflows/pr-monitor.yml:486 CodeRabbit の指摘取得はすべてのページを取得してください。 - [Minor] .github/workflows/pr-monitor.yml:640 push 競合が run 失敗になります。degrade 設計と不整合です。 - [Minor] autonomy-config.toml:50 コメントの記述と Actions variable の実状が食い違います。 - [Minor] docs/adr/adr-052-autonomy-execution-boundary-classes.md:99 同一 ADR 内に stale な参照が残ります。 - [Minor] docs/adr/adr-067-phase-b-unattended-fix-push.md:129 fix job の step 数が記載と一致しません。 * docs(wp17): CodeRabbit #352 の doc 指摘 3 件対応 + 段 0.5 期待動作の訂正 (WP-17 2c) 直前の takt fix コミット (findings 1-2 = pagination / push 競合の degrade 化) は検分の うえ維持した (js-yaml で 13 step 構造・id: push・continue-on-error を実測確認済み)。 本コミットは takt がタイムアウトで未対応だった doc 系 3 件と、計画書の期待動作訂正を まとめる。 ## CodeRabbit finding 3: autonomy-config.toml のコメントと variable 実状の食い違い コメントが「AUTONOMY_ENABLED が別途必要 (= 未設定)」と読める書き方で、実状 (設定済み、 かつマージ手順で削除→再設定される) と食い違っていた。**コメントに variable の設定状態を 書かない**形へ変更 — 外部 SaaS 側の実状と config 内の記述が食い違う drift は ADR-051 の クロスシステム coupling がまさに避けるべきとした形。現在値の正は GitHub UI 側にあると 明記した。 ## CodeRabbit finding 4: ADR-052 の stale 参照の残り 実装スコープ節 (L99) は 2c で訂正済みだったが、関連リンク節 (L138) に同じ stale 参照 (`gate.rs` の分類関数が「将来の再利用母体」) が残っていた。lib-docs-policy (ADR-057 で 切り出し済み) を指すよう訂正し、L99 の訂正記録への参照を付けた。 ## CodeRabbit finding 5: ADR-067 の step 数不一致 検証記録の「12 step」は起票時点の値で、pre-push レビュー 5 ラウンドの対応で `Fetch CodeRabbit review comments` step が増え現在は 13。数を直すだけでなく増えた経緯を 併記した (検証は追加後の構成で再実行済み)。 ## 計画書 § 2c 段 0.5 の期待動作訂正 (自己申告) 段 0.5 の記述「fix job は config 層で停止するのが期待動作」は**非 claude 対象では誤り**。 fix job の全 step は proceed ゲートされており、非 `claude/*` PR を対象にすると config 層 より手前の prefix 層 (applies step) で停止する。master-ref 調達・config 層 deny に到達 するには `claude/*` PR + docs 指摘が必要 (= 段 2 の構成)。誤った期待を持ってスモークを 実行すると「config deny が出ない = 壊れている?」と誤読するため、観測範囲を層単位で 明記し直した。段 1 の括弧書き (「config が有効化されて初めて prefix 層まで到達する」) も 同じ誤り (prefix 層は config より手前) のため訂正。段 2 へ coderabbitai[bot] permission 解決の観測項目 ([PHASE_B_ACTOR] marker) を追加した。 検証: pnpm lint:docs / lint:md 0 error、cargo test (config 消費 crate) 27 件 pass、 js-yaml パース 13 step 緑。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
概要
WP-17 PR 2(Phase B 無人 fix push)の再分割 2b です。判定コアの lib 抽出 2 件と、
その最初の呼び手である
cli-fix-push-gateを 同一 PR に入れます。incident(2026-08-02)の初回分割は抽出と呼び手を分離し、先頭 PR が missing-consumer で
REJECT され、fix が lib を丸ごと削除して空洞化しました。本 PR は ADR-069 § 決定 3-1
「抽出と最初の呼び手の間で切らない」に従い、その境界を同一 PR に戻しています。
1141 行で PR size gate の warning 帯(800〜1500)です。上記の理由により、これ以上の分割は
チェーン全体のコストを増やすだけと判断しました(ADR-069 § 決定 3-3)。
コミット
refactor(lib-scope-guard)— ADR-054 scope 判定コアを lib へ抽出(テスト 11 件)refactor(lib-autonomy-policy)— ADR-066 kill-switch 判定コアを lib へ抽出(テスト 21 件維持)feat(cli-fix-push-gate)— Phase B の fix push 直前ゲート(4 軸 AND、テスト 22 件)docs(wp17)— lib module doc を呼び手実在の現実に合わせる + chain 宣言の精緻化cli-fix-push-gate の設計
ADR-052 自動実行可クラスの 4 軸を 1 回の呼び出しで AND 評価し、1 つでも欠ければ非ゼロで
終了して push を止めます(= Phase A 相当の分析コメントのみへ degrade)。
--config+ envAUTONOMY_ENABLED--branch--diff-summary-file--findings-file+ 同 diff単一 exe にした理由:
cli-autonomy-gate && cli-fix-push-gateの連鎖にすると、workflow で&&を書き忘れた瞬間に kill-switch を通り越します。fail-closed 合成を呼び手のミスに依存させないため、kill-switch も本 exe が
lib-autonomy-policy経由で内包します。引数に既定値を設けない理由: CI で
--configに master ref の写しを渡し忘れた呼び手が、PR ブランチの config を黙って読む事故を防ぐためです(ADR-066 § 決定 3。自律 actor が
自分で kill-switch を解除できてしまう経路)。
exit コードは
0= push 可 /1= 拒否 /2= 引数不正で、呼び手は非ゼロをすべて「push しない」として扱います。allow / deny どちらも 4 軸すべての状態を含む 1 行を
出力します(無音 no-op 禁止、ADR-064 と同じ論理)。
lib 抽出の根拠
lib-scope-guard(ADR-054): 呼び手はcli-pr-monitor::stages::scope_guard(ローカル経路、jj diff)と
cli-fix-push-gate(CI 経路、git diff)。ADR-044 層 1 の「3+ crate 重複」ではなく判定の同一性を根拠にしています — 2 経路が分岐した瞬間に片方だけ緩む drift が
ADR-054 の防御を無効化するためです。
lib-autonomy-policy(ADR-066): 呼び手はcli-autonomy-gateとcli-fix-push-gate。ADR-066 § 決定 4 が「呼び手が env / config を直読みして独自に真偽を組み立てること」を
禁じており、判定を 1 か所へ集約する必要があります。
どちらも呼び手 2 件が本 PR の diff 内に揃っており、未消費の抽象ではありません。
chain 宣言(ADR-069 初回 dogfood)
未消費なのは
cli-fix-push-gateの workflow 呼び手 1 つだけです。これは後続 PR 2c のpr-monitor.ymlのfixjob、stepGate fix push (deterministic, 4-axis AND)として land します。宣言は計画書に記載し、ADR-069 § 決定 1 の 3 要件(diff 内の計画文書 / 抽出↔呼び手の
具体的ペアリング / 名前一致)を満たすよう、2c の実体と step 名・exe パス・4 引数を
照合して具体化しました。旧記載の
Gate fix pushは実名の前方一致にすぎなかったため実名に修正しています。
検証
cargo test --workspace1945 件 pass(新規 33 件を含む)cargo clippy --workspace --all-targets -- -D warnings緑pnpm lint:docs/pnpm lint:md0 error記録は 2c で land する ADR-067 § 検証記録にあります
後続
2c:
pr-monitor.ymlの Phase B fix job +autonomy-config.tomlのenabled = true+ADR-067 起票(約 470 行)。2c では ADR-069 の試験運用記録に本 PR の dogfood 結果
(宣言付き先頭 PR が missing-consumer で REJECT されなかったこと)を追記します。
🤖 Generated with Claude Code
Summary by CodeRabbit
新機能
ドキュメント