Skip to content

feat(cli-pr-monitor): rate-limit retry を CronCreate park モデルに移行 (Bundle b PR-1, 順位 53)#113

Merged
aloekun merged 2 commits into
masterfrom
feat/rate-limit-cron-park
May 5, 2026
Merged

feat(cli-pr-monitor): rate-limit retry を CronCreate park モデルに移行 (Bundle b PR-1, 順位 53)#113
aloekun merged 2 commits into
masterfrom
feat/rate-limit-cron-park

Conversation

@aloekun
Copy link
Copy Markdown
Owner

@aloekun aloekun commented May 5, 2026

Summary

  • Bundle b PR-1 (順位 53): cli-pr-monitor の rate-limit retry を std::thread::sleep ベースから CronCreate park モデル に切り替え、PR feat(hooks): comment-lint hook scope を変更行に限定 (順位 50 + 順位 51/52 follow-ups) #104 で実観測した 47 分 rate-limit でユーザー手動介入が必要だった致命点 を解消する。
  • ADR-030 (post-merge-feedback) で実証済の 責務分離パターン (機械的 Rust + Claude Code 定期チェックの分離) を CR rate-limit に適用する 3 例目。CronCreate には時間制約がない (60 分 cap は別ツール ScheduleWakeup の制約) ため、47 分でも 90 分でも cron で任意時刻予約可能。
  • durable: true.claude/scheduled_tasks.json に永続化され session 跨ぎを native サポート。

背景・動機

問題: PR #104 で CodeRabbit の 47 分 rate-limit を実観測。現状 cli-pr-monitor は同一プロセス内で std::thread::sleep する設計のため max_duration_secs=600s (10 分) cap でバウンスし、action_required 通知 → ユーザーが手動で @coderabbitai review を投稿する必要があった。

ユーザービジョンとの乖離: 「Code Rabbit が rate-limit にかかったら解除後に自動的に @coderabbitai review を投稿する」が実現できておらず、長時間 rate-limit ではユーザー操作が必要。

設計判断: 詳細は docs/coderabbit-monitoring-efficiency.md の「着手決定 (2026-05-05)」セクションを参照。max_duration_secs 拡大による sleep 延長案 (案 C) は session 終了時の silent loss を解消できないため却下し、CronCreate 分離設計 (案 A) を採用した。

設計概要

[L0 状態管理 / Rust]
  cli-pr-monitor が rate-limit を検出
   → state.next_wakeup_at_unix を保存
   → PARK signal を stdout に emit (CronCreate 呼び出しテンプレート + repo/pr/reset/exe/cwd)
   → exit (parked_rate_limit action)

[L1 wakeup / Claude Code]
  Claude Code が PARK signal を読み
   → CronCreate({ cron: <reset 時刻>, durable: true, recurring: false, prompt: "..." })

[wakeup 発火]
  Cron が prompt を REPL に送信
   → Claude Code が cli-pr-monitor.exe --monitor-only を再 invoke
   → reset 過去なら従来パスで @coderabbitai review 投稿 (Posted)
   → reset まだ未来なら再度 park (chain pattern)

主な変更

state.rs

  • PrMonitorStatenext_wakeup_at_unix: Option<i64> / wakeup_reason: Option<String> を追加 (#[serde(default, skip_serializing_if = "Option::is_none")])
  • PrMonitorState::new() デフォルトを None

stages/poll.rs

  • RateLimitOutcome enum (Posted / Parked { wakeup_at_unix: i64 } / Failed(String)) 導入
  • handle_rate_limit_retry を sleep 廃止 + park モデルに refactor:
    • until_unix_secs > nowParked 返却 (sleep しない)
    • until_unix_secs <= now → 即時 @coderabbitai review 投稿 (Posted、従来挙動)
    • PR 番号未確定 / gh post 失敗 → Failed
  • max_duration_secs > sleep_secs の予算チェック撤廃 (sleep 自体が廃止されたため)
  • format_park_signal (pure 関数): CronCreate 呼び出しテンプレートを含む構造化 stdout signal を生成
  • finalize_parked: state を parked_rate_limit action に遷移、PARK signal emit、early return
  • 副次的 refactor (touch-trigger ratchet 適用): run_poll_loopPollContext + run_one_iteration 等 11 helper に分割 (50 行ガイドライン準拠)

stages/monitor.rs

  • print_report を parked verdict 対応に拡張
  • 副次的 refactor: compute_verdict / print_findings_table に分割

PARK signal 形式 (stdout)

[PR_MONITOR_PARK]
pr: <number>
repo: <owner/repo>
reset_at_unix: <unix>
reset_at_iso_utc: <ISO 8601 UTC>
wait_total_seconds: <secs>
retry_count: <n>
max_retries: <max>
exe: <abs path>
cwd: <abs path>

ACTION REQUIRED: please schedule one-shot wakeup using CronCreate.

CronCreate({
  cron: <compute from reset_at_iso_utc in your local timezone, format "M H DoM Mon DoW">,
  recurring: false,
  durable: true,
  prompt: "Wakeup: rate-limit retry for PR #N (owner/repo). cd <cwd> && <exe> --monitor-only"
})
[/PR_MONITOR_PARK]

cron 式の計算は意図的に Claude Code (model) 側に委ねている: chrono を導入せず lib-pending-file の UTC ベース変換のみ利用するため、local timezone への変換は model に任せる方が確実。

既知の制約 (Bb-2 / Bb-3 で対応予定)

  • 単一 state file 設計は維持: 複数 PR 並行時の collision は Bb-2 (順位 54) / Bb-3 (順位 55) で扱う想定
  • session 終了時の wakeup catch-up は Bb-3 で対応: durable: true.claude/scheduled_tasks.json に永続化されるため fire-on-idle 時に発火するが、SessionStart hook 経由で pending wakeup を能動 catch-up するのは Bb-3
  • 過渡期: Bb-1 land 後 Bb-3 land 前の期間は AI 離席中の wakeup スキップが残る (durable で再起動時に拾えるため致命的ではない)
  • finalize_parked の write_state 失敗時の re-park ループ理論可能性 (pre-push-review OBS-1): write 失敗 → wakeup 発火 → 新プロセス起動 → rate_limit_last_retriggered_at が None のまま → 再 park、というループが理論上ありえる。現実的発生確率は低いが、Bb-2 / Bb-3 実装時に対称的なガードを検討する

ADR-018 との整合性

ADR-018 が廃止したのは「同一プロセス内で 4 段間接連携 (daemon → state file → CronCreate → Claude → skill) する旧設計」。本 PR は プロセス分離 + 責務分離原則 に基づき、Rust が状態管理、Claude Code が定期チェックを担当する分離設計を導入するため、ADR-018 の流儀と整合する。新 ADR で本パターン適用を明文化する想定 (Bb-2/Bb-3 で同 ADR を起案または既存 ADR を更新)。

Test plan

  • cargo test -p cli-pr-monitor: 125 passed / 0 failed (新規 6 件: state serde 3 件 + RateLimitOutcome 経路 2 件 + format_park_signal 2 件)
  • cargo clippy -p cli-pr-monitor -- -D warnings: clean
  • cargo fmt: applied
  • pnpm build:all: success (.claude/cli-pr-monitor.exe 配置済)
  • pre-push-review (pnpm push): APPROVED, 0 findings + 1 observation (上記 OBS-1 記載済)
  • dogfood: 47 分級 rate-limit シナリオでの auto-retry 動作確認 (実観測待ち、本 PR merge 後の運用で検証)
  • 派生プロジェクト (techbook-ledger / auto-review-fix-vc) deploy 確認 (本 PR merge 後)

関連リンク

Summary by CodeRabbit

  • Bug Fixes

    • レート制限の挙動を改め、未来リセット時はスケジュール予約(wake-up)を記録してPARKシグナルを出力、過去リセット時は即時投稿へ切替。再試行フローの不整合を解消し、永続化失敗時は対応要求に遷移するように変更。
  • Refactor

    • レポート出力の判定処理を整理・単純化。
  • Documentation

    • 監視機能設計書を更新(着手日・採用案・Cron挙動・ADR整合性・却下案・進捗)。
  • Tests

    • PARK振る舞いやスケジュール状態のシリアライズ等を追加/更新。

…le b PR-1, 順位 53)

PR #104 で 47 分 rate-limit を実観測した致命点 (max_duration_secs=600s cap で
auto-retry できず action_required 通知 → ユーザー手動介入が必要) への対策。

cli-pr-monitor が同プロセス内で std::thread::sleep する設計を廃止し、reset
時刻を state.next_wakeup_at_unix に保存して PARK signal を stdout に emit して
exit するモデルに切り替える。Claude Code 側が PARK signal を読み CronCreate
(durable: true) で wakeup を予約、reset 後に cli-pr-monitor.exe --monitor-only
が再 invoke されて @coderabbitai review が自動投稿される。

ADR-030 (post-merge-feedback) で実証済の責務分離パターン (機械的 Rust + Claude
Code 定期チェックの分離) を CR rate-limit に適用する 3 例目。CronCreate には
時間制約がない (60 分 cap は別ツール ScheduleWakeup の制約) ため、47 分でも
90 分でも cron で任意時刻予約可能。durable: true で
.claude/scheduled_tasks.json に永続化され session 跨ぎを native サポート。

主な変更:
- PrMonitorState に next_wakeup_at_unix / wakeup_reason を追加
- handle_rate_limit_retry を RateLimitOutcome enum 返却に refactor
  (Posted / Parked { wakeup_at_unix } / Failed)
- reset 時刻が未来 → Parked → state 更新 + PARK signal emit + parked_rate_limit
  action で early return
- reset 時刻が過去 → 従来通り即時 @coderabbitai review 投稿 (Posted)
- max_duration_secs > sleep_secs の予算チェック撤廃 (sleep 自体が廃止)
- print_report を parked verdict 対応に拡張
- 副次的 refactor (touch-trigger ratchet 適用): run_poll_loop を PollContext +
  run_one_iteration 等 11 helper に分割、print_report を compute_verdict +
  print_findings_table に分割

PARK signal フォーマット (stdout):
  [PR_MONITOR_PARK]
  pr / repo / reset_at_unix / reset_at_iso_utc / wait_total_seconds /
  retry_count / max_retries / exe / cwd
  + CronCreate({...}) 呼び出しテンプレート (cron 式は Claude Code が local TZ
    で計算、durable: true / recurring: false / prompt にコマンドを埋め込み)
  [/PR_MONITOR_PARK]

既知の制約 (PR description / Bb-2/Bb-3 で対応予定):
- 単一 state file 設計は維持 (複数 PR 並行時の collision は Bb-2/Bb-3 で扱う)
- session 終了時の wakeup catch-up は Bb-3 (SessionStart hook 拡張) で対応
- 過渡期: Bb-1 land 後 Bb-3 land 前は AI 離席中の wakeup スキップが残る
  (durable: true で再起動時 fire-on-idle するため致命的ではない)

設計根拠は docs/coderabbit-monitoring-efficiency.md の「着手決定 (2026-05-05)」
セクションを参照。

Tests:
- 125 passed / 0 failed (cargo test -p cli-pr-monitor)
- 新規テスト 6 件: state serde (3) + RateLimitOutcome 経路 (2) + format_park_signal (2)
- cargo clippy -D warnings clean
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 5, 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: b880a570-09f8-4284-8f67-290b1e1d455d

📥 Commits

Reviewing files that changed from the base of the PR and between 27379de and 43df40d.

📒 Files selected for processing (1)
  • src/cli-pr-monitor/src/stages/poll.rs

📝 Walkthrough

Walkthrough

このPRは、cli-pr-monitorのrate-limit再試行ロジックを「sleep→再実行」モデルから「park→wakeup」モデルへ移行し、スケジューリング用の状態フィールド追加、RateLimitOutcome列挙体導入、PARK信号フォーマッタ実装、および報告のverdict抽出を行う変更を含む。

Changes

Rate-limit Park/Wakeup実装

Layer / File(s) Summary
Data Shape
src/cli-pr-monitor/src/state.rs
PrMonitorStatenext_wakeup_at_unix: Option<i64>wakeup_reason: Option<String>を追加(serdeでNoneは省略)。PrMonitorState::newはこれらをNoneで初期化。
Core Logic
src/cli-pr-monitor/src/stages/poll.rs
ポーリングをPollContext + run_one_iterationに再構成。チェッカー呼び出し・JSON処理・状態構築・スキップ/タイムアウト判定を一箇所に集約し、rate-limit処理をRateLimitOutcomePosted/Parked{wakeup_at_unix}/Failed(String))で分岐する設計に改めた。未来resetはParkedとしてstateにwakeup情報を保存してPARK信号を出力、過去resetは即投稿(Posted)扱い。
Park信号フォーマッタ
src/cli-pr-monitor/src/stages/poll.rs
新規format_park_signalを追加し、[PR_MONITOR_PARK]...[/PR_MONITOR_PARK]ブロックでCronCreate(...)durable: true--monitor-only等)と構造化パラメータを出力する純粋関数を実装。
Rate-limit Finalizers / Persistence
src/cli-pr-monitor/src/stages/poll.rs
finalize_posted_retriggerは即投稿後にrate_limit_last_retriggered_at更新と永続化、finalize_parkedaction = "parked_rate_limit"設定・wakeupフィールド保存・永続化・PARK出力を行う。永続化失敗や投稿失敗は端的にaction_requiredで終端化。
Reporting/Integration
src/cli-pr-monitor/src/stages/monitor.rs
print_reportからverdict計算を抽出しcompute_verdictを導入。result.action == "parked_rate_limit"時は専用メッセージを返すように拡張。
Tests
src/cli-pr-monitor/src/stages/poll.rs tests
Parked結果のケース、format_park_signal出力検証、PR情報欠如時のFailed振る舞いなどのユニットテストを追加/更新。
Documentation
docs/coderabbit-monitoring-efficiency.md
着手決定日・採用案(Bundle b)、CronCreate設計方針(durable: true使用等)、ADR-018整合性、案C却下理由、Bb-1の実装完了(state追加、RateLimitOutcome化、park/publish化、sleep廃止、verdict拡張、テスト完了)を追記。

Sequence Diagram

sequenceDiagram
    participant Poll as Poll Loop
    participant Iter as run_one_iteration
    participant Checker as Checker
    participant RLH as RateLimitHandler
    participant State as State Persist
    participant Signal as Park Signal Formatter
    participant Stdout as Stdout

    Poll->>Iter: run_one_iteration()
    Iter->>Checker: invoke_checker(args)
    Checker-->>Iter: check result
    Iter->>RLH: handle_rate_limit_branch()
    alt reset_unix in past
        RLH->>RLH: post_review_immediately()
        RLH-->>Iter: RateLimitOutcome::Posted
        Iter->>State: persist(updated state)
        Iter-->>Poll: continue polling
    else reset_unix in future
        RLH-->>Iter: RateLimitOutcome::Parked{wakeup_at_unix}
        Iter->>State: set next_wakeup_at_unix/wakeup_reason, persist
        Iter->>Signal: format_park_signal()
        Signal-->>Stdout: [PR_MONITOR_PARK]..CronCreate..[/PR_MONITOR_PARK]
        Iter-->>Poll: return terminal PollResult (action_required/parked)
        Poll->>Poll: exit loop
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 分

Possibly related PRs

  • aloekun/claude-code-hook-test#38: cli-pr-monitorの初期実装変更と同一モジュール(poll.rs、state.rs、monitor.rs)を拡張する関係があり、直接のコード的関連が強い。
  • aloekun/claude-code-hook-test#97: 既存のsleep-and-retriggerフローを導入していたPRで、本PRはその再試行ロジックをpark/wakeupモデルへ置換するため密接に関連。
  • aloekun/claude-code-hook-test#100: ADRで提案されたrate-limit自動再試行設計を本PRが実装しており、設計・実装面で直接関連している。
🚥 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 のタイトルは主な変更内容を明確に反映している:rate-limit retry の sleep ベースから CronCreate park モデルへの移行(Bundle b PR-1)という実装の中核を正確に表現している。
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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/cli-pr-monitor/src/stages/monitor.rs (1)

56-63: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

起動直後の state 初期化で rate-limit の継続情報が消えます

ここで毎回 PrMonitorState::new(...) を即座に永続化しているため、park 復帰後の最初の poll では前回 run の rate_limit_retriesrate_limit_last_retriggered_at をもう読めません。結果として max_retries と dedup が invocation を跨いで効かず、同じ rate-limit comment に対して再 park / 再投稿を繰り返せます。

修正イメージ
     let init_state = PrMonitorState::new(
         pr_info.pr_number,
         pr_info.repo.clone(),
         pr_info.push_time.clone().unwrap_or_else(utc_now_iso8601),
     );
+    let mut init_state = init_state;
+    if let Some(existing) = crate::state::read_state() {
+        init_state.notified = existing.notified;
+        init_state.rate_limit_retries = existing.rate_limit_retries;
+        init_state.rate_limit_last_retriggered_at = existing.rate_limit_last_retriggered_at;
+    }
     if let Err(e) = write_state(&init_state) {
         log_info(&format!("[state] 初期化書き込み失敗 (継続): {}", e));
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli-pr-monitor/src/stages/monitor.rs` around lines 56 - 63, The code
always creates a fresh PrMonitorState via PrMonitorState::new and immediately
calls write_state(&init_state), which overwrites persisted rate_limit_retries
and rate_limit_last_retriggered_at and breaks cross-run deduping; change the
startup logic to first attempt to load existing state (use the corresponding
read_state function or storage read for this PR monitor), and only create+write
a new PrMonitorState if none exists, or merge/restore the persisted rate-limit
fields into init_state before calling write_state; ensure references to
PrMonitorState::new, init_state, write_state, and the rate_limit_retries /
rate_limit_last_retriggered_at fields are used so the persisted counters survive
process restarts and the initial poll sees the previous state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/cli-pr-monitor/src/stages/poll.rs`:
- Around line 489-495: The CronCreate prompt currently injects raw cwd and exe
into the command string (variables exe and cwd in poll.rs), producing unquoted
paths like `cd {cwd} && {exe} --monitor-only` which breaks on Windows when paths
contain spaces; update the code that builds the prompt to either properly
shell-escape/quote both exe and cwd (wrap with quotes and escape internal quotes
for Windows) or, better, stop embedding the whole command text and instead pass
a structured execution field (e.g., separate cwd and executable fields or an
args array) to CronCreate so the runner can invoke the command safely; apply the
same fix to the other occurrence noted around the reference at line ~515.
- Around line 347-351: When write_state(state) returns Err(e) we must not emit a
PARK signal (which creates a durable re-park loop without state persisted);
instead log the error and emit an action_required signal so the workflow
fails-open safely. Change the error branch that currently does log_info(...) and
proceeds to call format_park_signal(...)/println! to (a) log the full error, (b)
construct an action_required signal (include error details and context) rather
than calling format_park_signal, and (c) println! that action_required signal
(or otherwise route it to the same output path). Update references around
write_state, format_park_signal, the local variable signal, and the
parked_rate_limit handling to ensure the code paths diverge on write_state
failure to action_required.
- Around line 511-515: The CronCreate one-shot is being converted into a 5-field
cron (M H DoM Mon DoW) which drops seconds; keep seconds precision by passing
reset_at_iso_utc converted to the local timezone as an ISO 8601 timestamp
instead of converting to 5-field cron. Update the CronCreate payload
construction (the CronCreate block where cron: ... is set) to set cron to
reset_at_iso_utc converted to local timezone in ISO8601 with seconds (no
rounding), keep recurring: false and durable: true, and leave prompt as-is;
alternatively, if you must emit a 5-field cron, implement the explicit rule “if
seconds != 0, round up to next minute” and document that behavior.

---

Outside diff comments:
In `@src/cli-pr-monitor/src/stages/monitor.rs`:
- Around line 56-63: The code always creates a fresh PrMonitorState via
PrMonitorState::new and immediately calls write_state(&init_state), which
overwrites persisted rate_limit_retries and rate_limit_last_retriggered_at and
breaks cross-run deduping; change the startup logic to first attempt to load
existing state (use the corresponding read_state function or storage read for
this PR monitor), and only create+write a new PrMonitorState if none exists, or
merge/restore the persisted rate-limit fields into init_state before calling
write_state; ensure references to PrMonitorState::new, init_state, write_state,
and the rate_limit_retries / rate_limit_last_retriggered_at fields are used so
the persisted counters survive process restarts and the initial poll sees the
previous state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5676fd20-dd58-45f0-9075-489311112af8

📥 Commits

Reviewing files that changed from the base of the PR and between 9d45f3a and 27379de.

📒 Files selected for processing (4)
  • docs/coderabbit-monitoring-efficiency.md
  • src/cli-pr-monitor/src/stages/monitor.rs
  • src/cli-pr-monitor/src/stages/poll.rs
  • src/cli-pr-monitor/src/state.rs

Comment thread src/cli-pr-monitor/src/stages/poll.rs
Comment thread src/cli-pr-monitor/src/stages/poll.rs
Comment thread src/cli-pr-monitor/src/stages/poll.rs Outdated
Resolved findings:
- [Major] src/cli-pr-monitor/src/stages/poll.rs:351 state 永続化に失敗しても PARK を発行すると durable な再 park ループになります
- [Major] src/cli-pr-monitor/src/stages/poll.rs:495 CronCreate prompt のコマンドは path をクォートしないと Windows で壊れます
- [Major] src/cli-pr-monitor/src/stages/poll.rs:515 One-shot cron は ISO 8601 形式で秒精度をサポートするため、5 フィールド cron への変換を避ける
@aloekun
Copy link
Copy Markdown
Owner Author

aloekun commented May 5, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 5, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@aloekun aloekun merged commit 4a1440f into master May 5, 2026
1 check passed
@aloekun aloekun deleted the feat/rate-limit-cron-park branch May 5, 2026 11:38
aloekun added a commit that referenced this pull request May 5, 2026
…sibling parity 回帰テスト + CR Major fix (Bundle b PR-2 + 順位 75, Bb-2 + T2-2)

Bundle b PR-2 (順位 54) と 順位 75 (T2-2 = Bb-1 follow-up) を bundled で実装。
PR #114 の CodeRabbit Major 2 件 (head 不一致時の wakeup 誤判定 / fresh push
での recheck count 持ち越し) を fold-in 修正。

設計概要 (todo5.md spec 準拠):
- run_poll_loop を single-iteration model に書き換え:
  - is_wakeup=false (fresh push): checker 未呼び出し → INITIAL_REVIEW_WAIT_SECS=300s
    後の wakeup 予約 + park signal emit + early return (CR review 開始前の
    wasteful API call 回避)
  - is_wakeup=true (CronCreate wakeup): 1 回 check → terminal / rate-limit
    park (Bb-1) / review_recheck park (Bb-2) のいずれか
- run_monitor_only に detect_wakeup_resume: state.pr / state.repo / head_commit
  一致 + next_wakeup_at_unix <= now で wakeup 判定
- review_recheck_count を PrMonitorState に追加、build_state_for_iteration が
  wakeup 跨ぎで値を保持 (リセットは fresh push のみ)

主な変更:
- PrMonitorState に review_recheck_count: u32 と head_commit: Option<String> を追加
- PrInfo に head_commit を追加、get_pr_info / run_create_pr で gh pr view --json
  headRefOid を介して populate
- wakeup_reason 値に "review_recheck" を追加 (Bb-1 の "rate_limit_retry" と sibling)
- finalize_initial_review_park / finalize_review_recheck_park /
  schedule_next_review_recheck_park / finalize_review_recheck_max_reached を追加
- format_review_park_signal: [PR_MONITOR_PARK] envelope に reason: review_recheck
  discriminator 付き、Bb-1 と同一 format で Claude Code 側パーサ統一
- format_park_signal (Bb-1) にも reason: rate_limit_retry を追加で uniformity
- run_poll_loop シグネチャに is_wakeup: bool を追加、polling loop を撤廃
- start_monitoring (entry) と start_monitoring_wakeup を分離、reset skip 制御
- state_file_path() に env 変数 PR_MONITOR_STATE_FILE_OVERRIDE 経路追加
  (T2-2 fault injection 用、本番コードは env 設定なしで挙動変化なし)
- observe.rs 削除 + pnpm observe-pr script 削除 + --observe 引数削除
  (single-iteration 化で polling 自体が消えたため observer も不要、ADR-018
  addendum を実質 supersede)
- MonitorConfig::poll_interval_secs を #[allow(dead_code)] でマーク (Bb-3 で
  削除予定、後方互換のため保持)
- print_report を parked_review_recheck verdict 対応に拡張

CR Major fix (PR #114 review):
- CR Major #1 (monitor.rs detect_wakeup_resume): state.head_commit と pr_info.head_commit
  の比較を追加、新 commit が push されれば fresh push 経路に倒す。should_resume_wakeup
  pure helper に分離して testable 化、7 件の単体テストで条件網羅
  (head 一致 / head 不一致 / state head 欠 / pr_info head 欠 / pr or repo 不一致 /
  wakeup 未来 / next_wakeup_at_unix None)
- CR Major #2 (poll.rs finalize_initial_review_park): fresh push 経路で
  review_recheck_count を 0 に明示リセット + head_commit を pr_info から保存。
  state file の前サイクル残留 (count=3 等) が新 push に持ち越されないことを
  finalize_initial_review_park_resets_recheck_count test で検証

T2-2 (順位 75 follow-up) ★: 3 件の write_state 失敗時 fail-safe 回帰テスト追加
- finalize_parked_returns_action_required_when_write_state_fails:
  rate-limit park の fail-safe 確認 (PR #113 takt fix の固定化)
- schedule_next_review_recheck_park_returns_action_required_when_write_state_fails:
  review park の fail-safe 確認 (sibling parity)
- finalize_park_siblings_have_symmetric_write_state_handling:
  両 sibling とも write 失敗で action_required に収束する invariant を 1 テスト
  で machine-enforce
- env_override_lock helper で env var 共有による test 並列性 race を防止

副次的 refactor (touch-trigger ratchet 適用):
- start_monitoring_inner を try_acquire_monitor_lock + init_or_resume_state +
  run_takt_stage (+ invoke_takt_into_outcome) + finalize_repush に分割
- format_review_park_signal を collect_review_park_fields + format に分割
- finalize_review_recheck_park を finalize_review_recheck_max_reached +
  schedule_next_review_recheck_park に分割
- run_create_pr を write_early_reset_state + prepare_gh_pr_create_args +
  log_gh_pr_create_invocation + build_pr_info_from_gh_output に分割
- get_pr_info を find_pr_via_jj_bookmarks に Strategy B 抽出

Tests: 133 passed / 0 failed (cargo test -p cli-pr-monitor)。
新規テスト合計 13 件: state.rs 4 件 (review_recheck_count / head_commit serde +
legacy JSON compat) + monitor.rs 7 件 (should_resume_wakeup pure helper) +
poll.rs 4 件 (T2-2 fail-safe / sibling parity / CR Major #2 reset)。
cargo clippy --tests -D warnings clean。
aloekun added a commit that referenced this pull request May 5, 2026
…sibling parity 回帰テスト + CR Major fix (Bundle b PR-2 + 順位 75, Bb-2 + T2-2) (#114)

Bundle b PR-2 (順位 54) と 順位 75 (T2-2 = Bb-1 follow-up) を bundled で実装。
PR #114 の CodeRabbit Major 2 件 (head 不一致時の wakeup 誤判定 / fresh push
での recheck count 持ち越し) を fold-in 修正。

設計概要 (todo5.md spec 準拠):
- run_poll_loop を single-iteration model に書き換え:
  - is_wakeup=false (fresh push): checker 未呼び出し → INITIAL_REVIEW_WAIT_SECS=300s
    後の wakeup 予約 + park signal emit + early return (CR review 開始前の
    wasteful API call 回避)
  - is_wakeup=true (CronCreate wakeup): 1 回 check → terminal / rate-limit
    park (Bb-1) / review_recheck park (Bb-2) のいずれか
- run_monitor_only に detect_wakeup_resume: state.pr / state.repo / head_commit
  一致 + next_wakeup_at_unix <= now で wakeup 判定
- review_recheck_count を PrMonitorState に追加、build_state_for_iteration が
  wakeup 跨ぎで値を保持 (リセットは fresh push のみ)

主な変更:
- PrMonitorState に review_recheck_count: u32 と head_commit: Option<String> を追加
- PrInfo に head_commit を追加、get_pr_info / run_create_pr で gh pr view --json
  headRefOid を介して populate
- wakeup_reason 値に "review_recheck" を追加 (Bb-1 の "rate_limit_retry" と sibling)
- finalize_initial_review_park / finalize_review_recheck_park /
  schedule_next_review_recheck_park / finalize_review_recheck_max_reached を追加
- format_review_park_signal: [PR_MONITOR_PARK] envelope に reason: review_recheck
  discriminator 付き、Bb-1 と同一 format で Claude Code 側パーサ統一
- format_park_signal (Bb-1) にも reason: rate_limit_retry を追加で uniformity
- run_poll_loop シグネチャに is_wakeup: bool を追加、polling loop を撤廃
- start_monitoring (entry) と start_monitoring_wakeup を分離、reset skip 制御
- state_file_path() に env 変数 PR_MONITOR_STATE_FILE_OVERRIDE 経路追加
  (T2-2 fault injection 用、本番コードは env 設定なしで挙動変化なし)
- observe.rs 削除 + pnpm observe-pr script 削除 + --observe 引数削除
  (single-iteration 化で polling 自体が消えたため observer も不要、ADR-018
  addendum を実質 supersede)
- MonitorConfig::poll_interval_secs を #[allow(dead_code)] でマーク (Bb-3 で
  削除予定、後方互換のため保持)
- print_report を parked_review_recheck verdict 対応に拡張

CR Major fix (PR #114 review):
- CR Major #1 (monitor.rs detect_wakeup_resume): state.head_commit と pr_info.head_commit
  の比較を追加、新 commit が push されれば fresh push 経路に倒す。should_resume_wakeup
  pure helper に分離して testable 化、7 件の単体テストで条件網羅
  (head 一致 / head 不一致 / state head 欠 / pr_info head 欠 / pr or repo 不一致 /
  wakeup 未来 / next_wakeup_at_unix None)
- CR Major #2 (poll.rs finalize_initial_review_park): fresh push 経路で
  review_recheck_count を 0 に明示リセット + head_commit を pr_info から保存。
  state file の前サイクル残留 (count=3 等) が新 push に持ち越されないことを
  finalize_initial_review_park_resets_recheck_count test で検証

T2-2 (順位 75 follow-up) ★: 3 件の write_state 失敗時 fail-safe 回帰テスト追加
- finalize_parked_returns_action_required_when_write_state_fails:
  rate-limit park の fail-safe 確認 (PR #113 takt fix の固定化)
- schedule_next_review_recheck_park_returns_action_required_when_write_state_fails:
  review park の fail-safe 確認 (sibling parity)
- finalize_park_siblings_have_symmetric_write_state_handling:
  両 sibling とも write 失敗で action_required に収束する invariant を 1 テスト
  で machine-enforce
- env_override_lock helper で env var 共有による test 並列性 race を防止

副次的 refactor (touch-trigger ratchet 適用):
- start_monitoring_inner を try_acquire_monitor_lock + init_or_resume_state +
  run_takt_stage (+ invoke_takt_into_outcome) + finalize_repush に分割
- format_review_park_signal を collect_review_park_fields + format に分割
- finalize_review_recheck_park を finalize_review_recheck_max_reached +
  schedule_next_review_recheck_park に分割
- run_create_pr を write_early_reset_state + prepare_gh_pr_create_args +
  log_gh_pr_create_invocation + build_pr_info_from_gh_output に分割
- get_pr_info を find_pr_via_jj_bookmarks に Strategy B 抽出

Tests: 133 passed / 0 failed (cargo test -p cli-pr-monitor)。
新規テスト合計 13 件: state.rs 4 件 (review_recheck_count / head_commit serde +
legacy JSON compat) + monitor.rs 7 件 (should_resume_wakeup pure helper) +
poll.rs 4 件 (T2-2 fail-safe / sibling parity / CR Major #2 reset)。
cargo clippy --tests -D warnings clean。
aloekun added a commit that referenced this pull request May 6, 2026
…DR-018/034 に集約 (#117)

Bundle b (Bb-1/Bb-2/Bb-3、PR #113-115 + PR #116) で計画書の使命を完遂したため、docs-governance.md の retirement workflow に従って削除。


* ADR-018 に追記 (2026-05-06 セクション): Bundle b で CronCreate を park モデルとして再導入した経緯 + observer モード撤廃 + CronCreate の重要事実 (60 分上限なし、durable: true で session 跨ぎ) + 廃止された 4 段間接連携 vs Bundle b park モデルの比較。

* ADR-034 に追記 (Bundle b との関係): Sub-PR 2 の scope 縮小 (順位 42 = Bb-1 で実質達成済、順位 43 = ADR-018 追記で部分達成、残 順位 46/49 のみ)。Bundle 並行進行設計の教訓も記載。

* 順位 15 (cli-pr-monitor 通知 Recovery 経路) を Bb-3 SessionStart catch-up nudge で吸収済と判定し、todo3.md / todo.md priority table から削除。

* ADR-033 line 124 の領域特化計画書 example を一般化 (specific filename 参照を削除)。


由来: docs/coderabbit-monitoring-efficiency.md は Bundle b 計画書として試験運用 status で運用、Bundle b land 完了で「bundle が消化されたら役割を終える」自己宣言を達成。
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