diff --git a/CLAUDE.md b/CLAUDE.md index d5fafda6..59b0eb3d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -31,6 +31,7 @@ - [ADR-027: Push-time review を simplicity に限定し architectural review は post-PR に委ねる](docs/adr/adr-027-push-review-simplicity-focus.md) - [ADR-028: 外部可視成果物の生成コマンド (PR 作成/マージ) の実行ゲート](docs/adr/adr-028-pnpm-create-pr-gate.md) - [ADR-029: Post-Merge Feedback の自動起動 — pending file + 現セッション起動](docs/adr/adr-029-post-merge-feedback-auto-trigger.md) *(試験運用)* +- [ADR-030: 決定論的 Post-Merge Feedback — takt 経由の同期実行 + 失敗マーカーによる recovery](docs/adr/adr-030-deterministic-post-merge-feedback.md) *(試験運用 / Supersedes ADR-014 full, ADR-029 partial)* ## Build diff --git a/docs/adr/adr-030-deterministic-post-merge-feedback.md b/docs/adr/adr-030-deterministic-post-merge-feedback.md new file mode 100644 index 00000000..3d608f75 --- /dev/null +++ b/docs/adr/adr-030-deterministic-post-merge-feedback.md @@ -0,0 +1,244 @@ +# ADR-030: 決定論的 Post-Merge Feedback — takt 経由の同期実行 + 失敗マーカーによる recovery + +## ステータス + +試験運用 (2026-04-25) + +- **Supersedes ADR-014**: full — `/post-merge-feedback` skill 自体を廃止し、takt workflow に置き換える +- **Supersedes ADR-029**: partial — 層 3-4 (Claude session / skill 起動) を廃止。層 1 の `[[merge_pipeline.post_steps]]` `type = "ai"` スロットは流用するが、出力先を pending file から takt workflow 起動 + report file に変更する + +## コンテキスト + +### 問題: ADR-029 で実証された silent loss + +ADR-029 は `cli-merge-pipeline` → pending file → Stop hook → Claude が next turn で `additionalContext` を読む → skill 起動、という 4 層トリガー機構を採用した。PR #74 マージ後の dogfood で、この機構の **後半 2 層が非決定論的** であることが実証された。 + +| 層 | 機構 | 決定論性 | PR #74 で何が起きたか | +|---|------|---------|---------------------| +| 1 | `cli-merge-pipeline` が pending file を書き込む | ✅ 決定論的 | 正常動作 (`status=pending`) | +| 2 | Stop hook が pending を読み `additionalContext` を出力 | ✅ 決定論的 | 正常動作 (`status=dispatched`) | +| 3 | Claude が次ターンで `additionalContext` を読む | ❌ **非決定論的** | ユーザー入力先行で次ターン消失 | +| 4 | Claude が skill 起動命令と解釈・実行 | ❌ **非決定論的** | 層 3 で詰まったため未到達 | + +層 3 はセッションライフサイクル依存で、ユーザー入力 / VSCode 終了 / Claude Code 再起動で容易に壊れる。層 4 は skill design philosophy が ask-based — `AskUserQuestion` で中止可能、明示命令も無視可能。**must-run 要件に skill を主動線で使うのは設計ミス** と判定する。 + +PR #74 マージ後は pending file が `dispatched` で stuck した状態で session が終了 → 24h 後に stale 削除 → **フィードバック silent loss** という最悪の経路が再現された。 + +### 設計上の知見: skill 機構は本質的に "ask-based" + +ADR-014 の選択肢 3 (skill による明示呼び出し) は「セッション知見へアクセスできる」点で優れていたが、`/post-merge-feedback` を **必ず** 走らせるための強制力がない。ADR-029 はこのギャップを Stop hook + state file で埋めようとしたが、最終層がやはり Claude の判断 (skill 命令を解釈して実行する) に依存しており、決定論性を担保できなかった。 + +skill の哲学 (`AskUserQuestion` で中断可能、ユーザー優先) と must-run 要件は **構造的に両立しない**。決定論的な実行を要求するなら、Claude のターン取得や skill 実行に頼らない経路が必要。 + +### 既存の決定論パターン: takt 経由の同期実行 + +[ADR-015](adr-015-push-runner-takt-migration.md) (push-runner) と [ADR-018](adr-018-pr-monitor-takt-migration.md) (cli-pr-monitor) で確立済みのパターン: + +> 機械的ステップは Rust exe で、AI ステップは takt workflow で同期実行する + +このパターンは Claude Code session のライフサイクルに依存しないため、決定論的に AI 処理を走らせることができる。Stage 2 の AI レビュー / fix loop / supervise が `takt-test-vc ADR-0003` の知見通り 97-99% のクリーンパス削減を達成しており、本プロジェクトの先行 2 例で実証済み。 + +ADR-030 は **同じパターンを post-merge-feedback に適用する 3 例目** として位置付ける。 + +## 検討した選択肢 + +### 選択肢 A: ADR-029 を維持 (現状) + +pending file + Stop hook + skill。silent loss が再発するため **却下**。 + +### 選択肢 B: ADR-029 を維持 + skill 強制起動 (Anthropic API 直接呼び出し) + +`cli-merge-pipeline` が `claude -p "/post-merge-feedback"` を spawn する案。ADR-014 の選択肢 1 と同じ欠点 (新規 session ゆえセッション知見が失われる) が再発する。本プロジェクトの先行 ADR-015 / 018 が確立した「AI 処理は takt 経由」原則とも乖離する。**却下**。 + +### 選択肢 C: takt workflow + 失敗マーカー + UserPromptSubmit recovery (採用) + +`cli-merge-pipeline` が takt workflow を **同期実行** する。失敗時は `.md.failed` marker を残し、`UserPromptSubmit` hook が後続 prompt 入力時に検出して再実行を促す。 + +- L1 (takt 経由実行) は決定論的: takt は Rust 実装で session lifecycle 非依存 +- session 知見は **transcript 抽出** で取り戻す: `~/.claude/projects//*.jsonl` を commit 時刻で range filter (Phase 0 で実証済み) +- L2 (UserPromptSubmit hook) は best-effort だが、L1 が既に成功している場合の話なので silent loss にはつながらない +- **採用** + +### 選択肢 D: 旧 skill enrichment 層 (L3) を残す + +旧計画の L3 として「Claude がレポートを読んで対話的に enrichment」する skill 層を追加する案。ask-based の弱点を再導入してしまう (skill 哲学と must-run 要件の構造的不整合) ため **却下**。L1 の `aggregate-feedback` facet 内で必要な対話的判断は完結させる。 + +## 決定 + +**選択肢 C を採用する。** + +### アーキテクチャ: 2 層構成 + +| 層 | 機構 | 保証レベル | 失敗時 | +|---|------|-----------|--------| +| **L1 Floor** (決定論) | `cli-merge-pipeline` → takt workflow `post-merge-feedback` を **同期実行** | Deterministic invocation: 成功 → at-most-once でレポート生成、失敗 → `.failed` marker で retryable | soft: merge 成功扱い、`.claude/feedback-reports/.md.failed` marker 残存 | +| **L2 Recovery** (safety net) | `hooks-user-prompt-feedback-recovery` が `*.md.failed` を検出 → `additionalContext` で再実行指示 | At-least-once (ユーザーが何か入力すれば必ず発火) | hook 自体は決定論的、Claude の応答は best-effort (ただし floor は既存) | + +### 全体フロー + +```text +pnpm merge-pr (cli-merge-pipeline, ADR-013) + ├─ ... (マージ本体 + ローカル同期) + ├─ post_steps: type="ai" 分岐 + │ ├─ takt workflow `post-merge-feedback` を同期 spawn + │ ├─ 成功: .claude/feedback-reports/.md 生成 + │ └─ 失敗: .claude/feedback-reports/.md.failed marker (soft fail) + └─ exit 0 + │ + ▼ (任意のタイミングで Claude session が走るとき) +UserPromptSubmit hook (hooks-user-prompt-feedback-recovery, 新規) + ├─ .claude/feedback-reports/*.md.failed を glob 検索 + ├─ 不在: silent exit + └─ 存在: additionalContext で「未完了 feedback あり、再実行: pnpm feedback-retry 」 +``` + +### takt workflow 構成 (4 facets) + +[ADR-020](adr-020-takt-facets-sharing.md) の facets 共通化原則に倣う。本 workflow は以下 4 facet を順次 chain する: + +| facet | 役割 | 共有/専用 | +|---|---|---| +| `analyze-pr` | PR diff + reviews を分析。`E:\work\claude-code-skills\analyze-pr\SKILL.md` から port | 専用 (新規) | +| `analyze-session` | transcript range filter で抽出した user/assistant 履歴から実装時の学び・トラブル修正・ユーザー指示を抽出 | 専用 (新規) | +| `analyze-prepush-reports` | `.takt/runs//reports/*.md` (pre-push-review の simplicity / security レポート) を集約 | 専用 (新規) | +| `aggregate-feedback` | 上記 3 facets の出力を [Plankton 優先度](adr-014-post-merge-feedback.md#plankton-優先度テーブル) で統合 → ADR 提案 / 仕組み改善案を生成。旧 `/post-merge-feedback` skill の Phase 4 ロジックから port | 専用 (新規) | + +skill ベースで運用していた analyze-pr / post-merge-feedback Phase 4 のロジックは facet 化することで、takt の loop / supervise / fix 機構の上に乗せられる。fix loop 自体は本 workflow では不要 (修正対象がコードではなくレポート生成) のため、シンプルな chain 構造になる。 + +### 入力源 + +| 入力源 | 取得方法 | 用途 | +|---|---|---| +| PR data | `gh pr view --json ...` + `gh api .../pulls//comments` + `.../pulls//reviews` | `analyze-pr` | +| transcript | `~/.claude/projects//*.jsonl` を commit 時刻で range filter | `analyze-session` | +| pre-push reports | `.takt/runs//reports/*.md` | `analyze-prepush-reports` | + +### 出力 + +- 成功: `.claude/feedback-reports/.md` (Markdown レポート、ADR 提案 / 仕組み改善案を含む) +- 失敗: `.claude/feedback-reports/.md.failed` marker (内容は失敗理由 + 復旧手順) + +両方とも repository には含めない (`.gitignore` で除外、内部 artifact)。 + +### transcript 抽出戦略 (Phase 0 調査結果反映) + +```text +入力: +1. gh pr view --json commits,mergedAt → first_commit_time, end_time 取得 +2. ~/.claude/projects//*.jsonl の全ファイルを mtime ∈ [first_commit_time, end_time + 1day buffer] で粗フィルタ +3. 該当 file 内で entry.timestamp ∈ [first_commit_time, end_time] かつ type ∈ {user, assistant} を抽出 +4. 合成 in-memory log を analyze-session facet に渡す +``` + +#### transcript の制約 (Phase 0 で確認済) + +| 観察 | 影響 | +|---|---| +| `timestamp` は ISO 8601 ms 精度 | commit 時刻からの逆引き filter が容易 | +| `thinking` content は encrypted (`signature` のみ可視、`thinking` field は空) | chain-of-thought は抽出不可。user/assistant text + tool calls/outputs で十分 | +| `gitBranch` は `HEAD` 固定 (jj detached state のため) | branch 名 filter は **使えない**。**時刻 range で filter する必要がある** | +| 1.7 MB / 621 行 (現セッション例) | takt context window 圧迫の可能性。filter 後の絞り込みが必須 | +| `type: queue-operation` はノイズ | parsing で skip すべき | + +具体的なファイル所在: `~/.claude/projects//.jsonl` (1 session = 1 file、UUID 命名)。本プロジェクトでは `%USERPROFILE%\.claude\projects\e--work-claude-code-hook-test\` 配下。 + +### 失敗ポリシー: soft + +`takt` 失敗時の挙動: + +- merge は **成功扱い** で進める (PR は既にマージ済みなので巻き戻せない) +- `.claude/feedback-reports/.md.failed` marker を残す +- L2 recovery (UserPromptSubmit hook) が後続 prompt で発火 → ユーザーが `pnpm feedback-retry ` で再実行 + +**採用根拠**: hard fail (merge を失敗扱いにする) は既にマージ済みの PR を取り消せないため不可能。retry 機構を Floor の外側に持つことで、Floor 自体は exactly-once を保証しつつ failure 時の人手介入経路を確保する。 + +### レイテンシ + +`pnpm merge-pr` の所要時間が takt workflow 実行分 (数分) 増加する。ユーザー判断 (作業計画策定時に合意) として **数分の追加レイテンシは許容**。`pnpm merge-pr` は同期実行で待つ前提とする ([ADR-016](adr-016-long-running-command-strategy.md) の長時間コマンド戦略に該当)。 + +### Supersede 範囲 + +#### ADR-014 (full supersede) + +`/post-merge-feedback` skill 自体を廃止する。理由: + +- skill 機構は ask-based で must-run 要件と構造的に不整合 (本 ADR コンテキスト参照) +- skill が担っていた Phase 1-5 (PR 特定 → analyze-pr 呼び出し → セッション振り返り → 統合 → ユーザー承認) は、takt workflow の 4 facets + L2 recovery の組み合わせで再実装される +- セッション知見へのアクセスは「skill がメイン会話内で動く」ことではなく「transcript 抽出」で達成する。Phase 0 で実現可能性を確認済 + +ADR-014 のステータスを `Superseded by ADR-030` に更新する (Phase E で実施)。 + +#### ADR-029 (partial supersede) + +- **廃止**: 層 3 (Claude が次ターンで additionalContext を読む) と層 4 (skill 起動) +- **流用**: 層 1 の `[[merge_pipeline.post_steps]]` `type = "ai"` スロット — ADR-013 で予約された拡張ポイントを引き続き使う +- **置換**: pending file 機構 (`hooks-stop-feedback-dispatch` / `lib-pending-file` / `.claude/post-merge-feedback-pending.json`) は廃止し、takt workflow 起動 + report file ベースに置き換える + +ADR-029 のステータスを `Superseded by ADR-030` に更新する (Phase E で実施)。 + +### ADR-022 (責務分離原則) との整合性 + +L1 takt 経由の決定論実行は ADR-022 の以下の原則に整合: + +- **原則 1**: 全副作用は許可側に収まる + - `.claude/feedback-reports/.md` の新規書き込み → **新規 artifact への自己記述** + - `.claude/feedback-reports/.md.failed` marker → 同上 + - `additionalContext` 出力 → 現セッション内 Claude への指示 (草案生成に類する) + - commit description / bookmark 名 / PR title/body への介入は一切なし +- **対称性の回復**: ADR-029 設計では「Claude session が必要」という非対称が残っていたが、L1 takt 経由により **Claude 不在でも動く** 対称性が回復する。これは ADR-022 の自動化原則 (人間介入が optional) と整合 + +### ADR-028 (外部可視成果物ゲート) との関係 + +本 ADR は **内部 artifact のみ生成**: + +- `.claude/feedback-reports/.md` — local 専用、`.gitignore` で除外 +- `.claude/feedback-reports/.md.failed` — 同上 + +GitHub 上に観測可能な成果物 (PR / tag / commit description) は一切生成・改変しないため、ADR-028 の `permissions.ask` ゲートの **対象外**。 + +`pnpm merge-pr` 自体は ADR-028 の対象 (PR マージは外部可視) だが、これは既存ゲートで管理済み。本 ADR で追加するのは merge **後** の post_steps のみ。 + +## 実装タスク + +詳細な実装手順は [`docs/todo.md`](../todo.md) の「マージ後フィードバック機構の決定論化」セクション Phase B-F を参照。本 ADR は仕様のみを規定する。 + +- **Phase A**: 本 ADR 起案 (PR 1) — 設計のみ +- **Phase B**: takt workflow + 4 facets — L1 Floor (PR 2) +- **Phase C**: UserPromptSubmit hook — L2 Recovery (PR 3) +- **Phase D**: 廃止 (skill enrichment 不要、本 ADR 「検討した選択肢 D」参照) +- **Phase E**: 旧機構廃止 (PR 4 — Phase B/C dogfood 数回後) +- **Phase F**: dogfood 検証 (PR 4 マージ後 / 継続観察) + +## 影響 + +### Positive + +- **silent loss 0**: L1 が takt 経由の決定論実行になるため、セッションライフサイクル非依存で feedback report が生成される +- **session 知見の維持**: transcript 抽出により skill 経由と同等の情報源にアクセス可能 +- **既存パターンの再利用**: ADR-015 / 018 で確立した「機械的 = Rust、AI = takt」原則の 3 例目として、保守者の認知負荷を増やさない +- **責務分離の明確化**: ADR-022 の原則 1 (新規 artifact への自己記述) の枠内で完結し、Claude 不在でも動く対称性を回復 + +### Negative + +- **新規 takt workflow + 4 facets の追加保守コスト**: pre-push-review / post-pr-review に続く 3 つ目の workflow となる +- **`pnpm merge-pr` の所要時間が増える**: 数分の追加レイテンシ (ユーザー合意済) +- **派生プロジェクトへのバックポート工数**: takt-test-vc など派生 repo に展開する際は workflow + facets + UserPromptSubmit hook の 3 セットを移植する必要がある (Phase F dogfood 完了後の検討事項) + +### 将来の展望 + +- **Phase F dogfood 安定後の本採用化**: ステータスを `承認済み` に更新 +- **派生プロジェクトへのバックポート**: takt-test-vc / techbook-ledger 等に同機構を展開 +- **取りこぼし時の user-side recovery**: 現状は L2 で `pnpm feedback-retry` を促すが、Plankton 化 (CLAUDE.md / hook で自動再実行) も検討可能 (YAGNI で Phase F 後) + +## References + +- [ADR-013: Merge Pipeline](adr-013-merge-pipeline.md) — `[[merge_pipeline.post_steps]]` `type = "ai"` スロットの提供元 +- [ADR-014: Post-Merge Feedback](adr-014-post-merge-feedback.md) — 本 ADR で **full supersede**。Plankton 優先度テーブルは継承 +- [ADR-015: Push Pipeline takt 移行](adr-015-push-runner-takt-migration.md) — 「機械的 = Rust、AI = takt」原則の先行事例 (1 例目) +- [ADR-016: 長時間コマンド実行戦略](adr-016-long-running-command-strategy.md) — `pnpm merge-pr` の所要時間延伸の取り扱い根拠 +- [ADR-018: cli-pr-monitor takt 移行](adr-018-pr-monitor-takt-migration.md) — 同原則の 2 例目 (本 ADR は 3 例目) +- [ADR-020: takt facets 共通化戦略](adr-020-takt-facets-sharing.md) — 4 facets 分離方針の根拠 +- [ADR-022: 自動化コンポーネントの責務分離原則](adr-022-automation-responsibility-separation.md) — L1 takt 経由は本原則に整合 +- [ADR-026: Cargo workspace](adr-026-cargo-workspace.md) — 新 crate `hooks-user-prompt-feedback-recovery` 追加手順 +- [ADR-028: pnpm create-pr ゲート](adr-028-pnpm-create-pr-gate.md) — 外部可視成果物ゲートとの軸別境界 (本 ADR の射程外) +- [ADR-029: Post-Merge Feedback の自動起動](adr-029-post-merge-feedback-auto-trigger.md) — 本 ADR で **partial supersede** (層 1 流用、層 3-4 廃止) diff --git a/docs/todo.md b/docs/todo.md index 8fdad2ac..945d1d23 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -6,13 +6,228 @@ ## 現在進行中 -### (追って) ADR-014 試験運用フラグ解除 + takt-test-vc 反映 +### マージ後フィードバック機構の決定論化 (ADR-030 起案 + 実装) -> **参照**: `docs/adr/adr-029-post-merge-feedback-auto-trigger.md` — 本タスクは ADR-029 task 1-F。本プロジェクトでの dogfood が十分に回ってから着手。 +> **動機**: PR #74 マージ後の dogfood で、ADR-029 設計の **silent loss 問題** が顕在化した。Stop hook + skill ベースの auto-trigger は Claude のターン取得次第で機能せず、決定論的実行が成立しない。skill 機構は本質的に "ask-based" であり must-run 要件には不適合という設計上の知見が得られた。 +> +> **本タスクの位置づけ**: ADR-029 を partial supersede する新 ADR-030 を起案し、takt 経由の決定論的フィードバック機構へ移行する。本タスク完了で post-merge-feedback skill / pending file / Stop hook (hooks-stop-feedback-dispatch) はすべて廃止される。 -- **やろうとしたこと**: dogfood 1-2 週間で問題なければ ADR-014 を本採用化し、takt-test-vc へバックポート -- **現在地**: 未着手。本プロジェクトで実マージ数回の観察が前提 -- **詰まっている箇所**: dogfood 結果に依存するため着手タイミングは未定 +#### 背景: ADR-029 の構造的欠陥 (PR #74 dogfood で実証) + +ADR-029 は 4 層のトリガー機構を直列に積んでいるが、後半 2 層が非決定的: + +| 層 | 機構 | 決定論性 | PR #74 で何が起きたか | +|---|------|---------|---------------------| +| 1 | cli-merge-pipeline が pending file を書き込む | ✅ 決定論的 | 正常動作 (status=pending) | +| 2 | Stop hook が pending を読み additionalContext を出力 | ✅ 決定論的 | 正常動作 (status→dispatched) | +| 3 | Claude が次ターンで additionalContext を読む | ❌ **非決定的** | ユーザー入力先行で次ターン消失 | +| 4 | Claude が skill 起動命令と解釈・実行 | ❌ **非決定的** | (3 で詰まったので未到達) | + +層 3 はセッションライフサイクル依存 (ユーザー入力 / VSCode 終了 / Claude Code 再起動 で容易に壊れる)。層 4 は skill design philosophy が ask-based (AskUserQuestion で中止可、命令無視可)。**must-run 要件に skill を主動線で使うのは設計ミスと判定**。 + +dogfood では PR #74 マージ後、pending file が `dispatched` で stuck した状態で session が終了 → 24h 後に stale 削除 → フィードバック silent loss という最悪の経路が再現可能。 + +#### 設計決定: 2 層アーキテクチャ (新 ADR-030) + +| 層 | 機構 | 保証レベル | 失敗時 | +|---|------|-----------|--------| +| **L1 Floor** (決定論) | cli-merge-pipeline → takt workflow `post-merge-feedback` を **同期実行** | Deterministic invocation: 成功 → at-most-once でレポート生成、失敗 → `.failed` marker で retryable (詳細は ADR-030 参照) | soft: merge 成功、`.md.failed` marker 残存 | +| **L2 Recovery** (safety net) | UserPromptSubmit hook が `*.md.failed` を検出 → additionalContext で再実行指示 | At-least-once (ユーザーが何か入力すれば必ず発火) | hook 自体は決定論的、Claude の応答は best-effort (ただし floor は既存なので silent loss は起きない) | + +- **失敗ポリシー**: soft (merge 成功 + marker 残存。後続 prompt 入力で L2 が拾う) +- **skill enrichment 層 (旧案 L3) は廃止** (ask-based の弱点を再導入してしまうため) +- **入力源**: PR data (gh API) + pre-push reports (`.takt/runs/`) + transcript (`~/.claude/projects//*.jsonl`、commit 時刻 range filter) +- **出力**: `.claude/feedback-reports/.md` + +#### Phase 0 調査結果 (実施済 — 2026-04-25) + +##### transcript ファイル所在 (確認済) + +`~/.claude/projects//.jsonl` (1 session = 1 file, UUID 命名) + +本プロジェクト: `%USERPROFILE%\.claude\projects\e--work-claude-code-hook-test\` + +##### transcript スキーマ (確認済) + +```json +{ + "parentUuid": "...", + "isSidechain": false, + "type": "user" | "assistant" | "attachment" | "queue-operation", + "timestamp": "2026-04-25T05:44:35.040Z", + "sessionId": "", + "cwd": "E:\\work\\claude-code-hook-test", + "gitBranch": "HEAD", + "message": { + "role": "user" | "assistant", + "content": [ + { "type": "text", "text": "..." }, + { "type": "thinking", "thinking": "", "signature": "" }, + { "type": "tool_use", "name": "Bash", "input": {...} } + ] + } +} +``` + +##### 重要な制約 (実装時に必ず参照) + +| 観察 | 影響 | +|---|---| +| timestamp は ISO 8601 ms 精度 | commit 時刻からの逆引き filter が容易 | +| `thinking` content は encrypted (`signature` のみ可視、`thinking` field は空) | chain-of-thought は抽出不可。user/assistant text + tool calls/outputs で十分 | +| `gitBranch` は `HEAD` 固定 (jj detached state のため) | branch 名 filter は **使えない**。**時刻 range で filter する必要** | +| 1.7 MB / 621 行 (現セッション例) | takt context window 圧迫の可能性。filter 後の絞り込みが必須 | +| `type: queue-operation` はノイズ | parsing で skip すべき | + +##### transcript 抽出戦略 (Q1 = commit 時刻逆引きの具体化) + +```text +入力: +1. gh pr view --json commits,mergedAt → first_commit_time, end_time 取得 +2. ~/.claude/projects//*.jsonl の全ファイルを mtime ∈ [first_commit_time, end_time + 1day buffer] でフィルタ +3. 該当 file 内で entry.timestamp ∈ [first_commit_time, end_time] かつ type ∈ {user, assistant} を抽出 (queue-operation, attachment は除外) +4. 合成 in-memory log を analyze-session facet に渡す +``` + +#### ユーザー判断記録 (本タスク策定時に合意済) + +| 質問 | 回答 | +|---|---| +| 失敗時の挙動 | **soft** (merge 成功、後続 prompt で L2 が再実行) | +| レイテンシ許容 | **数分の追加レイテンシ OK** | +| Anthropic API 直接呼出し | **禁止** (pre-push-review / post-pr-monitor と同じ takt 経由パターンに統一) | +| transcript 紐付け方針 | **PR commit 時刻から逆引きして transcript 区間を抽出** | +| takt facets 構造 | **4 facets に分離** (`analyze-pr` / `analyze-session` / `analyze-prepush-reports` / `aggregate-feedback`) | +| PR 分割 | **PR 1 (ADR) → PR 2 (B) → PR 3 (C) → PR 4 (E)** の 4 段階 | +| 旧機構廃止 | 本作業計画に **Phase E として含める** (dogfood 数回後に実施) | + +#### 作業計画 + +##### Phase B: takt workflow + 4 facets — L1 Floor (PR 2) + +- [ ] `.takt/workflows/post-merge-feedback.yaml` 新規作成 + - 入力: PR 番号 (cli-merge-pipeline から渡される) + - facets を順次 chain +- [ ] facets を新設 (`.takt/facets/` 下): + - **`analyze-pr.md`**: PR diff + reviews を分析 (既存 `analyze-pr` skill から port。`E:\work\claude-code-skills\analyze-pr\SKILL.md` を参照) + - **`analyze-session.md`** (新規): transcript range filter で抽出した user/assistant 履歴から 実装時の学び・トラブル修正・ユーザー指示 を抽出。**Phase 0 調査結果の transcript 抽出戦略を参照** + - **`analyze-prepush-reports.md`** (新規): `.takt/runs//reports/*.md` (pre-push-review の simplicity / security レポート) を集約 + - **`aggregate-feedback.md`** (新規): 上記 3 facets の出力 + Plankton 優先度で統合 → ADR 提案 / 仕組み改善案を生成 (旧 post-merge-feedback skill の Phase 4 統合フィードバックロジックを port、`E:\work\claude-code-skills\post-merge-feedback\SKILL.md` を参照) +- [ ] `src/cli-merge-pipeline/` の post_steps `type = "ai"` 分岐を変更: + - 旧: pending file 書き込み (ADR-029) + - 新: takt workflow を spawn して同期実行 (push-runner / cli-pr-monitor の takt 起動方法に倣う) + - 出力 `.claude/feedback-reports/.md` を生成 + - 失敗時 `.md.failed` marker 書き込み (soft fail、merge は成功扱い) +- [ ] `.gitignore` に `.claude/feedback-reports/` を追加 (artifact、コミット対象外) +- [ ] テスト追加 (workflow 成功/失敗ケース、transcript 抽出正常性、cli-merge-pipeline 統合) + +##### Phase C: UserPromptSubmit hook — L2 Recovery (PR 3) + +- [ ] `src/hooks-user-prompt-feedback-recovery/` 新規 crate + - `Cargo.toml` を workspace member に追加 (ADR-026) + - `src/main.rs`: + - stdin から UserPromptSubmit event JSON を読む + - `.claude/feedback-reports/*.md.failed` を検索 + - 見つかれば additionalContext で「未完了 feedback あり、再実行: `pnpm feedback-retry `」を出力 + - 見つからなければ silent exit +- [ ] `Cargo.toml` (workspace root) の `members` に追加 +- [ ] `package.json` の `build:hooks-user-prompt-feedback-recovery` 追加、`deploy:hooks` に統合 +- [ ] (任意) `pnpm feedback-retry ` script 追加 (cli-merge-pipeline の post_steps を単独再実行する thin wrapper) +- [ ] settings.local.json + `templates/settings.json` の UserPromptSubmit hook エントリ登録 +- [ ] テスト追加 (failed marker 検出、additionalContext フォーマット) + +##### Phase D: ❌ 廃止 (skill enrichment 不要) + +##### Phase E: 旧機構廃止 (PR 4 — Phase B/C dogfood 数回後) + +- [ ] post-merge-feedback skill 削除 + - `~/.claude/skills/post-merge-feedback/` 削除 + - `E:\work\claude-code-skills\post-merge-feedback\` 削除 +- [ ] hooks-stop-feedback-dispatch.exe / `src/hooks-stop-feedback-dispatch/` crate 削除 + - workspace member から外す + - `package.json` の build/deploy script 削除 + - `.claude/hooks-stop-feedback-dispatch.exe` 配布物削除 +- [ ] `src/lib-pending-file/` crate 廃止 (cli-merge-pipeline からの依存削除、`pending_file.rs` も整理) +- [ ] `.claude/hooks-config.toml` から Stop hook の `hooks-stop-feedback-dispatch` 登録を削除 (既に明示登録されていない可能性あり、要確認) +- [ ] settings.local.json + `templates/settings.json` から Stop hook 登録解除 +- [ ] `.gitignore` から `.claude/post-merge-feedback-pending.json` 行を削除 (もう使わない) +- [ ] ADR-029 のステータスを `Superseded by ADR-030` に変更 +- [ ] ADR-014 のステータスを `Superseded by ADR-030` に変更 +- [ ] memory `feedback_*.md` / `project_*.md` で post-merge-feedback skill / pending file に言及している記述を更新 +- [ ] CLAUDE.md の Architecture Decisions リストで ADR-014 / ADR-029 のステータス記載を更新 + +##### Phase F: dogfood 検証 (PR 4 マージ後 / 継続観察) + +- [ ] 3-5 回の実マージで feedback report が **必ず生成** されることを確認 (silent loss 0 を証明) +- [ ] L2 recovery を人為的失敗で発火確認 (cli-merge-pipeline で takt fail を inject、`.md.failed` marker 残存 → 次 prompt で UserPromptSubmit hook 発火 → 再実行成功) +- [ ] transcript からの session 知見抽出が想定通りか確認 (実装時の学び・トラブル・ユーザー指示が拾えるか) +- [ ] feedback report の品質評価 (ADR 提案 / 仕組み改善案が出るか、Plankton 優先度が機能しているか) + +#### 作業可能になるための前提情報 (新セッションで必読) + +##### 既存コンポーネントとの参照関係 + +- **既存 takt workflow 例** (新 workflow `post-merge-feedback` も同じパターンで構築): + - `pre-push-review`: simplicity-review + security-review facets (ADR-020) + - `post-pr-review` (cli-pr-monitor 内): analyze-pr-review-comments + supervise / fix facets (ADR-018) +- **既存 cli-merge-pipeline**: post_steps `type = "ai"` 分岐の現行実装 (pending file 書き込み) を Phase B で takt workflow 起動に置き換える +- **既存 skill `analyze-pr`** (`E:\work\claude-code-skills\analyze-pr\SKILL.md`): facet `analyze-pr.md` への port 元 +- **既存 skill `post-merge-feedback`** (`E:\work\claude-code-skills\post-merge-feedback\SKILL.md`): Phase 4 統合フィードバックロジックを `aggregate-feedback.md` facet への port 元 + +##### 重要な既存 ADR (実装時に必ず参照) + +| ADR | 関係 | +|---|---| +| **ADR-014** | post-merge-feedback skill 自体の起案 (試験運用)。本タスク完了で **Superseded** | +| **ADR-015** | push-runner takt 移行。本タスクの設計パターン (CLI exe → takt workflow) の **先行事例** | +| **ADR-018** | cli-pr-monitor takt 移行。同上 | +| **ADR-020** | takt facets (fix/supervise) 共通化戦略。本タスクの **4 facets 分離方針の根拠** | +| **ADR-022** | 自動化コンポーネントの責務分離原則。L1 takt 経由は本原則に整合 (Claude 不在でも動く) | +| **ADR-026** | Cargo workspace。新 crate (`hooks-user-prompt-feedback-recovery`) はこのワークスペースに追加 | +| **ADR-028** | 外部可視成果物ゲート。本タスクは内部 artifact のみで対象外 | +| **ADR-029** | 本タスクで **partial supersede** (層 3-4 廃止、層 1 流用) | + +##### memory 参照 + +- `feedback_side_effect_integration.md`: cleanup / consume 処理を新 phase ではなく既存 phase 末尾に統合する原則 (本設計の Phase D 廃止判断にも適用) +- `feedback_verify_edit_results.md`: 大きな Edit 後は grep で見出し検証 (Phase B での takt workflow / facets ファイル作成時に有用) +- `project_takt_push_runner_learnings.md`: takt 導入の知見 (バージョン固定、ハイブリッド構成等) +- `project_takt_pre_push_iterations.md`: takt fix の child commit 自動収束パターン + +##### 残存する PR #74 の pending file の扱い + +- 現状 `.claude/post-merge-feedback-pending.json` に PR #74 の pending file が残存している可能性 (status=dispatched のまま consume されていない) +- **対処方針**: Phase E で pending file 機構ごと廃止するため明示対処不要。Phase A 着手前に手動 `rm` でもよい (新セッション開始時の状態を整理する目的なら推奨) + +##### 新セッションで最初に確認すべきこと + +1. `git log --oneline -5` で master の最新状態を確認 +2. `docs/todo.md` の本セクション (本記録) を読む +3. `docs/adr/adr-029-post-merge-feedback-auto-trigger.md` を読む (supersede 元の理解) +4. `docs/adr/adr-014-post-merge-feedback.md` を読む (skill 自体の元設計) +5. `docs/adr/adr-015-push-runner-takt-migration.md` / `docs/adr/adr-018-pr-monitor-takt-migration.md` を読む (takt 移行の先行事例として参考) +6. `docs/adr/adr-020-takt-facets-sharing.md` を読む (facets 共通化方針の根拠) +7. Phase A から着手: `docs/adr/adr-030-deterministic-post-merge-feedback.md` の起案 + +#### 完了基準 + +- Phase A〜F すべて完了 +- dogfood で 3-5 回連続マージ → 全 PR にレポート生成 (silent loss 0 を実証) +- ADR-029 / ADR-014 のステータスが `Superseded by ADR-030` に更新済 +- 旧 skill / hook / lib-pending-file が repository から削除済 +- 本 todo.md エントリを削除 (運用ルール: 完了タスクは ADR/仕組みに反映後に削除) + +#### 詰まっている箇所 + +なし (全方向確定済、Phase A から着手可能) + +### (追って) ADR-030 の takt-test-vc 反映 + +> **参照**: 上位タスク「マージ後フィードバック機構の決定論化」の Phase F 完了が前提。元の 1-F (ADR-014 本採用化 + takt-test-vc 反映) は ADR-014 が ADR-030 で Superseded されるため scope 変更。 + +- **やろうとしたこと**: 本プロジェクトで ADR-030 機構が安定稼働 (Phase F dogfood 完了) した後、takt-test-vc へ機構ごとバックポート +- **現在地**: 上位タスクの Phase F 完了待ち +- **詰まっている箇所**: ADR-030 実装 + dogfood 完了に依存 ---