Skip to content

feat(hooks): polling-anti-pattern preset で polling loop をブロック (Phase 1)#93

Merged
aloekun merged 1 commit into
masterfrom
feat/polling-anti-pattern
Apr 29, 2026
Merged

feat(hooks): polling-anti-pattern preset で polling loop をブロック (Phase 1)#93
aloekun merged 1 commit into
masterfrom
feat/polling-anti-pattern

Conversation

@aloekun
Copy link
Copy Markdown
Owner

@aloekun aloekun commented Apr 29, 2026

Summary

PreToolUse hook に polling-anti-pattern preset を追加し、until COND; do BODY; sleep N; done / while ! COND; do ... sleep N 形式の polling loop をブロックします。Claude Code が反射的に書きがちな polling pattern を決定論的に検出することで、PR #86 で実証された rate-limit 大量浪費 (1 セッションで 40%) の再発を防止します。

Why

PR #86 のセッション中、run_in_background: true で起動した背景タスクの完了確認用に until grep -q done; do sleep N; done 形式の polling が反射的に書かれ、Claude Code Max (5x) のレートリミットを 1 時間で 40% 消費する事故が発生しました。背景タスクは task-notification 経由で自動的に完了通知される設計のため、polling は本来不要です。ガイドライン (順位 26) と組み合わせ、決定論的検出 + ドキュメント補完の二層防御を構築する第一段階。

Changes

  • src/hooks-pre-tool-validate/src/main.rs
    • preset_polling_anti_pattern() を追加 (既存 preset 関数群と同一パターン)
    • 検出 regex: (?is)\buntil\b.*?\bdo\b.*?\bsleep\s+\d / (?is)\bwhile\s+!\s.*?\bdo\b.*?\bsleep\s+\d
    • \bdo\b 制約により bash loop syntax のみマッチ (echo 文字列・--until=DATE フラグ等の false positive を排除)
    • ブロック時メッセージに代替手段 (Monitor tool / run_in_background + 通知 / 単発取得) を提示
  • .claude/hooks-config.toml
    • 本プロジェクト用に "polling-anti-pattern"blocked_patterns に追加
    • デフォルトフォールバックには非追加 (後方互換維持、派生プロジェクトは明示 opt-in)
  • テスト 12 件追加 (block 6 / allow 6 / 後方互換 1 = 計 13 件、cargo test で全 pass)
    • block: 1-line / multi-line / [ ] / while ! / state file polling / chain
    • allow (false positive 排除確認): for loop / 単純 sleep / sleep なし / while true / echo string / --until= flag
  • docs/todo.md 順位 3 と docs/todo2.md 該当エントリを削除し、参照を整合化

Design notes

  • 当初設計案にあった "run_in_background → 直後の polling" の cross-tool-call 検出は YAGNI で見送り (transcript 読み取りが必要、複雑度に対する効果が低い)。in-command 検出だけで PR docs(todo): PR #85 post-merge-feedback の Tier 1-3 finding を全採用 #86 の canonical case を捕捉できる。
  • ExitCode 2 でブロックする選択 (spec は "warning" だが、PreToolUse hook framework の唯一のフィードバック手段が exit 2 のため、強制的な行動修正が目標と整合)。
  • 後方互換: opt-in 方式により既存の派生プロジェクトには影響しない。

Test plan

  • cargo test (122 件、polling 関連 13 件含め全 pass)
  • cargo fmt --check / cargo clippy --all-targets -- -D warnings 全 pass
  • 実機検証: real exe に JSON stdin で 6 ケース投入し、block/allow が期待通り動作することを確認
  • dogfood: コミットメッセージ中の polling pattern 文字列にも実際に hook が発火することを確認 (機能していることの直接的な証跡)
  • takt pre-push-review で APPROVE (3 iterations → 1 iteration、最終的に aggregate=approved)

Phase

todo.md 推奨実行順序 Phase 1 (順位 3, Tier 1) 完了。次は Bundle T (順位 9 + 14) を計画。

Summary by CodeRabbit

リリースノート

  • 新機能

    • ポーリング検出パターンを検証機構に追加しました。
  • ドキュメント

    • 完了した検出ルール実装の参照を更新しました。
    • 実施済みのタスク項目を削除しました。

- hooks-pre-tool-validate に preset_polling_anti_pattern を追加
- 検出パターン: `until COND; do BODY; sl<>p N; done` 形式
  および `while !` 版 (regex は \bdo\b 制約で loop syntax のみマッチ)
  echo 文字列や --until=DATE フラグの誤検出は \bdo\b 制約で排除
- .claude/hooks-config.toml で本プロジェクト用に opt-in
  (デフォルトフォールバックには非追加、後方互換維持)
- 14 件のテスト追加 (block 6 / allow 7 / 後方互換 1)
  後追加 negative test 3 件: echo string / git --until flag / for loop は誤検出しない

動機: PR #86 で実証された rate-limit 40% 浪費 (1 セッション) の再発防止。
背景タスクは task-notification 経由で自走するため polling 不要。
ブロック時は Monitor tool / run_in_background / 単発取得への誘導メッセージを表示する。

Note: 当初設計案にあった "run_in_background -> polling" の cross-tool-call
検出は YAGNI 適用で見送り (transcript 読み取りが必要、複雑度に対する効果が
低い)。in-command 検出で PR #86 の canonical case を捕捉できる。

todo.md 推奨実行順序 Phase 1 (順位 3) 完了。
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 29, 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: 5a8516d1-6b28-4cf6-b8b4-3733327f2f7f

📥 Commits

Reviewing files that changed from the base of the PR and between f8591a0 and abe498f.

📒 Files selected for processing (4)
  • .claude/hooks-config.toml
  • docs/todo.md
  • docs/todo2.md
  • src/hooks-pre-tool-validate/src/main.rs
💤 Files with no reviewable changes (1)
  • docs/todo2.md

📝 Walkthrough

Walkthrough

ポーリング・アンチパターン(until/while ! ... sleep ループ)を検出するプリセット「polling-anti-pattern」を pre-tool-validate 機能に追加し、設定ファイルに登録して有効化するとともに、対応する実装テストを整備し、関連ドキュメントを更新する変更です。

Changes

Cohort / File(s) Summary
Configuration
.claude/hooks-config.toml
pre_tool_validate.blocked_patterns に新規プリセット "polling-anti-pattern" を追加
Documentation
docs/todo.md, docs/todo2.md
Tier 1 ポーリング検出ルールの完了に伴い、関連 TODO エントリを削除・更新し、依存関係を明確化
Implementation
src/hooks-pre-tool-validate/src/main.rs
polling-anti-pattern プリセット関数を実装し、マルチラインポーリングパターンと複数の偽陽性除外ケースを含む包括的なテストスイートを追加

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

🚥 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 プルリクエストのタイトルは、ポーリング anti-pattern プリセットを使用してポーリングループをブロックする主要な変更を正確に反映しており、簡潔で明確です。
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
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

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

@aloekun aloekun merged commit a3f4abc into master Apr 29, 2026
1 check passed
@aloekun aloekun deleted the feat/polling-anti-pattern branch April 29, 2026 13:43
aloekun added a commit that referenced this pull request Apr 29, 2026
PR #93 post-merge-feedback finding #1 (Cross-File Reference Lifecycle ルール)
の retroactive 適用。永続成果物 (hook block messages / config / コード) から
ephemeral な docs/todo*.md セクション名への参照を排除する。

修正箇所 (3 件):

1. src/hooks-pre-tool-validate/src/main.rs (preset_polling_anti_pattern block message)
   `docs/todo.md の「Polling anti-pattern 検出ルール」を参照` を削除。
   当該 todo.md エントリは PR #93 自身で削除済みの dead pointer。
   ADR-018 への参照のみ残す (こちらは permanent reference)。

2. .claude/custom-lint-rules.toml (no-mutable-anchor 由来コメント)
   `[docs/todo.md](todo.md#推奨実行順序サマリー)` 形式の引用を、
   日本語 heading 自体を anchor 形式で書かない記述に書き換え。
   日付付き heading のスラッグは時間で変化するため、引用例自体が
   stale になる構造的問題があった (まさに本ルールが検出する pattern)。

3. .markdownlint-cli2.jsonc (config 由来コメント)
   `per docs/todo.md "Markdown linter hook 統合" task` を
   `introduced in PR #88 (markdownlint-cli2 PostToolUse hook integration)`
   に置換。PR 番号は permanent reference。

背景: ~/.claude/rules/common/coding-style.md に追加した
"Cross-File Reference Lifecycle" ルール (永続→ephemeral 参照禁止) の
retroactive sweep。grep で非 docs ファイルを洗い出し全件対応。

検証:
- cargo test -p hooks-pre-tool-validate: 122/122 pass
- grep "docs/todo" --glob '!docs/**': matches 0 (clean)
- ./.claude/hooks-pre-tool-validate.exe を rebuild
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