diff --git a/.claude/hooks-config.toml b/.claude/hooks-config.toml index 89432258..0a69a663 100644 --- a/.claude/hooks-config.toml +++ b/.claude/hooks-config.toml @@ -29,12 +29,16 @@ default_branch = "master" # trunk-based 前提、feature branch 運用では stale_check_enabled = true # [session_start.weekly_review_reminder] -# - ADR-031 Phase C: `/weekly-review` skill 起動の reminder (試験運用、ADR-039 experimental pattern 準拠)。 +# - ADR-031 Phase C / ADR-070: weekly-review の**監査リマインダー** (試験運用、ADR-039 準拠)。 +# ADR-070 で分析の主経路が cloud routine (週 1 schedule) へ移り、本 reminder は +# 「レビューを実行せよ」から「routine の稼働と結果の取り込みを確認せよ」へ転換した。 # 2 経路で発火: -# 1. `.claude/weekly-review-last-run.json` の mtime が `reminder_threshold_days` 超過 -# 2. `.claude/weekly-reviews/*.md.failed` marker 1 件以上残存 (前回失敗 resume promote) +# 1. `.claude/weekly-review-last-run.json` の `last_run_at` が `reminder_threshold_days` 超過 +# 2. `.claude/weekly-reviews/*.md.failed` marker 1 件以上残存 (前回ローカル実行の失敗 resume) # 両方該当する場合は 1 nudge にまとめて出力。 -# 3-5 週の dogfood 後に default-ON 昇格 or 却下を判定 (bounded lifetime)。 +# **重要**: last_run_at は skill の**ローカル実行時**にのみ更新される。cloud routine は +# 使い捨てクローンで動くため更新しない = 本 reminder は routine の実行を観測できない。 +# 発火は「routine が止まっている」の証拠ではなく定期監査の促し。 # Kill-switch: `enabled = false` で完全停止。 [session_start.weekly_review_reminder] # 試験運用元 (本リポジトリ) では明示的に enable して reminder を実発火させる運用。 @@ -43,8 +47,8 @@ stale_check_enabled = true # 派生プロジェクト deploy 時は default OFF (ADR-039 § 1 opt-in 契約) を維持。 # 次 PR (PR-3) で `[features].enabled` allow-list 方式に移行予定 = 本 `enabled = true` は暫定。 enabled = true -reminder_threshold_days = 7 # ADR-031 § トリガー方式: 「前回実行から 7 日経過で promote」と整合 -failed_marker_check_enabled = true # 前回失敗 marker 検出 → resume promote。false で staleness のみに限定可 +reminder_threshold_days = 30 # ADR-070: 監査サイクル。週次 (7 日) だと routine 正常時も毎週発火しノイズになる +failed_marker_check_enabled = true # 前回ローカル実行の失敗 marker 検出 → resume promote。false で staleness のみに限定可 # ADR-059 (試験運用、判定期限 2026-08-16): reminder 発火時に systemMessage (ユーザー可視 1 行) を # additionalContext と併せて出し、「発火しているのにユーザーに見えない」silent 化を解消する。 # source default OFF (派生 repo deploy 時は本行を置かない = additionalContext のみの従来挙動)。 diff --git a/CLAUDE.md b/CLAUDE.md index 25cbf527..ed568fd3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -70,6 +70,7 @@ - [ADR-067: Phase B 無人 fix push — agent を push の主体にしない 4 軸ゲート](docs/adr/adr-067-phase-b-unattended-fix-push.md) *(試験運用)* - [ADR-068: pre-push fix step の権限境界 — 後退検知 backstop と設計級 remedy の human routing](docs/adr/adr-068-fix-step-authority-boundary.md) *(試験運用)* - [ADR-069: PR chain 宣言規約 — 分割チェーンと missing-consumer 検査の両立](docs/adr/adr-069-pr-chain-declaration.md) *(試験運用)* +- [ADR-070: weekly-review の分析フェーズを cloud routine へ移行 — 常時性の獲得と成果物デリバリの未解決](docs/adr/adr-070-weekly-review-cloud-routine.md) *(試験運用)* ## 開発 convention / チェックリスト diff --git a/docs/adr/adr-031-weekly-review-pipeline.md b/docs/adr/adr-031-weekly-review-pipeline.md index ec405c2a..41433447 100644 --- a/docs/adr/adr-031-weekly-review-pipeline.md +++ b/docs/adr/adr-031-weekly-review-pipeline.md @@ -4,6 +4,8 @@ 承認済み (2026-06-01、試験運用 2026-04-27 → 本採用に昇格) +> **2026-08-04 更新 (WP-17 PR 4)**: 起動トリガーを [ADR-070](adr-070-weekly-review-cloud-routine.md) が変更した。分析フェーズ (Phase 1-2 = takt workflow 実行) は cloud routine (週 1 schedule) が担い、SessionStart reminder は「レビューを実行せよ」から「**routine の稼働と結果の取り込みを確認せよ**」の監査リマインダー (既定 30 日) へ転換。**Phase 3 (採否判断) / Phase 4 (task list 反映 + last-run 更新) は従来どおりローカルの人間作業**で、routine は置き換えではない。本 ADR のパイプライン設計そのものは変更なし。 +> > 本 ADR の運用パターンは [ADR-039 (試験運用標準パターン)](adr-039-experimental-feature-standard-pattern.md) で標準化された 3 点セット (config opt-in / kill-switch / bounded lifetime) の対象。本採用判定で ADR-039 の retirement workflow に従い、Phase C/D/E 用 ephemeral handoff doc を retire 済 (Phase E land と同 PR、git log で履歴 trackable)。 ### 採用判定の根拠 (Phase E dogfood 観測結果) diff --git a/docs/adr/adr-070-weekly-review-cloud-routine.md b/docs/adr/adr-070-weekly-review-cloud-routine.md new file mode 100644 index 00000000..7a515289 --- /dev/null +++ b/docs/adr/adr-070-weekly-review-cloud-routine.md @@ -0,0 +1,170 @@ +# ADR-070: weekly-review の分析フェーズを cloud routine へ移行 — 常時性の獲得と成果物デリバリの未解決 + +## ステータス + +試験運用 (2026-08-04) + +> 本 ADR は 2026-07-04 策定のハーネス改善計画 WP-17 PR 4 の決定記録である。[ADR-031](adr-031-weekly-review-pipeline.md) (weekly-review パイプライン) の実行トリガーを、ローカルセッション依存の SessionStart reminder から cloud routine (schedule) へ移す。 + +## コンテキスト + +### 解こうとしている問題 + +ADR-031 の weekly-review は「SessionStart reminder がユーザーに促す → ユーザーが `/weekly-review` を叩く」という起動経路だった。これは**ユーザーがセッションを開くことに依存する**ため、ハーネス改善計画が主戦場と定めた「(2) 自律実行の常時性」を満たさない。実際、reminder が約 4 週間発火し続けたのにユーザーが気付かなかった事例を ADR-031 § L1 が記録している。 + +WP-17 では PR 監視を GitHub Actions へ移し (ADR-067)、ローカルの時限 wakeup を廃止した (ADR-018 追記 2026-08-03)。同じ「常時性はローカルの工夫でなく常設インフラで担保する」原則を weekly-review にも適用する。 + +### weekly-review は 4 フェーズで、自動化できるのは前半だけ + +ADR-031 のフェーズ構成: + +| Phase | 内容 | 自動化可否 | +|---|---|---| +| 1 | 環境準備 (`pnpm install` / `cloud-setup.sh`) | ✅ 可 | +| 2 | takt workflow `weekly-review` 実行 (6 facet 並列 + 機械観測) | ✅ 可 | +| 3 | findings の採否判断 (AskUserQuestion) | ❌ **人間の判断が本質** | +| 4 | 採用分を `docs/todo.md` へ反映 + `weekly-review-last-run.json` 更新 | ❌ Phase 3 に従属 | + +routine が担えるのは Phase 1-2 に限られる。**routine は weekly-review skill の置き換えではなく、その分析フェーズの前倒し実行**である。 + +## 決定 (試験運用) + +### 1. 分析フェーズ (Phase 1-2) を cloud routine の schedule トリガーで実行する + +- routine は `pnpm install` → `cloud-setup.sh` → `pnpm exec takt -w weekly-review -t weekly-review --pipeline --skip-git` を実行し、findings を**報告するところまで**を責務とする。 +- **routine は commit / push / PR 作成を行わない**。ADR-031 の「findings の採否は人間が判断する」設計を維持し、Phase B (ADR-067) のような自律 push 経路をここに増やさない。 +- 作成・編集は claude.ai/code/routines の Web UI で行う (`/schedule` はクラウドセッション内から使用不可)。 + +### 2. SessionStart reminder を「staleness 検知」から「監査リマインダー」へ転換する + +**転換が必要な構造的理由**: `.claude/weekly-review-last-run.json` は skill Phase 4 がローカルで書き込むファイルだが、**cloud routine は使い捨てクローンで動くため書き込んでも破棄される**。routine 移行後もこのファイルは永久に更新されず、旧実装のままでは staleness reminder が毎セッション発火し続ける (2026-08-04 の手動実行で実観測。§ 検証記録)。 + +したがって reminder の意味自体を変える: + +- 「前回実行から N 日経過 → `/weekly-review` を実行せよ」(routine 移行後は嘘になる) を廃す。 +- 「routine の稼働と、その結果の取り込みを確認する時期です」へ改め、**この reminder はローカル state しか見ておらず cloud routine の実行を観測できない**ことを文言に明記する。 +- 閾値は週次サイクル (7 日) ではなく監査サイクル (既定 30 日) に合わせる。routine が正常でも発火するため、短い閾値は必ずノイズになる。 +- `.failed` marker 検出経路は**ローカル実行の失敗**を見るものなので従来どおり維持する。 + +### 3. routine run の成功判定は transcript を読むことでのみ行う + +routine run の緑ステータスは「インフラエラーなし」の意味でタスク成功を意味しない (§ 検証済みの外部事実)。したがって routine プロンプトに「各ステップの exit code を報告する」「失敗した場合は成功を装わず、どこで止まったかを報告する」を明示的に含める。 + +## 「2. 検証済みの前提事実」の永続化 (ハーネス改善計画からの移管) + +計画書 § 2 が「WP-17〜19 の ADR 起票時に最新値へ再確認したうえで永続化する」と定めていた routines 関連事実を以下へ移す。research preview のため仕様変動があり得る。 + +| 事実 | 出所 / 状態 | +|---|---| +| cloud routines は Anthropic 管理インフラで実行され、使用量は Max 枠を消費する | 計画書 2026-07-04 調査。**2026-08-04 の手動実行で挙動は矛盾なし** (7m18s の 6 facet 並列実行が完走) | +| アカウント毎の 1 日あたり run 数上限がある。**one-off run は daily cap の対象外** | 計画書 2026-07-04 調査。今回の検証は one-off run を使用 | +| GitHub トリガーは Claude GitHub App の webhook 経由で、**GitHub Actions の分数を消費しない**。webhook には per-routine / per-account の時間あたり上限あり (超過分は破棄) | 計画書 2026-07-04 調査。**本 ADR は schedule トリガーのみのため GitHub トリガー経路は未使用**。なお Claude GitHub App は本リポジトリに**インストール済み** (2026-08-04 ユーザー確認) のため、「schedule のみなら App 不要か」は本 ADR では検証していない | +| routine の作成・編集は Web UI で行う (`/schedule` はクラウドセッション内から使用不可) | 計画書 2026-07-04 調査。2026-08-04 の routine 作成でユーザーが Web UI 経由で実施 | +| **run の緑ステータスはタスク成功を意味しない** (インフラエラーなしの意味)。transcript 確認が必要 | 計画書 2026-07-04 調査。**本 ADR 決定 3 の根拠**として採用 | + +> 未再確認: daily run cap の具体値と webhook 上限の具体値は今回の検証で観測していない (one-off + schedule のみ使用のため到達せず)。GitHub トリガーを使う routine を追加する際に再確認すること。 + +## 検証記録 + +### 2026-08-04: 手動 (one-off) 実行 — routine 経路の実走確認 + +ユーザーが Web UI で routine を作成し、one-off run を実行した結果: + +| ステップ | コマンド | exit code | +|---|---|---| +| 1a | `pnpm install` | 0 | +| 1b | `bash scripts/cloud-setup.sh` | 0 | +| 2 | `pnpm exec takt -w weekly-review -t weekly-review --pipeline --skip-git` | 0 | + +- **takt workflow は完走**。6 facet を parallel 実行 → aggregate-weekly まで到達。2 iterations / 7m 18s / status: completed。途中失敗ステップなし、`.failed` marker なし。 +- run ディレクトリ `.takt/runs/20260803-164137-weekly-review/` に reports 8 ファイル (6 facet + `weekly-review.md` + `findings.json`) を全て生成。 +- findings 計 1 件 (critical 0 / high 0 / **medium 1** / low 0)。medium は `todo-preamble-drift` (todo14.md が 50KB 閾値を +36% 超過しているのに preamble が後継ファイルを未宣言)。 +- 機械観測: `.rs` 800 行超 0 件、`todo*.md` 50KB 超 2 件。 +- `cloud-setup.sh` が「Ollama: 未導入 — lint_screen / findings classification は skip (fail-open)」を警告。**本 workflow は Ollama を使わないため影響なし**。クラウド環境で Ollama 前提の機構が fail-open で degrade する設計 ([ADR-038](adr-038-local-llm-finding-classification.md) / [ADR-046](adr-046-local-llm-review-spike.md)) が意図どおり動いた実測でもある。 +- 制約遵守: コードの修正・commit・push・PR 作成はいずれも発生せず (`jj status`: working copy has no changes)。 + +**判定**: 決定 1 (分析フェーズの routine 実行) はクラウド Linux 環境で成立する。ADR-060 / ADR-063 のクラウド可搬性レイヤが weekly-review 経路でも機能することの実測を兼ねる。 + +### 同実行で確認された欠落 (決定 2 の根拠 + § 残課題の発端) + +takt を直接起動したため skill の Phase 3 / Phase 4 は走らず、以下が**未実施**だった: + +- `.claude/weekly-reviews/2026-08-03.md` への複写 (ディレクトリ自体が未作成) +- `.claude/weekly-review-last-run.json` の更新 + +ユーザー報告の「SessionStart reminder は次回も発火し続けます」が、決定 2 で述べた構造的問題の実観測である。なお**これは routine 特有ではなく、クラウドが使い捨てクローンである以上、Phase 4 をクラウドで実行しても同じ**である (書き込み先が破棄される)。 + +## 残課題 (未解決、本 ADR のスコープ外) + +### 成果物デリバリと実行主体の選択 — 本 ADR の中核の未解決問題 + +routine は findings を算出するが、その成果物は使い捨てクローン内の `.takt/runs/**` と routine run の transcript にしか存在しない。ユーザーが transcript を開いて読まなければ、7 分かけた 6 facet の分析は**そのまま消える**。「常時性を獲得した」と言えるのは分析の実行までで、**その結果が人間に届く経路は依然としてユーザーの能動的な確認に依存している**。ADR-031 の課題 (reminder が 4 週間発火し続けてもユーザーが気付かなかった) を、形を変えて再導入しかねない。 + +#### 前提: クラウド実行の価値は限定的である (冷静な評価) + +weekly-review のボトルネックは分析計算ではなく**人間の採否時間** (Phase 3/4) で、これはどの実行主体でも動かない。クラウド実行が buy するのは「セッション開始時に分析済みレポートが待っている = 数分の待ち時間短縮」に留まる。読まれていない分析の価値はゼロで、拾い上げ時に意味があるのは最新 1 回分のみ (HEAD が進んでいれば stale 化もする)。一方コストは、読まれない週も消費する Max 枠 + 配送機構の保守 + 外部依存。 + +PR 監視 (ADR-067) とは価値方程式が根本的に違う — あちらはイベント駆動で、人間の関与なしに完成品がユーザーが必ず見る場所 (PR) に届く。weekly-review は schedule 駆動で、成果が価値になるには人間の判断が必須。 + +**採用バー**: 配送ループが**追加の運用負担ほぼゼロ**で閉じるなら採用。閉じないなら**ローカル実行維持 (= routine 断念) が正解**であり、これは bounded lifetime の正規の出口である。なお計画書の受け入れ基準「PC 電源オフの週末をまたぐ」は監視が主痛点だった策定時の文言で、監視は Actions が引き受け済み。weekly-review にこの基準をどこまで課すかは再判断してよい。 + +#### 選択肢は配送方法だけでなく実行主体を含む 3 択 + +| 実行主体 | 内容 | 論点 | +|---|---|---| +| 1. cloud routine (本 ADR 決定 1) | schedule で分析、配送は下記チャネル選択 | research preview 依存。**push 認証が未検証** (下記) | +| 2. **GitHub Actions schedule workflow** | WP-17 で構築済みのバックボーンを再利用。claude-code-action + `CLAUDE_CODE_OAUTH_TOKEN` は pr-monitor (ADR-067) で稼働実績、Linux 実行は cloud-setup.sh + prebuilt バイナリ (ADR-063) を本 ADR 検証記録で実証済み、`claude/` への push は `GITHUB_TOKEN` で可能 (**ruleset 5 層目と整合**、push 可否に不確実性がない) | 全要素に稼働実績があり組み合わせのみ未検証。research preview 依存なし。観測性 (Actions タブ + run log) が高い。**なお「App 不要」は利点として数えない** — Claude GitHub App は既にインストール済み (2026-08-04) のため、routine 案にとってもインストールコストは発生しない | +| 3. ローカル維持 (ADR-031 の現状) | 分析 ~7 分をユーザーが待つだけ | **配送問題自体が存在しない**。失うのは「事前計算」の数分のみ | + +#### 配送チャネルの選択が検出問題を規定する + +- **通知を持たないチャネル (専用ブランチへ push)**: ローカル側に検出機構が必要になる。SessionStart hook は **fetch 済みの remote-tracking ref しか見えない** (fetch は push / merge フロー内でのみ発生) ため、検出は他作業の副産物に依存して遅延するか、hook にネットワークを入れる (既存設計原則違反) かの二択になる。 +- **通知を持つチャネル (GitHub Issue / PR コメント)**: GitHub 自身の通知がユーザーに届くため、**ローカル検出機構そのものが不要になる**。外部可視成果物の生成にあたるため [ADR-028](adr-028-pnpm-create-pr-gate.md) / [ADR-052](adr-052-autonomy-execution-boundary-classes.md) の自律実行境界での位置づけは要決定 (report 投稿は Phase A の分析コメントと同類で、push より制約が弱い)。 + +#### 実現可能性の未検証点 + +- **routine の push はローカル hook に阻まれる (2026-08-04 実測)**: one-off で検証したところ、`jj git push` は `jj-push-guard` プリセット ([ADR-015](adr-015-push-runner-takt-migration.md)) が無条件でブロックし `pnpm push` へ誘導する。routine は誘導先が自律レビュー / fix パイプライン全体を起動する (外部可視の副作用を伴う) と判断して実行を見送った。**認証層に到達していないため push 可否そのものは未検証**。 + - サンドボックスの remote は `http://local_proxy@127.0.0.1:/git/...` のローカル proxy 経由で、セッション内トークンが直接使われる構成ではない。`jj git fetch` は成功するため **read は通る** (jj コマンド自体は hook 対象外で、ブロックは push 系のみ)。 + - **含意**: 実行主体 1 + ブランチ配送を成立させるには、`jj git push -b claude/weekly-review-*` を docs-only 条件付きで許可する例外をローカル hook に新設する必要がある。これは [ADR-067](adr-067-phase-b-unattended-fix-push.md) (Phase B) と同クラスの「自律 push 経路の新設」であり、採用バー (追加運用負担ほぼゼロ) を明らかに超える。 + - 一方 **実行主体 2 (Actions) はこの問題が構造的に発生しない** — workflow の push はローカル hook 層を通らず、ゲートは Phase B と同じく workflow 自身のロジックが持つ。**配送先を Issue にする案も push 自体が不要**になるため同様に回避できる。 + - 副次観測: 本テストで `pnpm push` を実行しなかった判断は正しい。無人実行で自律パイプラインを起動すると PR コメント投稿・Max 枠消費・場合により auto-push という外部可視の副作用が発生する。得られる情報 (proxy が push を通すか) は、上記の含意により実行主体の判定にはもはや影響しない。 +- Actions 案 (実行主体 2) は個々の要素に稼働実績があり、未検証は組み合わせのみ。 + +#### 判定手順 + +bounded lifetime の decision trigger (b)「findings が実際に採用へ繋がったか」がこの問題の観測を兼ねる。schedule 実行が数回回った時点で transcript が読まれずに findings が流れていれば、配送ループ不成立の実証になる。 + +**2026-08-04 の push テストにより、実行主体 1 (routine) + ブランチ配送は既に劣後している** (上記のとおりローカル hook への例外新設が前提になり、採用バーを超える)。したがって (b) が不成立と判定された場合の実質的な選択は **実行主体 2 (Actions schedule) / 配送先を Issue に変更 / 断念 (ローカル維持)** の 3 つになる。判定時は (1) Actions 案の実装コスト、(2) Issue 配送の自律実行境界での位置づけ ([ADR-028](adr-028-pnpm-create-pr-gate.md) / [ADR-052](adr-052-autonomy-execution-boundary-classes.md))、(3) 断念のコスト (失うのは数分の待ち時間短縮のみ) を突き合わせる。 + +判断の入力として、決定 2 の監査リマインダーが暫定的な救済層になる (「routine の結果を取り込む時期です」と定期的に促す)。ただしこれも助言層であり、ADR-042 (ルール vs 仕組み化) の基準では決定論的な担保ではない。 + +## ADR-039 3 点セットの適用 + +| 項目 | 内容 | +|---|---| +| **Config opt-in** | routine 自体は Web UI 側の存在が opt-in。リマインダーの転換は `[session_start.weekly_review_reminder]` の既存 opt-in に相乗り (`enabled = false` で完全停止) | +| **Kill-switch** | routine の停止は Web UI で routine を無効化 / 削除。リマインダーは `enabled = false` | +| **Bounded lifetime** | decision trigger: **schedule 実行が 3〜5 回走った時点で、(a) 毎回 takt が完走するか、(b) findings が実際に採用へ繋がったか (= 成果物デリバリが機能しているか)、(c) Max 枠消費が許容範囲か、を確認して本採用 / 改訂 / 却下を判断**する。**2026-11-04 までに判定材料が集まらなければ、routine の実行頻度に照らして延長 / 却下を決める**。(b) が満たされない場合は § 残課題の判定手順に従い、実行主体 (routine / Actions schedule / ローカル維持) と配送チャネルを再決定する — **routine 断念 = ローカル維持は正規の出口**であり、その場合は本 ADR を却下ステータスへ更新し decision 2 のリマインダーを staleness 検知 (7 日) に戻す | + +## 帰結 + +### 利点 + +- 週次レビューの分析が**ユーザーのセッション開始に依存しなくなる**。PC 電源オフの週末をまたいでも実行される (WP-17 受け入れ基準)。 +- 7 分の 6 facet 並列分析がローカルセッションを占有しなくなる。 +- クラウド Linux 環境での実走が ADR-060 / ADR-063 の dogfood 機会を兼ねる。 + +### 欠点 / 留意点 + +- **成果物が人間に届く保証がない** (§ 残課題)。本 ADR の最大の弱点。 +- routine は Phase 1-2 のみで、Phase 3-4 (採否と反映) は依然ローカル作業。「移行」と呼ぶが置き換えではない。 +- ローカルの `weekly-review-last-run.json` は routine 実行では更新されないため、**ローカル実行の記録**としてのみ意味を持つ値になる。リマインダーの文言でこの非対称を明示する。 +- research preview のため routines の仕様変動リスクがある。 + +## 関連 + +- [ADR-031](adr-031-weekly-review-pipeline.md) — weekly-review パイプライン本体。本 ADR は起動トリガーのみを変更する +- [ADR-018](adr-018-pr-monitor-takt-migration.md) 追記 (2026-08-03) — 同じ「ローカルの時限機構を常設インフラへ移す」判断の先行例 +- [ADR-060](adr-060-cloud-harness-sessionstart-dispatcher.md) / [ADR-063](adr-063-linux-portability-release-binaries.md) — クラウド実行の可搬性レイヤ。本 ADR の実測はその dogfood を兼ねる +- [ADR-028](adr-028-pnpm-create-pr-gate.md) / [ADR-052](adr-052-autonomy-execution-boundary-classes.md) — § 残課題の案 A/B を判断する際の境界基準 +- [ADR-039](adr-039-experimental-feature-standard-pattern.md) — 試験運用標準パターン diff --git a/docs/harness-improvement-plan.md b/docs/harness-improvement-plan.md index d81a5c9d..70db026b 100644 --- a/docs/harness-improvement-plan.md +++ b/docs/harness-improvement-plan.md @@ -76,7 +76,7 @@ Anthropic 公式のハーネスエンジニアリング指針(決定論的基 | WP-14 | 3 | PowerShell 3 本の Rust 化 | S-M ×2 | なし | 完了(新規 ADR 不要判断 = 決定は各 crate doc + commit message に記録。実走確認済) | | WP-15 | 3 | Linux バイナリビルド + クラウド setup script | M | WP-13, 14 | 完了([ADR-063](adr/adr-063-linux-portability-release-binaries.md)。クラウド実測は [ADR-060](adr/adr-060-cloud-harness-sessionstart-dispatcher.md) dogfood で達成、以降は ADR-060 の bounded lifetime で管理。追補の陽性証拠設計は [ADR-064](adr/adr-064-monitor-success-positive-evidence.md) → park 実観測は § 残作業) | | WP-16 | 3 | CI matrix(移植退行防止) | S | WP-13, 14 | 観測中([ADR-065](adr/adr-065-ci-matrix-cross-os-regression.md)。2 OS matrix は PR #342 でマージ済・master 稼働中、初回観測期間に実バグ 1 件捕捉(PR #344 で修正)。観測継続と required check 化は → § 残作業) | -| WP-17 | 4 | イベント駆動バックボーン完成(Phase B + routines 移行 + 全体 kill-switch 前倒し) | M-L | WP-09, 10, 11(2026-08-02 充足確認済) | 着手中(PR 1 = [ADR-066](adr/adr-066-autonomy-global-kill-switch.md) #347 マージ済。PR 2 は incident を経て再分割 2a/2b/2c で実施 → § WP-17 PR 2。事前整備 [ADR-068](adr/adr-068-fix-step-authority-boundary.md) #348 / [ADR-069](adr/adr-069-pr-chain-declaration.md) #349 マージ済) | +| WP-17 | 4 | イベント駆動バックボーン完成(Phase B + routines 移行 + 全体 kill-switch 前倒し) | M-L | WP-09, 10, 11(2026-08-02 充足確認済) | 観測中(PR 1 #347 / 2a #350 / 2b #351 / 2c #352 / 3 #353 マージ済、PR 4 = [ADR-070](adr/adr-070-weekly-review-cloud-routine.md) 実施中。スモーク段 0/0.5/1 完了、段 2(allow 経路)が残。事前整備 [ADR-068](adr/adr-068-fix-step-authority-boundary.md) #348 / [ADR-069](adr/adr-069-pr-chain-declaration.md) #349 マージ済) | | WP-18 | 4 | 夜間 todo 消化ループ | M-L | WP-15, 17 | 未着手 | | WP-19 | 4 | 常時性ガード(自主減速 / 監査ループ。全体 kill-switch は WP-17 PR 1 へ前倒し) | S-M | WP-18 | 未着手 | @@ -115,7 +115,7 @@ Anthropic 公式のハーネスエンジニアリング指針(決定論的基 - **着手前決定(2026-08-02、ユーザー確認済み)**: 1. WP-19 ステップ 1(全体 kill-switch)を本 WP の PR 1 へ前倒し統合する。根拠: ADR-052 原則 5 は「config opt-in と kill-switch の両方が接続され機能していること」を自動実行可クラス有効化の前提条件とするため、kill-switch 無しに Phase B へ着手できない(本計画書の依存欄と ADR-052 契約の食い違いを解消)。WP-19 の残り(自主減速・監査ループ)は WP-18 後のまま。 2. ADR-064 検証残は PR 3 の wakeup 廃止に伴い移し替える: (a) park 実観測は機構ごと消えるため moot として閉じ、(b) レポート判定文の保留保証は GitHub Actions 経路の検証残として引き継ぐ。ADR-064 ステータス欄と ADR-018 amendment の両方に記録し、検証の穴を残さない。 - 3. Claude GitHub App は未インストール。ユーザーがインストールする方針(確認済み)。routine 移行(PR 4)はユーザーの Web UI 作業とセットのため最後に回す。 + 3. Claude GitHub App は未インストール(2026-08-02 時点)。ユーザーがインストールする方針(確認済み)。routine 移行(PR 4)はユーザーの Web UI 作業とセットのため最後に回す。→ **2026-08-04 更新: インストール済み(ユーザー確認)**。PR 4 の routine 作成・one-off 実行も完了(§ WP-17 PR 4)。 - **PR 分割**: 1 WP = 原則 1 PR からの明示的逸脱(kill-switch 前倒しにより 1 PR に収まらない)。PR 1 → 2 → 3 → 4 の順で依存する。 #### WP-17 PR 1: 全体 kill-switch(WP-19 ステップ 1 前倒し分) — 完了(PR #347、2026-08-02 マージ) @@ -213,16 +213,15 @@ jj log -r 'ylkowqkp | unksnyts | mxzwmsyp | lwpktvpm | lqxzpvuw | utpvkwql | rxv - 実装メモ(本 PR で確定した設計判断): 時刻窓アンカーの state 継続(`should_continue_state` = 同一 PR + 同一 head なら `started_at` / `fix_push_time` を維持)は park の付随物ではないため**残した**。落とすと手動再実行のたびに `--push-time` が「今」へリセットされ、push 後に届いた CR コメントが新着判定から漏れる。rate-limit の retry 上限 / comment dedup も同様に維持。 - 本 PR の PR がそのまま**スモーク段 1 の観測対象**を兼ねる(variable 再設定済みの状態で、非 `claude/` PR に対する fix job の prefix deny をマージ済み master 版 workflow で確認する)。 -#### WP-17 PR 4: weekly-review の cloud routine 移行(旧ステップ 2) +#### WP-17 PR 4: weekly-review の cloud routine 移行(旧ステップ 2) — 実施中(本 PR) -- **ユーザー作業(先行必須、Claude からは実行不可)**: - 1. Claude GitHub App を本リポジトリにインストール(github.com/apps/claude)。 - 2. claude.ai/code/routines の Web UI で routine を作成(schedule トリガー、週 1)。`/schedule` はクラウドセッション内から使用不可(「2. 検証済みの前提事実」参照)。 -- **Claude 側作業**: - 1. routine 用プロンプト草案の作成(weekly-review 相当の起動手順 + 結果確認手順。routine run の緑ステータスはタスク成功を意味しないため transcript 確認を含める)。 - 2. hooks-session-start の weekly_review staleness リマインダーをバックストップへ格下げ(主経路 = routine、リマインダー = routine 失敗時の救済である旨へ文言・閾値を調整)。 - 3. ADR 起票。起票時に「2. 検証済みの前提事実」の routines 事実(daily run cap / webhook 上限 / 緑ステータスの意味)を最新値へ再確認し永続化する(同節冒頭の必須要件)。 -- 留意: routine はクラウド(Linux)実行のため ADR-060(dogfood 3/5 回、判定期限 2026-09-30)/ ADR-063 の枠内で動き、その dogfood 機会を兼ねる。 +決定・検証記録は [ADR-070](adr/adr-070-weekly-review-cloud-routine.md) へ移管済み。以下は状態と残作業のみ。 + +- **ユーザー作業**: routine 作成(schedule、週 1)+ one-off 手動実行 — **完了(2026-08-04)**。Claude GitHub App は**本リポジトリにインストール済み**(2026-08-04 ユーザー確認)。したがって「schedule トリガーのみなら App 不要か」は**本 WP では未検証**(インストール済みの状態でしか観測していないため、不要であることを主張できない)。 +- **Claude 側作業**: routine プロンプト(ADR-070 に記載)/ リマインダーの監査リマインダー化 / ADR-070 起票 / routines の SaaS 事実の永続化 — **本 PR で完了**。 +- **実測(ADR-070 § 検証記録)**: one-off run で `pnpm install` → `cloud-setup.sh` → takt weekly-review が全て exit 0、6 facet 並列で 7m18s 完走、findings 1 件(medium)。クラウド Linux 実行が成立することを確認(ADR-060 / ADR-063 の dogfood を兼ねる)。 +- **移行で判明した構造的制約**: weekly-review は 4 フェーズで、routine が担えるのは Phase 1-2(分析)のみ。Phase 3(採否判断)は人間の判断が本質、Phase 4(task list 反映 + last-run 更新)はそれに従属する。**routine は skill の置き換えではなく分析フェーズの前倒し**。あわせて `weekly-review-last-run.json` は使い捨てクローンで更新されないため、リマインダーは routine の実行を観測できない(→ 意味を監査リマインダーへ転換、閾値 7 → 30 日)。 +- **未解決の残課題(ADR-070 § 残課題)**: routine の分析結果が transcript にしか残らず、ユーザーが読まなければ消える。選択は配送方法だけでなく**実行主体を含む 3 択**(routine / **GitHub Actions schedule**(WP-17 バックボーン再利用、research preview 非依存、push 可否に不確実性なし)/ ローカル維持 = routine 断念)で、配送先は**通知を持つチャネル(Issue 等)ならローカル検出機構が不要**になる。判定は ADR-070 bounded lifetime (b) の観測後に行い、**断念も正規の出口**。未検証点: routine の push 認証(App インストール済みの状態で push できるかを one-off 1 回で検証する。App の要否そのものは切り分けない — 既存連携を壊す価値がないため)。 #### WP-17 受け入れ基準 diff --git a/src/hooks-session-start/src/weekly_review.rs b/src/hooks-session-start/src/weekly_review.rs index 5ca97465..33b84b27 100644 --- a/src/hooks-session-start/src/weekly_review.rs +++ b/src/hooks-session-start/src/weekly_review.rs @@ -1,11 +1,21 @@ -//! ADR-031 Phase C: `/weekly-review` skill 起動 reminder。 +//! ADR-031 Phase C / ADR-070: weekly-review の**監査リマインダー** (バックストップ)。 +//! +//! ADR-070 で分析の主経路が cloud routine (週 1 schedule) へ移ったため、本 module の役割は +//! 「レビューを実行せよ」から「**routine の稼働と結果の取り込みを確認せよ**」へ転換した。 +//! +//! **重要な非対称**: `.claude/weekly-review-last-run.json` は skill Phase 4 が**ローカル実行時**に +//! のみ書き込む。cloud routine は使い捨てクローンで動くため書き込んでも破棄され、この値は +//! routine 実行では更新されない。したがって本 reminder は **cloud routine の実行を観測できない** — +//! 発火は「routine が止まっている」の証拠ではなく、定期的な監査を促す助言に過ぎない。 +//! threshold も週次サイクル (7 日) ではなく監査サイクル (既定 30 日) に合わせてある。 //! //! 2 種類の reminder を発火: -//! - last-run staleness: `.claude/weekly-review-last-run.json` の `last_run_at` が -//! `reminder_threshold_days` を超えていれば「`/weekly-review` の実行を検討」を nudge。 -//! `last_run_at` が欠落/不正な旧・破損データは stale 扱い (= 発火) にする。 +//! - last-run staleness: 上記 `last_run_at` が `reminder_threshold_days` を超えていれば +//! 「routine の稼働確認と結果取り込み」を nudge。`last_run_at` が欠落/不正な旧・破損データは +//! stale 扱い (= 発火) にする。 //! - failed marker: `.claude/weekly-reviews/*.md.failed` が 1 件以上存在すれば -//! 「前回 weekly-review が失敗、`/weekly-review` で resume」を nudge +//! 「前回**ローカル**実行が失敗、`/weekly-review` で resume」を nudge (これは routine ではなく +//! ローカル実行の失敗を見るため、従来どおりの意味を保つ) //! //! staleness の情報源を mtime にしない (欠落時も mtime にフォールバックしない) のは、状態ファイルが //! jj checkout / workspace materialization (ADR-045) のたびに再マテリアライズされ mtime が @@ -29,8 +39,13 @@ use crate::hooks_config::WeeklyReviewReminderConfig; use crate::past_time::PastTime; use crate::reaper::parse_iso8601_to_unix; -/// weekly review reminder の threshold (default 7 日、ADR-031 § トリガー方式 と整合)。 -const WEEKLY_REVIEW_DEFAULT_THRESHOLD_DAYS: u64 = 7; +/// weekly review reminder の threshold (default 30 日)。 +/// +/// ADR-070 で「週次サイクル (7 日)」から「監査サイクル (30 日)」へ変更した。分析の主経路が +/// cloud routine (週 1 schedule) へ移り、本 reminder は routine の稼働と結果取り込みを促す +/// **バックストップ**になったため。7 日のままだと、routine が正常に動いていても +/// `last_run_at` (ローカル実行でのみ更新) が古いまま毎週発火し、必ずノイズになる。 +const WEEKLY_REVIEW_DEFAULT_THRESHOLD_DAYS: u64 = 30; pub(crate) const WEEKLY_REVIEW_LAST_RUN_PATH: &str = ".claude/weekly-review-last-run.json"; const WEEKLY_REVIEW_REVIEWS_DIR: &str = ".claude/weekly-reviews"; @@ -155,8 +170,17 @@ fn build_weekly_review_staleness_lines( vec![ "[WEEKLY_REVIEW_REMINDER]".to_string(), format!( - "週次プロジェクト全体レビュー (ADR-031) が threshold ({} 日) を超えました (前回からの経過: {})。\n\ - 推奨: `/weekly-review` skill を起動して whole-tree レビューを実施 (push-runner / post-PR / post-merge の 3 パイプラインが見ない累積複雑度・横断的 ADR 整合性・ハーネス遵守 観点を補完)", + "weekly-review の**ローカル**実行記録が threshold ({} 日) を超えました (前回のローカル実行からの経過: {})。\n\ + \n\ + 注意: 分析の主経路は cloud routine (週 1 schedule、ADR-070) に移っており、\ + **本 reminder は cloud routine の実行を観測できません** (routine は使い捨てクローンで動くため \ + `weekly-review-last-run.json` を更新しない)。したがってこれは「routine が動いていない」の証拠では**なく**、\ + 定期的な監査を促すバックストップです。\n\ + \n\ + 推奨アクション:\n\ + 1. claude.ai/code/routines で weekly-review routine が予定どおり実行されているか確認する\n\ + 2. 直近 run の transcript を開き、findings の採否と task list への反映 (ADR-031 Phase 3 / Phase 4) が未処理なら取り込む\n\ + 3. routine が動いていない / 結果を取り込みたい場合は `/weekly-review` skill をローカルで起動する", threshold_days, elapsed_label, ), ] @@ -188,6 +212,11 @@ pub(crate) struct WeeklyReviewNudge { /// staleness も failed marker も無ければ `None` (additionalContext の発火条件と一致)。 /// 表示ノイズを抑えるため 1 行に限定する (単一行不変条件は `SingleLineMessage` が構造的に保証し、 /// `\n` / `\r` が混じっても構築時にサニタイズされる)。詳細は additionalContext に寄せる。 +/// +/// ADR-070 以降、分析の主経路は cloud routine (週 1 schedule)。本 message はその**稼働確認を +/// 促す監査リマインダー**であり、「routine が止まっている」の断定ではない — ローカル state +/// (`weekly-review-last-run.json`) は routine が使い捨てクローンで動くため更新されず、 +/// routine の実行を観測できないため。文言も「前回**ローカル**実行から」と限定する。 fn build_weekly_review_system_message( state: &WeeklyLastRunState, threshold_days: u64, @@ -200,20 +229,20 @@ fn build_weekly_review_system_message( let mut parts: Vec = Vec::new(); if staleness { let elapsed = match state { - WeeklyLastRunState::ElapsedDays(d) => format!("前回実行から {} 日経過", d), - WeeklyLastRunState::Missing => "実行記録なし".to_string(), - _ => "前回実行の記録が不正/欠落".to_string(), + WeeklyLastRunState::ElapsedDays(d) => format!("前回ローカル実行から {} 日経過", d), + WeeklyLastRunState::Missing => "ローカル実行の記録なし".to_string(), + _ => "ローカル実行の記録が不正/欠落".to_string(), }; parts.push(format!("{} (threshold {} 日)", elapsed, threshold_days)); } if failed_marker_count > 0 { parts.push(format!( - "前回実行が失敗 (.failed marker {} 件)", + "前回ローカル実行が失敗 (.failed marker {} 件)", failed_marker_count )); } Some(SingleLineMessage::new(format!( - "週次レビュー: {}。`/weekly-review` の実行を検討してください", + "週次レビュー監査: {}。routine の稼働と結果の取り込みを確認してください (claude.ai/code/routines)", parts.join("、") ))) } @@ -592,8 +621,18 @@ mod tests { let msg = nudge .system_message .expect("system_message_enabled = true なので systemMessage が付く"); - assert!(msg.as_str().contains("週次レビュー")); - assert!(msg.as_str().contains("実行記録なし")); + assert!(msg.as_str().contains("週次レビュー監査")); + assert!( + msg.as_str().contains("ローカル実行の記録なし"), + "ADR-070: staleness は**ローカル**実行の記録に限定した表現であること (cloud routine の\ + 実行は観測できないため「未実行」と断定しない): {}", + msg + ); + assert!( + msg.as_str().contains("routine"), + "監査対象が routine であることを示すこと: {}", + msg + ); let _ = std::fs::remove_dir_all(&root); } @@ -663,6 +702,59 @@ mod tests { let _ = std::fs::remove_dir_all(&root); } + /// ADR-070: 既定 threshold は監査サイクル (30 日)。週次サイクル (7 日) のままだと、 + /// cloud routine が正常に動いていてもローカルの `last_run_at` が更新されないため + /// 毎週発火して必ずノイズになる。 + #[test] + fn default_threshold_is_audit_cycle_not_weekly() { + assert_eq!( + WEEKLY_REVIEW_DEFAULT_THRESHOLD_DAYS, 30, + "routine 移行後の既定は監査サイクル (30 日)" + ); + assert!( + !weekly_review_staleness_hits(&WeeklyLastRunState::ElapsedDays(10), WEEKLY_REVIEW_DEFAULT_THRESHOLD_DAYS), + "routine が週次で回っていれば 10 日程度のローカル未実行では発火しないこと" + ); + assert!(weekly_review_staleness_hits( + &WeeklyLastRunState::ElapsedDays(31), + WEEKLY_REVIEW_DEFAULT_THRESHOLD_DAYS + )); + } + + /// ADR-070: additionalContext は「本 reminder が cloud routine を観測できない」ことと、 + /// routine の稼働確認を第一アクションとすることを明示する。これが無いと、routine が + /// 正常でも「レビュー未実施」と読める旧文言に戻り、ユーザーを誤誘導する。 + #[test] + fn additional_context_states_routine_is_unobservable_and_primary() { + let root = unique_temp_root("routine-framing"); + std::fs::create_dir_all(&root).unwrap(); + let config = WeeklyReviewReminderConfig { + enabled: Some(true), + reminder_threshold_days: Some(30), + failed_marker_check_enabled: Some(false), + system_message_enabled: Some(false), + }; + let nudge = compute_weekly_review_reminder_nudge(&root, &config, 2_000_000_000) + .expect("nudge fires"); + let ctx = &nudge.additional_context; + assert!( + ctx.contains("cloud routine の実行を観測できません"), + "観測不能であることの明示が必要 (誤誘導防止): {}", + ctx + ); + assert!( + ctx.contains("claude.ai/code/routines"), + "稼働確認先の導線が必要: {}", + ctx + ); + assert!( + ctx.contains("ローカル"), + "staleness がローカル実行に限定された指標であることを示すこと: {}", + ctx + ); + let _ = std::fs::remove_dir_all(&root); + } + #[test] fn build_weekly_review_system_message_none_when_fresh_and_no_marker() { assert!( @@ -674,7 +766,7 @@ mod tests { fn build_weekly_review_system_message_combines_staleness_and_marker() { let msg = build_weekly_review_system_message(&WeeklyLastRunState::Missing, 7, 2) .expect("staleness or marker があれば Some"); - assert!(msg.as_str().contains("実行記録なし")); + assert!(msg.as_str().contains("ローカル実行の記録なし")); assert!(msg.as_str().contains("失敗")); assert!(msg.as_str().contains("2 件")); }