Skip to content

refactor(cli-pr-monitor): poll.rs を 3 module に分割 (PR B、partial) - #214

Merged
aloekun merged 4 commits into
masterfrom
refactor-pr-b-poll-split-partial
Jun 19, 2026
Merged

refactor(cli-pr-monitor): poll.rs を 3 module に分割 (PR B、partial)#214
aloekun merged 4 commits into
masterfrom
refactor-pr-b-poll-split-partial

Conversation

@aloekun

@aloekun aloekun commented Jun 19, 2026

Copy link
Copy Markdown
Owner

Summary

PR #213 (PR A、refactor check-ci-coderabbit/main.rs を 7 module に分割) で残置した cli-pr-monitor/src/stages/poll.rs (2090 行) の file_length lint 違反を解消するための module 分割。部分達成 で mod.rs 内 test ブロックの sub-module 分配は未完了 (1404 行で 800 行制限超過のまま)。

加えて、本 PR には PR #213 post-merge-feedback 採用 3 件 (順位 212/213/214) の docs/todo*.md 登録を 別 commit として含んでいます。

2 commits 構成

# commit 内容
1 docs(todo): PR #213 post-merge-feedback 採用 3 件 + session 派生統合を追加 (順位 212/213/214) docs/todo10.md + docs/todo-summary.md に 3 タスク登録 (T1-1 PowerShell preset + T2-1 API ordering test + T2-2 境界条件 test)
2 refactor(cli-pr-monitor): poll.rs を 3 module に分割 (PR B、partial) poll.rs → poll/ ディレクトリ + rate_limit.rs + review_recheck.rs に分割

分割後の構成 (PR B 部分)

ファイル 行数 内容
poll/mod.rs 1404 (元 2090) PollResult / PollContext / run_poll_loop / iteration helpers / make_park_poll_result / recompute_action + 35 tests
poll/rate_limit.rs 417 rate-limit branch + dispatch + finalize_posted_retrigger + finalize_parked + shortcut helpers + RateLimitOutcome + handle_rate_limit_retry + post_review_immediately + format_park_signal + MergeableStatus (production only)
poll/review_recheck.rs 297 round_up_to_next_minute + compute_safe_minute + ReviewParkSignalFields + collect_review_park_fields + format_review_park_signal + finalize_initial_review_park + finalize_review_recheck_park + finalize_review_recheck_max_reached + schedule_next_review_recheck_park (production only)

設計判断

  • PollContext fields を pub(super) 化: sub-module からの test fixture 構築を許容
  • make_park_poll_result / make_action_required_result を pub(super) 化: review_recheck.rs と rate_limit.rs から共有
  • sub-module 内の private 関数も pub(super) 化 (compute_safe_minute_for_park_signal, format_review_park_signal, schedule_next_review_recheck_park, handle_rate_limit_retry, evaluate_rate_limit_shortcut, finalize_parked, finalize_posted_retrigger, format_shortcut_signal, format_park_signal): mod.rs に残置中の tests からの参照を維持
  • mod.rs の sub-module import を production / #[cfg(test)] で分離: production 経路 (handle_rate_limit_branch / finalize_initial_review_park / finalize_review_recheck_park / make_action_required_result) と test 経路 (evaluate_rate_limit_shortcut / format_park_signal / round_up_to_next_minute 等) の使用区別を明示、unused warning を解消

Test plan

  • cargo build -p cli-pr-monitor clean
  • cargo test -p cli-pr-monitor: 203 passed, 12 ignored (refactor 前後で同数、test 同一性確認)
  • cargo clippy -p cli-pr-monitor --tests -- -D warnings clean
  • PR diff の 752 行削除 (poll.rs → poll/mod.rs) + 297 (review_recheck.rs) + 417 (rate_limit.rs) で production logic 変更ゼロ確認

未完了 (follow-up PR 推奨)

mod.rs の test ブロック (~1000 行) を rate_limit.rs / review_recheck.rs に分配し、mod.rs を 800 行以下にする作業が未完了です。具体的には:

  • 共有 test helpers (env_override_lock, unwritable_state_path, invoke_finalize_parked_with_bad_path, invoke_review_park_with_bad_path, invoke_finalize_initial_review_park_with_bad_path, seed_stale_recheck_state, pr_info_for_initial_review_park_test, make_default_test_ctx, setup_posted_retrigger_fixture) の sub-module への分配 (memory feedback_test_dry_antipattern.md 適用で duplication 許容)
  • rate_limit 系 tests (rate_limit_state_, rate_limit_default_, rate_limit_dedup_, rate_limit_last_retriggered_at_, rate_limit_retry_, format_park_signal_, finalize_parked_, finalize_posted_retrigger_, evaluate_rate_limit_shortcut_, format_shortcut_signal_) を rate_limit.rs に移動
  • review_recheck 系 tests (schedule_next_review_recheck_park_, format_review_park_signal_, round_up_to_next_minute_, compute_safe_minute_, finalize_initial_review_park_, finalize_review_recheck_park_) を review_recheck.rs に移動

これらを完了すれば mod.rs は production のみで ~370 行になり、file_length lint を完全解消できます。

hooks-post-tool-comment-lint-rust の file_length lint は soft-feedback (block しない nag) のため、本 PR の部分達成でも他の lint / build / test には影響しません。

PR_SIZE_CHECK_OVERRIDE=1 使用について

PR #213 (PR A) と同様、本 PR も「mod.rs から 752 行削除 + 2 新規モジュール作成 = 純粋 refactor」のため pr_size_check で block される見込みでした。PR A と同じパターンの大型 refactor のため PR_SIZE_CHECK_OVERRIDE=1 で push しています。

理由:

  • production logic 変更ゼロ
  • 既存 203 test 全 pass、clippy clean
  • ADR-043 fail-closed の本来の防御対象 (silent disable / 質的劣化) に該当せず

PR A で同パターンが既にユーザー承認されているため、PR B も同様に override で進めました。もし不適切であればご指示ください。

関連

Summary by CodeRabbit

変更点

  • ドキュメント
    • 推奨実行順序に、PowerShell対応プリセット追加や解析順序・境界条件テストの新規タスクを追記しました。
  • 新機能/改善
    • PR監視のレート制限時に、条件を満たす場合のショートカット通知と、レビュー再チェックの予約・出力を強化しました。
  • リファクタリング
    • 監視処理の分岐を整理し、制御フローを見通しよくしました。
  • テスト
    • API返却順序の固定化と、時刻比較の境界(同値含む/除外)の回帰テストを追加しました。

aloekun added 2 commits June 19, 2026 19:27
PR #213 (PR A) で残置した cli-pr-monitor/src/stages/poll.rs (2090 行) の
file_length lint 違反を解消するため module 分割を実施。本 commit は **部分達成**
で mod.rs の test ブロック分割は未完了 (1404 行で 800 行制限超過)。

分割構成:
- poll.rs (2090 行) → poll/ ディレクトリ
  - poll/mod.rs (1404 行): PollResult, PollContext, run_poll_loop, iteration 系
    helpers (run_one_iteration, invoke_checker 等), make_park_poll_result,
    recompute_action + 35 tests
  - poll/rate_limit.rs (417 行): rate-limit branch (handle_rate_limit_branch
    + dispatch + finalize_posted_retrigger + finalize_parked + shortcut 系
    helpers + RateLimitOutcome + handle_rate_limit_retry + post_review_immediately
    + format_park_signal + MergeableStatus) — production only
  - poll/review_recheck.rs (297 行): review_recheck park (round_up_to_next_minute
    + compute_safe_minute + ReviewParkSignalFields + collect_review_park_fields
    + format_review_park_signal + finalize_initial_review_park +
    finalize_review_recheck_park + finalize_review_recheck_max_reached +
    schedule_next_review_recheck_park) — production only

検証:
- cargo build -p cli-pr-monitor: clean
- cargo test -p cli-pr-monitor: 203 passed, 12 ignored (refactor 前後で同数)
- cargo clippy -p cli-pr-monitor --tests -- -D warnings: clean

未完了 (follow-up PR で対応):
- mod.rs の test ブロック (~1000 行) を rate_limit/review_recheck.rs に分配し、
  mod.rs を 800 行以下にする
- 共有 test helpers (env_override_lock, unwritable_state_path, invoke_*_with_bad_path
  等) の sub-module への分配 (memory feedback_test_dry_antipattern 適用で
  duplication 許容)

設計決定:
- PollContext fields を pub(super) 化 (sub-module からの test 用 fixture 構築)
- make_park_poll_result, make_action_required_result を pub(super) 化
- compute_safe_minute_for_park_signal, format_review_park_signal,
  schedule_next_review_recheck_park, handle_rate_limit_retry,
  evaluate_rate_limit_shortcut, finalize_parked, finalize_posted_retrigger,
  format_shortcut_signal, format_park_signal を pub(super) 化 (mod.rs tests
  からの参照を維持)
- mod.rs の sub-module import を production / #[cfg(test)] で分離して unused
  warning を解消

scope 外:
- PR A 同様、pr_size_check は事前に override 必要見込み (純粋 refactor のため)
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

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: 3e6e8cfb-3072-48d8-8d3b-681010f19550

📥 Commits

Reviewing files that changed from the base of the PR and between 45c6a08 and 25203bd.

📒 Files selected for processing (3)
  • docs/todo-summary.md
  • docs/todo10.md
  • src/cli-pr-monitor/src/stages/poll/review_recheck.rs
✅ Files skipped from review due to trivial changes (2)
  • docs/todo-summary.md
  • docs/todo10.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/cli-pr-monitor/src/stages/poll/review_recheck.rs

📝 Walkthrough

Walkthrough

poll/mod.rs の rate-limit 制御と review-recheck 制御を新規サブモジュール rate_limit.rs(559行追加)と review_recheck.rs(355行追加)に分離・実装した。PollContextmake_park_poll_result の可視性を pub(super) に昇格し、mod.rs の対応実装行を削除した。docs に次フェーズの3タスク(PowerShell preset、API ordering テスト、境界条件テスト)を追記した。

Changes

poll サブモジュール分離と docs タスク追記

Layer / File(s) Summary
mod.rs:サブモジュール宣言と可視性調整
src/cli-pr-monitor/src/stages/poll/mod.rs
mod rate_limit; mod review_recheck; を宣言して分離先関数を import し、PollContextpub(super) + 全フィールド pub(super) に変更、make_park_poll_resultpub(super) に昇格、run_gh_quiet の import を除去した。
rate_limit.rs:型定義と分岐エントリポイント
src/cli-pr-monitor/src/stages/poll/rate_limit.rs
RateLimitOutcome enum(Posted / Parked / Failed)と MergeableStatus DTO を定義し、handle_rate_limit_branch が重複スキップ・上限判定・auto_retry 有効判定を経て dispatch_rate_limit_outcome へ処理を委譲する。make_max_retries_result で最大再試行超過結果を生成する。
rate_limit.rs:Posted/Parked 確定処理と park signal 生成
src/cli-pr-monitor/src/stages/poll/rate_limit.rs
finalize_posted_retrigger が retrigger 時刻保存と wakeup 予約を行い park signal を出力し、finalize_parked が park 状態を永続化して merge 可能条件を満たす場合に RATE_LIMIT_BUT_MERGEABLE shortcut signal を追加出力する。format_shortcut_signalformat_park_signalPR_MONITOR_PARK envelope + CronCreate 指示文字列を生成する。
rate_limit.rs:再試行ロジックと posted-retrigger park signal
src/cli-pr-monitor/src/stages/poll/rate_limit.rs
handle_rate_limit_retry が until_unix_secs 基準で park か即時投稿かを決定し、過去なら post_review_immediatelygh pr comment を実行して Posted / Failed を返す。PostedRetriggerParkFieldscollect_posted_retrigger_park_fields が park signal 生成用フィールドを収集し、format_posted_retrigger_review_park_signal が review_recheck reason の park signal + safe_minute cron 指示を組み立てる。テストで signal 内容を検証する。
review_recheck.rs:時刻計算と park signal 生成ユーティリティ
src/cli-pr-monitor/src/stages/poll/review_recheck.rs
round_up_to_next_minute が UTC 秒を次の完全分に切り上げ、compute_safe_minute_for_park_signal が有効/無効 wakeup_unix を処理して safe minute (Unix/ISO) を返す。collect_review_park_fields が state/ctx から PARK signal 必須フィールドを収集し、format_review_park_signalPR_MONITOR_PARK envelope と CronCreate 指示(safe_minute 起点で :00/:30 回避)を含む文字列を生成する。
review_recheck.rs:初回 park と recheck ライフサイクル
src/cli-pr-monitor/src/stages/poll/review_recheck.rs
finalize_initial_review_park が state を復元/初期化して initial_review_wait_secs 後の wakeup を予約し、finalize_review_recheck_park が recheck_count をインクリメントして上限到達・未到達に分岐する。finalize_review_recheck_max_reached が上限到達時に action_required を確定し wakeup 関連フィールドをクリアして永続化し、schedule_next_review_recheck_park が次回 wakeup を予約・永続化する。テストで state クリア検証を行う。
docs:次フェーズタスク追記
docs/todo10.md, docs/todo-summary.md
PowerShell destructive-write ブロック preset 設計(hooks-pre-tool-validate dispatch 追加、System.IO.File 破壊的呼び出しの block、__* scratch 例外許可)、parse_coderabbit_status API ordering テスト、parse_actionable_comments 境界条件テスト(submitted_at >= push_time inclusive 検証)の3タスクを todo10.md に追記し、todo-summary.md の推奨実行順序テーブルに順位 212〜214 を追加した。

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • aloekun/claude-code-hook-test#97: rate-limit 自動検出・再トリガー由来の handle_rate_limit_retry / finalize_* / 状態永続化処理と、本 PR の rate_limit.rs 分離が同一コード経路で直接接続している。
  • aloekun/claude-code-hook-test#113: RateLimitOutcome / park signal / 状態分岐ロジックを parked_rate_limit + CronCreate park モデルで扱う点が、本 PR の rate_limit.rs サブモジュール化と直接対応している。
  • aloekun/claude-code-hook-test#212: PARK signal の safe minute round-up(round_up_to_next_minute / compute_safe_minute_for_park_signal / format_review_park_signal)が本 PR の review_recheck.rs 実装と同一ロジックを扱っている。
🚥 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のタイトルは、poll.rsを3つのモジュールに分割するというリファクタリングの主要な変更内容を正確かつ簡潔に説明しており、チェンジセットの主な目的と一致している。
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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 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 `@docs/todo-summary.md`:
- Line 90: Priority 212 has an inconsistent Tier classification where the table
shows "🔧 Tier 2" but the task description references "T1-1" suggesting Tier 1.
Determine the correct priority level for this task and update the Tier column
value in the table row at line 90 of todo-summary.md to match the correct tier.
Additionally, verify and update the corresponding section in todo10.md at lines
476-480 to ensure both files use the same tier classification consistently
throughout.

In `@docs/todo10.md`:
- Line 476: There is a Tier classification contradiction in the task description
where line 476 states "Tier 1 `#1` 採用" while line 480 states "Tier 2", creating
ambiguity about the actual priority level. Determine which Tier classification
is the correct formal priority for this task, then update both instances (at
line 476 and line 480) to use the same Tier designation consistently.
Additionally, ensure that any corresponding priority entries in todo-summary.md
are also updated to reflect this consistent Tier classification.

In `@src/cli-pr-monitor/src/stages/poll/rate_limit.rs`:
- Around line 146-147: The PARK signal is using an incorrect timestamp for the
wake-up time. In the `finalize_posted_retrigger` function, the correct wake-up
time is calculated and stored as `park_at_unix` in the state at line 120, but
the `format_park_signal` call at line 146 is using `rl.until_unix_secs` which
may be a past or mismatched time. Fix this by passing the actual wake-up time
from the state (the `park_at_unix` value that was previously calculated) to the
signal formatting function instead of using `rl.until_unix_secs`, ensuring the
signal reflects the correct re-trigger time.

In `@src/cli-pr-monitor/src/stages/poll/review_recheck.rs`:
- Around line 178-189: The code only updates review_recheck_count, head_commit,
and fix_push_time when reusing an existing state from read_state(), but it does
not update the push_time field. This means when an existing state is returned,
it retains the old push_time from a previous cycle instead of being updated with
the fresh push_time from ctx. Add an assignment to set state.push_time to
ctx.push_time.to_string() (matching how it's set in the PrMonitorState::new()
constructor) to ensure all push-related information is refreshed when the state
is reused.
- Around line 247-263: Before persisting the state with write_state when the max
review recheck limit is reached and action_required is being set, clear the
wakeup reservation fields from the state object. Specifically, reset the
next_wakeup_at_unix and wakeup_reason fields to remove any scheduled wakeup
information, since this code path exits with action_required and no further PARK
signal will be sent. Add this cleanup right after setting state.action to
action_required and state.summary to the summary string, but before the
write_state call to ensure the persisted state doesn't incorrectly indicate a
future wakeup is pending.
🪄 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: 040713c1-936f-42cd-a3ad-bfc6934ac88a

📥 Commits

Reviewing files that changed from the base of the PR and between e208980 and 70b40c0.

📒 Files selected for processing (5)
  • docs/todo-summary.md
  • docs/todo10.md
  • src/cli-pr-monitor/src/stages/poll/mod.rs
  • src/cli-pr-monitor/src/stages/poll/rate_limit.rs
  • src/cli-pr-monitor/src/stages/poll/review_recheck.rs

Comment thread docs/todo-summary.md Outdated
Comment thread docs/todo10.md Outdated
Comment thread src/cli-pr-monitor/src/stages/poll/rate_limit.rs Outdated
Comment thread src/cli-pr-monitor/src/stages/poll/review_recheck.rs Outdated
Comment thread src/cli-pr-monitor/src/stages/poll/review_recheck.rs
aloekun added 2 commits June 19, 2026 20:56
PR #214 (PR B partial) takt post-pr-review iter 2 で 5 findings 検出 → 4 fix + 1
no-change-needed で convergence_verdict: fully_resolved。

修正内容:
- F1 (FIXED): rate_limit::finalize_posted_retrigger の max_retries 引数を削除し
  format_posted_retrigger_review_park_signal を新規追加 (rate-limit retry vs
  review_recheck park の semantic 分離、test 2 件追加)
- F2 (FIXED): rate_limit.rs に make_action_required_result import 追加 (CR 指摘)
- F3 (FIXED): review_recheck.rs の compute_safe_minute_for_park_signal と
  format_review_park_signal を pub(super) 化 (mod.rs tests から参照可能化)
- F4 (NO_CHANGE_NEEDED): 既に対応済
- F5 (FIXED): docs/todo*.md の analyzer label を T1-1 → feedback-T1-1 に
  統一 (CR 指摘の表記揺れ修正)

検証:
- cargo build -p cli-pr-monitor: clean
- cargo test -p cli-pr-monitor: 205 passed (refactor 前 203 + 新規 2 件)
- cargo clippy clean

metrics gate 例外: review_recheck.rs の +2 lines が pub(super) 化に伴う
visibility 変更で metrics gate FAIL (TRUE POSITIVE GATE, ACCEPTABLE DELTA)。
takt convergence でも fully_resolved 判定済。supervisor ratification 待ち。
CR 再レビューで Major 3 件追加検出 → 全件修正:

F1 (FIXED): review_recheck::finalize_initial_review_park で read_state() が
既存 state を返した場合、PrMonitorState::new(...) が呼ばれず ctx の最新値
(pr/repo/started_at) が反映されない logic bug。fresh push 経路で前 push の
PR/repo/started_at 情報を引き継ぐリスクがあったため、明示的に上書き処理を
追加。fix_push_time も ctx 優先 + state fallback の方向に統一。

F2 (FIXED): docs/todo10.md の順位 212 entry に Tier 分類 disambiguation を
追加。analyzer feedback report の Tier 1 #1 と project tier 列の Tier 2 が
矛盾するように見えるため、memory feedback_tier_classification の
re-classification rule を明示参照する補足 paragraph を追加。

F3 (FIXED): docs/todo-summary.md の順位 212 entry に同様の Tier 分類補足を
追加。CR が「T1-1 vs Tier 2」の表記揺れと判定したため。

検証:
- cargo test -p cli-pr-monitor: 205 passed
- cargo clippy -p cli-pr-monitor --tests -- -D warnings: clean

CR スレッド対応: takt-fix iter 1 (2m 42s、approved) では未検出の Major 3 件
だったため手動修正。
@aloekun
aloekun merged commit 4e07a35 into master Jun 19, 2026
1 check passed
@aloekun
aloekun deleted the refactor-pr-b-poll-split-partial branch June 19, 2026 13:58
aloekun added a commit that referenced this pull request Jun 21, 2026
…uctive-write-block preset 追加 (順位 212)

PR #213 (refactor PR A) 作業中に発生した PowerShell スクリプトでの
`check-ci-coderabbit/src/main.rs` (2369 行) 0 byte 化事故の機械強制
対策。memory `feedback_no_powershell_inplace_edit` の codify と併用する
mechanical defense layer として preset と dispatch を追加した。

設計:

- 新 preset `powershell-destructive-write-block` (5 BlockedPattern)
  - `[System.IO.File]::WriteAllText|WriteAllBytes|WriteAllLines` (事故直接因)
  - `Out-File` (redirect 系 cmdlet)
  - `Set-Content -Value` (cmdlet 版書込、-Value flag 付きのみ false positive 回避)
- すべての pattern に `(?i)` flag を付与 (PowerShell の .NET method 名 +
  cmdlet 名は case-insensitive)
- Exception を path 位置に scope (= command 全体ではなく cmdlet/method 直後の
  最初の引数のみ):
  - WriteAllText/Bytes/Lines: `\(\s*['"]__` で open paren 直後限定
  - Out-File: `Out-File\s+(?:-FilePath\s+)?['"]?__` (unquoted も allow)
  - Set-Content: `Set-Content\s+(?:-(?:Literal)?Path\s+)?['"]?__`
- Set-Content main pattern は `[^|]*-Value` で param 順非依存
  (`-Value` が `-Path` より先に来る reversed order でも block)
- dispatch table に `PowerShell` arm 追加 + `handle_powershell_tool` 関数
  (handle_bash_tool と同形で `tool_input.command` を build_blocked_patterns
  全 preset で照合)
- `default_preset_names` に登録 = Config::default() で default-on

追加 test 19 件 (= 11 base + 8 edge case):

base 11 件:
- positive (5): WriteAllText / WriteAllBytes / WriteAllLines / Out-File /
  Set-Content -Value の destructive write block
- negative scratch (2): `__*` prefix path への write は allow
- negative legit (3): Get-ChildItem / Where-Object / Set-Content 単体 (false
  positive 回避)
- default-on (1)

edge case 8 件 (pre-push-review Warning 4 件に対応、ADR-043 fail-closed):
- W-002 SEC: lowercase `.NET` method 名 (`writealltext`) でも block
- W-001 SEC: `__` 文字列が value 位置にあるとき block (path 位置に scope)
- W-001 SEC: ReadAllText で `__` を読んでも WriteAllText 先が prod なら block
- W-001 SIMP: Set-Content の reversed parameter order (`-Value` 先) でも block
- W-001 SIMP: Set-Content の reversed order でも scratch path なら allow
- W-002 SIMP: unquoted scratch path (`Out-File __output.txt`) は allow
- value 位置の `__` を path と誤認しない (Set-Content 版)

検証:
- cargo test -p hooks-pre-tool-validate: 221 passed (= 202 既存 + 19 new)
- cargo clippy -p hooks-pre-tool-validate -- -D warnings: clean
- cargo test --workspace: 全テスト pass (regression なし)

Note: `src/hooks-pre-tool-validate/src/main.rs` は本 PR 後 2900 行になり
file_length lint (800 行ガイドライン) を超過するが、本 PR は順位 212 の
mechanical defense 追加に scope を限定し、ファイル分割 refactor は別 PR
(PR #213/#214 と同 pattern) で対応する。
aloekun added a commit that referenced this pull request Jun 22, 2026
…derabbit 境界 test (順位 212-215) (#215)

* docs(todo): PR #214 post-merge-feedback T3-1 採用 (順位 215)

PR #214 (refactor PR B partial) post-merge-feedback report の T3-1 を採用:
`~/.claude/rules/common/coding-style.md` に "Defensive State Reset in
State Machines" section を追記する todo entry を docs/todo-summary.md +
docs/todo10.md に追加。

由来: PR #214 round 2 で CR Major #4 fix として
`finalize_initial_review_park` 内 `read_state()` 後の
`state.pr`/`state.repo`/`state.started_at` 無条件上書きが land。同型
`head_commit`/`review_recheck_count` reset と合わせて 5 field の確立
された defensive pattern を future reviewer が "redundant" と誤判定して
削除しないよう global rule で防御する。

Severity Medium / Frequency Medium / Effort S / Adoption Risk None で
ユーザー承認 (2026-06-20)。pre-push:simplicity + pre-push:security の
独立 2 ソース検出。実装時は `feedback_global_config_backup` 適用必須。

* test(check-ci-coderabbit): API ordering + 境界条件テスト追加 (順位 213, 214)

順位 213 (PR #213 post-merge-feedback T2-1 採用):
parse_coderabbit_status の `.first()` semantics を test 名 +
doc comment + assert message で explicit 化。GitHub statuses API の
reverse-chronological 返却 implicit assumption を test fixture で
固定し、refactor 時の意味喪失を機械的に検出する。

順位 214 (PR #213 post-merge-feedback T2-2 採用):
parse_actionable_comments の `submitted_at >= push_time` inclusive
比較を境界で固定する 2 test を追加:
- actionable_includes_review_at_exact_push_time: `==` 境界の inclusive 比較を保証
- actionable_excludes_review_before_push_time: 配列 latest 位置に
  sentinel (Actionable: 99, 11:00) を置き、rfind が time filter で
  正しく除外することを確認 (filter 壊れた場合 99 が返り test 落ちる)

既存 rule⑦ `no-time-field-strict-greater` は変数名抽出後を catch
不可なため、test による second defense layer として機能。
memory `feedback_test_dry_antipattern.md` 適用で独立 setup。

検証:
- cargo test -p check-ci-coderabbit: 96 passed (= 93 + 3 new)
- 既存 actionable_filters_by_time との相補的 boundary 検証

* feat(hooks-pre-tool-validate): PowerShell dispatch + powershell-destructive-write-block preset 追加 (順位 212)

PR #213 (refactor PR A) 作業中に発生した PowerShell スクリプトでの
`check-ci-coderabbit/src/main.rs` (2369 行) 0 byte 化事故の機械強制
対策。memory `feedback_no_powershell_inplace_edit` の codify と併用する
mechanical defense layer として preset と dispatch を追加した。

設計:

- 新 preset `powershell-destructive-write-block` (5 BlockedPattern)
  - `[System.IO.File]::WriteAllText|WriteAllBytes|WriteAllLines` (事故直接因)
  - `Out-File` (redirect 系 cmdlet)
  - `Set-Content -Value` (cmdlet 版書込、-Value flag 付きのみ false positive 回避)
- すべての pattern に `(?i)` flag を付与 (PowerShell の .NET method 名 +
  cmdlet 名は case-insensitive)
- Exception を path 位置に scope (= command 全体ではなく cmdlet/method 直後の
  最初の引数のみ):
  - WriteAllText/Bytes/Lines: `\(\s*['"]__` で open paren 直後限定
  - Out-File: `Out-File\s+(?:-FilePath\s+)?['"]?__` (unquoted も allow)
  - Set-Content: `Set-Content\s+(?:-(?:Literal)?Path\s+)?['"]?__`
- Set-Content main pattern は `[^|]*-Value` で param 順非依存
  (`-Value` が `-Path` より先に来る reversed order でも block)
- dispatch table に `PowerShell` arm 追加 + `handle_powershell_tool` 関数
  (handle_bash_tool と同形で `tool_input.command` を build_blocked_patterns
  全 preset で照合)
- `default_preset_names` に登録 = Config::default() で default-on

追加 test 19 件 (= 11 base + 8 edge case):

base 11 件:
- positive (5): WriteAllText / WriteAllBytes / WriteAllLines / Out-File /
  Set-Content -Value の destructive write block
- negative scratch (2): `__*` prefix path への write は allow
- negative legit (3): Get-ChildItem / Where-Object / Set-Content 単体 (false
  positive 回避)
- default-on (1)

edge case 8 件 (pre-push-review Warning 4 件に対応、ADR-043 fail-closed):
- W-002 SEC: lowercase `.NET` method 名 (`writealltext`) でも block
- W-001 SEC: `__` 文字列が value 位置にあるとき block (path 位置に scope)
- W-001 SEC: ReadAllText で `__` を読んでも WriteAllText 先が prod なら block
- W-001 SIMP: Set-Content の reversed parameter order (`-Value` 先) でも block
- W-001 SIMP: Set-Content の reversed order でも scratch path なら allow
- W-002 SIMP: unquoted scratch path (`Out-File __output.txt`) は allow
- value 位置の `__` を path と誤認しない (Set-Content 版)

検証:
- cargo test -p hooks-pre-tool-validate: 221 passed (= 202 既存 + 19 new)
- cargo clippy -p hooks-pre-tool-validate -- -D warnings: clean
- cargo test --workspace: 全テスト pass (regression なし)

Note: `src/hooks-pre-tool-validate/src/main.rs` は本 PR 後 2900 行になり
file_length lint (800 行ガイドライン) を超過するが、本 PR は順位 212 の
mechanical defense 追加に scope を限定し、ファイル分割 refactor は別 PR
(PR #213/#214 と同 pattern) で対応する。

* fix(hooks-pre-tool-validate): Set-Content exception を順序非依存に対応 (PR #215 CR Minor #1)

PR #215 CR review が指摘した Minor finding を対応:
`Set-Content` の exception regex が main pattern と order-independence
を共有していなかったため、`Set-Content -Value $x -Path "__file"` のような
reversed parameter order での scratch path 指定が false positive で block
されていた。

修正:

旧 exception:
  `(?i)\bSet-Content\b\s+(?:-(?:Literal)?Path\s+)?['"]?__`
  → `-Path` が Set-Content 直後にある順序のみカバー

新 exception:
  `(?i)\bSet-Content\b(?:\s+['"]?__|[^|]*\s-(?:Literal)?Path\s+['"]?__)`
  → 2-alternative:
    - alt 1: `Set-Content` 直後の positional `__` path
    - alt 2: `Set-Content` 以降のいずれかの位置の `-Path`/`-LiteralPath`
      後の `__` path

CR の修正案は `(?=...)` lookahead を使っていたが、Rust 標準 `regex` crate
は lookahead 非対応 (compile failure) のため、同 semantics を 2-alt 構成で
lookahead なしに実装。main pattern `[^|]*-Value` が既に `-Value` 存在を
保証しているため、exception 側に lookahead-based verification は不要。

test 追加:
- `Set-Content -Value $x -Path "__file.txt"` (reversed order) → allow
- `Set-Content -Value $x -LiteralPath "__file.txt"` (LiteralPath 変種) → allow
- 既存の positional case (`Set-Content "__file.txt" -Value $x`) は維持

検証:
- cargo test -p hooks-pre-tool-validate: 221 passed
- cargo clippy -p hooks-pre-tool-validate -- -D warnings: clean

由来: PR #215 CR Minor #1 (discussion_r3448719154)、ADR-043 fail-closed
原則による security gate と false-positive 抑制の両立。
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