From 3857039ad87f874f96eaee256bc4b656162505b0 Mon Sep 17 00:00:00 2001 From: aloekun Date: Mon, 10 Aug 2026 20:44:58 +0900 Subject: [PATCH] =?UTF-8?q?docs(todo):=20=E6=9C=AC=E3=82=BB=E3=83=83?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=A7=E5=AE=9F=E6=B8=AC=E3=81=97?= =?UTF-8?q?=E3=81=9F=E9=81=8B=E7=94=A8=E5=95=8F=E9=A1=8C=205=20=E4=BB=B6?= =?UTF-8?q?=E3=82=92=E9=A0=86=E4=BD=8D=20397-401=20=E3=81=B8=E7=99=BB?= =?UTF-8?q?=E9=8C=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit あわせて post-merge feedback (#376/#377/#380/#381/#382) の採用分 10 件を順位 402-411 へ 登録した。2026-08-10 に採用候補を系統別へ分類し、ユーザーが採否を決定したもの。 採用: 系統 A (観測の完全性) 3 件 / 系統 B (重複実装の予防) 3 件 / 系統 C (shell・config パースの安全性) 3 件 却下: 系統 D (workflow セキュリティ標準化) / 系統 E (PAT 失効監視) — 様子見 形を変えて採用: 系統 F — 「CLAUDE.md に rustfmt 非適用の方針を書く」ではなく 「cargo fmt を PreToolUse でブロックする」(順位 411) 系統 F の変更理由 (ユーザー判断): 規約は CLAUDE.md に書いた時点で毎セッション読まれ コンテキストを圧迫するが、PreToolUse hook は発火するまでコストがゼロで、ブロックと 同時に正しいコマンドをフィードバックできる。読み手は規約を覚えていなくても正しい経路へ 到達する。ADR-042 の mechanizable 判定を満たすため機構側が正しい。 **この非対称は現行 ADR-042 に無い**。同 ADR の判断基準は「機械判定できるか」「投資対効果」 が中心で、「規約は常時コンテキストを消費し hook は発火時のみ」という観点が明示されて いない。ルール追加を検討するたびに効く一般則なので、順位 411 の作業範囲に ADR-042 への 追記を含めた。 ## 計画書 (harness-improvement-plan.md) の WP-18 節を再編成 **WP-18 で生んだ問題と、WP-18 の運用で日常的に踏む問題を WP-18 の外へ押し出さない** (2026-08-10 ユーザー方針) ため、残作業を 3 区分へ分けて完了条件を明示した。 従来は観測と派生タスクが 1 表に混在し、WP-18 の完了に何が要るのかが読み取れなかった。 - (1) 観測待ち — 機構は整備済みで事象か期限を待つもの - (2) 運用問題の対処 — WP-18 が生んだ (基準 1) / WP-18 の運用で踏む潜在バグ (基準 2)。 順位 397 / 398-400 / 401 / 410。**完了条件に含める** - (3) WP-18 外の派生 — 順位 396 / 411 / 402-409 等。完了条件に含めない (3) を完了条件から外すのは、§ 7 の退役条件が「全 WP が完了または見送り」である以上、 リポジトリ全体の一般則を WP-18 に紐づけると計画書が永久に退役できなくなるため。 ただし**優先度が低いという意味ではない** — 順位 396 (flaky テスト) と 411 (cargo fmt ブロック) はいずれも高優先度で、WP-18 とは独立に早期着手する旨を明記した。 あわせて古い記述を実測に合わせた: - 見出しの「実装・スモークは 2026-08-08 までにほぼ完了」→ 決定 16 という新規実装が 2026-08-10 に入ったため「観測中 + 運用問題の対処中」へ - 「前 2 者は順位 394 後の run で判定できる」→ 順位 394 は完了済みで実際の前提は決定 16。 同一ファイル内の自己矛盾だった - WP-17 残課題節にも同じ「順位 394 後の run」が残っていたため同期。あわせて 「代替解は draft 廃止」が誤りだったことも記録した ## todo 側 - 順位 396 を Tier 2 → **Tier 1** へ格上げ (ユーザー判断)。単発の Severity では Tier 2 相当だが、flaky テストは「また flake だろう」という読み替えを生み実バグの見落とし 経路になるため。両 OS matrix (ADR-065) の信号品質を守る意味で早期に潰す - 順位 411 に早期着手の根拠を追記 (cargo fmt は反射的に実行されやすい) - **却下を negative result として記録**: 系統 D / E は様子見。trunk 保護の drift 対処 2 件は却下 (予防側は順位 405 で押さえた / 共有 lib 化は network isolation 設計と 抵触しうる)。**再採用条件は「同型の drift が今後も再発する場合」**と明記した --- CodeRabbit レビュー対応 (#384、5 件すべて修正) --- 1. WP-18 完了条件でスモーク未確定の扱いが不明確 (Major) (c) だけを非必須と書き (a)(b) の扱いが無かった。(a)(b) は事象待ちで**自力で発生させ られない**ため、条件に含めると WP を閉じられない。3 件すべてを非ブロッカーとし、 理由と移管先・期限を表で明記した。(a)(b) は 2026-11-06 時点で未観測なら 「機会が来なかった」として見送り ADR-067 の bounded lifetime へ委ねる。 2. 順位 411 の要約が詳細計画と不一致 (Minor) summary は「正しいコマンドを提示」だが、cargo fmt に**代替コマンドは存在しない** (手で直すのが正)。「正しい対処を提示」へ変更し、詳細側にもその旨を明記した。 3. 順位 398 の完了判定を対象 PR に束縛すべき (Major) 「report 生成を完了根拠にする」案が不十分だった。copy_feedback_report は find_latest_run_dir で最新 run を選ぶだけで **pr_number と照合していない**ため、 別 PR の report を現在の PR の {pr_number}.md へコピーし得る。また takt の終了は timeout や失敗でも起こるので終了した事実は report 完成を証明しない。実装を読んで 裏付けたうえで、完了判定には「成功終了」と「対象 PR のものであること」の両方が 要る旨を追記した。本セッションで実際に context.json が別 PR を指していた事象とも 同型である。 4. 旧語彙 lint の extensions から yaml が漏れている (Minor) 拡張子は eq_ignore_ascii_case の文字列一致で **yml と yaml は別物**。本リポジトリは .github/workflows/*.yml と .coderabbit.yaml の両方を持つため、yaml を落とすと 後者が未検査になる。両方を対象に加え、理由も併記した。 5. cargo fmt の検出対象が未定義 (Major) 完全一致だけでは cargo fmt --all / cargo +stable fmt / rustup run stable cargo fmt / cargo-fmt が素通りする。作業計画の先頭に「検出範囲を先に決める」を追加し、完了基準に 「完全一致に限定する場合は素通りする形態を明記する」ことを求める形にした。 いずれも実物 (takt.rs の実装 / linter の拡張子判定 / リポジトリ内の .yml と .yaml の 共存) を確認したうえで妥当と判断している。 --- docs/harness-improvement-plan.md | 62 ++++- docs/todo-summary2.md | 17 +- docs/todo21.md | 380 ++++++++++++++++++++++++++++++- 3 files changed, 450 insertions(+), 9 deletions(-) diff --git a/docs/harness-improvement-plan.md b/docs/harness-improvement-plan.md index 6bcb1477..346b8041 100644 --- a/docs/harness-improvement-plan.md +++ b/docs/harness-improvement-plan.md @@ -153,30 +153,78 @@ Anthropic 公式のハーネスエンジニアリング指針(決定論的基 #### 後続へ引き継ぐ残課題 - **停止側の実走は config 側のみ残る**: variable 側 3 状態(`'true'` / `'false'` / 未設定)は 2026-08-08 の WP-18 停止側スモークですべて実測済([ADR-066](adr/adr-066-autonomy-global-kill-switch.md) § 実走観測 2)。**残るのは config 側(master ref の `autonomy-config.toml` で `enabled = false`)の実走のみ**(exe 単体 drill で固定済み)。ADR-066 bounded lifetime の観測(3〜5 run、期限 2026-11-02)で埋める。 -- **Phase B の自動起動経路は生存している(2026-08-09 訂正)**: 一度「不成立」と記帳したが誤りで、起動契機のコメントが夜間 draft PR に供給されていなかっただけ。#373 で CodeRabbit がコメントした時点で `issue_comment` 経路は発火し、Phase A が自動起動した([ADR-067](adr/adr-067-phase-b-unattended-fix-push.md) § 検証記録の追記)。対処として置いた [ADR-072](adr/adr-072-nightly-todo-loop.md) 決定 11(`@coderabbitai review` の明示トリガー)は **bot 投稿が無視されるため撤回**し、代替解は順位 394 の draft 廃止。**残るのは Phase B 本体(無人 fix push)への到達と `coderabbitai[bot]` allowlist の要否**で、いずれも順位 394 後の run で判定する。 +- **Phase B の自動起動経路は生存している(2026-08-09 訂正)**: 一度「不成立」と記帳したが誤りで、起動契機のコメントが夜間 draft PR に供給されていなかっただけ。#373 で CodeRabbit がコメントした時点で `issue_comment` 経路は発火し、Phase A が自動起動した([ADR-067](adr/adr-067-phase-b-unattended-fix-push.md) § 検証記録の追記)。対処として置いた [ADR-072](adr/adr-072-nightly-todo-loop.md) 決定 11(`@coderabbitai review` の明示トリガー)は **bot 投稿が無視されるため撤回**し、代替解は当初「順位 394 の draft 廃止」としたが**これも誤り**で、真の原因は author が bot であることだった([ADR-019](adr/adr-019-coderabbit-review-hybrid-policy.md) § CodeRabbit は bot 作成 PR を自動レビューしない)。解決は [ADR-072](adr/adr-072-nightly-todo-loop.md) 決定 16。**残るのは Phase B 本体(無人 fix push)への到達と `coderabbitai[bot]` allowlist の要否**で、決定 16 により**観測機会は供給されるようになった**(あとは docs 指摘の出る夜間 PR に当たるのを待つ)。 - **routine 出力の受け渡し手段が未決**: 分析結果が transcript にしか残らずユーザーが読まなければ消える。実行主体を含む 3 択(routine / GitHub Actions schedule / ローカル維持 = 断念)で、**断念も正規の出口**。判定は ADR-070 bounded lifetime (b) の観測後([ADR-070](adr/adr-070-weekly-review-cloud-routine.md) § 残課題)。 -### WP-18: 夜間 todo 消化ループ — 観測中(実装・スモークは 2026-08-08 までにほぼ完了、本番稼働中) +### WP-18: 夜間 todo 消化ループ — 観測中 + 運用問題の対処中(本番稼働中) > 夜間に 1 タスクを無人実装し **PR 作成で停止**する(マージ判断は人間)ループ。**設計・決定・検証記録は [ADR-072](adr/adr-072-nightly-todo-loop.md) が正**(背圧は [ADR-071](adr/adr-071-draft-pr-backpressure.md)、外部設定の実体は ADR-072 § 外部設定の実体)。本節は残作業の索引だけを残す。着手前決定・PR 構成・PR chain 宣言・受け入れ基準の達成記録は役目を終えたため削除した(経緯は git log と #361〜#370 の PR 本文を参照)。 > > **2026-08-09 の方針変更(実装済み)**: 停止点を draft PR から**通常 PR**へ変更した(旧 順位 394 → [ADR-072](adr/adr-072-nightly-todo-loop.md) 決定 15)。トリガーがユーザー指示か自動採択かで扱いを区別せず、commitment 点をマージ 1 点に集約する(ユーザー判断)。draft であることが CodeRabbit の自動レビュー対象外を招いていた構造そのものを解消した。背圧の指標も同時に「未マージの `claude/` PR 数」へ改め([ADR-071](adr/adr-071-draft-pr-backpressure.md))、名称を `autonomous` 系へ揃えた。 +> +> **2026-08-10 の追加実装**: draft 廃止だけではレビューが付かないことが実走で判明した。**ブロック要因は draft ではなく PR の author が bot であること**で、CodeRabbit の組み込み挙動(設定では制御不可)だった。`review-request.yml` を新設し、**PR 作成者は bot のまま、レビュー要求だけを人間資格情報で出す**分離で解決した([ADR-072](adr/adr-072-nightly-todo-loop.md) 決定 16)。制約の全体像は [ADR-019](adr/adr-019-coderabbit-review-hybrid-policy.md) § CodeRabbit は bot 作成 PR を自動レビューしない が真実源。 + +**達成したこと**: 台帳([claude-code-web-tasks.md](claude-code-web-tasks.md))から Rust 分類関数が選んだ 1 タスクを、kill-switch(ADR-066)と背圧(ADR-071)の上で無人実装し PR 作成で止めるループが本番稼働に入った(2026-08-08 に schedule 初回実走で draft PR [#365](https://github.com/aloekun/claude-code-hook-test/pull/365) = `claude/nightly-203` を作成)。定常運用開始前に必須とした prompt injection 対策 4 件(順位 378-381 → ADR-072 決定 12-14 + [ADR-035](adr/adr-035-doc-evaluation-policy.md) / `lib-docs-policy` の同期)と外部設定の実体記録(順位 384 → ADR-072 § 外部設定の実体)も完了。実走スモーク 10 項目は **7 充足 / 3 未確定**(Phase B 本体の到達 / `coderabbitai[bot]` allowlist の要否 / トークン露出)で、一覧は ADR-072 § 実走スモークが正。前 2 者は決定 16 でレビューが毎回付くようになり、観測機会が供給されるようになった(残るは事象待ち)。停止側実測は WP-17 の残課題([ADR-066](adr/adr-066-autonomy-global-kill-switch.md) bounded lifetime の variable 側 3 状態)も同時に埋めた(ADR-066 § 実走観測 2)。 -**達成したこと**: 台帳([claude-code-web-tasks.md](claude-code-web-tasks.md))から Rust 分類関数が選んだ 1 タスクを、kill-switch(ADR-066)と背圧(ADR-071)の上で無人実装し PR 作成で止めるループが本番稼働に入った(2026-08-08 に schedule 初回実走で draft PR [#365](https://github.com/aloekun/claude-code-hook-test/pull/365) = `claude/nightly-203` を作成)。定常運用開始前に必須とした prompt injection 対策 4 件(順位 378-381 → ADR-072 決定 12-14 + [ADR-035](adr/adr-035-doc-evaluation-policy.md) / `lib-docs-policy` の同期)と外部設定の実体記録(順位 384 → ADR-072 § 外部設定の実体)も完了。実走スモーク 10 項目は **7 充足 / 3 未確定**(Phase B 本体の到達 / `coderabbitai[bot]` allowlist の要否 / トークン露出)で、一覧は ADR-072 § 実走スモークが正。前 2 者は順位 394 後の run で同時に判定できる。停止側実測は WP-17 の残課題([ADR-066](adr/adr-066-autonomy-global-kill-switch.md) bounded lifetime の variable 側 3 状態)も同時に埋めた(ADR-066 § 実走観測 2)。 +**2026-08-10 に運用が一巡した**: 決定 16 の確立で bot 作成 PR にレビューが付く経路が成立し(PR 作成から CodeRabbit の反応まで 15 秒、完全自動)、**採用率測定の 1 件目 [#381](https://github.com/aloekun/claude-code-hook-test/pull/381) をマージ**した。あわせて台帳の後始末運用を明文化した(マージ時に該当順位を削除しないと除外マーカーが消えて再選択される、[claude-code-web-tasks.md](claude-code-web-tasks.md))。**この一巡で運用問題が 5 件見つかっており**(§ 残作業 の「運用問題の対処」)、実装が終わっても運用が回るとは限らないことが実証された。 #### 残作業 +> **3 区分に分ける(2026-08-10 再編成)**。従来は観測と派生タスクが 1 表に混在しており、**WP-18 の完了に何が要るのか**が読み取れなかった。2026-08-10 の運用一巡で運用問題が 5 件見つかったことを機に、(1) 観測待ち、(2) 運用問題の対処、(3) WP-18 外の派生、へ分けた。 + +##### (1) 観測待ち — 機構は整備済みで、事象か期限を待つもの + | 内容 | 管理先 | 期限 / 条件 | |---|---|---| -| ~~順位 393 / 394 の実走確認~~ → **充足(2026-08-10)**。夜間 PR に CodeRabbit のレビューが付くことを実走で確認した。ただし**原因は draft ではなく author が bot であること**で、決定 15 の前提は誤りだった。解決は [ADR-072](adr/adr-072-nightly-todo-loop.md) 決定 16(レビュー要求だけを人間 identity で出す別 workflow)。制約の全体像は [ADR-019](adr/adr-019-coderabbit-review-hybrid-policy.md) § CodeRabbit は bot 作成 PR を自動レビューしない が正 | ADR-072 決定 16 | 完了 | +| ~~順位 393 / 394 の実走確認~~ → **充足(2026-08-10)**。夜間 PR に CodeRabbit のレビューが付くことを実走で確認した。ただし**原因は draft ではなく author が bot であること**で、決定 15 の前提は誤りだった。解決は [ADR-072](adr/adr-072-nightly-todo-loop.md) 決定 16 | ADR-072 決定 16 | 完了 | | **スモーク未確定 (a) Phase B 本体の到達 と (b) `coderabbitai[bot]` allowlist の要否** — 決定 16 で**観測機会は供給されるようになった**(夜間 PR に毎回レビューが付く)。あとは **docs 指摘の出る夜間 PR に当たるのを待つ**だけで、未確定の理由が「機構が無い」から「事象待ち」へ変わった | ADR-072 § 実走スモーク | 事象待ち(機構は整備済み) | -| **スモーク未確定 3 件のうち (c) `cargo` サブプロセスへのトークン露出**(順位 374 の残り。**意図的保留** — 初版 probe の設計欠陥を解消した安全な probe を設計してから 1 回で観測。ADR-072 決定 5 の Bash 再付与判断の材料でもある) | ADR-072 § 残課題 | 急がない(Bash 非付与が保守側)。**完走条件には含めない** | -| **採用率 2 週間測定**(WP 全体の受け入れ基準。人間がマージした割合 50% 超で継続・拡大 — 参考値であり統計的意味は無い、ADR-072 § 欠点)。測定は weekly-review の自律アクション棚卸し(WP-19 ステップ 3)へ載せて仕組み化。**開始起点 = 2026-08-10 に確定**(PR に CodeRabbit のレビューが付く実走確認をもって完走とする定義。意図的保留のトークン露出は完走条件に含めない)。1 件目は [#381](https://github.com/aloekun/claude-code-hook-test/pull/381) | ADR-072 § 試験運用判断基準 | 2026-08-24 に中間確認、2026-11-06 までに判定 | +| **スモーク未確定 (c) `cargo` サブプロセスへのトークン露出**(順位 374 の残り。**意図的保留** — 初版 probe の設計欠陥を解消した安全な probe を設計してから 1 回で観測。ADR-072 決定 5 の Bash 再付与判断の材料でもある) | ADR-072 § 残課題 | 急がない(Bash 非付与が保守側)。**完走条件には含めない** | +| **採用率 2 週間測定**(WP 全体の受け入れ基準。人間がマージした割合 50% 超で継続・拡大 — 参考値であり統計的意味は無い、ADR-072 § 欠点)。測定は weekly-review の自律アクション棚卸し(WP-19 ステップ 3)へ載せて仕組み化。**開始起点 = 2026-08-10 に確定**。1 件目は [#381](https://github.com/aloekun/claude-code-hook-test/pull/381)(採用) | ADR-072 § 試験運用判断基準 | 2026-08-24 に中間確認、2026-11-06 までに判定 | | 稼働後 1 週間の run 頻度・Max 枠消費を観測して schedule 頻度を調整 | 運用ノート(本表のみ) | 稼働中 | -| ~~順位 395: 週次レビューで浮きブランチを検出し削除を提案~~ → **実装完了(2026-08-09)**。`cli-stale-branch-scan` / `pnpm stale-branch-scan`。設計と判定規則は [ADR-031](adr/adr-031-weekly-review-pipeline.md) § 残存ブランチ検出 が正。自動削除はしない(提案のみ) | [ADR-031](adr/adr-031-weekly-review-pipeline.md) | 完了。WP-19 ステップ 3 の一部を先取り済み | + +##### (2) 運用問題の対処 — WP-18 が生んだ / WP-18 の運用で日常的に踏むもの + +> **WP-18 の完了条件に含める**(2026-08-10 ユーザー方針)。判定基準は 2 つ。**基準 1**: WP-18 の機構が無ければ起きなかった。**基準 2**: 以前から存在したが WP-18 の運用で踏み、**使い勝手に影響する**潜在バグ。**WP-18 が生んだ問題を WP-18 の外へ押し出さない**ための区分である。 + +| 内容 | 基準 | 管理先 | 期限 / 条件 | +|---|---|---|---| +| **順位 397: `pnpm merge-pr` が夜間 PR を検出できない** — WP-18 が新設した「bot が作った PR を人間がマージする」経路で生まれた。夜間 PR は remote 専用ブックマークしか持たず、`gh pr merge` は hook でブロックされるため**逃げ道が無い**。毎回 `jj bookmark track` が要る | 1 | [todo21.md](todo21.md) | 🚀 Tier 1、**採用率測定の運用に直結** | +| **順位 398-400: post-merge feedback の進行中ガードと復旧経路** — 連続マージで確実に踏み、復旧に手動介入(context 削除)が要る。marker は**危険な手順(stale context を読む直接起動)を案内**しており、誤った PR のレポートを生成しうる。3 件は同一機構の 3 面で同一 PR で扱える | 2 | [todo21.md](todo21.md) | 🔧 Tier 2、運用中に再発する | +| **順位 401: CodeRabbit クォータの窓が直近消費に追随する** — 決定 16 で**自律 PR が毎晩 1 レビュー消費**するようになり、人間の作業が集中する日に競合する。ADR-019 へ観測を記録する | 1 | [todo21.md](todo21.md) | 💎 Tier 3、記録のみ | +| **順位 410: `autonomy-config.toml` の boolean パース edge case をテスト固定** — WP-18 成果物(`review-request.yml` / `cli-autonomy-gate`)自身の堅牢化。kill-switch 判定の fail-open は [#380](https://github.com/aloekun/claude-code-hook-test/pull/380) で修正済みだが、確認がテストとして残っていない | 1 | [todo21.md](todo21.md) | 🔧 Tier 2、成果物の堅牢化 | + +##### (3) WP-18 外の派生 — 完了条件に含めない + +> WP-18 の PR やその CI で見つかったが、**WP-18 の機構とは独立**していて対処も独立に進められるもの。これらを WP-18 に紐づけると、**一般則の整備が終わるまで本計画書を退役できなくなる**(§ 7 の退役条件と衝突する)。優先度が低いという意味ではない — 順位 396 と 411 は**いずれも高優先度**で、WP-18 とは別に早期着手する。 + +| 内容 | 管理先 | 期限 / 条件 | +|---|---|---| +| **順位 396: hooks smoke suite の Linux `ETXTBSY` flake** — WP-18 の CI で見つかったが別クレートの既存テスト競合で、WP-18 の経路とは無関係。**flaky テストは「また flake だろう」で実バグを見落とす経路を作る**ため、両 OS matrix([ADR-065](adr/adr-065-ci-matrix-cross-os-regression.md))の信号品質を守る意味で**早期に潰す** | [todo21.md](todo21.md) | **高優先度**(WP-18 とは独立に着手) | +| **順位 411: `cargo fmt` を PreToolUse でブロック** — WP-18 作業中の誤実行が発端だが、対象は開発環境全般。**規約ではなく機構で弾く**判断([ADR-042](adr/adr-042-rule-vs-mechanism-boundary.md))。反射的に実行されやすく無関係な差分を生むため**早期に塞ぐ** | [todo21.md](todo21.md) | **高優先度**(WP-18 とは独立に着手) | +| 順位 402-409(post-merge feedback 採用分のうち一般則。観測の完全性 / 重複実装の予防 / shell·config パースの安全性)。**順位 410 のみ (2) へ分類**した(WP-18 成果物自身の堅牢化のため) | [todo-summary2.md](todo-summary2.md) | リポジトリ全体に適用する一般則 | +| ~~順位 395: 週次レビューで浮きブランチを検出し削除を提案~~ → **実装完了(2026-08-09)**。`cli-stale-branch-scan` / `pnpm stale-branch-scan`。設計と判定規則は [ADR-031](adr/adr-031-weekly-review-pipeline.md) § 残存ブランチ検出 が正 | [ADR-031](adr/adr-031-weekly-review-pipeline.md) | 完了。WP-19 ステップ 3 の一部を先取り済み | | 順位 382(injection payload regression test。依存先の順位 380 完了で unblock)/ 順位 383(`is_separator_row` のパイプ検証欠落) | [todo-summary2.md](todo-summary2.md) | 🔧 Tier 2、任意 | | 順位 375-377(レビュー対応チェックリスト / push-runner bookmark 前進 / 防御の格上げ判断) | [todo-summary2.md](todo-summary2.md) | 🔧 2〜💎 3、WP-18 完了後 | +#### WP-18 の完了条件 + +本 WP は以下の両方を満たした時点で `完了` とする。 + +1. **(1) 観測待ちのうち採用率測定が判定に至ること**(2026-11-06 期限)。 + + **スモーク未確定 3 件はいずれも完了条件に含めない**(非ブロッカー)。理由と移管先は次のとおり。 + + | 未確定 | 完了条件に含めない理由 | 移管先 / 期限 | + |---|---|---| + | (a) Phase B 本体の到達 / (b) `coderabbitai[bot]` allowlist の要否 | **事象待ちであり自力で発生させられない**(docs 指摘の出る夜間 PR に当たるまで観測できない)。機構は決定 16 で整備済みで、待つ以外にできることが無い。これを条件にすると WP を閉じられない | [ADR-072](adr/adr-072-nightly-todo-loop.md) § 実走スモーク。**2026-11-06 時点で未観測なら「機会が来なかった」として見送り**、ADR-067 の bounded lifetime へ委ねる | + | (c) `cargo` サブプロセスへのトークン露出 | **意図的保留**(2026-08-09 ユーザー決定)。安全な probe を設計してから 1 回で観測する。決定 5 の Bash 非付与が保守側のため未観測でも安全側 | ADR-072 § 残課題。期限なし(急がない) | + +2. **(2) 運用問題の対処がすべて決着していること**(対処するか、根拠つきで見送るかのいずれか)。 + +2 を条件に含めるのは、**WP-18 が生んだ問題と、WP-18 の運用で日常的に踏む問題を、WP-18 の外へ押し出さないため**(2026-08-10 ユーザー方針)。実装が終わった時点で WP を閉じると、運用の綻びが「誰の担当でもないタスク」として残る。 + +**(3) は完了条件に含めない**。§ 7 の退役条件が「全 WP が完了または見送り」である以上、リポジトリ全体の一般則を WP-18 に紐づけると本計画書が永久に退役できなくなる。 + ローカルで同ループを回す場合のみ [ADR-045](adr/adr-045-jj-workspace-parallel-sessions.md) の workspace を使う(クラウドは使い捨てクローンのため分離不要)。 ### WP-19: 常時性ガード diff --git a/docs/todo-summary2.md b/docs/todo-summary2.md index a9c4029a..0237bcc7 100644 --- a/docs/todo-summary2.md +++ b/docs/todo-summary2.md @@ -143,7 +143,22 @@ | 390 | 🔧 Tier 2 | **台帳 framing 区切りの定数と workflow リテラルの cross-file 一致を CI 検証 (#369 T2 採用)** | todo21.md | M | なし (LEDGER_DATA_FRAME_MARKER と ===BEGIN/END_LEDGER_DATA=== が対。片側変更で ADR-072 決定 13 の framing が破れる) | | 391 | 🔧 Tier 3 | **jj の落とし穴 (squash 方向・空コミットでの bookmark ずれ) を dev-conventions へ (#369 T3 採用)** | todo21.md | S | なし (本セッションで複数回踏んだ。コミット確定は describe+bookmark set、new は新作業時のみ、を明文化) | | 392 | 🔧 Tier 3 | **push パイプラインの terminal outcome を telemetry へ記録し失敗回数・原因を機械集計可能にする** | todo21.md | M | なし (2026-08-09 WP-18 失敗頻度分析で構造化記録の欠落が判明。stage + reason code を ADR-055 系へ fail-open で追記し ADR-062 月次で集計。順位 386/387/376 の効果測定ベースラインにもなる) | -| 396 | 🔧 Tier 2 | **hooks smoke suite の並列実行が Linux で `ETXTBSY` を起こす** | todo21.md | S | なし (#376 CI で ubuntu のみ失敗、windows は成功、当該クレートは無変更。2 テストが並列に exe を copy→spawn し、fork した子が copy 側の書き込み fd を継承するため exec が Text file busy。直近 15 run で初出だが ADR-065 の両 OS matrix の信号品質を下げる) | +| 396 | 🚀 Tier 1 | **hooks smoke suite の並列実行が Linux で `ETXTBSY` を起こす (flaky テスト、早期修正)** | todo21.md | S | なし (#376 CI で ubuntu のみ失敗、windows は成功、当該クレートは無変更。2 テストが並列に exe を copy→spawn し、fork した子が copy 側の書き込み fd を継承するため exec が Text file busy。直近 15 run で初出だが ADR-065 の両 OS matrix の信号品質を下げる。**flaky を放置すると「また flake だろう」で実バグを見落とす**ため WP-18 とは独立に早期着手する = 2026-08-10 ユーザー判断で Tier 1 へ格上げ) | +| 397 | 🚀 Tier 1 | **`pnpm merge-pr` が夜間 PR (remote 専用ブックマーク) を検出できない** | todo21.md | S-M | なし (2026-08-10 実測。#381 のマージが「PR が見つかりません」で exit 1。夜間 PR は remote 専用 bookmark しか持たず、`gh pr merge` は hook でブロックされるため**ブロックされる経路と動かない経路しかない**。採用率測定で毎回踏む) | +| 398 | 🔧 Tier 2 | **post-merge-feedback の進行中ガードが完了済みの run を進行中と誤判定する** | todo21.md | S-M | なし (2026-08-10 実測。ガードは context.json の mtime 1500s だけを見て完了を見ない。連続マージで確実に踏む。順位 399/400 と同一機構) | +| 399 | 🔧 Tier 2 | **`--feedback-only ` が同じガードに阻まれ復旧手段として機能しない** | todo21.md | S | なし (2026-08-10 実測。PR 番号を引数で受け context 非依存の設計なのにガードだけ context 鮮度を見る。復旧専用コマンドが復旧に使えない) | +| 400 | 🔧 Tier 2 | **post-merge-feedback の marker が危険な復旧手順を案内し安全な手順に触れていない** | todo21.md | XS-S | なし (2026-08-10 実測。takt 直接起動は stale context を読み誤った PR のレポートを生成しうる。実際に #382 の marker 時点で context は #383 を指していた。安全な --feedback-only に言及が無い) | +| 401 | 💎 Tier 3 | **CodeRabbit 無料枠の窓は固定時刻でなく直近の消費に追随する** | todo21.md | XS | なし (2026-08-10 実測。56 分告知どおりに投げても通らず、間に成功した別レビューで窓がずれた。拒否応答には解除時間が無い。決定 16 で自律 PR が毎晩 1 本消費するため人間作業と競合) | +| 402 | 🚀 Tier 1 | **「対処後は効果を観測するまで完了と見なさない」を明文化 (系統 A-1)** | todo21.md | S | なし (2026-08-10 採用。決定 11 は投稿の成否だけ見て 10 時間気づけず、決定 15 は同じ症状が続くか確かめる前に解決済みと記録した。fail-open は効果の観測を別に用意して初めて成立する) | +| 403 | 🚀 Tier 1 | **AI レビューの数値・外部仕様の主張は仮説として扱い実測で二重検証 (系統 A-2)** | todo21.md | S | なし (2026-08-10 採用。組合せ数の指摘は観察は正しいが提示値も誤り (実測 384)、gh のオプション併用提案は実行時エラー、jq 正規表現案はパースエラー。観察と修正手段の確信度は別) | +| 404 | 🔧 Tier 2 | **外部依存の非同期応答待ちに timeout / retry を明記する convention (系統 A-3)** | todo21.md | S | なし (2026-08-10 採用。cli-stale-branch-scan の初版が timeout 無しで、同期実行経路の無診断ハング要因だった) | +| 405 | 🔧 Tier 2 | **新規 crate 実装時に既存同種コンポーネントとの重複を確認 (系統 B-1)** | todo21.md | S | なし (2026-08-10 採用。default_branch 解決ロジックの複製が CodeRabbit 指摘後に同一 PR 内で再発。34 crate 規模では記憶に頼れない) | +| 406 | 🔧 Tier 2 | **旧 API 廃止時に enum / config key / CLI flag の 3 形態すべての reject をテスト固定 (系統 B-2)** | todo21.md | S | なし (2026-08-10 採用。改名時に CLI フラグだけ test suite から漏れ、別名として通れば fail-open になる) | +| 407 | 🔧 Tier 2 | **旧語彙が live code に出現したら reject するカスタムリントルール (系統 B-3)** | todo21.md | S | なし (2026-08-10 採用。132 箇所の改名で CodeRabbit が同一 PR 内だけで 4 箇所の取りこぼしを指摘。ADR-007 の既存 regex 基盤で足り docs は extensions で自然に除外) | +| 408 | 🚀 Tier 1 | **safety-critical な config 比較に shell glob を禁止し exact-match を必須化 (系統 C-1)** | todo21.md | S | なし (2026-08-10 採用。kill-switch 判定の部分一致で fail-closed を謳う step 自身が fail-open だった。該当コメントが無かったため症状が出ず潜伏していた) | +| 409 | 🔧 Tier 2 | **shell の部分一致比較を検出するカスタムリントルール (系統 C-2)** | todo21.md | S-M | 順位 408 (規約側)。検出対象を安全装置の判定に絞れるかが採否の分かれ目。絞れなければ却下も正規の出口 (ADR-042 の mechanizable 判定) | +| 410 | 🔧 Tier 2 | **`autonomy-config.toml` の boolean パース edge case をテスト固定 (系統 C-3)** | todo21.md | S | なし (2026-08-10 採用。順位 408 の修正時に手で確認した edge case がテストとして残っていない。workflow の awk と Rust 側の解釈一致も確認できると望ましい) | +| 411 | 🚀 Tier 1 | **`cargo fmt` を PreToolUse でブロックし正しい対処を提示 (系統 F、規約ではなく機構)** | todo21.md | S | なし (2026-08-10 ユーザー判断で提案の形を変更。規約は毎セッション読まれコンテキストを圧迫するが hook は発火時のみコストが出る。ADR-042 へこの非対称を追記するのも本エントリの範囲。**反射的に実行されやすく無関係な差分を生む**ため WP-18 とは独立に早期着手する = 2026-08-10 ユーザー判断) | **戦略**: Tier 1 を 2〜3 セッションで片付け → Tier 2 で ADR-032 の前提 + rate-limit + convergence cost 削減を進める → Tier 3 で ADR-032 を land + ドキュメント整備。Tier 4-5 は cleanup / 外部展開で daily efficiency への直接効果は小さい。 diff --git a/docs/todo21.md b/docs/todo21.md index 16d402c9..66593f3b 100644 --- a/docs/todo21.md +++ b/docs/todo21.md @@ -244,7 +244,9 @@ > > **参照**: [ADR-065](adr/adr-065-ci-matrix-cross-os-regression.md) (両 OS matrix の意義)、PR [#376](https://github.com/aloekun/claude-code-hook-test/pull/376)。 > -> **実行優先度**: 🔧 Tier 2 — Severity Medium (実バグではないが CI の信号品質を下げる) / Frequency Low (初観測) / Effort S / Adoption Risk Low (テスト実行方法の変更のみ)。 +> **実行優先度**: 🚀 Tier 1 — Severity Medium (実バグではないが CI の信号品質を下げる) / Frequency Low (初観測) / Effort S / Adoption Risk Low (テスト実行方法の変更のみ)。 +> +> **2026-08-10 に Tier 1 へ格上げ (ユーザー判断)**。単発の Severity では Tier 2 相当だが、**flaky テストは「また flake だろう」という読み替えを生み、実バグの見落とし経路になる**。両 OS matrix ([ADR-065](adr/adr-065-ci-matrix-cross-os-regression.md)) の信号品質そのものを守る意味で早期に潰す。**WP-18 の完了条件には含めない** (別クレートの既存競合で WP-18 の経路と無関係、計画書 § WP-18 残作業 (3) 参照) が、着手は WP-18 と独立に早める。 #### 作業計画 @@ -256,3 +258,379 @@ - hooks smoke suite が Linux で `ETXTBSY` を起こさないこと (再現手順付きで確認)。 - 同型パターンが他 suite に無いこと、またはあれば同じ対処が入っていること。 + +--- + +## WP-18 の運用経路で実測した問題 (2026-08-10 登録) + +> **由来**: 2026-08-10 のセッションで、夜間ループの PR を**人間がマージする経路**を初めて通した際に実測した 5 件。いずれも「自動化が別の自動化の前提を崩す」型で、[ADR-022](adr/adr-022-automation-responsibility-separation.md) の責務分離が扱う領域にある。 +> +> **順位 398-400 は同じ機構 (post-merge-feedback の進行中ガード) の 3 つの面**で、対処は別々だが同一 PR で扱える。 + +### `pnpm merge-pr` が夜間 PR (remote 専用ブックマーク) を検出できない + +> **動機**: PR [#381](https://github.com/aloekun/claude-code-hook-test/pull/381) (`claude/nightly-163`) をマージしようとして `エラー: 現在のブックマークに紐づく PR が見つかりません` で exit 1 した。原因は **夜間ループの PR が remote 専用ブックマークしか持たない**こと。`claude/nightly-163@origin` は remote 側にのみ存在し、[`get_jj_bookmarks`](../src/lib-jj-helpers/src/lib.rs) が返すのは**ローカル**ブックマークなので検出できない。 +> +> **これまで顕在化しなかった理由**: 従来マージしてきた PR はすべて自分で `jj bookmark create` したローカルブックマークを持っていた。**bot が remote に作った PR を人間がマージする**経路は WP-18 で初めて通った。 +> +> **回避策 (実施済み)**: `jj bookmark track claude/nightly-163 --remote=origin` でローカル化してから `pnpm merge-pr`。ただし**非自明で、採用率測定では毎回踏む**。 +> +> **なぜ `gh pr merge` で代替できないか**: `hooks-pre-tool-validate` が `gh pr merge` をブロックし `pnpm merge-pr` へ誘導する ([ADR-013](adr/adr-013-merge-pipeline.md))。**ブロックされる経路と、動かない経路しかない**状態になっている。 +> +> **対処案**: (a) PR 検出時に remote 専用ブックマークも探索対象へ含める、(b) 見つからない場合に track を促す loud なヒントを出す、(c) `--pr <番号>` オプションを足して bookmark 非依存で指定できるようにする。(c) は順位 399 の `--feedback-only` と設計が揃う。 +> +> **参照**: [ADR-013](adr/adr-013-merge-pipeline.md)、[ADR-072](adr/adr-072-nightly-todo-loop.md) (夜間 PR の生成元)、[lib-jj-helpers](../src/lib-jj-helpers/src/lib.rs)、順位 386 (同じ bookmark 探索の別問題)。 +> +> **実行優先度**: 🚀 Tier 1 — Severity Medium (マージ経路が止まる。loud failure だが回避策が非自明) / Frequency High (**夜間 PR をマージするたび**。採用率測定の運用に直結) / Effort S-M / Adoption Risk Low。 + +#### 作業計画 + +- [ ] 対処案 (a)(b)(c) から選ぶ。順位 386 の bookmark 探索見直しと同一 PR で扱えるか検討する +- [ ] remote 専用ブックマークのみが存在する状態を再現する回帰テストを追加する +- [ ] `pnpm merge-pr` の失敗メッセージに track 手順を含める (どの案を採っても有用) + +#### 完了基準 + +- 夜間ループの PR を `jj bookmark track` 無しで `pnpm merge-pr` できること、または失敗時に**実行可能な回避手順**が出力されること。 + +### post-merge-feedback の進行中ガードが、完了済みの run を進行中と誤判定する + +> **動機**: #383 を 11:13Z にマージし、その約 4 分後に #382 をマージしたところ、#382 の feedback が `前回の post-merge-feedback workflow がまだ進行中の可能性 (context.json が 223s 前に書かれた)` で失敗した。**実際には #383 の run は完了していた** (`383.md` 生成済み、takt プロセスも不在)。 +> +> **問題の型**: ガードは `context.json` の **mtime が 1500 秒以内か**だけを見ており、**run が完了したかを見ていない**。したがって完了済みでも 25 分間は次の feedback が起動できない。マージを連続実行する運用 (本セッションでは 4 本連続) では確実に踏む。 +> +> **対処案**: (a) run の完了を示すマーカーを見て、完了していれば通す、(b) context.json に `status` を持たせて mtime 判定をやめる、(c) 進行中判定を takt プロセスの実在確認に変える。**「進行中を防ぐ」目的自体は正しい** — context.json が上書きされると誤った PR の transcript が使われるため (順位 400 参照)。 +> +> **完了判定は対象 PR に束縛すること (レビュー指摘、2026-08-10)**。単純に「report が生成されているか」を見るのでは不十分である。`copy_feedback_report` ([takt.rs](../src/cli-merge-pipeline/src/feedback/takt.rs)) は `find_latest_run_dir` で**最新 run を選ぶだけで `pr_number` と照合していない**ため、別 PR の report を現在の PR の `{pr_number}.md` へコピーし得る。また takt の終了は timeout や失敗でも起こるので、**終了した事実は report 完成を証明しない**。完了判定には少なくとも「run が成功終了した」ことと「その run が対象 PR のものである」ことの両方が要る (task label / context.json / report / reconciliation の PR 番号が一致すること)。 +> +> **参照**: [ADR-030](adr/adr-030-deterministic-post-merge-feedback.md) (marker + recovery 設計)、[cli-merge-pipeline](../src/cli-merge-pipeline/src/feedback/)、順位 388 (同じ feedback 経路の別 race)。 +> +> **実行優先度**: 🔧 Tier 2 — Severity Medium (feedback が欠落する。手動復旧は可能) / Frequency Medium (連続マージ時) / Effort S-M / Adoption Risk Low。 + +#### 作業計画 + +- [ ] 完了判定の根拠 (report 生成 / takt exit / プロセス実在) を 1 つ選び、mtime 判定と置き換えるか併用するか決める +- [ ] 完了済み context が残っている状態で次の feedback が通ることをテストで固定する +- [ ] 順位 388 (成功したのに failed marker) と同一 PR で扱えるか検討する + +#### 完了基準 + +- 直前の feedback が**完了していれば**、25 分待たずに次の feedback が起動できること。 + +### `--feedback-only ` が同じガードに阻まれ、復旧手段として機能しない + +> **動機**: 順位 398 の失敗後、marker の復旧手順に従って `pnpm merge-pr --feedback-only 382` を実行したが、**同じガードで失敗した** (`context.json が 314s 前に書かれた`)。 +> +> **問題の型**: `--feedback-only` は **PR 番号を引数で受け取り context.json に依存しない**設計 ([`run_ai_step_for`](../src/cli-merge-pipeline/src/pipeline.rs) が `pr_number` を直接受ける) なのに、**進行中ガードだけは context.json の鮮度を見る**。結果、**復旧専用のコマンドが復旧に使えない**。 +> +> 実際の復旧は「進行中の takt が無いことを確認 → `context.json` を手動削除 → 再実行」でしか通らなかった。これはツールのエラーメッセージが案内する手順ではあるが、**専用コマンドがあるのに手動介入が要る**のは設計の齟齬である。 +> +> **対処案**: `--feedback-only` は引数の PR 番号を正とし、進行中ガードを **skip する**か、**別の完了判定 (順位 398)** に置き換える。前者を採る場合、context.json を必ず上書きしてから走らせて stale 読みを防ぐこと。 +> +> **参照**: [pipeline.rs](../src/cli-merge-pipeline/src/pipeline.rs) (`run_feedback_only` / `run_ai_step_for`)、[ADR-030](adr/adr-030-deterministic-post-merge-feedback.md)、順位 398 / 400。 +> +> **実行優先度**: 🔧 Tier 2 — Severity Medium (復旧経路が塞がる) / Frequency Medium (順位 398 が起きるたび) / Effort S / Adoption Risk Low。 + +#### 作業計画 + +- [ ] `--feedback-only` がガードをどう扱うべきか決める (skip / 完了判定へ置換) +- [ ] 引数の PR 番号で context を再構築してから走ることをテストで固定する +- [ ] 順位 398 と同一 PR で扱う + +#### 完了基準 + +- 失敗した feedback を `--feedback-only ` だけで復旧できること (手動のファイル削除を要しない)。 + +### post-merge-feedback の marker が、危険な復旧手順を案内し安全な手順に触れていない + +> **動機**: `.failed` marker の「復旧手順」は `pnpm exec takt -w post-merge-feedback -t "..."` の直接起動を案内している。しかし**これは `context.json` を読み直すだけ**で、context が別 PR を指していると**誤った PR の transcript でレポートを生成する**。 +> +> marker 自身が「再実行前に `pr_number` が一致することを必ず確認してください」と警告してはいるが、**実際に #382 の marker が出た時点で context は #383 を指していた** (2026-08-10 実測)。警告を読み飛ばせば誤ったレポートが生成される。 +> +> **一方、安全な `--feedback-only ` には触れていない**。これは PR 番号を引数で受けるため stale context の影響を受けない (ただし順位 399 のガード問題は別途ある)。 +> +> **問題の型**: **危険な手順が案内され、安全な手順が案内されていない**。ADR-030 の recovery 設計が `--feedback-only` の追加より前に書かれたまま更新されていない可能性が高い。 +> +> **対処案**: marker のテンプレートを更新し、(1) `--feedback-only ` を第一手段として案内、(2) takt 直接起動は「context の pr_number を確認したうえでの最終手段」に降格、(3) 順位 399 が解決したら手動削除の案内も不要になる。 +> +> **参照**: [ADR-030](adr/adr-030-deterministic-post-merge-feedback.md)、[cli-merge-pipeline の marker 生成箇所](../src/cli-merge-pipeline/src/feedback/)、順位 398 / 399。 +> +> **実行優先度**: 🔧 Tier 2 — Severity Medium (誤ったレポート生成 = 誤情報の永続化) / Frequency Low (marker が出たときだけ) / Effort XS-S / Adoption Risk None。 + +#### 作業計画 + +- [ ] marker テンプレートの復旧手順を書き換える (`--feedback-only` を第一手段に) +- [ ] takt 直接起動を残す場合は `pr_number` 確認を手順の前段に置く +- [ ] 順位 399 の解決後に手順を再度見直す + +#### 完了基準 + +- marker の復旧手順に従うだけで、**誤った PR のレポートが生成され得ない**こと。 + +### CodeRabbit 無料枠の窓は固定時刻ではなく直近の消費に追随する + +> **動機**: 2026-08-10 に PR #382 のレビューがレート制限で 3 回失敗した。1 回目の通知は `Next review available in: 56 minutes` と告知していたが、**その 56 分後に投げても通らなかった**。間に #383 のレビューが成功しており、**その消費で窓が後ろへずれた**と解釈するのが観測に整合する。 +> +> **実測**: +> +> | 時刻 | 出来事 | +> |---|---| +> | 09:54:40Z | 自動レビュー試行 → `Review limit reached` / **56 分**告知 | +> | 10:26Z 台 | #383 へ手動トリガー → **成功** | +> | 10:28 / 10:30Z | #382 へ手動トリガー → `Review rate limited.` | +> | 10:51:54Z | **告知どおりの時刻**に投稿 → やはり `Review rate limited.` | +> +> **併せて判明した点**: 手動トリガーへの拒否応答 (`Review rate limited.`) には**解除までの時間が含まれない**。時間が書かれるのは自動レビュー試行時の `Review limit reached` だけ。 +> +> **運用上の含意**: [ADR-072](adr/adr-072-nightly-todo-loop.md) 決定 16 で**自律 PR が毎晩 1 本レビューを消費する**ようになった。[ADR-019](adr/adr-019-coderabbit-review-hybrid-policy.md) のクォータ設計は人間が開く PR を母数にしていたため、**人間の作業が集中する日に自律 PR のレビューと競合する**構造が新たに生まれている。 +> +> **対処案**: まず [ADR-019](adr/adr-019-coderabbit-review-hybrid-policy.md) へ観測事実として記録する (窓の挙動 / 拒否応答に時間が無い / 自律 PR との競合)。そのうえで、待機時刻の見積りは「**最後にレビューが成功した時刻**」を基準にする運用へ改める。機械化するかは頻度を見てから判断する。 +> +> **参照**: [ADR-019](adr/adr-019-coderabbit-review-hybrid-policy.md) § CodeRabbit は bot 作成 PR を自動レビューしない / § 再トリガー抑止ガード、[ADR-072](adr/adr-072-nightly-todo-loop.md) 決定 16。 +> +> **実行優先度**: 💎 Tier 3 — Severity Low (待ち時間の見積りが外れるだけ) / Frequency Medium (無料枠を使い切る日) / Effort XS (記録のみ) / Adoption Risk None。 + +#### 作業計画 + +- [ ] ADR-019 へ観測事実を追記する (窓の挙動 / 拒否応答の情報量 / 自律 PR との競合) +- [ ] 待機見積りの基準を「最後の成功時刻」へ改める旨を記載する + +#### 完了基準 + +- レート制限に当たったとき、次に投げるべき時刻の見積り方が ADR-019 から読み取れること。 + +--- + +## post-merge feedback 採用分 (#376/#377/#380/#381/#382、2026-08-10 採否確定) + +> **由来**: WP-18 の一連 PR の post-merge feedback で挙がった採用候補を、2026-08-10 に系統別へ分類してユーザーが採否を決定した。**系統 A (観測の完全性) / B (重複実装の予防) / C (shell・config パースの安全性) を採用**、系統 D (workflow セキュリティ標準化) / E (PAT 失効監視) は却下 (様子見)。 +> +> **系統 F は提案の形を変えて採用**した — 「規約を書く」ではなく「PreToolUse で弾く」(順位 411)。 +> +> **却下したもの (negative result として記録)**: +> +> - **系統 D (workflow セキュリティ標準化)** / **系統 E (PAT 失効監視)** — 様子見。有用だが緊急性が低い。 +> - **trunk 保護の drift 対処 2 件** (`cli-push-runner` と `cli-stale-branch-scan` の `effective_default_branch()` クロスクレート一致テスト / `lib-config` 抽出) — **却下 (2026-08-10 ユーザー判断)**。pre-push review と post-merge feedback の双方が独立に指摘した Severity High の項目だが、(1) 予防側は順位 405 (新規 crate 実装時の重複確認) で押さえた、(2) 共有 lib 化は `cli-stale-branch-scan` の意図的な network isolation 設計 ([ADR-031](adr/adr-031-weekly-review-pipeline.md)) と抵触しうる、の 2 点から見送る。**再採用条件: 同型の drift が今後も再発する場合**。 + +### 系統 A-1: 「対処後は効果を観測するまで完了と見なさない」を明文化する + +> **動機**: 本セッションで**同じ誤りを 2 回**踏んだ。[ADR-072](adr/adr-072-nightly-todo-loop.md) 決定 11 は `@coderabbitai review` の投稿が成功したことだけを見て、**相手が反応していない事実に 10 時間気づけなかった**。決定 15 は draft を廃止した後に**同じ症状が続くかを確かめる前**に「解決した」と記録し、原因が別 (author が bot) だと後から判明した。 +> +> **問題の型**: 「対処を実施した」と「対処が効いた」を同一視している。助言層を fail-open にすること自体は [ADR-043](adr/adr-043-security-gates-fail-closed.md) に沿って正しいが、**fail-open は効果の観測を別に用意して初めて成立する**。 +> +> **対処案**: `docs/dev-conventions.md` に「対処の完了条件は**対処後の観測**である」旨を追記する。最低限含める点: (1) 症状ベースの問題では対処後に同じ症状が消えたことを確認するまで完了としない、(2) 外部サービス依存の対処は「送った」ではなく「相手が反応した」を観測する、(3) 観測できない対処は完了扱いにせず未確定として記録する。 +> +> **既に機構化された部分**: `.github/workflows/review-request.yml` は投稿後に CodeRabbit の反応を待ち、無ければ red で落とす (決定 16)。本エントリはこれを**一般則として言語化**するもの。 +> +> **参照**: [ADR-072](adr/adr-072-nightly-todo-loop.md) 決定 11 (撤回) / 決定 15 (前提の訂正) / 決定 16、[ADR-043](adr/adr-043-security-gates-fail-closed.md)、[ADR-042](adr/adr-042-rule-vs-mechanism-boundary.md) (本件は判断を伴うため rule 側)。 +> +> **実行優先度**: 🚀 Tier 1 — Severity High (誤った「解決済み」記録が次の判断を汚染する) / Frequency Medium (本セッションだけで 2 回) / Effort S / Adoption Risk None。 + +#### 作業計画 + +- [ ] `docs/dev-conventions.md` に節を追加し、上記 3 点を具体例 (決定 11 / 決定 15) 付きで書く +- [ ] 既存の「LLM を含む自動化経路は実走でしか検証できない」節との重複を整理する + +#### 完了基準 + +- 「対処したが効果を確認していない」状態を完了と記録してよいか、convention から判断できること。 + +### 系統 A-2: AI レビューが出す数値・仕様の主張は仮説として扱い実測で二重検証する + +> **動機**: 2026-08-10 に CodeRabbit が「`3 × 15 × 4 × 2` は 360 なので 216 は誤り」と指摘した。**観察は正しかったが提示された数値も誤り**で、実際に列挙を数えると **384** だった (外部フラグは `None` + 7 + 8 = 16 通りで、因数の 15 も誤っていた)。指摘をそのまま採用していれば誤った数値を land させていた。 +> +> 同種の例が同セッションで複数出ている: `--paginate --slurp` と `--jq` の併用提案は **gh 2.95.0 で実行時エラー**になり、jq の `test()` を使う正規表現案は**パースエラー**だった。いずれも**観察 (問題の指摘) は正しく、修正手段が誤っていた**。 +> +> **問題の型**: AI レビューの finding は「問題の指摘」と「修正案」が同じ確信度で提示されるが、**後者の正しさは前者を保証しない**。特に数値・外部ツールの仕様・API の挙動は、レビュアーが実行環境を持たないまま推論している。 +> +> **対処案**: [ADR-050](adr/adr-050-iteration-aware-decision-criteria.md) (multi-iteration workflow の decision criteria) に「AI レビューが提示する数値・外部ツール仕様・API 挙動の主張は仮説として扱い、採用前に実測する」旨を統合する。既存の finding 判定基準の一部として書くのが自然。 +> +> **参照**: [ADR-050](adr/adr-050-iteration-aware-decision-criteria.md)、[ADR-047](adr/adr-047-prepush-refute-facet.md) (反証機構の射程)、PR [#377](https://github.com/aloekun/claude-code-hook-test/pull/377) / [#380](https://github.com/aloekun/claude-code-hook-test/pull/380) (実例)。 +> +> **実行優先度**: 🚀 Tier 1 — Severity Medium (誤った修正を land させる) / Frequency Medium (本セッションで 3 回) / Effort S / Adoption Risk None。 + +#### 作業計画 + +- [ ] ADR-050 へ追記し、実例 (組合せ数の誤り、gh のオプション非互換、jq のパースエラー) を根拠として残す +- [ ] 「観察は採るが修正手段は実測する」という分離を明示する + +#### 完了基準 + +- AI レビューの finding を採用する際、どこまでを信頼しどこから実測するかが ADR-050 から読み取れること。 + +### 系統 A-3: 外部依存の非同期応答待ちに timeout / retry を明記する convention + +> **動機**: `src/cli-stale-branch-scan/src/collect.rs` の初版は `git ls-remote` / `gh pr list` に timeout を持たず、pre-push review が「weekly-review skill 内で同期実行されるため、ここが止まるとパイプライン全体が無診断でハングする」と指摘した。同 PR で `lib_subprocess::wait_with_timeout_basic` による 60 秒 timeout を入れた。 +> +> **問題の型**: 外部サービスへの待ちは DNS/TCP hang・一時障害・認証プロンプト待ちで無期限に止まりうる。**同期実行される経路では、1 箇所の hang がパイプライン全体を無診断で止める**。 +> +> **対処案**: `docs/dev-conventions.md` に「外部サービスへの待ちには必ず timeout を置き、超過は loud に失敗させる」旨を追記する。あわせて既存の外部待ち step を棚卸しし、timeout の無い箇所を洗い出す (`cli-pr-monitor` の poll、workflow の待機 step 等)。 +> +> **参照**: [ADR-016](adr/adr-016-long-running-command-strategy.md) (長時間コマンド実行戦略)、`src/lib-subprocess/`、PR [#377](https://github.com/aloekun/claude-code-hook-test/pull/377)。 +> +> **実行優先度**: 🔧 Tier 2 — Severity Medium (無診断ハング) / Frequency Low / Effort S / Adoption Risk None。 + +#### 作業計画 + +- [ ] `docs/dev-conventions.md` に timeout 必須の旨を追記する +- [ ] 既存の外部待ち経路を棚卸しし、timeout 不在の箇所を列挙する (対処は別エントリでもよい) + +#### 完了基準 + +- 新規に外部サービスを待つコードを書くとき、timeout の要否と失敗時の扱いが convention から決まること。 + +### 系統 B-1: 新規 crate / exe 実装時に既存同種コンポーネントとの重複を確認する + +> **動機**: `cli-stale-branch-scan` が `push-runner-config.toml` の `default_branch` 解決ロジックを `cli-push-runner` から手で複製した。**CodeRabbit 指摘で 1 度直した後、同一 PR 内で再び同型の漏れ**が出ている (top-level のみ読む → section override も読む)。既存実装の存在を先に確認していれば避けられた。 +> +> **問題の型**: 新規 crate を作る際、既存 crate に同じ問題を解いたコードがあるかを確認する手順が無い。34 crate 規模では**記憶に頼れない**。 +> +> **対処案**: `CLAUDE.md` の開発 convention に、新規 crate / exe 実装時のチェックリストとして (a) 既存同種コンポーネントとの機能重複を確認する、(b) 共用化しない判断をした場合は**ミラー元と理由をコード doc に明記**する、を追加する。(b) は PR #377 で実践済み (`TrunkConfig::effective_default_branch` の doc に `cli-push-runner` のミラーである旨を記載)。 +> +> **参照**: [ADR-044](adr/adr-044-subprocess-utility-extraction-boundary.md) (共通化と分離の線引き)、[ADR-051](adr/adr-051-cross-system-config-coupling.md)、PR [#377](https://github.com/aloekun/claude-code-hook-test/pull/377)。 +> +> **実行優先度**: 🔧 Tier 2 — Severity Medium (silent drift の温床) / Frequency Medium (2 回再発) / Effort S / Adoption Risk None。 + +#### 作業計画 + +- [ ] `CLAUDE.md` の開発 convention へチェックリストを追加する +- [ ] ADR-044 の抽出境界判断と矛盾しないことを確認する (「必ず共通化せよ」ではない) + +#### 完了基準 + +- 新規 crate を作るとき、既存重複の確認とミラー宣言が手順として踏まれること。 + +### 系統 B-2: 旧 API 廃止時に enum / config key / CLI flag の 3 形態すべての reject をテストで固定する + +> **動機**: `draft-pr` から `autonomous-pr` への改名で、`Operation::parse` と config キーの旧名 reject は unit test で固定したが、**CLI フラグの旧名だけが exe drill 確認どまり**で test suite に入っていなかった。CodeRabbit 指摘で追加した (PR [#376](https://github.com/aloekun/claude-code-hook-test/pull/376))。 +> +> **問題の型**: 1 つの概念が **enum variant / config key / CLI flag** の 3 形態で表に出る設計では、改名時に**どれか 1 つが漏れる**。漏れた形態が「別名として通る」と fail-open になる。 +> +> **対処案**: `docs/dev-conventions.md` に「旧 API 廃止時は 3 形態すべての reject をテストで固定する」チェックリストを追加する。本リポジトリでは rename が頻出 (ADR 一覧に rename 系決定が多数)。 +> +> **参照**: [ADR-071](adr/adr-071-draft-pr-backpressure.md) の unit test 節 (3 形態の reject を固定した実例)、PR [#376](https://github.com/aloekun/claude-code-hook-test/pull/376)。 +> +> **実行優先度**: 🔧 Tier 2 — Severity Medium (改名漏れが fail-open になる) / Frequency Medium / Effort S / Adoption Risk None。 + +#### 作業計画 + +- [ ] `docs/dev-conventions.md` へチェックリストを追加する +- [ ] 順位 407 (旧語彙 lint) と役割分担を明確にする (lint = live code の検出、本エントリ = reject のテスト固定) + +#### 完了基準 + +- 改名 PR で 3 形態の reject テストが揃っていることをレビューで確認できること。 + +### 系統 B-3: 旧語彙が live code に出現したら reject するカスタムリントルール + +> **動機**: `draft-pr` から `autonomous-pr` への改名 (12 ファイル 132 箇所) で、**CodeRabbit が同一 PR 内だけで 4 箇所の取りこぼしを段階的に指摘**した。10 ファイル以上に跨る rename は本リポジトリで反復的に発生する。 +> +> **対処案**: [ADR-007](adr/adr-007-custom-linter-layer-boundary.md) の regex 層 (`.claude/custom-lint-rules.toml`) に、旧語彙が **live code (`rs` / `toml` / `yml` / `yaml`)** に出現したら reject するルールを追加する。**docs は extensions フィルタで自然に除外**される (歴史記録として旧名を残すため)。 +> +> **既存基盤で足りる**: post-merge feedback は当初「新規 stop hook とバッチ検証テスト」で Effort M と見積もったが、ADR-007 の確立済み regex 基盤がそのまま使え、extensions を絞れば docs 除外も自動で効くため Effort S に下がる。real-time 検知 (hook) がバッチテストの目的を包含する。 +> +> **運用上の注意**: 改名ごとにルールを足す形になるため、**寿命のあるルール**として扱う (改名が浸透したら削除)。[ADR-039](adr/adr-039-experimental-feature-standard-pattern.md) の bounded lifetime と同じ発想。 +> +> **参照**: [ADR-007](adr/adr-007-custom-linter-layer-boundary.md)、PR [#376](https://github.com/aloekun/claude-code-hook-test/pull/376)。 +> +> **実行優先度**: 🔧 Tier 2 — Severity Medium / Frequency Medium (rename のたび) / Effort S / Adoption Risk None。 + +#### 作業計画 + +- [ ] `.claude/custom-lint-rules.toml` に旧語彙 reject ルールを追加 (extensions は `rs` / `toml` / `yml` / `yaml`。**拡張子は文字列一致** (`eq_ignore_ascii_case`) なので `yml` と `yaml` は別物で、本リポジトリは `.github/workflows/*.yml` と `.coderabbit.yaml` の両方を持つ) +- [ ] `rule_test_coverage_check` / `incident_eval.rs` の既存 3 つの test 群を満たす fixture を用意する +- [ ] ルールの寿命 (いつ削除するか) をコメントに明記する + +#### 完了基準 + +- 旧語彙を live code に書くと hook が reject し、docs では reject されないこと。 + +### 系統 C-1: safety-critical な config 比較に shell glob を禁止し exact-match を必須化する + +> **動機**: `.github/workflows/review-request.yml` の kill-switch 判定が glob による**部分一致**だった。この形では `enabled = false` の行に `true` を含むコメントが付くだけで判定が反転し、**fail-closed を謳う step 自身が fail-open** する。pre-push review が検出し、コメントと空白を除いた値の厳密一致へ修正した (PR [#380](https://github.com/aloekun/claude-code-hook-test/pull/380))。 +> +> **問題の型**: shell の glob は「含む」であって「等しい」ではない。**安全装置の判定にこれを使うと、無関係な文字列が混じるだけで反転する**。しかも今日の config には該当コメントが無かったため**症状が出ずに潜伏**していた。 +> +> **対処案**: [ADR-043](adr/adr-043-security-gates-fail-closed.md) に「安全装置の config 比較に glob / 部分一致を使わない。コメントと空白を除いた値の厳密一致を用いる」旨を追記する。あわせて `docs/dev-conventions.md` に「構造化 config (TOML/JSON/YAML) を shell や awk で自前パースする場合の注意」を書く。 +> +> **参照**: [ADR-043](adr/adr-043-security-gates-fail-closed.md)、[ADR-066](adr/adr-066-autonomy-global-kill-switch.md) (kill-switch の 2 面契約)、PR [#380](https://github.com/aloekun/claude-code-hook-test/pull/380)。 +> +> **実行優先度**: 🚀 Tier 1 — Severity High (安全装置の silent fail-open) / Frequency Low / Effort S / Adoption Risk None。 + +#### 作業計画 + +- [ ] ADR-043 へ追記する (実例と、なぜ症状が出ずに潜伏するかを含める) +- [ ] `docs/dev-conventions.md` へ shell や awk での config パース時の注意を書く + +#### 完了基準 + +- 安全装置の判定を shell で書くとき、比較方法の選択が ADR-043 から決まること。 + +### 系統 C-2: shell の部分一致比較を検出するカスタムリントルール + +> **動機**: 順位 408 と同じ実例。**規約だけでは同じ形を再び書く**ため、決定論層で検出する。 +> +> **対処案**: [ADR-007](adr/adr-007-custom-linter-layer-boundary.md) の regex 層に、shell や workflow 内で glob による値比較をしている箇所を検出するルールを追加する。**boolean や enum らしき値 (`true` / `false` / `enabled` 等) を含む場合に限定**して false positive を抑える。 +> +> **判断が要る点**: 部分一致が正当な用途 (文字列検索) もあるため、**検出対象を安全装置の判定に絞れるか**が採否の分かれ目。絞れないなら順位 408 の規約のみで運用し、本エントリは却下してよい ([ADR-042](adr/adr-042-rule-vs-mechanism-boundary.md) の mechanizable 判定)。 +> +> **参照**: [ADR-007](adr/adr-007-custom-linter-layer-boundary.md)、[ADR-042](adr/adr-042-rule-vs-mechanism-boundary.md)、順位 408。 +> +> **実行優先度**: 🔧 Tier 2 — Severity Medium / Frequency Low / Effort S-M / Adoption Risk Medium (false positive)。 + +#### 作業計画 + +- [ ] 検出対象を安全装置の判定に絞れるか調べる (絞れなければ却下し理由を記録する) +- [ ] 絞れる場合、`.claude/custom-lint-rules.toml` へ追加し fixture を用意する + +#### 完了基準 + +- 採用・不採用のいずれかが根拠つきで記録され、採用時は false positive が実運用で問題にならないこと。 + +### 系統 C-3: `autonomy-config.toml` の boolean パース edge case をテストで固定する + +> **動機**: 順位 408 の修正時に、値に trailing comment が付く場合・空値・引用符付き・複数セクション境界・不正な TOML といった edge case を手で確認した。**この確認はテストとして残っていない**。 +> +> **対処案**: `cli-autonomy-gate` 側に、config の boolean 解釈が上記 edge case で期待どおり fail-closed に倒れることを固定するテストを追加する。**workflow 側の awk 実装と Rust 側の解釈が一致すること**も併せて確認できると望ましい (どちらも同じ config を読むため)。 +> +> **参照**: [ADR-066](adr/adr-066-autonomy-global-kill-switch.md)、[ADR-041](adr/adr-041-test-isolation-patterns.md)、PR [#380](https://github.com/aloekun/claude-code-hook-test/pull/380)。 +> +> **実行優先度**: 🔧 Tier 2 — Severity Medium / Frequency Low / Effort S / Adoption Risk None。 + +#### 作業計画 + +- [ ] boolean パースの edge case テストを追加する (trailing comment / 空値 / 引用符 / セクション境界 / 不正 TOML) +- [ ] workflow の awk 実装と Rust 側の解釈が一致することを fixture で確認できるか検討する + +#### 完了基準 + +- config の boolean 解釈が edge case で fail-closed に倒れることがテストで固定されていること。 + +### 系統 F (形を変えて採用): `cargo fmt` を PreToolUse でブロックし正しいコマンドを提示する + +> **動機**: 本リポジトリは **rustfmt を意図的に適用しない** (引数ペアを 1 行に保つ独自整形)。2026-08-10 に `cargo fmt` を誤実行し、無関係な 3 ファイルに整形差分が入って巻き戻しに工数を要した。 +> +> **なぜ規約ではなく機構か (2026-08-10 ユーザー判断)**: 規約は **`CLAUDE.md` に書いた時点で毎セッション読まれ、コンテキストを圧迫する**。一方 PreToolUse hook は**発火するまでコストがゼロ**で、しかも**ブロックと同時に正しいコマンドをフィードバック**できるため、読み手 (Claude Code) は規約を覚えていなくても正しい経路へ到達する。これは [ADR-042](adr/adr-042-rule-vs-mechanism-boundary.md) の「mechanizable なら仕組み化する」判断そのものであり、`cargo fmt` の検出は**コマンド文字列の一致で足りる**ため mechanizable 判定を満たす。 +> +> **この観点は ADR-042 に無い**: 現行 ADR-042 の判断基準は「機械判定できるか」「投資対効果」が中心で、**「規約はコンテキストを消費し続けるが hook は発火時のみ」という非対称**が明示されていない。ルール追加を検討するたびに効く一般則なので、本エントリで併せて追記する。 +> +> **対処案**: `src/hooks-pre-tool-validate/src/presets/basic.rs` の既存パターン (`rm -rf` / `cd /d` / `git` シェルラッパー) に倣い、`cargo fmt` をブロックするルールを追加する。メッセージには (1) 本リポジトリが rustfmt 非適用であること、(2) 整形が必要なら手で最小限に行うこと、(3) 例外的に実行したい場合の判断材料、を含める。**代替コマンドは存在しない** (手で直すのが正) ため、提示するのは「正しいコマンド」ではなく**正しい対処**である。 +> +> **参照**: [ADR-042](adr/adr-042-rule-vs-mechanism-boundary.md)、[ADR-001](adr/adr-001-hooks-implementation-language.md)、`src/hooks-pre-tool-validate/src/presets/basic.rs`、PR [#376](https://github.com/aloekun/claude-code-hook-test/pull/376) (誤実行の実例)。 +> +> **実行優先度**: 🚀 Tier 1 — Severity Medium (無関係な差分混入。レビュー負荷と巻き戻し工数) / Frequency Medium (fmt は反射的に実行されやすい) / Effort S / Adoption Risk Low (例外実行の手段を残すこと)。 +> +> **早期着手する (2026-08-10 ユーザー判断)**。`cargo fmt` は**反射的に実行されやすい**ため、規約が無い状態が続くほど誤実行の機会が増える。**WP-18 の完了条件には含めない** (対象は開発環境全般で WP-18 の機構と無関係、計画書 § WP-18 残作業 (3) 参照) が、着手は WP-18 と独立に早める。 + +#### 作業計画 + +- [ ] **検出対象の範囲を先に決める**。`cargo fmt` の完全一致だけでは `cargo fmt --all` / `cargo +stable fmt` / `rustup run stable cargo fmt` / `cargo-fmt` が素通りする。全形態を弾くなら正規化してから判定し、fixture も全形態を網羅する。完全一致に留めるならその範囲を完了基準へ明記する (レビュー指摘、2026-08-10) +- [ ] `presets/basic.rs` に `cargo fmt` ブロックを追加する (既存 3 パターンの構造を踏襲) +- [ ] ブロックメッセージに「なぜ非適用か」と代替手順を含める +- [ ] `rule_test_coverage_check` / `incident_eval.rs` の fixture を追加する +- [ ] ADR-042 へ「規約は常時コンテキストを消費し、hook は発火時のみ」という非対称を追記する + +#### 完了基準 + +- **決めた検出範囲**において `cargo fmt` が PreToolUse でブロックされ、メッセージだけで正しい対処に到達できること。範囲を完全一致に限定した場合は、素通りする形態 (`--all` 付き / toolchain 指定 / `cargo-fmt`) を完了基準に明記すること。 +- ADR-042 に規約と機構のコスト非対称が記録されていること。