feat(cli-pr-monitor): 重複起動 file lock + ポーリング間隔延長 (Phase 3 / 順位 12) + Bundle V 登録 - #96
Conversation
|
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 (2)
📝 WalkthroughWalkthroughcli-pr-monitor のデフォルトポーリング間隔を120秒→180秒に延長し、プロセス重複を防ぐファイルベースのリポジトリロックを追加。関連ドキュメントの優先度表記を更新し、Tier 3 のドキュメント整合タスクを追加。 Changes
Sequence Diagram(s)sequenceDiagram
participant CLI as "CLI / main"
participant Lock as "lock module"
participant FS as "Filesystem (.claude/pr-monitor.lock)"
participant Monitor as "start_monitoring"
CLI->>Lock: acquire("start_monitoring")
Lock->>FS: create_new() or read lock file
alt created (Acquired)
FS-->>Lock: wrote lock (PID, start_time, mode)
Lock-->>CLI: Acquired(guard)
CLI->>Monitor: proceed with monitoring (holds lock)
Monitor-->>Lock: release on Drop at end
else exists and fresh (Busy)
FS-->>Lock: read holder PID + age
Lock-->>CLI: Busy(holder_pid, age)
CLI->>CLI: log holder and exit (code 0)
else stale or corrupt (Overwrite / takeover)
FS-->>Lock: overwrite with new lock
Lock-->>CLI: Acquired(guard)
CLI->>Monitor: proceed with monitoring (holds lock)
else io error (Unavailable)
FS-->>Lock: I/O error
Lock-->>CLI: Unavailable(reason)
CLI->>Monitor: proceed without lock
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 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. Review rate limit: 0/1 reviews remaining, refill in 60 minutes.Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/cli-pr-monitor/src/lock.rs`:
- Around line 167-205: parse_iso8601 currently forwards parsed
month/day/hour/minute/second into days_from_epoch/unix_timestamp without
validating ranges, causing panics for malformed timestamps like
"2026-99-01T00:00:00Z"; update parse_iso8601 to validate that month is 1..=12,
hour is 0..=23, minute and second are 0..=59, and that day is within the valid
number of days for the parsed month (accounting for is_leap for February), and
return None if any check fails so callers treat invalid parse as stale instead
of panicking; keep days_from_epoch/unix_timestamp unchanged but rely on
parse_iso8601 to guarantee valid inputs.
- Around line 75-109: The code currently returns LockResult::Acquired even when
filesystem ops fail (create_dir_all, write_all, create_new, or takeover write),
which falsely signals a held lock; change this to return a distinct failure
state (e.g. add LockResult::Unavailable or LockResult::IoError) instead of
Acquired in all error branches where we couldn't create or write the lock (refer
to functions/builders: build_lock_content, read_fresh_lock, MonitorLock and the
match arms handling OpenOptions::new().create_new and std::fs::write); update
the match arms so any I/O error paths (create_dir_all failure, f.write_all
error, create_new non-AlreadyExists errors, and takeover write failure) return
the new LockResult variant, and then update the caller in monitor.rs to treat
that variant as "skip/explicit failure" rather than "Acquired."
🪄 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: 57f5b89e-8257-4e4b-8b8b-eecde6288f2e
📒 Files selected for processing (6)
docs/todo.mddocs/todo3.mdsrc/cli-pr-monitor/src/config.rssrc/cli-pr-monitor/src/lock.rssrc/cli-pr-monitor/src/main.rssrc/cli-pr-monitor/src/stages/monitor.rs
PR #88 で実証された rate-limit 浪費 (Claude Code Max を 1 時間で 40% 消費) への 直接対策。tool 側の polling 動作を 2 軸で改善し、Polling anti-pattern 検出 (PR #86 T1-1, 完了済) と組み合わせて 3 層の rate-limit 抑制を成立させる。 ## 順位 12 本実装 ### 重複起動 file lock (新 module: src/cli-pr-monitor/src/lock.rs) - `start_monitoring` の polling + takt 並走を `.claude/pr-monitor.lock` で 1 アクティブ監視にゲート - atomic create-or-fail (`OpenOptions::create_new`) で TOCTOU race を排除 - AlreadyExists 時のみ既存 lock の stale 判定にフォールバック - fresh (start_time が threshold 内): Busy 返却 → caller は no-op exit - stale (threshold 超過 / parse 不能): overwrite で takeover - stale_threshold = 1800s (max_duration_secs 600s の 3x 安全マージン) - Drop guard で正常終了時 cleanup、crash 時は次インスタンスが stale 経由で takeover - guard 対象は `start_monitoring` のみ。`--observe` (read-only) と `--mark-notified` (one-shot mutation) は対象外で並走可能 ### ポーリング間隔延長 (config.rs) - DEFAULT_POLL_INTERVAL を 120s → 180s - 単独セッションでも polling 回数を 5 → ~3 サイクル/監視に削減 (~40% 削減) - max_duration_secs (600s) は維持 ### ISO 8601 parser (lock.rs 内蔵) - chrono 依存を増やさず手書き parse で start_time の age を計算 - うるう年・月日・時分秒を正確にハンドル (`is_leap` / `days_from_epoch` テスト済) - util::utc_now_iso8601() の出力 format と round-trip することを test で検証 ## テスト - 9 件の lock test 追加 (cli-pr-monitor 全体 106 passed): - acquire / drop の基本動作 - fresh lock が second acquire を block - stale lock の takeover (1980 timestamp で確実に stale) - corrupt lock (parse 不能) の takeover - **concurrent_acquire_only_one_wins**: 8 thread 同時 acquire で 1 つだけが Acquired になることを検証 (advisor 指摘の TOCTOU race を真に検証する test) - lock_format_matches_util_iso8601: util との format alignment 確認 - parse_iso8601 / is_leap 単体動作 ## docs/todo 系列の更新 (Bundle V 登録 + Phase 3 完了反映、同 PR 同梱) ユーザー方針 (memory: feedback_minimize_pr_count_during_rate_limit) により、 直前 session の Bundle V 登録 (順位 31-33 の table 追加 + 詳細エントリ) と本 Phase 3 完了反映 (順位 12 削除 + narrative 整合化) を同 PR で land する。 - docs/todo.md: 順位 12 削除 + Bundle V 順位 31-33 追加 + narrative 三段構え (Polling anti-pattern + cli-pr-monitor lock + post-pr-review rate-limit) に更新 - docs/todo3.md: 順位 12 詳細エントリ削除 + Bundle V 詳細 3 件追加 ## 期待効果 - 重複起動セッション間の polling 並走を確実に防止 - DEFAULT_POLL_INTERVAL 延長で単独セッションの polling 総回数を ~40% 削減 - rate-limit 抑制 3 層 (Claude 側 polling 禁止 + tool 側ポーリング頻度削減 + review 単位の自動再トリガー [Tier 2 残]) のうち 2 層を完成
Resolved findings: - [Major] src/cli-pr-monitor/src/lock.rs:109 `Acquired` を「実際には lock できていない」ケースにも返しています。 - [Major] src/cli-pr-monitor/src/lock.rs:205 壊れた `start_time` で panic します。
073de72 to
0a6b2cd
Compare
Resolved findings: - [Major] src/cli-pr-monitor/src/lock.rs:109 `Acquired` を「実際には lock できていない」ケースにも返しています。 - [Major] src/cli-pr-monitor/src/lock.rs:205 壊れた `start_time` で panic します。
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/cli-pr-monitor/src/lock.rs (1)
90-110:⚠️ Potential issue | 🟠 Major書き込み失敗後も
Acquiredを返してしまっています。Line 95 / Line 110 では、実際には lock file を正常に残せていないのに caller が「lock 保持中」と解釈します。空/部分書き込みの file は次回
parse失敗で stale 扱いになるので、重複起動防止がここで抜けます。write_all/ takeoverwriteの失敗はUnavailableに寄せ、新規作成側は部分 file を削除して戻す方が安全です。差分案
Ok(mut f) => { if let Err(e) = f.write_all(content.as_bytes()) { - log_info(&format!("[lock] 新規 lock 書き込み失敗 (継続): {}", e)); + log_info(&format!("[lock] 新規 lock 書き込み失敗: {}", e)); + let _ = std::fs::remove_file(&path); + return LockResult::Unavailable { + reason: e.to_string(), + }; } LockResult::Acquired(MonitorLock { path }) } @@ if let Err(e) = std::fs::write(&path, content) { - log_info(&format!("[lock] takeover 書き込み失敗 (継続): {}", e)); + log_info(&format!("[lock] takeover 書き込み失敗: {}", e)); + return LockResult::Unavailable { + reason: e.to_string(), + }; } LockResult::Acquired(MonitorLock { path })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/cli-pr-monitor/src/lock.rs` around lines 90 - 110, The code returns LockResult::Acquired even when writes fail (in the OpenOptions branch using f.write_all and in the takeover branch using std::fs::write), which can leave a corrupt/empty lock file; change the logic so that any write error causes the function to return LockResult::Unavailable (or equivalent) instead of Acquired, and for the new-create branch delete the partial file if write_all fails before returning Unavailable; keep the existing fresh-check using read_fresh_lock/stale_threshold_secs and only return MonitorLock with LockResult::Acquired after a successful write.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/cli-pr-monitor/src/lock.rs`:
- Around line 354-356: このテストは thread 内で matches!(acquire_at(p, "concurrent",
1800), LockResult::Acquired(_)) を直接評価しているため取得した MonitorLock が即時 drop されて flaky
です。修正するには各 thread が acquire_at の戻り値(LockResult)を保持してから全 thread を join し、その後で各
LockResult を調べて exactly one が Acquired であることをアサートしてください; 該当する識別子は acquire_at,
LockResult::Acquired, MonitorLock, handles, thread::spawn,
b.wait()(および同様のブロックが存在する 359–369 範囲)です。
- Around line 163-170: The current parse_age_secs function uses
now.saturating_sub(then) which produces age=0 for future timestamps and prevents
treating future/broken timestamps as stale; modify parse_age_secs (and its use
of parse_iso8601) to detect if then > now and return None in that case,
otherwise return Some(now - then) as i64—ensure you compare the same integer
types (after converting SystemTime to i64 seconds) and avoid saturating_sub so
future timestamps are treated as parse failures/stale.
---
Duplicate comments:
In `@src/cli-pr-monitor/src/lock.rs`:
- Around line 90-110: The code returns LockResult::Acquired even when writes
fail (in the OpenOptions branch using f.write_all and in the takeover branch
using std::fs::write), which can leave a corrupt/empty lock file; change the
logic so that any write error causes the function to return
LockResult::Unavailable (or equivalent) instead of Acquired, and for the
new-create branch delete the partial file if write_all fails before returning
Unavailable; keep the existing fresh-check using
read_fresh_lock/stale_threshold_secs and only return MonitorLock with
LockResult::Acquired after a successful write.
🪄 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: 23020d50-2f44-416f-9f41-79d2abd5b487
📒 Files selected for processing (6)
docs/todo.mddocs/todo3.mdsrc/cli-pr-monitor/src/config.rssrc/cli-pr-monitor/src/lock.rssrc/cli-pr-monitor/src/main.rssrc/cli-pr-monitor/src/stages/monitor.rs
🚧 Files skipped from review as they are similar to previous changes (3)
- src/cli-pr-monitor/src/main.rs
- src/cli-pr-monitor/src/stages/monitor.rs
- docs/todo.md
Finding D (Major, parse_age_secs): 未来日時 (時計巻き戻し / 破損 future timestamp) が saturating_sub で age=0 となり永続 fresh 扱いになる bug を修正。明示的に then > now を None (stale 扱い) で返し、crash recovery が機能するようにする。 test future_timestamp_lock_is_taken_over を追加。 Finding E (Minor, concurrent test flakiness): 1 barrier 構成だと先行 thread の guard が判定後に即 drop されて後続 thread が逐次 Acquired する race window が 存在した。start barrier + finish barrier の 2 barrier 構成にし、全 thread が 判定終了まで Acquired guard を保持するよう修正。 両者とも CR の suggestion 準拠。lock test 全 13 件 pass、clippy clean。
0a6b2cd to
66246c1
Compare
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
CR review @ commit e7b8d49 で検出された 3 Major + 1 Nitpick を修正。 ## 修正内容 ### Finding 1 (Major): parse_rate_limit に push_time フィルタ追加 過去セッションの rate-limit comment を新セッション開始時に誤って拾うバグを修正。 `comment.created_at >= push_time` フィルタを追加し、parse_new_comments / parse_findings と同じ規則に揃える。これにより本 PR 検証中に観測された 22 分遅延の根因の一つが解消される。 ### Finding 2 (Major): parse_new_comments で rate-limit comment 除外 rate-limit comment が new_comments にカウントされると `decide()` が action_required を早期 return し、poll.rs の rate-limit retry 経路に入らずに監視終了するバグ。 "Rate limit exceeded" を含む comment を新規コメント集計から除外する。22 分遅延のもう 一つの根因。 ### Finding 3 (Major): write_state 失敗時の自動 retry 停止 retrigger 後の state 永続化に失敗すると、次 iteration で `rate_limit_retries` と `rate_limit_last_retriggered_at` の復元に失敗し、dedup と max_retries が壊れて `@coderabbitai review` 重複投稿の可能性がある。失敗時は action_required で抜ける。 ### Finding 4 (Nitpick): docs/todo.md 順位 絶対参照削除 ADR-033 の「絶対番号は table のみに保持」原則に従い、Bundle W/X 説明文から `(順位 34)` `(順位 35)` 等の絶対参照を削除しタスク名参照に置換。 ### .gitignore: pr-monitor.lock 除外 cli-pr-monitor の重複起動防止 lock file (PR #88 / #96 で導入) が .gitignore 漏れで PR snapshot に混入する事故を防ぐ。 ## テスト - check-ci-coderabbit: 59 tests pass (rate_limit_filters_out_past_session_comments, rate_limit_includes_comment_at_exact_push_time, comments_excludes_rate_limit を追加) - cli-pr-monitor: 118 tests pass - clippy + fmt clean (変更パッケージ)
CR review @ commit 79b7c3d で検出された 3 Major + 1 Minor のうち Finding 1 (updated_at) を除く 3 件を修正。 ## 修正内容 ### Finding 2 (Major): rate-limit sleep を max_duration で cap `handle_rate_limit_retry` に `remaining_monitor_secs` 引数を追加し、 sleep が監視残り予算を超える場合は Err を返して retry を停止する。 これまでは max_duration を素通りして 30 分超ブロックする可能性があった。 ### Finding 3 (Major): handle_rate_limit_retry を Result 化 gh pr comment 投稿失敗 / PR 番号未確定の場合に retries++ や last_retriggered_at 更新を実施しないよう、関数を Result<(), String> に 変更。caller は Ok 時のみ dedup key を更新し、Err 時は action_required で抜ける。これにより失敗 retrigger が dedup で perma-skip 化する silent failure を防ぐ。 ### Finding 4 (Minor): docs/todo.md 第2層 PR 参照に #96 追記 "(PR #88 T2-4、完了済)" → "(PR #88 T2-4 / #96、完了済)" に履歴整合。 ## テスト追加 (2 件) - rate_limit_retry_returns_err_when_sleep_exceeds_budget: remaining=60s だが sleep=600s 必要なケースで Err を返し state 不変を確認 - rate_limit_retry_returns_err_when_pr_number_missing: PR 番号未確定で Err を返し state 不変を確認 ## 未対応 (任意) Finding 1 (updated_at): CR が rate-limit comment を編集する挙動は実観測なし。 将来的な堅牢性向上のため todo 化候補。
* feat(cli-pr-monitor): rate-limit 自動検出 + 再トリガーロジック (Phase 4 / 順位 13) PR #89 T2-1 の自動化。CodeRabbit が `Rate limit exceeded` コメントを投稿した 場合、reset 時刻 + 60s buffer まで sleep し `@coderabbitai review` を再投稿する。 ## 実装 - check-ci-coderabbit: rate-limit comment を検出し reset 時刻を計算する `parse_rate_limit` を追加。`Please wait N minutes M seconds` の正規表現抽出 + ISO 8601 → unix epoch 秒の手動パース (chrono 依存追加なし)。 - cli-pr-monitor: poll loop に rate-limit retry ブランチを追加 - `RateLimitConfig`: auto_retry_enabled (default true) + max_retries (default 3) - `state.rate_limit_retries`: 累積 retry 回数を state.json で persist - `state.rate_limit_last_retriggered_at`: dedup key (advisor 指摘対応) - `handle_rate_limit_retry`: sleep → gh pr comment 投稿 → counter++ ## dedup の必要性 (advisor finding) `comment_created_at` で dedup しないと、同一 rate-limit comment が iteration を 跨いで PR コメント一覧に残り、`(until_unix_secs - now).max(0) = 0` となって 即時 retrigger を繰り返す。結果 max_retries=3 が数秒で消費され誤って `action_required` で抜ける。`rate_limit_last_retriggered_at` で同じ created_at を skip し、CR が新しい rate-limit comment を投稿した時点で再度対象にする。 ## テスト - check-ci-coderabbit: rate-limit detection / parsing 11 tests 追加 (56 passed) - cli-pr-monitor: poll dedup + state persistence 4 tests 追加 (116 passed) ## 完了タスク - 順位 13 を docs/todo.md table から削除、todo3.md detail 削除 - 順位 19 (REJECT-ESCALATE) が rate-limit critical 系の最後の Tier 2 残 * fix(cli-pr-monitor): CodeRabbit 指摘 (PR #97) 反映 + 誤混入 lock 削除 - handle_rate_limit_retry の log format 修正: retry={}/{} の 2 番目に rl.until_unix_secs (unix 秒) が渡されていたバグ。max_retries に修正。 以前は "retry=1/1735689600" のような意味不明な出力になっていた。 - RateLimitConfig の parse test 追加 (defaults + custom)。既存 config_fix_defaults / config_fix_custom と同パターンで網羅。 - 前 commit で誤って snapshot された .claude/pr-monitor.lock を削除 (.gitignore 追加は後続 commit で実施)。 * fix(cli-pr-monitor): CodeRabbit round 2 指摘 (PR #97) 反映 + .gitignore 整備 CR review @ commit e7b8d49 で検出された 3 Major + 1 Nitpick を修正。 ## 修正内容 ### Finding 1 (Major): parse_rate_limit に push_time フィルタ追加 過去セッションの rate-limit comment を新セッション開始時に誤って拾うバグを修正。 `comment.created_at >= push_time` フィルタを追加し、parse_new_comments / parse_findings と同じ規則に揃える。これにより本 PR 検証中に観測された 22 分遅延の根因の一つが解消される。 ### Finding 2 (Major): parse_new_comments で rate-limit comment 除外 rate-limit comment が new_comments にカウントされると `decide()` が action_required を早期 return し、poll.rs の rate-limit retry 経路に入らずに監視終了するバグ。 "Rate limit exceeded" を含む comment を新規コメント集計から除外する。22 分遅延のもう 一つの根因。 ### Finding 3 (Major): write_state 失敗時の自動 retry 停止 retrigger 後の state 永続化に失敗すると、次 iteration で `rate_limit_retries` と `rate_limit_last_retriggered_at` の復元に失敗し、dedup と max_retries が壊れて `@coderabbitai review` 重複投稿の可能性がある。失敗時は action_required で抜ける。 ### Finding 4 (Nitpick): docs/todo.md 順位 絶対参照削除 ADR-033 の「絶対番号は table のみに保持」原則に従い、Bundle W/X 説明文から `(順位 34)` `(順位 35)` 等の絶対参照を削除しタスク名参照に置換。 ### .gitignore: pr-monitor.lock 除外 cli-pr-monitor の重複起動防止 lock file (PR #88 / #96 で導入) が .gitignore 漏れで PR snapshot に混入する事故を防ぐ。 ## テスト - check-ci-coderabbit: 59 tests pass (rate_limit_filters_out_past_session_comments, rate_limit_includes_comment_at_exact_push_time, comments_excludes_rate_limit を追加) - cli-pr-monitor: 118 tests pass - clippy + fmt clean (変更パッケージ) * fix(cli-pr-monitor): CodeRabbit round 3 指摘 (PR #97) 反映 CR review @ commit 79b7c3d で検出された 3 Major + 1 Minor のうち Finding 1 (updated_at) を除く 3 件を修正。 ## 修正内容 ### Finding 2 (Major): rate-limit sleep を max_duration で cap `handle_rate_limit_retry` に `remaining_monitor_secs` 引数を追加し、 sleep が監視残り予算を超える場合は Err を返して retry を停止する。 これまでは max_duration を素通りして 30 分超ブロックする可能性があった。 ### Finding 3 (Major): handle_rate_limit_retry を Result 化 gh pr comment 投稿失敗 / PR 番号未確定の場合に retries++ や last_retriggered_at 更新を実施しないよう、関数を Result<(), String> に 変更。caller は Ok 時のみ dedup key を更新し、Err 時は action_required で抜ける。これにより失敗 retrigger が dedup で perma-skip 化する silent failure を防ぐ。 ### Finding 4 (Minor): docs/todo.md 第2層 PR 参照に #96 追記 "(PR #88 T2-4、完了済)" → "(PR #88 T2-4 / #96、完了済)" に履歴整合。 ## テスト追加 (2 件) - rate_limit_retry_returns_err_when_sleep_exceeds_budget: remaining=60s だが sleep=600s 必要なケースで Err を返し state 不変を確認 - rate_limit_retry_returns_err_when_pr_number_missing: PR 番号未確定で Err を返し state 不変を確認 ## 未対応 (任意) Finding 1 (updated_at): CR が rate-limit comment を編集する挙動は実観測なし。 将来的な堅牢性向上のため todo 化候補。 * fix(check-ci-coderabbit): rate-limit 計算を updated_at 基準に変更 (PR #97 round 3 Finding 1 実観測対応) CR が rate-limit comment を編集して wait 時間を更新するケースが本 PR の dogfood で 実観測された (created_at=2026-04-30T11:11:51Z, updated_at=2026-04-30T14:38:32Z で "wait 21 minutes" に編集)。created_at 基準だと reset 時刻を 3 時間以上前として誤算定し、 premature retrigger → CR 再 rate-limit → comment edit のループに陥る危険があった。 ## 修正内容 - GhComment に updated_at フィールドを追加 - rate_limit_event_time(): updated_at fallback created_at を返すヘルパー追加 - parse_rate_limit: 計算基準を event_time に変更 (フィルタ / sort / until 計算) - RateLimitInfo.comment_created_at: 値の意味を「event_time」に拡張 (フィールド名は維持) - state.rs RateLimitState.comment_created_at の doc 更新 ## テスト追加 (3 件) - rate_limit_uses_updated_at_when_present: 実観測ケース (created_at != updated_at) で updated_at 基準計算を確認 - rate_limit_falls_back_to_created_at_when_updated_at_missing: 既存挙動の後方互換性を確認 - rate_limit_edited_comment_yields_new_dedup_key: 編集前後で dedup key が変化することを確認 (新 wait 時間で再 trigger 可能)
…除) (#199) * feat(cli-pr-monitor): PastTime newtype + proptest for parse_iso8601/from_parts (Bundle W) Bundle W (順位 34 PBT + 順位 35 PastTime newtype) を Tier 1 として独立 land。 PR #96 で実証された Finding D class (saturating_sub silent semantic mismatch) を 型層で再発不能化し、proptest で regression net を確立。 ## 順位 35: PastTime newtype src/cli-pr-monitor/src/lock.rs に PastTime { epoch_secs, captured_now } を導入。 `from_iso8601_now` / `from_parts` で「parse 成功 + then <= now」の 2 ステップを construction に閉じ込め、`age_secs()` が常に非負となる invariant を構造的に保証。 旧 `parse_age_secs(iso8601) -> Option<i64>` は削除し、`read_fresh_lock` を PastTime ベースに refactor。public API への影響なし (lock module 内 closure)。 ## 順位 34: proptest properties src/cli-pr-monitor/Cargo.toml の [dev-dependencies] に proptest = "1" を追加。 lock.rs の #[cfg(test)] mod proptests に 5 properties を記述: - P1 past_time_age_is_correct_when_in_past: then <= now で age_secs == now - then - P2 past_time_rejects_future: then > now で必ず None (Finding D 直接対応) - P3 parse_iso8601_never_panics: 任意 string で panic しない - P4 parse_iso8601_rejects_pre_epoch_year: year < 1970 を必ず reject - P5 parse_iso8601_accepts_well_formed: 有効範囲内の正規 ISO 8601 を必ず accept PastTime 単体 unit test 4 件 (accepts_past / accepts_equal / rejects_future / accepts_unix_epoch_origin / rejects_far_future_year_9999) も追加。 実行時間: cargo test -p cli-pr-monitor lock:: で 0.04s (todo4.md 完了基準 「pre-push pipeline +1 秒以内」を充足)。 ## 依存解除 Bundle W は元来「順位 19 (REJECT-ESCALATE) land 後着手」とユーザー指示があったが、 順位 19 自身が Status update 2026-06-06 で「5-10 PR baseline 観測フェーズ」に移行 した結果、Tier 1 → Tier 2 の priority inversion + 待機解除タイミング未定でデッド ロックに近い状態だった。 依存解除の根拠 (advisor 検証済): - ADR-037 (fix-trust shortcut, PR #106 land): `convergence_verdict: fully_resolved` で COMPLETE 直行 → iteration が verdict ベースで有限化、無限 loop 経路は解消済 - ADR-043 (fail-closed 原則, PR #194 land): security/quality gate の semantic を構造化 - PR #194 (mechanical gate 強化): merge 前の検証層を拡充 これにより PBT 由来の test 失敗を fix loop が処理しきれず暴走する旧懸念は解消済。 docs/todo-summary.md 順位 34 row および docs/todo4.md 順位 34 実行優先度行に 依存解除の根拠を明記。 ## scope 外 - Finding E (concurrency race): proptest は data generation には強いが thread interleaving 網羅は苦手 (todo4.md 詰まっている箇所に既記載)。Bundle X (順位 36 cargo-mutants + 順位 37 stress runner) に委譲 - 派生プロジェクト deploy 計画: Bundle W land 後の別 task として todo 登録予定 ## 完了確認 - cargo build -p cli-pr-monitor: 成功 - cargo test -p cli-pr-monitor lock::: 23 passed (proptest 5 + PastTime 4 + 既存 14) - cargo clippy -p cli-pr-monitor --all-targets -- -D warnings: clean Refs: 順位 34, 35 (todo-summary.md), todo4.md Bundle W entries * docs(todo): Bundle W (順位 34 + 35) land 完了に伴い削除
PR #199 (Bundle W) の post-merge-feedback report (.claude/feedback-reports/199.md) で analyzer が ✅ 採用候補とした 2 件を、ユーザー承認 (2026-06-08) を経て docs/todo*.md 系列に登録する。 ## 順位 197: hooks-session-start orphan age 計算に proptest 追加 (T2-1) Bundle W (PR #199) で `cli-pr-monitor::lock` に PastTime newtype + proptest properties を 導入し、`saturating_sub` silent semantic mismatch (Finding D) を構造的に排除した。 同じ bug class が `src/hooks-session-start/src/main.rs:236` の orphan age 計算 (`now_unix.saturating_sub(start_unix)`) にも存在し、clock rewind / future timestamp で age=0 → orphan reaper が「young」判定でスキップ → `.failed` marker 未生成 → ADR-030 L2 recovery 停止という具体的 failure chain が確認済。Bundle W の pattern を hooks-session-start に展開する。 - Tier 2 / Effort M / Severity High / Frequency Medium / Adoption Risk None - proptest 1.x は既存 dev-dependencies、新規依存追加は hooks-session-start のみ ## 順位 198: ADR-NNN Timestamp invariant safety (T3-2) PR #96 Finding D + PR #199 Bundle W で同型 bug class が 2 件観測 (Frequency Medium)。 「時刻計算における silent failure class と型レベル防御」を永続化し、派生プロジェクト (techbook-ledger / auto-review-fix-vc) への transferability を確保する ADR を新設。 - Tier 3 / Effort M / Severity Medium / Frequency Medium / Adoption Risk None - 順位 135 codified placeholder policy 適用 (ADR 番号は land 時 PR で確定) - CLAUDE.md ADR list 追記 ## 変更ファイル - docs/todo10.md: 順位 197/198 詳細 entry を 2 セクション追加 - docs/todo-summary.md: table に順位 197/198 行を追加
PR #199 (Bundle W) の post-merge-feedback report (.claude/feedback-reports/199.md) で analyzer が ✅ 採用候補とした 2 件を、ユーザー承認 (2026-06-08) を経て docs/todo*.md 系列に登録する。 ## 順位 197: hooks-session-start orphan age 計算に proptest 追加 (T2-1) Bundle W (PR #199) で `cli-pr-monitor::lock` に PastTime newtype + proptest properties を 導入し、`saturating_sub` silent semantic mismatch (Finding D) を構造的に排除した。 同じ bug class が `src/hooks-session-start/src/main.rs:236` の orphan age 計算 (`now_unix.saturating_sub(start_unix)`) にも存在し、clock rewind / future timestamp で age=0 → orphan reaper が「young」判定でスキップ → `.failed` marker 未生成 → ADR-030 L2 recovery 停止という具体的 failure chain が確認済。Bundle W の pattern を hooks-session-start に展開する。 - Tier 2 / Effort M / Severity High / Frequency Medium / Adoption Risk None - proptest 1.x は既存 dev-dependencies、新規依存追加は hooks-session-start のみ ## 順位 198: ADR-NNN Timestamp invariant safety (T3-2) PR #96 Finding D + PR #199 Bundle W で同型 bug class が 2 件観測 (Frequency Medium)。 「時刻計算における silent failure class と型レベル防御」を永続化し、派生プロジェクト (techbook-ledger / auto-review-fix-vc) への transferability を確保する ADR を新設。 - Tier 3 / Effort M / Severity Medium / Frequency Medium / Adoption Risk None - 順位 135 codified placeholder policy 適用 (ADR 番号は land 時 PR で確定) - CLAUDE.md ADR list 追記 ## 変更ファイル - docs/todo10.md: 順位 197/198 詳細 entry を 2 セクション追加 - docs/todo-summary.md: table に順位 197/198 行を追加
…-1/T3-2 採用 (#200) * feat(cli-docs-lint): priority_inversion validator — Tier 1→Tier 2 依存を機械検知 PR #199 (Bundle W) で実観測した「Tier N が Tier N+k に依存 + 待ち先 Tier N+k 自身が 観測フェーズ」パターンを cli-docs-lint に validator 化。決定論的検出層で Claude 判断 介入なし (`feedback_pipeline_over_rules.md` 適用)。 ## 実装 - `src/cli-docs-lint/src/priority_inversion.rs` (新規): todo-summary.md table を parse し - 順位/Tier 抽出 (`parse_row`, `parse_tier`) - 依存記述から 順位 NN 参照を抽出 (`extract_referenced_ranks`、複合 `順位 NN/MM` 対応) - 「なし」プレフィックスで context note を排除 (`has_no_dependency_prefix`) - 依存先 Tier > 自分の Tier かつ resolved-marker なしを violation 化 - `src/cli-docs-lint/src/lib.rs`: `priority_inversion` module を export - `src/cli-docs-lint/src/main.rs`: `--check priority-inversion` mode + `all` 経路統合 + help / describe_mode に追記 ## resolved-marker 設計 依存記述内で「順位 NN ... land 済 / 完了 / retired / 採用昇格済」が 80 char window 以内に現れる場合 resolved 扱い (= inversion check skip)。「land 済 (2026-06-07)」のような 日付付きマーカーや「Bundle X land 済」のような bundle-level マーカーをカバー。 ## 性能 (F-2 対応、pre-push reviewer non-blocking finding) `parse_tier` / `extract_referenced_ranks` の regex は `std::sync::LazyLock<Regex>` で module 初期化時に 1 度だけ compile (per-row 再 compile を回避)。既存 codebase の OnceLock 利用 (cli-pr-monitor) と整合する std::sync::* family。 ## テスト fixture 設計 (F-3 対応、pre-push reviewer non-blocking finding) `check_content_skips_when_referenced_rank_missing_from_table` テストの fixture を `"順位 19 land 後推奨"` (resolved-marker 非含有) に変更。missing-rank 経路を厳密に exercise する形に修正し、将来 fixture に rank 19 行を追加した際の test 経路 silent shift (missing-rank → resolved-marker) を防止。 ## prefix-match 防御 (pre-push takt-fix で auto 補正) `has_resolved_marker_after` に `c.is_ascii_digit()` ガードを追加し、`"順位 19"` が `"順位 195"` 等の prefix match で誤検出するバグを防止。専用 unit test 追加。 ## テスト & dogfood - 38 unit tests pass (priority_inversion 18 + preamble 9 + cross_ref 11) - 現 docs/todo-summary.md に対し 0 violations (false positive なし) - pnpm lint:docs OK (preamble + cross-ref + priority-inversion) ## 統合 - `pnpm lint:docs` 経由で `--check all` 実行時に自動実行 - `.claude/cli-docs-lint.exe` rebuild + deploy 済 (release profile) - ADR-039 試験運用パターン継承 (kill-switch `CLI_DOCS_LINT_DISABLE=1`、3-5 PR dogfood 後に default-ON 昇格 / 却下を判定) * docs(todo): 順位 19 (REJECT-ESCALATE) retire — ADR-037/043 + PR #194 land で代替経路実現済、PR #194-後 8 runs で escalation 候補 0 件 PR #199 (Bundle W) session 内合意 (ユーザー指示 2026-06-07): 「A の PR マージまで進ん だら、B. 順位 19 自体の Tier 再評価 のタスク化を進める」を実行し、実測値に基づき (C) retire を決定。 ## 決定根拠 (empirical data) PR #194 (mechanical gate 強化) land 後の takt run logs を集計: | Run | iteration 上限到達 | supervise step 到達 | escalation 候補 | |---|---|---|---| | 8 runs (pre-push-review 7 + post-pr-review 1) | 0/8 | 0/8 | 0/8 | REJECT-ESCALATE が解決するはずの「iteration 上限到達 + supervise/fix_supervisor の 無限ループ」が **PR #194 land 以降 1 件も観測されていない**。代替経路で十分実用に 耐えていることが定量的に確認できた: - ADR-037 (fix-trust shortcut、PR #106 land): `convergence_verdict: fully_resolved` で COMPLETE 直行 → iteration が verdict ベースで有限化 - ADR-043 (fail-closed 原則、PR #194 land): security/quality gate semantics 構造化 - PR #194 (mechanical gate 強化): merge 前 clippy + 空 commit sweep の決定論層追加 ## 変更内容 - docs/todo3.md: 順位 19 entry block を削除 (-46 行) - docs/todo-summary.md: table から 順位 19 row を削除 (-1 行) 順位 19 を gate にしていた Tier 1 (旧 順位 34/35) は既に PR #199 で priority inversion 解除済のため、本 retire で残るブロッキングはない。 ## 関連 PR / ADR - PR #199 (Bundle W、priority inversion 解除) - ADR-037 fix-trust shortcut - ADR-043 Security/Quality Gate Fail-Closed 原則 - PR #194 mechanical gate 強化 (clippy + 空 commit sweep) * docs(todo): post-merge-feedback T2-1/T3-2 採用 — 順位 197/198 追加 PR #199 (Bundle W) の post-merge-feedback report (.claude/feedback-reports/199.md) で analyzer が ✅ 採用候補とした 2 件を、ユーザー承認 (2026-06-08) を経て docs/todo*.md 系列に登録する。 ## 順位 197: hooks-session-start orphan age 計算に proptest 追加 (T2-1) Bundle W (PR #199) で `cli-pr-monitor::lock` に PastTime newtype + proptest properties を 導入し、`saturating_sub` silent semantic mismatch (Finding D) を構造的に排除した。 同じ bug class が `src/hooks-session-start/src/main.rs:236` の orphan age 計算 (`now_unix.saturating_sub(start_unix)`) にも存在し、clock rewind / future timestamp で age=0 → orphan reaper が「young」判定でスキップ → `.failed` marker 未生成 → ADR-030 L2 recovery 停止という具体的 failure chain が確認済。Bundle W の pattern を hooks-session-start に展開する。 - Tier 2 / Effort M / Severity High / Frequency Medium / Adoption Risk None - proptest 1.x は既存 dev-dependencies、新規依存追加は hooks-session-start のみ ## 順位 198: ADR-NNN Timestamp invariant safety (T3-2) PR #96 Finding D + PR #199 Bundle W で同型 bug class が 2 件観測 (Frequency Medium)。 「時刻計算における silent failure class と型レベル防御」を永続化し、派生プロジェクト (techbook-ledger / auto-review-fix-vc) への transferability を確保する ADR を新設。 - Tier 3 / Effort M / Severity Medium / Frequency Medium / Adoption Risk None - 順位 135 codified placeholder policy 適用 (ADR 番号は land 時 PR で確定) - CLAUDE.md ADR list 追記 ## 変更ファイル - docs/todo10.md: 順位 197/198 詳細 entry を 2 セクション追加 - docs/todo-summary.md: table に順位 197/198 行を追加 * fix(cli-docs-lint): priority_inversion の resolved-marker window を char-based 化 (CR Major #1) PR #200 CodeRabbit review で指摘された Major finding を修正。 ## Bug `has_resolved_marker_after` の window 計算が以下の 2 点で spec とズレていた: 1. **byte vs char**: `(abs_pos + RESOLUTION_WINDOW_CHARS)` は **byte** 演算。日本語 1 文字 = 3 bytes なので「80 文字 window」のつもりが実質 ~27 文字 window に縮退 2. **window 開始位置**: `&haystack[abs_pos..]` で needle (順位 NN) 自体を window に含めていた。 spec は「順位参照の **直後** から N 文字以内」 具体 failure 例 (CR review 提示): - `"順位 19" + "あ"*40 + " land 済"` で、marker "land 済" は char-distance 41 (window 80 内) - 旧 byte-based: 80 bytes window は ~26 chars のみカバー → marker 圏外 → resolved 扱いされず → **false negative (= inversion 誤検出 → reviewer に noise)** ## Fix ```rust let window_end = haystack[after..] .char_indices() .nth(RESOLUTION_WINDOW_CHARS) .map(|(i, _)| after + i) .unwrap_or(haystack.len()); let window = &haystack[after..window_end]; ``` - 起点を `after` (needle 直後) に変更 - `char_indices().nth(N)` で N 文字目の byte offset を取得 → char-based window 不要になった `next_char_boundary` ヘルパー関数を削除。 ## Test regression test 追加 (`is_resolved_detects_marker_across_multibyte_gap`): - `"順位 19" + "あ".repeat(40) + " land 済"` で resolved 判定が true になることを assert - 旧実装ではこの test は fail する (byte window で marker 圏外) ## Doc update `RESOLUTION_WINDOW_CHARS` の doc に「**文字数** (バイト数ではない)」を明記。 multi-byte でも spec 通り動作することを comment で保証。 ## 検証 - cargo test -p cli-docs-lint: 19 priority_inversion tests pass (新規 regression 1 含む) - cargo clippy -p cli-docs-lint --all-targets -- -D warnings: clean - pnpm lint:docs: OK (preamble + cross-ref + priority-inversion) - .claude/cli-docs-lint.exe rebuild + deploy 済 Refs: CR comment on PR #200 (src/cli-docs-lint/src/priority_inversion.rs:182)
…exception codify (PR #203 follow-up) (#204) * docs(todo): 順位 198 を PR #203 T3-1 採用で 3 観測目に昇格 PR #203 post-merge-feedback Tier 3 #1 (ADR-NNN: Timestamp invariant safety) を採用。 analyzer は新規 entry 提案だが、順位 198 が既に同 ADR 提案として登録済 (PR #199 T3-2) のため、新規追加ではなく既存 entry の data point 強化として merge した。 主な変更: - 動機: 2 件観測 (Medium) → 3 件観測 (High) に Frequency 昇格 - 本タスクの位置づけ: PR #203 T3-1 採用情報 + 既存 entry 強化の判断根拠を追記 - 参照: .claude/feedback-reports/203.md Tier 3 #1 + PR #203 を追加 - 設計決定 § 1 コンテキスト: PR #203 hooks-session-start port を観測実例に追加 - 派生プロジェクト適用: "順位 197 で実装予定" → "PR #203 で実装済" に更新 - 作業計画: PR #96 / #199 / #203 の 3 観測すべてを ADR 実装時に inline cite 順位 194 (task 着手前 grep 確認 rule、PR #196 採用) の初実践例となる。 analyzer の重複提案を運用層で吸収する明示的 pattern。 * docs(adr-039): mechanical lint exception を § 1.b として明記 + checklist 上位判定追加 PR #203 post-merge-feedback で「順位 177 file_size_check が ADR-039 機械適用で default OFF にされ、user 期待と乖離した」事象を発見。順位 147 file_length lint (default ON 固定) と順位 177 file_size_check (default OFF) の asymmetry が 標準パターンの over-application を示した。 主な変更: § 1 (Config opt-in) の改訂 - 「適用対象を明示」する section に再構成 - 「behavior の妥当性が不確定な experimental feature」と適用範囲を限定 - 「採否判定 (採用 / 却下 / 継続) のフェーズが必要なもの」を判定基準として追加 § 1.b 新設 (mechanical lint default ON 許容) - 4 条件 (non-blocking / 決定論 / scope 限定 / recovery hint 明確) すべて満たす機能を § 1 対象外として default ON 配布を許容 - 該当する実装例: 順位 147 file_length lint / 順位 177 file_size_check - 該当しない例: post-merge-feedback (ADR-014/030) / weekly-review (ADR-031) / local-llm-finding-classification (ADR-038) - PR #197 順位 177 の誤適用を本 PR (PR #203 由来) で訂正と明記 § 新規 feature 追加時 checklist (4 点 → 5 点に拡張) - § 0 「上位判定」を最初に追加: 「そもそも § 1 適用対象か?」 - § 1.b 4 条件すべて満たす → default ON で配布、4 点 checklist は skip - 1 つでも欠ける → 従来通り 4 点 mechanical checklist 実施 - 判断に迷う場合は conservative default (default OFF) を選択 - 本判定を skip して機械適用すると order-application 発生 (PR #197 で実観測) 由来: PR #203 post-merge-feedback で発見された systemic 問題への対応。 派生プロジェクトへの自動波及はなし (本 ADR は本リポジトリ専用、`~/.claude/rules/` 配下ではないため)。 * docs(adr-007): Layer 0.5 file_size_check 追記を削除 順位 177 file_size_check は ADR-007 で扱う「正規表現層 / AST 層」の判断フロー対象外で あり、metadata-only check (`std::fs::metadata.len()`) という性質上、独立した Layer 区分を設ける積極的理由がない。「Layer 0.5」概念を ADR に codify することで: - 後続の metadata-only check 追加時に Layer 0.5 への配置判断を毎回迫る - ADR-007 本体の Q1/Q2/Q3 判断フロー (regex / AST) との整合性が複雑化 - ADR-039 opt-in pattern 言及が「導入リスク」未定義のまま記載されている という systemic な over-abstraction の温床になっていた。本 PR で「順位 177 は単純な custom linter の一つとして扱う」方針 (ユーザー判断、2026-06-12) に従い、Layer 0.5 追記を削除する。今後 file_size_check 系の linter を追加する場合は ADR-007 の通常 判断フローに従い、必要なら都度 ADR 改訂で対応する。 * fix(hooks-config): file_size_check を default ON 化 + ADR 参照表記修正 ADR-039 § 1.b (mechanical lint 例外、本 PR で同時 codify) に従い、順位 177 file_size_check を default ON で配布する。順位 147 file_length lint と同 pattern。 主な変更: enabled = false → enabled = true - 4 条件 (non-blocking / 決定論的閾値 / scope 限定 / recovery hint 明確) すべて満たすため - additionalContext warning のみで block しない (failure mode が無害) - paths glob で scope 宣言的に限定 - todo*.md / Rust source に明示的 recovery hint コメント修正: - "ADR-039 § 3 opt-in pattern" → "ADR-039 § 1.b mechanical lint 例外" (§ 3 は bounded lifetime、opt-in は § 1。元コメントは誤参照) - "Layer 0.5" → "custom linter" (ADR-007 Layer 0.5 追記削除に追従) - 4 条件 (1.b 適用根拠) を明示 - 順位 147 file_length lint を同類例として cite - bounded lifetime dogfood の記述を削除 (mechanical lint は dogfood phase 不要) 影響: - 既存 grandfather (>50KB 既存ファイル) は touch されるまで warning なし - 触られた >50KB ファイル (例: docs/todo10.md) は次の Edit/Write で warning が出る - 本 PR で todo10.md の split (Commit 5) を同時実施し、初回 dogfood も完了させる * docs(todo): todo10.md を分割して file_size_check 50KB threshold 内に収める 本 PR で順位 177 file_size_check を default ON 化したことにより、touched で 50KB 超のファイル (= 本 PR 着手時の docs/todo10.md = 57KB) に warning が出る状態になった。 本 commit で todo10.md から PR #185 〜 PR #196 era の 8 エントリを新規 docs/todo12.md に分離し、todo10.md を 27KB まで縮小して threshold 内に収める。同時に hook の dogfood としても機能 (順位 177 が想定する recovery flow = 新 todo<N+1>.md 新設 + entry 移管 が実際に機能することを実観測)。 主な変更: docs/todo12.md (新規 158 行) - 順位 176 (PR #185 T2-#4): check-ci-coderabbit format variant fixture 追加 - 順位 178 (週次レビュー S02): state.rs behavioral invariant test - 順位 179 (週次レビュー S03): rate-limit retry decision boundary test - 順位 180 (週次レビュー C01): lib-report-formatter markdown pipe escape - 順位 181 (Phase D D-A): aggregate-weekly findings.json raw JSON - 順位 182 (Phase D D-B): /weekly-review skill 重複検出 (簡易 grep) - 順位 193 (PR #196 T2-1): Companion helper group 署名整合 compile-time test - 順位 194 (PR #196 T3-5): development-workflow.md grep step 追記 - 専用ファイル (新規追加先ではない)、todo11.md と同 role docs/todo10.md (-377 行、57KB → 27KB) - 上記 8 エントリを削除 - preamble に todo12.md 分離の経緯を記述 - 新セッション確認対象を「12 file」→「13 file」に更新 docs/todo-summary.md - preamble に todo12.md の説明を追記 - 8 行の「ファイル」列を todo10.md → todo12.md に変更 (sed 一括置換) 由来: 本 PR (PR #204) の hooks-config.toml 変更 (commit 4) で file_size_check default ON 化に伴う初回 dogfood。順位 177 設計の recovery flow が機能した実証 データとなる。 技術メモ: sed -i '13,390d' で 8 entries 削除、Edit tool で 380 行の old_string 構築は実用的でないため Bash 経路を選択 (ユーザーの「適切な粒度」要件と整合、 独立 commit に集約)。
Summary
トークン削減ロードマップ Phase 3 (順位 12: cli-pr-monitor ポーリング延長 + 重複起動ロック) の本実装に加え、PR #95 post-merge-feedback 由来の Bundle V 登録 (順位 31-33) を同梱します。両者とも token-reduction 系で論理的近接、PR 数最小化方針 (
MEMORY.md:feedback_minimize_pr_count_during_rate_limit) により同 PR で land。Why
PR #88 で実証された rate-limit 浪費 (Claude Code Max を 1 時間で 40% 消費) への直接対策。複数セッションが同時に cli-pr-monitor の polling を回すと、Claude API への並走 polling で rate-limit を急速消費する事象が観測されていました。
本 PR で rate-limit 抑制の 3 層のうち 2 層を完成させます:
Scope (2 論理ユニット同梱)
Phase 3 本実装 (順位 12)
重複起動 file lock — 新 module src/cli-pr-monitor/src/lock.rs
OpenOptions::create_newによる atomic create-or-fail (TOCTOU race を排除)start_monitoringのみ。--observe(read-only) と--mark-notified(one-shot) は対象外で並走可ポーリング間隔延長 src/cli-pr-monitor/src/config.rs
DEFAULT_POLL_INTERVAL: 120s → 180smax_duration_secs(600s) は維持 (検出窓は変えない)ISO 8601 parser (lock.rs 内蔵)
chrono依存を増やさず、手書き parser で start_time の age を計算。util::utc_now_iso8601()出力との format alignment は test で round-trip 検証済 (advisor 指摘反映)。Bundle V 登録 (PR #95 post-merge-feedback)
docs/todo.mdtable への 順位 31-33 追加 +docs/todo3.mdに詳細 3 件追加。実装は別 PR で予定。.takt/**と.claude/**を明示除外Test plan
concurrent_acquire_only_one_wins: 8 thread 同時 acquire で 1 つだけが Acquired になることを検証 (advisor 指摘の TOCTOU race を真に検証する test)OpenOptions::create_newに書き換えDesign notes
pr-monitor-config.tomlで将来 override 可能 (本 PR では非導入)。Phase
Token 削減 Phase 3 完了。次は Phase 4 (順位 13: post-pr-review に rate-limit 自動検出 + 再トリガー、rate-limit critical 系の最終 1 つ)。
Summary by CodeRabbit
バグ修正
ドキュメント