feat(cli-pr-monitor): rate-limit retry を CronCreate park モデルに移行 (Bundle b PR-1, 順位 53)#113
Conversation
…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
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughこのPRは、cli-pr-monitorのrate-limit再試行ロジックを「sleep→再実行」モデルから「park→wakeup」モデルへ移行し、スケジューリング用の状態フィールド追加、 ChangesRate-limit Park/Wakeup実装
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 分 Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
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. Comment |
There was a problem hiding this comment.
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_retriesとrate_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
📒 Files selected for processing (4)
docs/coderabbit-monitoring-efficiency.mdsrc/cli-pr-monitor/src/stages/monitor.rssrc/cli-pr-monitor/src/stages/poll.rssrc/cli-pr-monitor/src/state.rs
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 への変換を避ける
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
…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。
…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。
…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 が消化されたら役割を終える」自己宣言を達成。
Summary
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 でユーザー手動介入が必要だった致命点 を解消する。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) を採用した。設計概要
主な変更
state.rsPrMonitorStateにnext_wakeup_at_unix: Option<i64>/wakeup_reason: Option<String>を追加 (#[serde(default, skip_serializing_if = "Option::is_none")])PrMonitorState::new()デフォルトをNoneにstages/poll.rsRateLimitOutcomeenum (Posted/Parked { wakeup_at_unix: i64 }/Failed(String)) 導入handle_rate_limit_retryを sleep 廃止 + park モデルに refactor:until_unix_secs > now→Parked返却 (sleep しない)until_unix_secs <= now→ 即時@coderabbitai review投稿 (Posted、従来挙動)Failedmax_duration_secs > sleep_secsの予算チェック撤廃 (sleep 自体が廃止されたため)format_park_signal(pure 関数): CronCreate 呼び出しテンプレートを含む構造化 stdout signal を生成finalize_parked: state をparked_rate_limitaction に遷移、PARK signal emit、early returnrun_poll_loopをPollContext+run_one_iteration等 11 helper に分割 (50 行ガイドライン準拠)stages/monitor.rsprint_reportを parked verdict 対応に拡張compute_verdict/print_findings_tableに分割PARK signal 形式 (stdout)
cron 式の計算は意図的に Claude Code (model) 側に委ねている: chrono を導入せず lib-pending-file の UTC ベース変換のみ利用するため、local timezone への変換は model に任せる方が確実。
既知の制約 (Bb-2 / Bb-3 で対応予定)
durable: trueで.claude/scheduled_tasks.jsonに永続化されるため fire-on-idle 時に発火するが、SessionStart hook 経由で pending wakeup を能動 catch-up するのは Bb-3finalize_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: cleancargo fmt: appliedpnpm build:all: success (.claude/cli-pr-monitor.exe配置済)pnpm push): APPROVED, 0 findings + 1 observation (上記 OBS-1 記載済)関連リンク
docs/coderabbit-monitoring-efficiency.md— 着手決定セクション + 進捗更新docs/todo5.md— Bb-1 (順位 53) 作業詳細Summary by CodeRabbit
Bug Fixes
Refactor
Documentation
Tests