diff --git a/.github/workflows/nightly-todo.yml b/.github/workflows/nightly-todo.yml index 9e68e84b..d6a28e06 100644 --- a/.github/workflows/nightly-todo.yml +++ b/.github/workflows/nightly-todo.yml @@ -48,6 +48,10 @@ jobs: # kill-switch の第 1 層 (workflow 式)。Actions variable は admin のみ書き込み可で、 # 'true' ちょうどの完全一致でなければ job ごと起動しない。exe 側 (第 2 層) は # 1|true|yes|on を受理するため受理値が異なる点に注意 (ADR-066)。 + # + # **2026-08-08 に実測済み**: `'false'` と未設定の 2 状態で dispatch し、いずれも + # job が skip されブランチ・draft PR・App token のどれも作られないことを確認した + # (ADR-072 § 実走スモーク)。現在値と登録先は同 ADR § 外部設定の実体 を見ること。 if: vars.AUTONOMY_ENABLED == 'true' runs-on: ubuntu-latest # Implement (--max-turns 60、他 job の実績比で最大 40 分規模) + @@ -384,6 +388,12 @@ jobs: # App の権限は Contents / Pull requests の write のみで **Workflows は付けていない**。 # .github/workflows/** を含む push は権限側でも通らず、Guard step の禁止リストと # 二重になる。 + # + # **外部設定の実体は ADR-072 § 外部設定の実体 に記録してある** (ADR-051 決定 1 の + # 相互参照)。App 名 / インストール範囲 / 付与権限の実際 / vars と secrets の登録先 / + # 各値が欠けたときの倒れ方 / 鍵ローテーション時の再構築手順はそちらを見ること。 + # この 2 行 (`vars.NIGHTLY_APP_ID` / `secrets.NIGHTLY_APP_PRIVATE_KEY`) を変えるときは + # 同 ADR の表も同一 PR で更新する (ADR-051 決定 3)。 # 本 step にも continue-on-error を付けない (決定 10)。App installation token の # 発行は GitHub への外部 API 呼び出しであり、失敗は gh/network の障害 (secret の # 誤設定・GitHub 側の障害等) であって設計された結末ではない。green + [NIGHTLY_SKIP] @@ -454,6 +464,35 @@ jobs: --title "feat: 順位 ${RANK} の無人実装 (nightly-todo)" \ --body-file "$RUNNER_TEMP/pr-body.md" + # CodeRabbit の自動レビューは draft PR を対象外にしている + # (.coderabbit.yaml の reviews.auto_review.drafts: false = ADR-019 のクォータ設計)。 + # 一方 ADR-052 / 本 ADR は夜間ループを **draft PR で止める**。この 2 つを素直に + # 組み合わせると「夜間 PR は永久にレビューされない」状態になる (2026-08-08 に PR #365 で + # 実測)。両立のため、draft PR 作成後に**明示トリガーを 1 回だけ**投げる。 + # ADR-019 が fix push で採っている「自動増分レビューを止め、明示 `@coderabbitai review` を + # 1 回投稿する」と同じ形で、レート消費は 1 PR あたり 1 回に留まる。 + # + # **token は App のものを使う。** job の GITHUB_TOKEN は pull-requests: read しか + # 持たず、書き込み権限を戻すと決定 8 § 副次効果 (agent が触れる唯一の資格情報を + # read-only にした) を巻き戻してしまう。 + # + # **本 step は continue-on-error で受ける。** 決定 10 は gh / network の失敗を red と + # 分類するが、それは無人ループの本体経路 (実装 → ゲート → push → PR 作成) の話であり、 + # レビュー起動は成果物が出来た**後**に走る助言層にあたる。[ADR-043] が「fail-closed は + # ゲート関数のみ。助言層は fail-open が正しい」と定めているため、ここで run を red に + # すると「draft PR は正しく出来たのにレビュー起動だけ失敗した夜」が「本当に壊れた夜」と + # 同じ色になる。代わりに outcome を Report outcome へ出して可視化する。 + - name: Request a CodeRabbit review (drafts are excluded from auto review) + id: request-review + continue-on-error: true + if: steps.publish.outcome == 'success' + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + BRANCH: ${{ steps.select.outputs.branch }} + run: | + set -euo pipefail + gh pr comment --repo "${{ github.repository }}" "$BRANCH" --body "@coderabbitai review" + # 無音で終わらせない (ADR-064 と同じ論理)。どの段で止まったかを 1 行で出す。 - name: Report outcome if: '!cancelled()' @@ -468,10 +507,15 @@ jobs: GATE_OUTCOME: ${{ steps.gate.outcome }} APP_TOKEN_OUTCOME: ${{ steps.app-token.outcome }} PUBLISH_OUTCOME: ${{ steps.publish.outcome }} + REQUEST_REVIEW_OUTCOME: ${{ steps.request-review.outcome }} run: | - echo "[NIGHTLY] preflight=${PREFLIGHT_OUTCOME:-<未実行>} select=${SELECT_OUTCOME:-<未実行>} implement=${IMPLEMENT_OUTCOME:-<未実行>} verify=${VERIFY_OUTCOME:-<未実行>} publish_tree=${PUBLISH_TREE_OUTCOME:-<未実行>} guard=${GUARD_OUTCOME:-<未実行>} integrity=${INTEGRITY_OUTCOME:-<未実行>} gate=${GATE_OUTCOME:-<未実行>} app_token=${APP_TOKEN_OUTCOME:-<未実行>} publish=${PUBLISH_OUTCOME:-<未実行>}" + echo "[NIGHTLY] preflight=${PREFLIGHT_OUTCOME:-<未実行>} select=${SELECT_OUTCOME:-<未実行>} implement=${IMPLEMENT_OUTCOME:-<未実行>} verify=${VERIFY_OUTCOME:-<未実行>} publish_tree=${PUBLISH_TREE_OUTCOME:-<未実行>} guard=${GUARD_OUTCOME:-<未実行>} integrity=${INTEGRITY_OUTCOME:-<未実行>} gate=${GATE_OUTCOME:-<未実行>} app_token=${APP_TOKEN_OUTCOME:-<未実行>} publish=${PUBLISH_OUTCOME:-<未実行>} request_review=${REQUEST_REVIEW_OUTCOME:-<未実行>}" if [ "${PUBLISH_OUTCOME:-}" = "success" ]; then echo "[NIGHTLY] draft PR を作成しました。" + # 助言層の失敗は run を red にしないが、無音にもしない (ADR-043)。 + if [ "${REQUEST_REVIEW_OUTCOME:-}" != "success" ]; then + echo "[NIGHTLY_WARN] CodeRabbit レビューの明示トリガー投稿に失敗しました (request_review=${REQUEST_REVIEW_OUTCOME:-<未実行>})。draft PR は作成済みなので、必要なら PR へ手で '@coderabbitai review' を投稿してください。" + fi exit 0 fi echo "[NIGHTLY_SKIP] 本 run は draft PR を作りませんでした。上の 1 行で停止段を特定してください。" diff --git a/docs/adr/adr-072-nightly-todo-loop.md b/docs/adr/adr-072-nightly-todo-loop.md index 1d2d967a..3d86b404 100644 --- a/docs/adr/adr-072-nightly-todo-loop.md +++ b/docs/adr/adr-072-nightly-todo-loop.md @@ -224,6 +224,24 @@ clone は shallow にしない。新規ブランチの push で shallow update pre-push simplicity review はここを「他の停止点と同様に graceful degradation すべき」と指摘したが、上記の理由で**現状を維持する**。指摘が再発しないよう決定として記録しておく。 +### 11. draft PR 作成後に CodeRabbit レビューを明示トリガーする + +**夜間 draft PR は、放っておくと永久にレビューされない。** 初回の実走 (2026-08-08、PR #365) で確定した: + +- [`.coderabbit.yaml`](../../.coderabbit.yaml) は `reviews.auto_review.drafts: false` を設定している ([ADR-019](adr-019-coderabbit-review-hybrid-policy.md) の無料枠クォータ設計) +- 一方 [ADR-052](adr-052-autonomy-execution-boundary-classes.md) と本 ADR は、夜間ループを **draft PR で止める**と決めている (commitment 点の手前) +- 両者を素直に組み合わせると、夜間 PR にはレビューが付かない。さらに [ADR-067](adr-067-phase-b-unattended-fix-push.md) の Phase B は `issue_comment` / `pull_request_review` で起動するため、**Phase B も夜間 PR では永久に発火しない** + +**どちらの設定も単体では正しく、衝突は 2 つを繋いだときにだけ現れる。** 実際、本 ADR § 欠点 は「Phase A が夜間 draft PR で自動起動しない可能性がある」と書きながら、原因を CodeRabbit の起動条件ではなく**自リポジトリの設定**に求めていなかった。設定ファイル 1 行と ADR の決定が論理結合している型で、[ADR-051](adr-051-cross-system-config-coupling.md) が扱う coupling の内部版にあたる。 + +**解き方は `drafts: true` ではなく明示トリガーを採る。** `drafts: true` は ADR-019 が解いたレート消費の問題を戻す。ready 化は ADR-052 の commitment 点を侵す。採ったのは **draft PR 作成後に `@coderabbitai review` を 1 回だけ投稿する**方法で、これは ADR-019 が fix push に対して既に採っている形 (`auto_incremental_review: false` + 監視が明示トリガーを 1 回投稿) と同型である。レート消費は 1 PR あたり 1 回に留まる。 + +**token は App のものを使う。** job の `GITHUB_TOKEN` は `pull-requests: read` しか持たない。書き込みを戻すと決定 8 § 副次効果 (agent が触れる唯一の GitHub 資格情報を read-only にした) を巻き戻すため、App installation token を使う。 + +**この step は決定 10 の red 分類の例外で、`continue-on-error` を付ける。** 決定 10 が red と定めるのは無人ループの**本体経路** (実装 → ゲート → push → PR 作成) の失敗であり、レビュー起動は成果物が出来た**後**に走る助言層にあたる。[ADR-043](adr-043-security-gates-fail-closed.md) が「fail-closed はゲート関数のみに適用。助言層は fail-open が正しい」と定めているため、ここで run を red にすると「draft PR は正しく出来たのにレビュー起動だけ失敗した夜」が「本当に壊れた夜」と同じ色になる。**無音にはしない** — `Report outcome` が `request_review` の outcome を出し、失敗時は `[NIGHTLY_WARN]` で手動投稿を促す。 + +**未検証**: **CodeRabbit が bot (App) の投稿した `@coderabbitai review` に反応するか**は未確認である。bot 同士のループを避けるため他 bot のコメントを無視する実装は珍しくない。次回の夜間 run で反応の有無を実測し、無反応なら (a) PAT 経由の投稿 (ADR-067 の ruleset backstop を bypass するため不可)、(b) `drafts: true` への方針転換とレート影響の再評価、(c) 人手で投げる運用、の 3 択で再判断する (§ 残課題)。 + ## 試験運用判断基準 (ADR-039) | 項目 | 内容 | @@ -299,24 +317,66 @@ pre-push review を 12 サイクル通す過程で、blocking な欠陥 10 件 #4 は `sha256sum -c` が基準値ファイルを読めない時点で失敗するため、fail-closed が自然に成立する。加えて基準値は `$RUNNER_TEMP` = ワークスペース外にあり agent のサンドボックスが届かないため、防御は 2 層になっている。 -### 実走スモーク — **未実施** +### 実走スモーク — allow 経路は **schedule の初回実走で成立** (2026-08-08) + +**スモークは `workflow_dispatch` で意図的に始める前に、schedule (03:00 JST) の初回実走が先に成立した。** 2026-08-07 18:19 UTC の run が台帳の順位 203 を選び、draft PR [#365](https://github.com/aloekun/claude-code-hook-test/pull/365) (`claude/nightly-203`) の作成まで完走した。 + +**この順序は褒められたものではない。** 受け入れ基準の中核である実走検証を、人間が観測装置を用意した dispatch ではなく**本番の無人 run が先に消化した**形になっている。結果的に成功したが、失敗していれば観測の準備が無いまま夜間に壊れた成果物が出ていた。ここでの教訓は、`AUTONOMY_ENABLED` を立てた時点で schedule も同時に有効になるという事実が、スモーク計画に織り込まれていなかったこと (§ 残課題)。 + +観測項目は **9 件**である (起票時の 8 件 + [#364](https://github.com/aloekun/claude-code-hook-test/pull/364) で受け入れ基準へ追加した停止側 1 件)。以降の集計はこの 9 件を母数にする。 -**本 ADR の受け入れ基準のうち実走スモークは、この PR の時点では満たしていない。** 実行には (a) 本 workflow が master に存在するか対象ブランチ ref へ `workflow_dispatch` できること、(b) 台帳に無人可マークがあること (= WP-18 PR 2 のマージ) の両方が要る。 +| 観測項目 | 出所 | 結果 (2026-08-08) | +|---|---|---| +| Actions variable `AUTONOMY_ENABLED` が `true` ちょうどで設定されており job が起動すること | ADR-066 § 決定 2 (完全一致要件) | **充足** — job が起動し完走 | +| `claude/nightly-*` の **ref 作成**が App token で通ること (ruleset の除外が creation にも効くこと) | ADR-067 段 0 の ruleset。Phase B が観測したのは既存ブランチへの update のみ | **充足** — `claude/nightly-203` が新規作成された | +| **App token で作った draft PR に `ci.yml` の 2 OS run が紐づくこと** | 決定 8 (仕様は公式で確認済み、実環境での成立は未観測) | **充足** — PR の author は `nightly-todo-aloekun[bot]`、`rust (ubuntu-latest)` / `rust (windows-latest)` がいずれも success。承認待ちにならなかった | +| 決定 7 の照合が実 runner 上でも通ること (誤検知で毎晩止まらないこと) | 本 ADR § integrity 機構の drill | **充足 (1 run)** — 誤検知せず publish へ到達。毎晩の安定性は継続観測 | +| `publish/` の clone + rsync が実 runner で成立し、`work/` の変更が過不足なく運ばれること | 決定 9 (`--delete` による削除の反映を含む) | **充足** — commit は 1 ファイル 18 行追加・削除ゼロで、順位 203 の指定範囲と完全に一致 | +| WP-17 残課題: Phase B の自動起動経路が成立するか | [ADR-067](adr-067-phase-b-unattended-fix-push.md) § 検証記録 | **不成立と判明** — CodeRabbit が draft を自動レビューしないため起動契機のコメント自体が発生しない (決定 11 で対処) | +| WP-17 残課題: `coderabbitai[bot]` allowlist の要否 | 同上 | **判定不能** — 上記より CodeRabbit のイベントが発生していない。決定 11 の明示トリガーが効いてから再判定 | +| **`cargo` サブプロセスから `CLAUDE_CODE_OAUTH_TOKEN` / `GITHUB_TOKEN` が見えるか** | pre-push security review の warning | **未観測** — 使い捨ての `build.rs` を仕込む専用 run が要る (§ 残課題) | +| **停止側: `AUTONOMY_ENABLED` が `'false'` / 未設定で何も作られないこと** | ADR-066 の 3 状態。#364 で受け入れ基準へ追加 | **充足** (2026-08-08、ユーザー実測) — `'false'` と未設定の 2 状態で `workflow_dispatch` (`dry_run` オフ = push / PR 作成をする設定) を実行し、**2 回とも job が skip**。ブランチ・draft PR・App token のいずれも作られなかった。確認後 `'true'` へ復旧済み | + +**停止側は `dry_run` をオフにして検証した。** `AUTONOMY_ENABLED` が `'true'` でなければ job の `if:` で止まるため `dry_run` の値は判定に関与しないが、**あえて「push も PR 作成もする設定」で実行**することで「dry_run だから作られなかったのでは」という解釈の余地を消している。 + +**残るのはトークン露出 1 項目のみ。** 使い捨ての `build.rs` から `env | grep -i token` を出す専用 run が要り、本番 schedule では観測できない。反復は [ADR-067](adr-067-phase-b-unattended-fix-push.md) § 段 2 の知見 2 に従い、**マージせずブランチ ref への `workflow_dispatch`** で行う。 -反復は [ADR-067](adr-067-phase-b-unattended-fix-push.md) § 段 2 の知見 2 に従い、**マージせずブランチ ref への `workflow_dispatch`** で行う。`dry_run` 入力でゲート通過まで走らせて push を止められるようにしてある。 +### 外部設定の実体 (2026-08-08 確認) -スモークで同梱観測する項目: +[ADR-051](adr-051-cross-system-config-coupling.md) が内部設定と外部 SaaS 設定の論理結合に課す 3 点のうち、**(2) 期待値の組み合わせ表**を本節が担う。**秘密値そのもの (`NIGHTLY_APP_PRIVATE_KEY` の鍵本文や発行済み token) は記録しない** — 記録するのは結合の存在と期待値であって、秘密の実値ではない。 -| 観測項目 | 出所 | +| 項目 | 実体 | |---|---| -| WP-17 残課題: Phase B の自動起動経路が成立するか | [ADR-067](adr-067-phase-b-unattended-fix-push.md) § 検証記録 | -| WP-17 残課題: `coderabbitai[bot]` allowlist の要否 | 同上 | -| **`cargo` サブプロセスから `CLAUDE_CODE_OAUTH_TOKEN` / `GITHUB_TOKEN` が見えるか** | pre-push security review の warning | -| 決定 7 の照合が実 runner 上でも通ること (誤検知で毎晩止まらないこと) | 本 ADR § integrity 機構の drill | -| **App token で作った draft PR に `ci.yml` の 2 OS run が紐づくこと** | 決定 8 (仕様は公式で確認済み、実環境での成立は未観測) | -| Actions variable `AUTONOMY_ENABLED` が `true` ちょうどで設定されており job が起動すること | ADR-066 § 決定 2 (完全一致要件) | -| `claude/nightly-*` の **ref 作成**が App token で通ること (ruleset の除外が creation にも効くこと) | ADR-067 段 0 の ruleset。Phase B が観測したのは既存ブランチへの update のみ | -| `publish/` の clone + rsync が実 runner で成立し、`work/` の変更が過不足なく運ばれること | 決定 9 (`--delete` による削除の反映を含む) | +| App 名 | `nightly-todo-aloekun` (PR の author は `nightly-todo-aloekun[bot]` として現れる) | +| 作成日 | **未確認** — App の設定ページに表示が見当たらなかった。監査で必要になった時点で GitHub の Audit log から引く | +| インストール範囲 | **Only select repositories** = `claude-code-hook-test` のみ | +| 付与権限 | Contents: **Read and write** / Pull requests: **Read and write** / Metadata: **Read-only** / Workflows: **No access** | +| `NIGHTLY_APP_ID` | repository **variable** (Actions → Variables) | +| `NIGHTLY_APP_PRIVATE_KEY` | repository **secret** (Actions → Secrets) | +| `AUTONOMY_ENABLED` | repository **variable** (Actions → Variables)。現在値 `'true'` | + +**付与権限は決定 8 の設計意図と完全に一致していた。** 同決定は「Contents: write / Pull requests: write / Metadata: read のみ。**Workflows は付けない**」と書いており、実体もそのとおりだった。Workflows が No access であることは、`.github/workflows/**` を含む push が権限層でも通らないことを意味し、決定 6 の禁止リストと二重の防御になっている。 + +**既存の Claude GitHub App とは別物である。** あちらは Workflows を含む広い権限を持ち、Claude Code の cloud 経路が使う。混同して「もう入っているから不要」と判断しないこと。 + +#### 値が欠けたときにどう倒れるか + +| 欠落 | 挙動 | 根拠 | +|---|---|---| +| `AUTONOMY_ENABLED` が `'false'` / 未設定 | **job ごと起動しない**。ブランチ・draft PR・App token のいずれも作られない | **実測済** (2026-08-08、§ 実走スモーク)。job の `if: vars.AUTONOMY_ENABLED == 'true'` | +| `autonomy-config.toml` の `enabled = false` | `cli-autonomy-gate` が deny。pre-flight で agent 起動前に止まり、green + `[NIGHTLY_SKIP]` で終わる | exe 単体の drill 8 シナリオ ([ADR-066](adr-066-autonomy-global-kill-switch.md)) | +| `max_open_draft_prs` に到達 | 同上 (背圧 deny) | drill 12 シナリオ ([ADR-071](adr-071-draft-pr-backpressure.md)) | +| `NIGHTLY_APP_ID` / `NIGHTLY_APP_PRIVATE_KEY` が欠落 | `Mint App token` が失敗 → **run は red**。`publish` は `if: steps.app-token.outcome == 'success'` で skip されるため、ブランチも draft PR も作られない | **設計上の期待値 (未実測)**。同 step は決定 10 に従い `continue-on-error` を持たない | + +最終行は**未実測**である。資格情報を意図的に壊す run は、復旧を伴うため実施していない。fail-closed の側 (欠けても成果物が出ない) は step の `if:` 連鎖から構造的に言えるが、**run が red で終わるか green で終わるか**は実際に落としてみないと確定しない。 + +#### 再構築手順 (鍵ローテーション / 派生プロジェクトへの展開) + +1. GitHub App を作成する。権限は上表のとおり (**Workflows は付けない**) +2. インストール先を対象リポジトリ 1 つに絞る (Only select repositories) +3. App ID を repository **variable** `NIGHTLY_APP_ID` へ登録 +4. 秘密鍵を生成し repository **secret** `NIGHTLY_APP_PRIVATE_KEY` へ登録 (鍵本文はリポジトリにも ADR にも残さない) +5. `AUTONOMY_ENABLED` を variable として `'true'` に設定する。**これを立てると schedule も同時に有効になる** (§ 残課題) 3 行目は決定 5 で Bash を落とした後も残る経路の確認である。agent は `cargo` を直接叩けないが、`work/` へ書いた `build.rs` は `Verify deterministically` step の `cargo test` が実行する。同 step に `env:` を置いていないことがトークン非露出の根拠なので、**その前提が実 runner で成立するか**を使い捨ての `build.rs` から `env | grep -i token` を出して実測する。 @@ -344,7 +404,10 @@ pre-push review を 12 サイクル通す過程で、blocking な欠陥 10 件 ### 残課題 -- **実走スモークの完走** (§ 検証記録)。本 ADR の受け入れ基準の中核であり、未実施。 +- **実走スモークの残り 1 項目** (§ 実走スモーク)。allow 経路 (schedule 初回実走) と停止側 2 状態 (dispatch) は 2026-08-08 に充足した。残るのは **`cargo` サブプロセスへのトークン露出**で、使い捨ての `build.rs` を仕込む専用 run が要る。この観測は決定 5 の「Bash 再付与を再検討してよいか」の判断材料でもある。 +- **外部設定の実体は記録したが、作成日と資格情報欠落時の run の色は未確定** (§ 外部設定の実体)。前者は GitHub の Audit log から引ける。後者は資格情報を意図的に壊す run が要り、復旧を伴うため実施していない。 +- **`AUTONOMY_ENABLED` を立てると schedule も同時に有効になる**。スモークを「まず dry_run で」と計画していたのに、変数を立てた時点で本番の夜間 run が先に走った (§ 実走スモーク)。**観測装置の準備前に無人 run が始まる**構造なので、次に同種の自律機能を足すときは「有効化の粒度」を dispatch 限定と schedule 込みで分けられるか検討する。 +- **CodeRabbit が bot 投稿の `@coderabbitai review` に反応するか** (決定 11)。無反応なら明示トリガーの設計自体が成立しないため、次回の夜間 run で最優先に確認する。 - **外部設定 (GitHub App / repository variables・secrets) の実体が未記録**。決定 8 は「なぜ App token か」を厚く残す一方、App 名・インストール範囲・付与権限の実際・`NIGHTLY_APP_ID` (variable) / `NIGHTLY_APP_PRIVATE_KEY` (secret) / `AUTONOMY_ENABLED` (variable) の登録先と欠落時の倒れ方を 1 行も書いていない。[ADR-051](adr-051-cross-system-config-coupling.md) が内部設定と外部 SaaS 設定の論理結合に課す 3 点 (相互参照コメント / 期待値の組み合わせ表 / 両側同一 PR) が未実施の状態にあたる。実走スモークで GitHub UI を触る際に**設定メタデータ** (名前・登録先の別・付与権限のスコープ・所有者・ローテーション方針・欠落時の挙動) を確認し、§ 外部設定の実体 として本 ADR へ追記する。**秘密値そのもの (`NIGHTLY_APP_PRIVATE_KEY` の鍵本文や発行済み token) は ADR にも git 履歴にも残さない** — ADR-051 が記録を課すのは「結合の存在」と「期待値の組み合わせ」であって、秘密の実値ではない。 - **`master-ref/` を agent のファイルシステムから外す**。決定 7 は検知どまりで、防止には別 job + artifact 受け渡しへの構造変更が要る。実走スモークで agent が実際にワークスペース外へ手を伸ばすか観測してから判断する。 - **authority gate の直前で draft 数を再計数するか**。現状は job 冒頭のスナップショットを使い回す (§ 決定 4)。閾値を 1 件超えて push される事象が実運用で観測されたら入れる。 diff --git a/docs/harness-improvement-plan.md b/docs/harness-improvement-plan.md index 131f25d0..2788b9e3 100644 --- a/docs/harness-improvement-plan.md +++ b/docs/harness-improvement-plan.md @@ -154,23 +154,25 @@ Anthropic 公式のハーネスエンジニアリング指針(決定論的基 - **routine 出力の受け渡し手段が未決**: 分析結果が transcript にしか残らずユーザーが読まなければ消える。実行主体を含む 3 択(routine / GitHub Actions schedule / ローカル維持 = 断念)で、**断念も正規の出口**。判定は ADR-070 bounded lifetime (b) の観測後([ADR-070](adr/adr-070-weekly-review-cloud-routine.md) § 残課題)。 - **Phase B の実効価値は WP-18 に依存**: 対象が docs 指摘に限られるため、WP-18 の夜間ループが `claude/` ブランチ PR を作り始めるまで発火機会が小さい(ADR-067 § 欠点)。 -### WP-18: 夜間 todo 消化ループ — 3 PR マージ済(2026-08-07、実走スモーク未実施) +### WP-18: 夜間 todo 消化ループ — 実装 land 済 + allow 経路の実走成立(2026-08-08) > 夜間に 1 タスクを無人実装し **draft PR 作成で停止**する(マージ判断は人間)ループを、WP-17 のバックボーン上に組む。 **進捗**: PR 1(背圧、[#361](https://github.com/aloekun/claude-code-hook-test/pull/361))/ PR 2(タスク台帳、[#362](https://github.com/aloekun/claude-code-hook-test/pull/362))/ PR 3(夜間 workflow、[#363](https://github.com/aloekun/claude-code-hook-test/pull/363))すべて **master へマージ済み**(2026-08-07)。 -**ただし WP としては未完**。残作業は 2 系統ある(→ § 残作業): +**2026-08-08 に夜間ループが初めて実走し、draft PR [#365](https://github.com/aloekun/claude-code-hook-test/pull/365)(`claude/nightly-203`)の作成まで完走した。** ただし **WP としては未完**で、残作業は 2 系統ある(→ § 残作業): -1. **実走スモーク**(順位 374。**外部設定の実体記録 384 を同時実施**)— 受け入れ基準の中核。コードは land したが、**まだ 1 度も走っていない** +1. **実走スモークの残り**(順位 374。**外部設定の実体記録 384 を同時実施**)— allow 経路は上記で成立したが、**停止側(`AUTONOMY_ENABLED` の 3 状態)とトークン露出の 2 項目が未観測**。どちらも本番 schedule では観測できず `workflow_dispatch` の専用 run が要る 2. **prompt injection 対策 4 件**(順位 378-381)— #363 の post-merge-feedback が Tier 1 として挙げたもの。定常運用開始前に必須 +**なお allow 経路は `workflow_dispatch` ではなく schedule の本番 run が先に消化した** — `AUTONOMY_ENABLED` を立てると schedule も同時に有効になるため。結果は成功だったが、観測装置の準備前に無人 run が始まる構造だった点は [ADR-072](adr/adr-072-nightly-todo-loop.md) § 残課題 に記帳した。 + #### 残作業 | 順位 | 内容 | Tier | 期限 | |---|---|---|---| -| 374 | 実走スモーク(`workflow_dispatch` の `dry_run` から開始、観測 8 項目) | 🚀 1 | 次に着手する作業 | -| 384 | 外部設定(GitHub App / repository variables・secrets)の実体を [ADR-072](adr/adr-072-nightly-todo-loop.md) へ記録([ADR-051](adr/adr-051-cross-system-config-coupling.md) 違反の解消) | 🚀 1 | **順位 374 と同時**(スモークで GitHub UI を触る過程で実値が揃う) | +| 374 | 実走スモーク(**allow 経路・停止側とも 2026-08-08 に充足**。残りは**トークン露出 1 項目**のみで、使い捨て `build.rs` を仕込む `workflow_dispatch` の専用 run が要る) | 🚀 1 | 残り 1 項目 | +| 384 | 外部設定(GitHub App / repository variables・secrets)の実体を [ADR-072](adr/adr-072-nightly-todo-loop.md) へ記録([ADR-051](adr/adr-051-cross-system-config-coupling.md) 違反の解消) | 🚀 1 | **完了**(2026-08-08、ADR-072 § 外部設定の実体。todo エントリの削除は docs バッチで行う) | | 378 | 台帳を [ADR-035](adr/adr-035-doc-evaluation-policy.md) の docs-only 除外パス表へ追加 | 🚀 1 | 定常運用開始前 | | 379 | agent の tool scope を `work/**` へ限定(現行は `$GITHUB_WORKSPACE` 全体) | 🚀 1 | 定常運用開始前 | | 380 | 台帳フィールドを agent prompt へ untrusted data として明示 framing | 🚀 1 | 定常運用開始前 | @@ -195,7 +197,7 @@ Anthropic 公式のハーネスエンジニアリング指針(決定論的基 - **PR 構成(新規 3 本)**: 1. **PR 1: 背圧実装 + ADR 起票(M)— 実装済み(2026-08-06、[ADR-071](adr/adr-071-draft-pr-backpressure.md))**。閾値判定の層は実装時判断で **(a) gate 内**を採った(`GateInputs` に実測値と閾値を渡す)。`Operation::backpressure_connected()` は廃し、`requires_draft_backpressure()`(指標の要求のみ・状態を持たない)と `GateInputs::{open_draft_prs, max_open_draft_prs}`(状態)へ分けて二重管理を避けた。閾値は `autonomy-config.toml` の `[autonomy] max_open_draft_prs = 3`。実 exe による drill 12 シナリオと unit test 40 件で実測を固定済み。SaaS 課金・上限事実(§ 2)の最新値再確認と永続化も同 ADR で完了。 2. **PR 2: タスク台帳のブラッシュアップ(docs、S)— 実装済み(2026-08-06、[#362](https://github.com/aloekun/claude-code-hook-test/pull/362))**。stale 行 2 件(順位 120 / 134、どちらも land 済み)を削除し棚卸し履歴 section を新設。無人可の 2 段階分類を導入して 14 件中 7 件をユーザー承認のうえマーク(見送り 7 件も理由を表で明示)。lifecycle は「空になっても retire しない」定期更新台帳へ改訂。weekly-review への接続は新 step を足さず既存の観点⑤(`review-todo-whole` facet)に Criterion 3 として相乗りさせた。 - 3. **PR 3: 夜間 workflow(schedule、M-L)— マージ済み(2026-08-07、[#363](https://github.com/aloekun/claude-code-hook-test/pull/363)、[ADR-072](adr/adr-072-nightly-todo-loop.md))、ただし実走スモークは未実施**。タスク選択は新規 exe `cli-nightly-task-select`(実装時判断で shell ではなく Rust を採用 — markdown table の境界に回帰テストを書く場が要るため)。`.github/workflows/nightly-todo.yml` が 17 step で選択 → 実装 → コストフィルタ(`cargo test` + `cargo clippy`)→ **clean publish tree の用意** → ガードレール禁止リスト → **ゲート資産の改ざん検知** → gate → **App token 発行** → draft PR 作成を回す。schedule は毎日 03:00 JST(2026-08-06 ユーザー確認)。**push / PR 作成は workflow step が gate 経由で実行し、agent は push の主体にしない**(ADR-067 と同型)。 + 3. **PR 3: 夜間 workflow(schedule、M-L)— マージ済み(2026-08-07、[#363](https://github.com/aloekun/claude-code-hook-test/pull/363)、[ADR-072](adr/adr-072-nightly-todo-loop.md))。2026-08-08 の schedule で初回実走し allow 経路が成立**。タスク選択は新規 exe `cli-nightly-task-select`(実装時判断で shell ではなく Rust を採用 — markdown table の境界に回帰テストを書く場が要るため)。`.github/workflows/nightly-todo.yml` が 18 step で選択 → 実装 → コストフィルタ(`cargo test` + `cargo clippy`)→ **clean publish tree の用意** → ガードレール禁止リスト → **ゲート資産の改ざん検知** → gate → **App token 発行** → draft PR 作成を回す。schedule は毎日 03:00 JST(2026-08-06 ユーザー確認)。**push / PR 作成は workflow step が gate 経由で実行し、agent は push の主体にしない**(ADR-067 と同型)。 改ざん検知と clean publish tree はいずれも pre-push security review の REJECT を受けて追加した([ADR-072](adr/adr-072-nightly-todo-loop.md) 決定 7 / 決定 9)。後者は「agent が `work/.git` を書き換えると App token を持つ step で任意コマンドが走る」経路への対処で、危険な設定キーの列挙(deny-list)で 2 回連続レビュー漏れを指摘されたため、**Implement 終了後に新規 clone した作業ツリーで git 操作を行う**構造へ切り替えた。**App token は Windows CI を draft PR へ紐づけるために導入した**(同 決定 8)— `GITHUB_TOKEN` で作成した PR は `pull_request` run が承認待ちになり、人間が Approve するまで `ci.yml` が動かない。Windows を主開発環境とする本プロジェクトで、2 OS 検証を人間の操作待ちにする設計は採れないため(2026-08-07 ユーザー判断)。PAT ではなく App を使うのは、オーナーの PAT が Repository admin として ADR-067 の ruleset backstop を bypass してしまうため。 @@ -220,9 +222,9 @@ Anthropic 公式のハーネスエンジニアリング指針(決定論的基 |---|---| | 背圧の決定論層 drill(12 シナリオ、実 exe) | **充足**(PR 1、[ADR-071](adr/adr-071-draft-pr-backpressure.md) § 検証記録) | | タスク選択の境界固定(unit test 25 件 + 実データ選択) | **充足**(PR 3、[ADR-072](adr/adr-072-nightly-todo-loop.md) § 検証記録) | - | **実走スモーク — 有効時のみ `claude/nightly-*` の draft PR が作られること** | **未実施**(順位 374)。前提 (a) workflow が master にあること・(b) 台帳に無人可マークがあることは**いずれも 2026-08-07 のマージで充足済み**。加えて GitHub UI 側で `AUTONOMY_ENABLED` を `'true'`(完全一致)に設定する操作と、`dry_run=false` の完走には App(`NIGHTLY_APP_ID` / `NIGHTLY_APP_PRIVATE_KEY`)の登録が要る。**この UI 操作で確認した設定メタデータは順位 384 で ADR-072 へ記録する**(秘密値そのものは記録しない)。反復は [ADR-067](adr/adr-067-phase-b-unattended-fix-push.md) 段 2 の知見 2 に従い**マージせず ref 指定の dispatch** で行う(`dry_run` 入力あり) | - | **停止側の実走 — 無効時に何も作られないこと**(`AUTONOMY_ENABLED` の 3 状態 = `'true'` / `'false'` / 未設定 で dispatch し、`false` と未設定では job 起動・ブランチ作成・draft PR・App token のいずれも発生しないことを確認) | **未実施**(順位 374 に同梱)。**成功経路だけの確認では kill-switch が効くことを示せない** — 本プロジェクトは背圧 12 シナリオ・kill-switch 8 シナリオと決定論層の deny を drill で固めてきたが、夜間ループの停止側は実走未観測。WP-17 の残課題(明示的 `false` と config 側 deny が実走未観測、[ADR-066](adr/adr-066-autonomy-global-kill-switch.md) bounded lifetime)と同じ穴なので、ここで一緒に埋める。deny 結果は [ADR-072](adr/adr-072-nightly-todo-loop.md) § 実走スモーク へ記帳する | - | スモークの同梱観測 8 項目(内訳は [ADR-072](adr/adr-072-nightly-todo-loop.md) § 実走スモークの表を参照。主なものは **App token 作成 PR に `ci.yml` の 2 OS run が紐づくこと**、`AUTONOMY_ENABLED` の設定、`claude/nightly-*` の **ref 作成**が通ること、WP-17 残課題 2 件) | **未実施**(一覧は [ADR-072](adr/adr-072-nightly-todo-loop.md) § 実走スモークの表が正。WP-17 の 2 件は ADR-067 § 検証記録に「WP-18 着手時に実測」と記帳済み) | + | **実走スモーク — 有効時のみ `claude/nightly-*` の draft PR が作られること** | **充足**(2026-08-08、[PR #365](https://github.com/aloekun/claude-code-hook-test/pull/365) = `claude/nightly-203`)。ただし **`workflow_dispatch` ではなく schedule の本番 run が先に消化した** — `AUTONOMY_ENABLED` を立てた時点で schedule も有効になるため。結果は成功だったが、観測装置の準備前に無人 run が走る構造だった点は [ADR-072](adr/adr-072-nightly-todo-loop.md) § 残課題 に記帳 | + | **停止側の実走 — 無効時に何も作られないこと**(`AUTONOMY_ENABLED` の 3 状態 = `'true'` / `'false'` / 未設定 で dispatch し、`false` と未設定では job 起動・ブランチ作成・draft PR・App token のいずれも発生しないことを確認) | **充足**(2026-08-08、ユーザー実測)。`'false'` と未設定の 2 状態で **`dry_run` をオフ(= push / PR 作成をする設定)**にして dispatch し、2 回とも job が skip。確認後 `'true'` へ復旧済み。**これで WP-17 の残課題(明示的 `false` と未設定の実走未観測、[ADR-066](adr/adr-066-autonomy-global-kill-switch.md) bounded lifetime)も同時に埋まった** | + | スモークの同梱観測 **9 項目**(起票時の 8 件 + #364 で追加した停止側 1 件。内訳は [ADR-072](adr/adr-072-nightly-todo-loop.md) § 実走スモークの表が正) | **6 充足 / 1 不成立 / 1 判定不能 / 1 未観測**。**充足** = `AUTONOMY_ENABLED` の完全一致起動・`claude/nightly-*` の ref 作成・**App token 作成 PR に `ci.yml` の 2 OS run が紐づくこと**(決定 8 の核心)・決定 7 の照合が誤検知しないこと(1 run)・`publish/` の rsync が過不足なく運ぶこと・**停止側 2 状態**。**不成立** = WP-17 残課題の Phase B 自動起動(CodeRabbit が draft を自動レビューしないため契機が発生しない → [ADR-072](adr/adr-072-nightly-todo-loop.md) 決定 11 で対処)。**判定不能** = `coderabbitai[bot]` allowlist の要否(上記より CodeRabbit のイベントが発生しないため。決定 11 の明示トリガーが効いてから再判定)。**未観測** = トークン露出のみ | | **WP 全体**: 2 週間の試験運用で無人 draft PR の採用率(人間がマージした割合)を測定。**50% 超で継続・拡大、未満なら対象クラスを絞って再試行** | **未着手**(スモーク完走後に開始)。測定は weekly-review の自律アクション棚卸し(WP-19 ステップ 3)に載せて仕組み化する | なお採用率 50% は根拠のある閾値ではなく、2 週間・最大 14 件(背圧により実際はより少ない)では統計的な意味を持たない([ADR-072](adr/adr-072-nightly-todo-loop.md) § 欠点)。判断材料の 1 つとして扱う。