Skip to content

fix(pr-monitor): findings agent 出力のコードフェンスを決定論層で除去 (ADR-067) - #357

Merged
aloekun merged 1 commit into
masterfrom
fix/findings-json-codefence
Aug 4, 2026
Merged

fix(pr-monitor): findings agent 出力のコードフェンスを決定論層で除去 (ADR-067)#357
aloekun merged 1 commit into
masterfrom
fix/findings-json-codefence

Conversation

@aloekun

@aloekun aloekun commented Aug 4, 2026

Copy link
Copy Markdown
Owner

概要

ADR-067 Phase B のスモーク段 2、2 回目の実走(2026-08-04)で Extract findings JSON step が
exit 1 で失敗し、findings 取得直後で停止しました。その修正です。

1 コミット / 1 ファイル。

原因: 出力形式の保証を指示層だけに委ねていた

findings agent は仕込んだ docs 指摘 3 件を過不足なく検出していましたが、出力を
コードフェンス (```json) で囲んで返しました。

jq: parse error: Invalid numeric literal at line 2, column 0
[FIX_PUSH_DENY] findings agent の出力が JSON 配列ではありません
(以下、フェンスで囲まれた JSON 配列が続く)

prompt は「前置き・後書き・コードフェンスを一切含まない JSON 配列のみ」と明示していましたが、
agent はこれに従いませんでした。指示層だけで出力形式を保証する設計では、モデルの気まぐれで
経路全体が止まります
(ADR-042: ルールでなく仕組みで守る)。

修正: 決定論層でフェンス行を落とす

jq に渡す前に sed でフェンス行のみを除去します。

設計上の線引き

  • フェンスを剥がす以上のことはしません。 中身の修復(引用符の補完、部分抽出等)は試みません
    — ヒューリスティックな救済を入れると「壊れた入力をそれらしく通す」経路ができ、fail-closed の
    意味が失われます
  • fail-closed は維持。 剥がした後も配列でなければ従来どおり exit 1 で止まります
  • 診断出力を生出力(剥がす前)に変更。 フェンス以外が原因で落ちた場合に、agent が実際に
    何を返したかがログに残ります

prompt 側も二層目の防御として残し、「フェンスがあると後続がパースに失敗する」理由と発生日を
明記しました(指示は保険であり主たる担保ではない旨も併記)。

検証

実際に落ちた入力でロジックを実測しました(推測ではありません)。

入力 結果
コードフェンスで囲まれた配列(実際に落ちた形) 除去後にパース成功、isArray: true、要素 2 件
フェンスなしの素の配列 副作用なくパース成功

段 2 の中間成果(修正済み箇所はすべて正常動作)

2 回目の実走で、これまでの修正が実走で効いていることを確認できました。

項目 結果
Fetch CodeRabbit review comments 通過(#356 の修正が効いた)
決定論的著者フィルタ(#352) coderabbitai[bot] の投稿を正しく抽出
findings agent の検出精度 仕込んだ不整合 3 点を過不足なく検出
degrade 分岐(#353 で修正) GATE_OUTCOME: skipped を正しく判別し正確な理由を出力

残るは Apply fixesCompute fix diff summaryGate fix pushPush fix後半 4 stepで、
まだ一度も動いていません。

所感: LLM を含む workflow は静的検査で検証しきれない

実走 2 回でバグ 2 件(gh api のフラグ排他制約、出力形式の指示層依存)が出ました。いずれも
pre-push simplicity / security review・CodeRabbit・js-yaml 構文検証の 4 種をすべて通過した
もので、実行時にしか検出できない類でした。段 2(実走スモーク)を必須としたマージ手順の設計が
妥当だったことの裏返しでもあります。

この一般化は WP-17 完了後の docs バッチで ADR へ記録します。

🤖 Generated with Claude Code

Summary by CodeRabbit

  • 改善
    • AIによる検出結果の形式チェックを強化し、期待される配列形式のみを受け付けるようになりました。
    • 不正な形式の結果は明確に表示され、処理が安全に停止するよう改善しました。
    • コードフェンスなどの不要な装飾を自動的に除去し、結果の解析精度を向上しました。

スモーク段 2 の 2 回目の実走 (2026-08-04) で `Extract findings JSON` step が exit 1 で失敗し、
Phase B が findings 取得直後で停止した。

## 原因: 出力形式の保証を指示層だけに委ねていた

findings agent は仕込んだ docs 指摘 3 件を**過不足なく検出**していたが、出力を ```json で
囲んで返した:

    jq: parse error: Invalid numeric literal at line 2, column 0
    [FIX_PUSH_DENY] findings agent の出力が JSON 配列ではありません
    ```json
    [
      {"file": "docs/phase-b-smoke-test.md", ...},
      ...
    ]
    ```

prompt は「前置き・後書き・**コードフェンスを一切含まない** JSON 配列のみ」と明示していたが、
agent はこれに従わなかった。**指示層だけで出力形式を保証する設計では、モデルの気まぐれで
経路全体が止まる** (ADR-042: ルールでなく仕組みで守る)。

## 修正: 決定論層でフェンス行を落とす

`Extract findings JSON` で jq に渡す前に sed でフェンス行のみを除去する:

    sed -e 's/^[[:space:]]*```[[:alnum:]]*[[:space:]]*$//' "$RAW_FILE" > "$FINDINGS_FILE"

設計上の線引き:

- **フェンスを剥がす以上のことはしない**。中身の修復 (引用符の補完、部分抽出等) は試みない。
  agent が全く違う形式を返した場合は従来どおり落とすべきで、ヒューリスティックな救済は
  「壊れた入力をそれらしく通す」経路を作る
- **fail-closed は維持**。剥がした後も配列でなければ下の guard が exit 1 で止める
- 診断出力を `$RAW_FILE` (剥がす前) に変更。フェンス以外が原因で落ちた場合に、agent が実際に
  何を返したかがログに残る

prompt 側も二層目の防御として残し、「フェンスがあると後続がパースに失敗する」という理由と
実際に発生した日付を明記した (指示は保険であり主たる担保ではない旨も併記)。

## 検証

- **実際に落ちた入力でロジックを実測**: ```json で囲まれた配列 → 除去後にパース成功
  (isArray: true、要素 2 件)。フェンスなしの素の配列 → 副作用なくパース成功
- js-yaml パースで 13 step 構造と claude_args 全行のフラグ形式を確認
- 実走検証はスモーク段 2 (PR #355) の 3 回目の dispatch が兼ねる

## 併せて観測できたこと (段 2 の中間成果)

- findings agent が仕込んだ不整合 3 点を正確に検出 = 観測装置の設計は妥当
- 決定論的著者フィルタ (#352) と gh api 修正 (#356) が実走で正常動作
- degrade 分岐 (#353 で修正) が `GATE_OUTCOME: skipped` を正しく判別し、
  「gate step に到達しませんでした」を出力 = fail-closed と診断が設計どおり機能

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 49eb2f87-66c1-4740-a853-8c6be5369bc7

📥 Commits

Reviewing files that changed from the base of the PR and between 2c3ef68 and b53c696.

📒 Files selected for processing (1)
  • .github/workflows/pr-monitor.yml

📝 Walkthrough

Walkthrough

pr-monitor.yml が findings agent に素の JSON 配列を要求します。出力からコードフェンス行を除去して検証します。形式が不正な場合は生出力をログに表示し、処理を停止します。

Changes

findings 出力検証

Layer / File(s) Summary
出力契約と検証処理
.github/workflows/pr-monitor.yml
findings agent に素の JSON 配列を要求します。コードフェンス行を除去して JSON 配列を検証します。不正形式の場合は除去前の生出力を表示し、fail-closed で終了します。

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed タイトルは、findings agentのコードフェンスを決定論的に除去する修正を明確に示しており、変更内容と一致します。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/findings-json-codefence

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@aloekun

aloekun commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@aloekun
aloekun merged commit 5f61892 into master Aug 4, 2026
3 checks passed
@aloekun
aloekun deleted the fix/findings-json-codefence branch August 4, 2026 13:31
aloekun added a commit that referenced this pull request Aug 4, 2026
WP-17 (イベント駆動バックボーン完成) で唯一残っていた受け入れ基準 — 実走スモーク段 2
= Phase B allow 経路の完走 — が 2026-08-04 に充足したことを記帳し、#350#357 の
post-merge feedback 採否とセッション発案のハーネス改善 1 件を todo へ確定する。
コード変更は含まない。

## 段 2 の結果 (ADR-067 § 検証記録)

`workflow_dispatch` を 4 回実行し、4 回目で 13 step 完走に至った。

| 回 | 検出した欠陥 | 対処 |
|---|---|---|
| 1 | `gh api` は `--slurp` と `--jq` を併用できない | PR #356 |
| 2 | findings agent の出力がコードフェンスで囲まれ `jq` が失敗 | PR #357 |
| 3 | `Apply fixes` が findings ファイルを読めず空 diff | PR #358 |
| 4 | なし (完走) | — |

4 回目の実測: findings 3 件 / `Apply fixes` は `permission_denials_count=0`・`num_turns=6` /
`Gate fix push` が `[FIX_PUSH_ALLOW] autonomy=allowed branch=isolated content=docs-only
scope=in-scope(1 files)` / `Push fix` が `ee9557a..86f424c` を push / `Report degrade to
Phase A` は skip (= degrade しなかった証拠)。

無人 fix が書いた内容も実測検証し、仕込んだ不整合 3 点を過不足なく修正・範囲外の編集
ゼロ (1 ファイル / 3 insertions / 3 deletions) であることを確認した。

`AUTONOMY_ENABLED` を削除した状態での dispatch では fix job 自体が skip され、ADR-066 の
「欠損 → 安全状態」が実 Actions ランタイムで機能することも確認した。

## ADR-067 § 残課題 1 の方針を訂正

同節に書いていた修正方針「`allowedTools` を `Read(findings-input/**)` にする」は**誤り**
だった。同ディレクトリには未要約の raw な CodeRabbit テキスト (`comments.json` /
`reviews.json`) があり、glob を与えると write 権限を持つ fix agent がそれを直接読める。
findings agent と fix agent を分離した § 決定 2 / ADR-054 の設計目的が崩れる。実装時の
pre-push security review が REJECT で指摘し、単一ファイル指定に改めて land した (#358)。

**静的検査を通らないのはコードだけではない — ADR に書かれた修正方針そのものが誤って
いることがある**。この一般化を dev-conventions へ convention として追加した。

## 主な変更

- `docs/adr/adr-067-*.md`: 段 2 完走の検証記録、残課題 1〜4 を「段 2 で閉じた課題」へ移し
  方針訂正を明記、ruleset の実体 (`~ALL` 対象 / `claude/**` 除外 / admin bypass) を残課題へ、
  `coderabbitai[bot]` allowlist の follow-up 判断 (追加しない = WP-18 で再判断) を追記
- `docs/adr/adr-066-*.md`: bounded lifetime の実走観測 1 run 目を記録 (trigger (a)(c) 充足、
  (b) は variable 側のみ充足で config 側の実走観測は未実施)
- `docs/adr/adr-068-*.md`: Phase B 1 run 目で docs-only fix の事実性誤りが観測されなかった
  ことを記録 (追加機構の再検討条件は未充足のまま)
- `docs/harness-improvement-plan.md`: 引き継ぎ節を実施記録へ書き換え、WP-17 受け入れ基準の
  段 0〜2 を充足済みへ更新
- `docs/dev-conventions.md` + `CLAUDE.md`: convention「LLM を含む自動化経路は実走でしか
  検証できない」を追加
- `docs/todo20.md` (新規) + `docs/todo-summary2.md`: 順位 365-373 の 9 エントリを登録。
  内訳は post-merge feedback 採用 19 件を実装時の PR 粒度で 8 エントリへまとめたもの (365-372)
  と、Rust exe の自動再ビルド 2 層案 (373)。却下 5 件も理由付きで記録
- `docs/todo.md` / `docs/todo14.md` / `docs/todo3-11.md`: todo14.md が約 70KB に達したため
  新規追加先を todo20.md へ。preamble 数詞を 22 へ更新 (cli-docs-lint の検出に追従)

## 順位 373 の実測データ (Rust exe の自動再ビルド)

`.claude/*.exe` は gitignore された生成物で、`pnpm build:all` を明示実行しない限り古い
バイナリが使われ続ける。2026-08-04 時点で `cli-fix-push-gate` (3/3 ファイル)・
`cli-autonomy-gate` (1/1)・`hooks-session-start` (1/8) の 3 パッケージが古い状態だった。

| 操作 | 小パッケージ | 大パッケージ (30 ファイル) |
|---|---|---|
| `cargo build --release` (変更あり) | 4.3 秒 | 9.9 秒 |
| `cargo check` | 0.39 秒 | 0.70 秒 |

この差から、PostToolUse には `cargo check` を、Stop hook には `build --release` + deploy を
割り当てる 2 層構成を採る。PostToolUse で build しない理由 (編集ごとの 4〜10 秒、中間状態の
compile error がノイズになる、deploy が毎回走る) も todo エントリに記録した。

## 自動起動経路への懸念 (新規記録)

段 1 で `coderabbitai[bot]` の permission が `none` と実測され `pull_request_review` 経路が
恒久 deny になるため、Phase B の起動は `issue_comment` (walkthrough) 経路だけになる。この
経路は初回 1 回きりで、その時点では CodeRabbit の実レビューがまだ無いことが多い。「findings
がある状態で Phase B が自動起動する窓」が実質的に無い可能性がある。段 2 は
`workflow_dispatch` による手動起動だったため**自動起動経路は未検証**である。WP-18 着手時に
bot allowlist の要否と併せて実測する (ADR-067 § 検証記録に記録)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
aloekun added a commit that referenced this pull request Aug 5, 2026
WP-17 (イベント駆動バックボーン完成) で唯一残っていた受け入れ基準 — 実走スモーク段 2
= Phase B allow 経路の完走 — が 2026-08-04 に充足したことを記帳し、#350#357 の
post-merge feedback 採否とセッション発案のハーネス改善 1 件を todo へ確定する。
コード変更は含まない。

## 段 2 の結果 (ADR-067 § 検証記録)

`workflow_dispatch` を 4 回実行し、4 回目で 13 step 完走に至った。

| 回 | 検出した欠陥 | 対処 |
|---|---|---|
| 1 | `gh api` は `--slurp` と `--jq` を併用できない | PR #356 |
| 2 | findings agent の出力がコードフェンスで囲まれ `jq` が失敗 | PR #357 |
| 3 | `Apply fixes` が findings ファイルを読めず空 diff | PR #358 |
| 4 | なし (完走) | — |

4 回目の実測: findings 3 件 / `Apply fixes` は `permission_denials_count=0`・`num_turns=6` /
`Gate fix push` が `[FIX_PUSH_ALLOW] autonomy=allowed branch=isolated content=docs-only
scope=in-scope(1 files)` / `Push fix` が `ee9557a..86f424c` を push / `Report degrade to
Phase A` は skip (= degrade しなかった証拠)。

無人 fix が書いた内容も実測検証し、仕込んだ不整合 3 点を過不足なく修正・範囲外の編集
ゼロ (1 ファイル / 3 insertions / 3 deletions) であることを確認した。

`AUTONOMY_ENABLED` を削除した状態での dispatch では fix job 自体が skip され、ADR-066 の
「欠損 → 安全状態」が実 Actions ランタイムで機能することも確認した。

## ADR-067 § 残課題 1 の方針を訂正

同節に書いていた修正方針「`allowedTools` を `Read(findings-input/**)` にする」は**誤り**
だった。同ディレクトリには未要約の raw な CodeRabbit テキスト (`comments.json` /
`reviews.json`) があり、glob を与えると write 権限を持つ fix agent がそれを直接読める。
findings agent と fix agent を分離した § 決定 2 / ADR-054 の設計目的が崩れる。実装時の
pre-push security review が REJECT で指摘し、単一ファイル指定に改めて land した (#358)。

**静的検査を通らないのはコードだけではない — ADR に書かれた修正方針そのものが誤って
いることがある**。この一般化を dev-conventions へ convention として追加した。

## 主な変更

- `docs/adr/adr-067-*.md`: 段 2 完走の検証記録、残課題 1〜4 を「段 2 で閉じた課題」へ移し
  方針訂正を明記、ruleset の実体 (`~ALL` 対象 / `claude/**` 除外 / admin bypass) を残課題へ、
  `coderabbitai[bot]` allowlist の follow-up 判断 (追加しない = WP-18 で再判断) を追記
- `docs/adr/adr-066-*.md`: bounded lifetime の実走観測 1 run 目を記録 (trigger (a)(c) 充足、
  (b) は variable 側のみ充足で config 側の実走観測は未実施)
- `docs/adr/adr-068-*.md`: Phase B 1 run 目で docs-only fix の事実性誤りが観測されなかった
  ことを記録 (追加機構の再検討条件は未充足のまま)
- `docs/harness-improvement-plan.md`: 引き継ぎ節を実施記録へ書き換え、WP-17 受け入れ基準の
  段 0〜2 を充足済みへ更新
- `docs/dev-conventions.md` + `CLAUDE.md`: convention「LLM を含む自動化経路は実走でしか
  検証できない」を追加
- `docs/todo20.md` (新規) + `docs/todo-summary2.md`: 順位 365-373 の 9 エントリを登録。
  内訳は post-merge feedback 採用 19 件を実装時の PR 粒度で 8 エントリへまとめたもの (365-372)
  と、Rust exe の自動再ビルド 2 層案 (373)。却下 5 件も理由付きで記録
- `docs/todo.md` / `docs/todo14.md` / `docs/todo3-11.md`: todo14.md が約 70KB に達したため
  新規追加先を todo20.md へ。preamble 数詞を 22 へ更新 (cli-docs-lint の検出に追従)

## 順位 373 の実測データ (Rust exe の自動再ビルド)

`.claude/*.exe` は gitignore された生成物で、`pnpm build:all` を明示実行しない限り古い
バイナリが使われ続ける。2026-08-04 時点で `cli-fix-push-gate` (3/3 ファイル)・
`cli-autonomy-gate` (1/1)・`hooks-session-start` (1/8) の 3 パッケージが古い状態だった。

| 操作 | 小パッケージ | 大パッケージ (30 ファイル) |
|---|---|---|
| `cargo build --release` (変更あり) | 4.3 秒 | 9.9 秒 |
| `cargo check` | 0.39 秒 | 0.70 秒 |

この差から、PostToolUse には `cargo check` を、Stop hook には `build --release` + deploy を
割り当てる 2 層構成を採る。PostToolUse で build しない理由 (編集ごとの 4〜10 秒、中間状態の
compile error がノイズになる、deploy が毎回走る) も todo エントリに記録した。

## 自動起動経路への懸念 (新規記録)

段 1 で `coderabbitai[bot]` の permission が `none` と実測され `pull_request_review` 経路が
恒久 deny になるため、Phase B の起動は `issue_comment` (walkthrough) 経路だけになる。この
経路は初回 1 回きりで、その時点では CodeRabbit の実レビューがまだ無いことが多い。「findings
がある状態で Phase B が自動起動する窓」が実質的に無い可能性がある。段 2 は
`workflow_dispatch` による手動起動だったため**自動起動経路は未検証**である。WP-18 着手時に
bot allowlist の要否と併せて実測する (ADR-067 § 検証記録に記録)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
aloekun added a commit that referenced this pull request Aug 5, 2026
WP-17 (イベント駆動バックボーン完成) で唯一残っていた受け入れ基準 — 実走スモーク段 2
= Phase B allow 経路の完走 — が 2026-08-04 に充足したことを記帳し、#350#357 の
post-merge feedback 採否とセッション発案のハーネス改善 1 件を todo へ確定する。
コード変更は含まない。

## 段 2 の結果 (ADR-067 § 検証記録)

`workflow_dispatch` を 4 回実行し、4 回目で 13 step 完走に至った。

| 回 | 検出した欠陥 | 対処 |
|---|---|---|
| 1 | `gh api` は `--slurp` と `--jq` を併用できない | PR #356 |
| 2 | findings agent の出力がコードフェンスで囲まれ `jq` が失敗 | PR #357 |
| 3 | `Apply fixes` が findings ファイルを読めず空 diff | PR #358 |
| 4 | なし (完走) | — |

4 回目の実測: findings 3 件 / `Apply fixes` は `permission_denials_count=0`・`num_turns=6` /
`Gate fix push` が `[FIX_PUSH_ALLOW] autonomy=allowed branch=isolated content=docs-only
scope=in-scope(1 files)` / `Push fix` が `ee9557a..86f424c` を push / `Report degrade to
Phase A` は skip (= degrade しなかった証拠)。

無人 fix が書いた内容も実測検証し、仕込んだ不整合 3 点を過不足なく修正・範囲外の編集
ゼロ (1 ファイル / 3 insertions / 3 deletions) であることを確認した。

`AUTONOMY_ENABLED` を削除した状態での dispatch では fix job 自体が skip され、ADR-066 の
「欠損 → 安全状態」が実 Actions ランタイムで機能することも確認した。

## ADR-067 § 残課題 1 の方針を訂正

同節に書いていた修正方針「`allowedTools` を `Read(findings-input/**)` にする」は**誤り**
だった。同ディレクトリには未要約の raw な CodeRabbit テキスト (`comments.json` /
`reviews.json`) があり、glob を与えると write 権限を持つ fix agent がそれを直接読める。
findings agent と fix agent を分離した § 決定 2 / ADR-054 の設計目的が崩れる。実装時の
pre-push security review が REJECT で指摘し、単一ファイル指定に改めて land した (#358)。

**静的検査を通らないのはコードだけではない — ADR に書かれた修正方針そのものが誤って
いることがある**。この一般化を dev-conventions へ convention として追加した。

## 主な変更

- `docs/adr/adr-067-*.md`: 段 2 完走の検証記録、残課題 1〜4 を「段 2 で閉じた課題」へ移し
  方針訂正を明記、ruleset の実体 (`~ALL` 対象 / `claude/**` 除外 / admin bypass) を残課題へ、
  `coderabbitai[bot]` allowlist の follow-up 判断 (追加しない = WP-18 で再判断) を追記
- `docs/adr/adr-066-*.md`: bounded lifetime の実走観測 1 run 目を記録 (trigger (a)(c) 充足、
  (b) は variable 側のみ充足で config 側の実走観測は未実施)
- `docs/adr/adr-068-*.md`: Phase B 1 run 目で docs-only fix の事実性誤りが観測されなかった
  ことを記録 (追加機構の再検討条件は未充足のまま)
- `docs/harness-improvement-plan.md`: 引き継ぎ節を実施記録へ書き換え、WP-17 受け入れ基準の
  段 0〜2 を充足済みへ更新
- `docs/dev-conventions.md` + `CLAUDE.md`: convention「LLM を含む自動化経路は実走でしか
  検証できない」を追加
- `docs/todo20.md` (新規) + `docs/todo-summary2.md`: 順位 365-373 の 9 エントリを登録。
  内訳は post-merge feedback 採用 19 件を実装時の PR 粒度で 8 エントリへまとめたもの (365-372)
  と、Rust exe の自動再ビルド 2 層案 (373)。却下 5 件も理由付きで記録
- `docs/todo.md` / `docs/todo14.md` / `docs/todo3-11.md`: todo14.md が約 70KB に達したため
  新規追加先を todo20.md へ。preamble 数詞を 22 へ更新 (cli-docs-lint の検出に追従)

## 順位 373 の実測データ (Rust exe の自動再ビルド)

`.claude/*.exe` は gitignore された生成物で、`pnpm build:all` を明示実行しない限り古い
バイナリが使われ続ける。2026-08-04 時点で `cli-fix-push-gate` (3/3 ファイル)・
`cli-autonomy-gate` (1/1)・`hooks-session-start` (1/8) の 3 パッケージが古い状態だった。

| 操作 | 小パッケージ | 大パッケージ (30 ファイル) |
|---|---|---|
| `cargo build --release` (変更あり) | 4.3 秒 | 9.9 秒 |
| `cargo check` | 0.39 秒 | 0.70 秒 |

この差から、PostToolUse には `cargo check` を、Stop hook には `build --release` + deploy を
割り当てる 2 層構成を採る。PostToolUse で build しない理由 (編集ごとの 4〜10 秒、中間状態の
compile error がノイズになる、deploy が毎回走る) も todo エントリに記録した。

## 自動起動経路への懸念 (新規記録)

段 1 で `coderabbitai[bot]` の permission が `none` と実測され `pull_request_review` 経路が
恒久 deny になるため、Phase B の起動は `issue_comment` (walkthrough) 経路だけになる。この
経路は初回 1 回きりで、その時点では CodeRabbit の実レビューがまだ無いことが多い。「findings
がある状態で Phase B が自動起動する窓」が実質的に無い可能性がある。段 2 は
`workflow_dispatch` による手動起動だったため**自動起動経路は未検証**である。WP-18 着手時に
bot allowlist の要否と併せて実測する (ADR-067 § 検証記録に記録)。

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
aloekun added a commit that referenced this pull request Aug 8, 2026
* docs(todo): WP-18 で検出した問題を 4 エントリへ登録 (順位 374-377)

WP-18 (夜間 todo 消化ループ、#361 / #362 / #363) の実装中に pre-push review・
CodeRabbit・ユーザー指摘で検出した問題 9 件のうち、todo 登録が要る 8 件を **実装時の
PR 粒度**で 4 エントリへまとめる。切り分けは 2026-08-06 にユーザー確認済み。

## 評価時の #1 を検証し、todo 登録が不要になった

#1 は「Bash prefix 許可の悪用可能性検証と全経路への横展開」として登録予定だった。
ユーザー指示により登録前に検証したところ、**前提が誤りだった**。

#363 の security review は「`Bash(cargo test:*)` は前方一致でシェルを解釈しないため
`cargo test` に任意コマンドを連結すると通過する」と主張していた。公式ドキュメントは
これを明確に否定している:

  Claude Code is aware of shell operators, so a rule like `Bash(safe-cmd *)` won't give
  it permission to run the command `safe-cmd && other-cmd`. The recognized command
  separators are `&&`, `||`, `;`, `|`, `|&`, `&`, and newlines. A rule must match each
  subcommand independently.

`--allowedTools` も同じルール体系に属する (managed settings の deny を --allowedTools で
上書きできない、と明記されている)。

結果:

- `pr-monitor.yml` の Phase A 分析 agent に**当該の穴は無く、対処不要**。production の
  live な穴という当初の見立ては誤りだった
- 残作業は `ADR-072` 決定 5 の根拠記述の訂正のみで、#363 が open のうちに同 PR へ直接
  反映する。よって todo エントリを立てない

検証結果と経緯はセクション冒頭の対応表に残した。

## この一件自体を教訓として取り込んだ

「レビュー指摘への対応時チェックリスト」エントリに 4 項目目を追加した — **指摘が技術的
前提 (ツールの挙動・仕様) に依拠しているなら、対処より先にその前提を検証する**。とくに
設計変更や他経路への横展開を伴う場合。

今回は未検証の前提のまま (a) agent から Bash を落とす設計変更を行い、(b) それを ADR の
決定として記録し、(c) さらに「同じ形が production にもある」と横展開の警告まで出していた。
一次情報に当たれば 1 回の WebFetch で否定できた。

## 内訳

- 順位 374: WP-18 夜間ループの実走スモーク実施 (Tier 1、評価時の #2/#3)
- 順位 375: レビュー指摘への対応時チェックリスト (Tier 2、評価時の #4/#5/#6 + 今回の #1)
- 順位 376: push-runner の bookmark 自動前進がスタック境界を壊す (Tier 2、評価時の #7)
- 順位 377: 夜間ループの防御を検知から防止へ格上げする判断 (Tier 3、評価時の #8/#9)

## まとめ方の方針

リポジトリの既存バッチ登録 (#350#357 の 24 件を 8 エントリへ) と同じく実装時の PR 粒度で
まとめた。評価時の番号との対応はセクション冒頭に表で残してある。

順位 374 (スモーク) の観測項目は `ADR-072` の実走スモーク節に表があるため、todo 側は
スケジューリングの掛かりだけを持ちチェックリストを複製しない。同じ表を 2 箇所で管理すると
必ず drift する (#362 の post-merge feedback が指摘した single source-of-truth 問題と同型)。

ADR-033 (絶対番号は table のみに保持) に従い、エントリ本文には順位番号を書いていない。
todo20.md は 50KB 閾値内。pnpm lint:docs / markdownlint ともに green。

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(todo): #363 post-merge feedback の採用 6 件を登録 (順位 378-383)

WP-18 最終 PR (#363、ADR-072) のマージ後 feedback が Tier 1 に 4 件・Tier 2 に 2 件を
採用候補として挙げた。ユーザー承認 (2026-08-07) を得て登録する。

## 6 件は 1 本の根から出ている

台帳 (docs/claude-code-web-tasks.md) の 内容 / 対象ファイル / 注意 は自由記述のまま
無人 agent のプロンプトへ流入する。agent は $GITHUB_WORKSPACE 全体に書き込め、その
出力は draft PR 本文という公開面に出る。ADR-054 の信頼境界そのもの。

- 378 (XS) 台帳を ADR-035 の docs-only 除外パス表へ追加 — 他 3 件の前提。台帳だけを
  変える PR が緩い評価経路に乗ると、対策そのものを迂回する台帳 PR が通りうる
- 379 (S)  tool scope を work/** へ限定 — ADR-072 決定 7 の改ざん検知が必要になって
  いる根本原因。実装後も検知層は残す (防御を 1 枚に減らす変更ではない)
- 380 (M)  台帳フィールドを untrusted data として明示 framing
- 381 (S)  台帳由来 SUMMARY の draft PR 本文出力に screening
- 382 (M)  injection payload の regression test (380 に依存)
- 383 (S)  is_separator_row のパイプ検証欠落

## 期限を「定常運用開始前」に固定する

実効リスクは現時点では低い — 悪意ある台帳行を master へマージするのはユーザー自身で、
単独運用では外部からの注入経路が無い。ただし夜間ループが定常運用に入り draft PR の
流量が増えると前提が変わるため、無期限の Tier 積みにしない。

順位 374 (実走スモーク) は dry_run で PR を作らないため本件の実害が無く、待たせない。

## 383 は実コードで確認済み

is_table_row は行頭 | を要求するが、is_separator_row は split_cells の結果しか見ない。
split_cells("---") は ["---"] を返し全セルが '-' のみなので真になる。markdown の
水平線がセパレータ行として通る (todo ファイル自身が --- を使っている)。
ADR-072 決定 2 の fail-closed 設計の coverage hole。

## 併せて

順位 374 のスモーク観測項目数を 4 → 8 へ修正した (ADR-072 側の実測と不一致だった)。

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(harness-plan): WP-18 を 3 PR マージ済へ更新し残作業 2 系統を明示する

#363 のマージ (2026-08-07) で WP-18 の実装 3 本がすべて land した。

## 「実装完了 = WP 完了」ではないことを表で残す

コードは全部 master にあるが、**夜間ループはまだ 1 度も走っていない**。この状態を
「実装済」の一語で片付けると、次のセッションが受け入れ基準を満たしたものと誤読する。
残作業を 2 系統に分けて表にした:

1. 実走スモーク (順位 374) — 受け入れ基準の中核
2. prompt injection 対策 4 件 (順位 378-381) — 定常運用開始前に必須

## スモークの前提が充足したことを記録

受け入れ基準の表は「(a) workflow が master にある (b) 台帳に無人可マークがある」を
未充足として書いていたが、**両方ともマージで解消した**。残る操作は GitHub UI 側の
AUTONOMY_ENABLED 設定のみなので、その 1 点へ書き換えた。

## 依存関係を明示する

378-381 は 1 本の根 (台帳の自由記述が無検証で agent プロンプトへ流入) から出ている。
一方スモークは dry_run で PR を作らないため本件の実害が無い。したがって
**スモークは 378-381 を待たずに着手してよい**と明記した。次セッションが順序で
迷わないようにするため。

## 未 push の改善 3 点の所在を残す

#363 の最終 push が security REJECT で止まったため、改ざん検知の red 化 / 決定 10 の
色分け表 / 決定 6 の列挙基準が master に載っていない。ローカル bookmark
wp18/unpushed-improvements (fc22403c) に保持していることを記録した。いずれも
可観測性と文書の改善で、fail-closed 自体は master 版でも成立している。

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(todo): 外部設定 (GitHub App / variables / secrets) の実体記録を登録 (順位 384)

新セッションでの指摘 (2026-08-07): workflow は vars.NIGHTLY_APP_ID /
secrets.NIGHTLY_APP_PRIVATE_KEY を参照するが、App の作成・インストール・登録を
記録した文書がリポジトリ内に無い。

## 欠けているのは設計根拠ではなく運用実体

ADR-072 決定 8 は「なぜ App token か」「なぜ PAT ではないか (オーナー PAT は
Repository admin として ADR-067 の ruleset backstop を素通りする)」「どの権限を
付けるか (Workflows は付けない)」「なぜ publish 直前に発行するか (token 寿命
1 時間)」を厚く残している。

記録が無いのは以下:

- App を実際に作成した事実・日付・名称・インストール範囲
- NIGHTLY_APP_ID = variable / NIGHTLY_APP_PRIVATE_KEY = secret という登録先の別
- 既存の Claude GitHub App との区別 (あちらは Workflows を含む広い権限を持つ別物)
- 再構築手順 (鍵ローテーション・派生プロジェクト展開)

NIGHTLY_APP の文字列はリポジトリ全体で workflow の 2 行と ADR 残課題の 1 行にしか
現れない。

## これは ADR-051 違反

ADR-051 (クロスシステム設定 coupling) は内部設定と外部 SaaS 設定が論理結合する場合に
(1) 両設定ファイルへの相互参照コメント (2) 期待値の組み合わせ表の ADR 必須記載
(3) 変更は両側を同一 PR、の 3 点を規律として定めている。workflow ↔ GitHub App +
repository variables/secrets はこの型で、3 点とも未実施。

前例として ADR-067 段 0 は repository ruleset を ruleset 名つきで「設定済み」と
記録している。ADR-072 は同じ扱いをしていない。同型の欠落が AUTONOMY_ENABLED にも
あり (ADR-066 は「Actions variable を使う」とは書くが現状値を記録していない)、
本エントリで一緒に扱う。

## 順位 374 と同時実施にする理由

スモークでは AUTONOMY_ENABLED の設定と App token の実動確認のため GitHub UI を
触るので、その過程で実値がすべて揃う。先行して記録しようとすると値が確定せず
二度手間になる。

## 教訓を残す

App の作成手順・Expire user authorization tokens の扱い・既存 App との違いは
2026-08-07 のセッションでユーザーへ提示したが、リポジトリへ残さなかった。会話は
次のセッションに残らないが workflow は残る。参照だけが残って由来が消える状態を
作った。順位 375 と同じクラスの失敗としてエントリ本文に記録した。

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(review): CodeRabbit Major 4 件を妥当性判定のうえ 3 件へ対応する (#364)

自動 fix 経路 (f698d19) が 1 件 (#4 台帳への非記録ルール追加) を対応済みで、
本コミットはその内容を含んだうえで残りを手で対応した結果である。同一ファイルの
近接行のため path 単位で分離できず 1 コミットに畳み込まれている。

## 妥当性の判定

severity ラベルではなく、プロジェクトの設計方針に照らして 1 件ずつ判定した。

- #1 adr-072:335 (秘密値を ADR に記録しない) — **妥当**。「実走スモークで実値を
  確認し ADR へ追記する」は秘密鍵本文まで書くと読める。ADR-051 が記録を課すのは
  結合の存在と期待値の組み合わせであって秘密の実値ではない。設定メタデータに
  限定し、鍵本文と token は ADR にも git 履歴にも残さないことを明記した
- #2 harness-improvement-plan:223 (受け入れ基準が成功経路だけ) — **妥当**。本
  プロジェクトは背圧 12 シナリオ・kill-switch 8 シナリオと停止側を drill で
  固めてきたが、夜間ループの停止側は実走未観測。WP-17 の残課題 (明示的 false と
  config 側 deny が実走未観測) と同じ穴。AUTONOMY_ENABLED の 3 状態を受け入れ
  基準へ追加した。指摘本文が名指しした 2 箇所 (計画書 L223 / todo20 L293-304) の
  両方に反映している
- #3 todo20:480 (prompt injection の回帰 fixture) — **妥当**。順位 382 の payload
  例 "; echo PWNED; #" は shell injection であって prompt injection ではない。
  台帳テキストが流れ込む先は shell ではなく LLM プロンプトなので、テストが目的と
  噛み合っていなかった。自然言語 adversarial payload (本命) と shell/パース形式
  payload (堅牢性) の 2 系統へ分離した

## 自動 fix の実測確認

f698d19 は 1 ファイル 2 行追加のみで範囲外の編集ゼロ。内容も妥当だったため
そのまま採用した (ADR-068 の後退検知の趣旨に沿って diff を実測で確認済み)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant