Skip to content

feat(post-merge-feedback): Bundle c-1 (D-7) — L1 Drop guard + L2 orphan reaper + ADR-030 spec#154

Merged
aloekun merged 1 commit into
masterfrom
bundle-c-1-pmf-recovery-d7
May 13, 2026
Merged

feat(post-merge-feedback): Bundle c-1 (D-7) — L1 Drop guard + L2 orphan reaper + ADR-030 spec#154
aloekun merged 1 commit into
masterfrom
bundle-c-1-pmf-recovery-d7

Conversation

@aloekun
Copy link
Copy Markdown
Owner

@aloekun aloekun commented May 13, 2026

Summary

ADR-030 §「失敗マーカーによる recovery」を abrupt termination 経路 (SIGPIPE / SIGTERM / kill -9 / SIGKILL / power loss / OOM Killer / panic) でも保証する 3 層 recovery を実装。PR #109 で実証された silent loss を構造的に解消する。

  • L1 in-processfeedback::run 冒頭で .failed marker を pre-emptive 書込み + RAII FailedMarkerGuard (idempotent backup)。Rust default SIGPIPE = SIG_DFL は unwind せず Drop が走らないため、marker のディスク先置きで救済 (順位 63)。
  • L2 out-of-processhooks-session-startorphan run reaper を追加。.takt/runs/*/meta.jsonstatus: "running" + post-merge-feedback task prefix + ORPHAN_THRESHOLD_SECS (= 1500s = TAKT_TIMEOUT_SECS + 余裕 5 分) 経過の run を検出 → .failed marker 生成 + meta.json 更新。Advisor 指摘の ADR-030 §Reconciliation false-positive 対策 として、<pr>.md 成功レポートが既存の場合は skip (takt parent kill 後に descendants が完成した path) (順位 64)。
  • ADR-030 spec amendment — 「Abrupt 終了の多層 recovery」subsection を追加。L1/L2 責務分離マトリクス、Reconciliation-aware reaper skip ルール、SLA を const name 参照 (drift 防止) で明文化 (順位 67)。

Phase D dogfood data point #7

指標
screen_decision human_review (fallback)
findings 0
fallback_reason Ollama HTTP timeout (os error 10060) — server unavailable
## Diagnostic section 欠落 (失敗が mistral:7b 到達前)
pre-push pipeline 合計 757s / pre-push-review 1 iter APPROVE

新規 failure mode 観測 (Phase E 採否判定の追加考慮事項):

  • D-3/D-4/D-5/D-6 で観測した「hallucinate FP」「docs-only unused-import」とは異なる、Ollama サーバ自体の network timeout を確認
  • num_ctx overflow ではない (Phase C 解消域外)
  • fallback path は設計通り soft-fail で push pipeline を block せず

Design notes

  • A (signal trap) を skip した理由: Rust default の SIGPIPE 動作は SIG_DFL で即時終了し Drop guard すら走らない。nix crate + Unix/Windows cfg gate を追加しても pre-emptive marker と比べた marginal な追加カバー範囲は panic 経路のみ。よって C (pre-emptive) を主軸に、Drop guard を backup として配置する 2 層構成にスコープを絞った。
  • constants の crate 間共有戦略: TAKT_TASK_PREFIX / ORPHAN_THRESHOLD_SECS を直接 dep 化 (cycle risk) ではなく inline duplicate + 両 crate の test で literal pin (advisor option c) を採用。drift 検出は cargo test で機械的に行う。
  • run() 50 行ガイドライン: pre-emptive marker 追加で run() が 50 行超 (72 行) になったため、prepare_transcript / reconcile_takt_output ヘルパーに分割して責務を明示化。
  • Reconciliation-aware skip: reaper の冪等性は .failed marker 既存だけでなく <pr>.md 成功レポート既存も check (advisor フィードバック反映)。stale meta.json を放置する方が、false-positive .failed で UserPromptSubmit hook が毎 prompt nag するより害が少ない設計トレードオフ。

Bundle c の残作業 (本 PR scope 外)

  • 順位 65 — exe + --help を PreToolUse でブロック (todo7.md L266-)
  • 順位 66 — 長時間 subprocess の pipe truncate 禁止ルール (todo7.md L318-)

両者は Bundle c-2 として別 PR 化推奨 (c-1 = 中断後 recovery / c-2 = 中断 trigger の事前防止)。

Test plan

  • cargo test --workspace — 901+ tests pass / 0 failures (新規: feedback.rs 7 件 + hooks-session-start 16 件)
  • cargo clippy -p cli-merge-pipeline -p hooks-session-start clean
  • release exe build + .claude/ deploy 完了
  • markdown lint clean (ADR-030 / todo7.md / todo-summary.md / analysis.md / todo8.md)
  • Phase D dogfood data point feat(logger): Logger拡張・JSTタイムスタンプ追加・スクリプトTS化 #7 (LINT_SCREEN_ENABLED=true で push 実施、新 failure mode 観測)
  • post-merge-feedback 自動起動 + Bundle c-1 の dogfood 観察 (kill -9 シミュレーションは将来課題)

Refs

Summary by CodeRabbit

リリースノート

  • ドキュメント

    • マージ後フィードバック機能の信頼性仕様を更新。プロセス異常終止時のリカバリーメカニズムを明記しました。
  • 改善

    • マージ後フィードバック処理を強化。システムが予期しない終了からより堅牢に回復するよう改良しました。

Review Change Stack

…an reaper + ADR-030 spec

PR #109 で実証された ADR-030 仕様違反 (SIGPIPE で feedback workflow が silent 中断 +
.failed marker 未生成) への構造的解消。3 層 (pre-emptive marker / RAII Drop guard /
out-of-process reaper) で abrupt termination 経路を多層防御する。

## 順位 63 — cli-merge-pipeline pre-emptive marker + Drop guard

- write_pending_marker を feedback::run の concurrent_run_guard 直後に追加
- RAII FailedMarkerGuard を armed 状態で生成、Ok path で disarm
- Drop guard は idempotent (marker.exists() check で caller の detailed marker を保護)
- Rust default SIGPIPE は SIG_DFL = unwind せず即時終了するため Drop は呼ばれない。
  pre-emptive marker のディスク先置きで救済する設計 (signal trap A は skip)
- run() を 50 行ガイドライン遵守のため prepare_transcript / reconcile_takt_output に分割

## 順位 64 — hooks-session-start orphan run reaper

- .takt/runs/*/meta.json scan: status=running + post-merge-feedback task prefix +
  ORPHAN_THRESHOLD_SECS (1500s = TAKT_TIMEOUT_SECS + 余裕 5 分) 経過の run を検出
- reap: .failed marker 生成 + meta.json status=failed 更新 (reaped_by field 追加)
- 冪等性: 既存 .failed marker または .claude/feedback-reports/<pr>.md 成功レポート
  存在時は skip (advisor 指摘の ADR-030 §Reconciliation false-positive 対策)
- ISO 8601 parser は check-ci-coderabbit::parse_iso8601_to_unix と同型 (no chrono dep)
- TAKT_TASK_PREFIX_PMF / ORPHAN_THRESHOLD_SECS は cli-merge-pipeline 側と inline duplicate +
  両 crate の test で literal pin (drift 検出)

## 順位 67 — ADR-030 spec amendment

- "Abrupt 終了の多層 recovery (Bundle c-1 で追加)" subsection を追加
- L1 in-process / L2 out-of-process の責務分離マトリクス
- Reconciliation-aware reaper skip ルールを spec として明記
- SLA は ORPHAN_THRESHOLD_SECS / TAKT_TIMEOUT_SECS を name で参照 (数値 drift 防止)

## 検証

- workspace tests: 901+ pass / 0 failures (新規 test: feedback.rs 7 件 + session-start 16 件)
- clippy clean (Error::other 移行 + dead_code on canonical const)
- release exe ビルド + .claude/ にコピー済

## Bundle c 残り (本 PR scope 外)

- 順位 65 (exe + --help PreToolUse block) / 順位 66 (pipe truncate global rule) は
  Bundle c-2 として todo7.md に残置

Refs: ADR-030, PR #109 (root cause), feedback-reports/109.md (Tier 1 #1/#2 + Tier 3 #6)
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 62bfb0fd-9d01-4746-b5b3-e41dd55b0ab2

📥 Commits

Reviewing files that changed from the base of the PR and between aed014a and da5d8ae.

📒 Files selected for processing (7)
  • docs/adr/adr-030-deterministic-post-merge-feedback.md
  • docs/local-llm-offload-analysis.md
  • docs/todo-summary.md
  • docs/todo7.md
  • docs/todo8.md
  • src/cli-merge-pipeline/src/feedback.rs
  • src/hooks-session-start/src/main.rs
💤 Files with no reviewable changes (1)
  • docs/todo7.md

📝 Walkthrough

Walkthrough

ADR-030 にポストマージフィードバックの多層確定的回復設計を追加指定し、cli-merge-pipeline にインプロセス pre-emptive marker・RAII guard を、hooks-session-start にアウトオブプロセス orphan reaper を実装。異常終了時も .failed マーカー確実生成、次セッション開始時に orphan 検出・修復。

Changes

多層確定的回復メカニズム実装

Layer / File(s) Summary
ADR-030 多層回復設計仕様
docs/adr/adr-030-deterministic-post-merge-feedback.md
ADR-030に「Abrupt 終了の多層 recovery」セクション追加。L1(インプロセス)の pre-emptive marker + RAII drop guard、L2(アウトオブプロセス)の orphan reaper(hooks-session-start から起動)、責務分離・SLA保証(TAKT_TIMEOUT_SECS/ORPHAN_THRESHOLD_SECS連動)を明記。
インプロセス回復ヘルパー・ガード実装
src/cli-merge-pipeline/src/feedback.rs
新規定数 ORPHAN_THRESHOLD_SECS(1500秒)、公開関数 failed_marker_path()、pre-emptive write_pending_marker()、RAII FailedMarkerGuard(drop時に backup pending marker書き込み、既存marker上書き回避) を実装。単体テストで marker layout、orphan threshold 不変式、pending marker、guard disarm・backup・idempotency を網羅。
フィードバックランナー refactoring
src/cli-merge-pipeline/src/feedback.rs
run() 早期フェーズを再構成:pending marker → guard arm → prepare_transcript() 委譲。post-takt に reconcile_takt_output() 導入、報告コピー・marker管理・エラー構造化を統一。
アウトオブプロセス orphan reaper
src/hooks-session-start/src/main.rs
.takt/runs/*/meta.json スキャンし "post-merge-feedback for #N" で status="running" かつ startTime > 1500秒 の orphan を検出。.failed marker 作成(既存スキップ)、meta.json を status="failed" + reaped_by 更新。emit_session_start_output に reaper nudge 追記。ISO-8601 解析、orphan 検出・修復ロジック、marker idempotency、nudge生成を包括的にテスト(528行追加)。
タスク追跡・進捗更新
docs/local-llm-offload-analysis.md, docs/todo-summary.md, docs/todo7.md, docs/todo8.md
Phase D-7 ステータス「進行中」に更新、Bundle c/k エントリ再整理、PR#109 PreToolUse ブロック & ADR-030 仕様更新タスク、ADR-038 mistral 5回観測拡張、coding-style.md 多ファイル退役チェックリスト追記。

Sequence Diagram(s)

sequenceDiagram
  participant Client as User/CI
  participant FbRun as feedback::run()
  participant Guard as FailedMarkerGuard
  participant Takt as takt workflow
  participant Report as feedback-report.md
  participant SessionStart as hooks-session-start (次セッション)
  participant Meta as meta.json

  Note over FbRun,Guard: L1: インプロセス即時回復

  FbRun->>FbRun: 同時実行ガード確認
  FbRun->>FbRun: pending marker 即座に書き込み
  FbRun->>Guard: guard arm
  FbRun->>Takt: takt workflow 実行開始

  alt takt 実行成功 && 報告生成
    Takt->>Report: feedback-report.md 生成
    FbRun->>FbRun: reconcile_takt_output() 試行
    FbRun->>FbRun: 報告コピー成功
    FbRun->>Guard: disarm (guard発火抑止)
    FbRun->>FbRun: pending marker 削除
    FbRun->>Client: success 返却
  else takt timeout/失敗 || reconcile失敗
    FbRun->>FbRun: 異常終了
    Guard->>FbRun: drop 発火
    Guard->>FbRun: backup pending marker 維持
  end

  Note over SessionStart,Meta: L2: アウトオブプロセス定期修復

  SessionStart->>Meta: .takt/runs/*/meta.json 走査
  loop orphan threshold 超過ラン検出
    Meta-->>SessionStart: "post-merge-feedback for `#N`" + status="running" + startTime > 1500s
    alt marker既存 || 成功報告既存
      SessionStart->>SessionStart: reconciliation skip
    else orphan確定
      SessionStart->>Meta: status="failed", reaped_by="hooks-session-start" 更新
      SessionStart->>FbRun: .failed marker 作成
    end
  end
  SessionStart->>Client: additionalContext に reaper nudge 追記
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • aloekun/claude-code-hook-test#77: 同じ src/cli-merge-pipeline/src/feedback.rs のタクトワークフロー L1 実装をベースに本 PR は pre-emptive marker・guard・reconciliation 機能を追加で扡張。
  • aloekun/claude-code-hook-test#115: 本 PR は src/hooks-session-start/src/main.rsemit_session_start_output/additionalContext に既存 PR monitor catch-up nudge を保持しつつ orphan reaper nudge を新規追記。
  • aloekun/claude-code-hook-test#80: 本 PR が生成・更新する .claude/feedback-reports/<pr>.md.failed marker を、PR#80 の UserPromptSubmit L2 フック側で回復コンテキスト生成に消費される。
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PRタイトルは主要な変更内容(L1 Drop guard + L2 orphan reaper + ADR-030 spec)を明確に記述しており、PR目的と完全に一致している。
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@aloekun aloekun merged commit c872da2 into master May 13, 2026
1 check passed
@aloekun aloekun deleted the bundle-c-1-pmf-recovery-d7 branch May 13, 2026 17:18
aloekun added a commit that referenced this pull request May 15, 2026
…hase D Round 2 完遂反映 (#155)

* docs(llm-offload): Phase D Round 2 完遂状況を analysis.md / outcomes.md に反映 (D-7 #154 結果込み)

* feat(lint-screen): Bundle k-1 (順位 123) — Markdown ハンク除外フィルターを追加

Phase D dogfood で 5 PR 連続観測された false positive (mistral:7b が docs-only
diff や `.md` ファイルに対して Rust の `unused-import` を hallucinate) を
拡張子ベースの mechanical filter で構造的に解消する。

## 設計

- `src/cli-push-runner/src/stages/lint_screen.rs` に `filter_excluded_hunks` 関数を追加
- `EXCLUDED_EXTENSIONS = ["md", "markdown"]` を hardcode (大文字小文字無視)
- `diff --git ` 行を file-diff 境界として 1 ハンク = 1 chunk に分割、各 chunk の
  `+++ b/<path>` (または `--- a/<path>`) から拡張子を抽出して判定
- 全ハンクが対象外拡張子 (= docs-only diff) の場合は `FilterResult::AllExcluded` を
  返し、invoke を完全に skip + skip-report (`screen_decision: skipped` + 理由)
  を書き出す short-circuit path に分岐

## なぜ filter 適用箇所を (b) lint_screen stage 内にしたか

- (a) `.takt/review-diff.txt` 生成時に drop すると review 全体の汎用性を壊す
- (c) prompt 内で「.md は無視せよ」と instruct は LLM 信頼で危険
- (b) lint_screen stage 限定で副作用最小、Bundle k 順位 123 計画通り

## なぜ `run_lint_screen` を `invoke_and_write_report` に分割したか

filter 経路追加で 50 行ガイドラインを超過 (58 行)。invoke + write_report の
2 step を `invoke_and_write_report` ヘルパーに切り出して 30 行台に収めた。

## テストカバレッジ (10 件新規)

- Rust-only / mixed (Rust + .md) / pure .md / pure .markdown
- 大文字 (.MD / .Markdown) も除外対象に含む
- `something.mdxyz.rs` のような中途 `.md` を含む path は除外しない
- `/dev/null` create / delete の片側 path 判定
- 3-file mixed diff で hunk 境界が正しく保たれる
- skip-report 本文に "skipped" / "docs-only diff" / "Bundle k 順位 123" を含む

## 累積 false positive への効果見積

5 観測のうち:
- D-4 CR fix (TOML) — scope 外 (TOML は除外対象に含まない) = 残存
- D-5 ×2 + D-6 (Markdown / docs-only) — **構造的解消** (3 件消滅)
- D-5 初回 (Rust + docs mixed) — Rust 部分は依然 mistral:7b に渡る = scope 内 Rust FP が残る可能性

期待: Phase E 採否判定前の FP rate を 5/7 → ~2/7 に低減。残 2 件は別 root cause
(Rust scope hallucinate / TOML hallucinate) のため別途分析が必要。

Refs: Bundle k 順位 123 (todo8.md 削除済)、ADR-038 Known failure mode、
docs/local-llm-offload-phase-d-outcomes.md L162 (false positive 累積観測)

* fix(lint-screen): CR #155 Major — chunk_has_excluded_extension で +++ b/ (new path) を優先

CodeRabbit Major 指摘の rename 時除外漏れ bug を解消する。unified diff の慣例で
`--- a/<path>` が `+++ b/<path>` より先に出現するため、find_map の旧実装では
両者を OR で取ると常に旧パスが優先されてしまう問題があった。

## 修正前の bug

```rust
let path = chunk.lines().find_map(|line| {
    line.strip_prefix("+++ b/")
        .or_else(|| line.strip_prefix("--- a/"))
}).unwrap_or("");
```

`*.rs → *.md` の rename で:
- `--- a/src/a.rs` を先に処理 → `find_map` が "src/a.rs" を返す
- `+++ b/docs/a.md` に到達せず
- 拡張子 = "rs" → EXCLUDED_EXTENSIONS に含まれず → mistral:7b が `.md` 内容を見る
- = Bundle k-1 の filter contract (新パス優先) に違反

## 修正後

```rust
let new_path = chunk.lines().find_map(|line| line.strip_prefix("+++ b/"));
let old_path = chunk.lines().find_map(|line| line.strip_prefix("--- a/"));
let path = new_path.or(old_path).unwrap_or("");
```

new_path を chunk 全体から先に探す → rename でも新拡張子で判定。new_path が無い
場合 (= `+++ /dev/null` の delete) のみ old_path にフォールバック。

## テスト追加 (2 件)

- `filter_excluded_hunks_prefers_b_path_on_rename_to_markdown`: `*.rs → *.md`
  rename が AllExcluded になることを assert
- `filter_excluded_hunks_keeps_rename_from_md_to_rust`: 逆方向 (`*.md → *.rs`)
  rename が Kept になることを assert (symmetric 検証)

## 累積テスト

cli-push-runner stages::lint_screen 24 → 26 件 pass、clippy clean、release exe deploy 済

Refs: PR #155 CR review コメント (Major 🟠 Quick win)、Bundle k-1 順位 123、ADR-038
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant