Skip to content

feat(hooks): markdownlint-cli2 を PostToolUse hook に統合#88

Merged
aloekun merged 4 commits intomasterfrom
feat/markdownlint-md-hook
Apr 28, 2026
Merged

feat(hooks): markdownlint-cli2 を PostToolUse hook に統合#88
aloekun merged 4 commits intomasterfrom
feat/markdownlint-md-hook

Conversation

@aloekun
Copy link
Copy Markdown
Owner

@aloekun aloekun commented Apr 28, 2026

Summary

docs/todo.md 順位 1 (Tier 1) の Markdown linter (markdownlint-cli2) hook 統合を実施。ADR-002 の二段階構成 (Biome + oxlint) を .md ファイルに拡張し、PostToolUse hook で .md 編集時に自動 lint/fix を実現する。

変更内容 (4 commits)

commit 内容
chore(lint): markdownlint-cli2 を導入 + lint:md script markdownlint-cli2 ^0.22.1 を devDependencies に追加。.markdownlint-cli2.jsonc で MD028/MD040/MD058 のみ有効化 (default: false)、ignores に node_modules / target / .claude/feedback-reports / .claude/skills / .takt/runs / .pytest_cachepnpm lint:md script 追加
chore(lint): markdown 既存違反を解消し clean baseline 確立 10 件の既存違反を解消 (47 files で 0 errors): MD040×6 手動 (text × 5、sh × 1)、MD028×2 手動、MD058×2 auto-fix。これにより hook 統合後の .md 編集で違反 warning が即時噴出する事態を回避
feat(hooks): PostToolUse hook で .md を markdownlint-cli2 で自動 lint/fix .claude/hooks-config.toml[post_tool_linter]extensions = ["md"] パイプラインを追加。1 段目 --fix (auto-fixable な違反を即時修正) + 2 段目 diagnostic (残違反を additionalContext で出力)
docs(todo): markdown linter hook 統合 完了に伴い削除 推奨実行順序サマリーから順位 1 行を削除。順位 9 / 順位 11 の依存参照と戦略テキスト「Tier 1 (1〜6)→(2〜6)」を更新。詳細セクション (~48 行) も削除

動作確認 (本セッション内 dogfood 完了)

  • pnpm lint:md: 47 files, 0 errors (clean baseline 確立済)
  • PostToolUse hook 発火確認:
    • MD040 (auto-fix 不可) を含む .md を Write → 2 段目 diagnostic が Summary: 1 error(s) を additionalContext で返却
    • MD058 (auto-fix 可) を含む .md を Write → 1 段目 --fix がファイル本体を自動修正、2 段目 diagnostic が 0 error(s)、Claude Code の stale-file 検出メタ通知も発火
  • .markdownlint-cli2.jsoncignores 設定が hook 経由でも反映 (出力の Finding: 行で確認)
  • takt pre-push-review: simplicity + security 両方 APPROVE (1 iteration、3m 22s)

関連

  • 順位 1 完了に伴い順位 9 (ADR-032 PR-broken-link) の依存「順位 1 (clean baseline)」が解消され「なし (順位 1 完了済)」に更新
  • 順位 11 (ADR-032 PR-β) の依存からも「1」を削除
  • 派生プロジェクト (techbook-ledger, auto-review-fix-vc) への deploy は本リポジトリでの運用安定後に別途実施予定
  • 参照: .claude/feedback-reports/81.md (post-merge-feedback Tier 1 finding) / ADR-002 (PostToolUse linter composition)

Test plan

  • pnpm lint:md で 0 errors
  • .md への Edit/Write で hook が発火し --fix + diagnostic が動作
  • pnpm push の quality_gate (lint/test/build/rust-test) が全 PASS
  • takt pre-push-review (simplicity + security) が APPROVE
  • CI (GitHub Actions) PASS
  • CodeRabbit review で重大な findings なし

Summary by CodeRabbit

リリースノート

  • New Features

    • Markdownファイルのリント機能を追加しました。
  • Chores

    • ドキュメント全体のMarkdownフォーマッティングを改善しました。
    • リント設定ファイルと自動実行スクリプトを追加しました。

aloekun added 4 commits April 28, 2026 20:12
- markdownlint-cli2 ^0.22.1 を devDependencies に追加
- .markdownlint-cli2.jsonc で MD028/MD040/MD058 のみ有効化 (default: false)
- ignores に node_modules / target / .claude/feedback-reports / .claude/skills / .takt/runs / .pytest_cache を指定
- pnpm lint:md script を追加 (リポジトリ全体の診断用)
10 件の既存違反を解消し markdownlint-cli2 で 0 errors 達成 (47 files):
- MD040 (fenced-code-language) × 6 件: ASCII art / file tree / pipeline diagram に `text`、shell snippet に `sh` を付与
- MD028 (no-blanks-blockquote) × 2 件: blockquote 内の空行を `>` に修正 (auto-fix 不可、手動)
- MD058 (blanks-around-tables) × 2 件: --fix で自動修正

これにより hook 統合後 (次 commit) の `.md` 編集で違反 warning が即時噴出する事態を回避。
ADR-002 の二段階構成 (Biome + oxlint) を .md ファイルに拡張:
- 1 段目: markdownlint-cli2 --fix で auto-fixable な違反 (MD058) を自動修正
- 2 段目: markdownlint-cli2 で残違反を診断し additionalContext に出力

これで .md 編集毎に hooks-config.toml の post_tool_linter pipeline が発火し、
CodeRabbit が MD028/MD040/MD058 系の Nitpick を出す前に解消される。

参照: docs/todo.md 順位 1, .claude/feedback-reports/81.md Tier 1 finding
順位 1 (Markdown linter hook 統合) の本作業 commits は親 zkusmypv で完了済。
本 commit は対応する todo.md エントリの cleanup のみ:
- 推奨実行順序サマリー table から順位 1 行を削除
- 順位 9 (ADR-032 PR-broken-link) の依存を「順位 1 (clean baseline)」→「なし (順位 1 完了済 = clean baseline 確立済)」に更新
- 順位 11 (ADR-032 PR-β) の依存から「1」を削除
- 戦略テキスト「Tier 1 (1〜6)」→「Tier 1 (2〜6)」
- 詳細セクション (### Markdown linter (markdownlint-cli2) の PostToolUse hook 統合) を全削除

順位 numbering の renumbering は本 commit のスコープ外
(memory feedback_no_unauthorized_reorder.md の不変原則に基づく)。
派生プロジェクトへの deploy は本リポジトリでの運用安定後に別途実施予定。
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 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: 15cc5950-6550-4568-9530-c4f78aa5a6af

📥 Commits

Reviewing files that changed from the base of the PR and between aba456b and c7c99d3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • .claude/hooks-config.toml
  • .markdownlint-cli2.jsonc
  • .takt/facets/instructions/fix.md
  • docs/adr/adr-002-post-tool-use-linter-composition.md
  • docs/adr/adr-003-hooks-layout-and-build-strategy.md
  • docs/adr/adr-005-hooks-path-resolution-with-template.md
  • docs/adr/adr-015-push-runner-takt-migration.md
  • docs/todo.md
  • docs/todo2.md
  • package.json

📝 Walkthrough

Walkthrough

markdownlint-cli2統合の実装を完了する。後処理ツールリント設定、markdownlint設定ファイル、npmスクリプトを追加。ドキュメント内のMarkdown形式を統一化し、完了したタスクをtodoリストから削除。

Changes

Cohort / File(s) Summary
Markdown Linter Integration
.claude/hooks-config.toml, .markdownlint-cli2.jsonc, package.json
markdownlint-cli2後処理リント設定、ツール設定ファイル追加、lint:mdスクリプトと開発依存関係を追加。
Documentation Format Standardization
docs/adr/adr-002-post-tool-use-linter-composition.md, docs/adr/adr-003-hooks-layout-and-build-strategy.md, docs/adr/adr-005-hooks-path-resolution-with-template.md, docs/adr/adr-015-push-runner-takt-migration.md, docs/todo.md, docs/todo2.md, .takt/facets/instructions/fix.md
コードフェンスに言語タグ(textsh)を追加してMarkdown形式を統一化。todo.mdでは完了したmarkdownlint統合タスクを削除し、依存関係と優先順位を更新。

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 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 プルリクエストのタイトルは、変更セットの主要な目的である markdownlint-cli2 の PostToolUse hook への統合を明確に説明しており、ファイル一覧やノイズを含まない簡潔で具体的な表現になっています。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.


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.

@aloekun aloekun merged commit 75f1646 into master Apr 28, 2026
1 check passed
@aloekun aloekun deleted the feat/markdownlint-md-hook branch April 28, 2026 11:56
aloekun added a commit that referenced this pull request Apr 28, 2026
* docs(todo): PR #88 post-merge-feedback の Tier 1/2 finding を採用

PR #88 post-merge-feedback (.claude/feedback-reports/88.md) で生成された 7 件の
finding のうち、ユーザー判断により #1-5 を採用、#6-7 を見送り。

採用 finding (5 件):
- T1 #1 (順位 5): Stop hook の `pnpm lint:md` 統合 — XS、順位 1 完了済の gap closure
- T1 #2 (順位 6): AI 生成一時スクリプト pattern の pre-push 検出 — Small、順位 1 と関連
- T2 #3 (順位 13): `vitest` を devDependencies に固定 — Small
- T2 #4 (順位 12): `cli-pr-monitor` ポーリング延長 + 重複起動ロック — Medium、★ rate-limit critical
- T2 #5 (順位 14): `pnpm create-pr` 必須引数ヘルプ改善 — Small

見送り finding:
- T3 #6: hook 統合時の commit 分割基準 → グローバルルール (~/.claude/) 編集は permission denied、要別経路
- T3 #7: jj rebase conflict 解消手順 → 同上

変更:
- docs/todo3.md 新設 (todo2.md が 50KB に到達したため、PR #88 以降の新規エントリは todo3.md へ)
- docs/todo.md 推奨実行順序サマリーに 5 件を Tier 別に挿入し、20 → 24 タスクへ全 renumber
- 戦略テキストと cross-reference を全面更新
- todo2.md / todo3.md 内の 順位 N 参照を新採番へ追従
- 順位 1 (markdownlint hook 統合) は merged 済として削除参照を merged context に書き換え

* fix(review): apply CodeRabbit fixes for #89

Resolved findings:
- [Minor] docs/todo.md:49 順位参照の文言が現行テーブルと不整合です
- [Major] docs/todo3.md:7 見出しリンクのアンカーが壊れる可能性があります
aloekun added a commit that referenced this pull request Apr 29, 2026
* feat(adr): ADR-033 todo numbering simplification (試験運用)

PR #85/#86/#88/#89/#90/#91/Bundle 1 で連続発生した renumber 作業の構造的負債を
解消する ADR を起案。

問題:
- docs/todo.md の本文中 `順位 N` cross-reference が線形コストで増加
- PR #91 では 4 件追加に対し本文 8 箇所修正、過去 PR で stale reference の
  追従漏れによる CodeRabbit Minor 指摘が発生 (a15b263 で修正)
- 採番のみが情報源で、本文と表の往復が発生

決定 (選択肢 B 採用):
- 絶対番号 (`順位 N`) は推奨実行順序サマリー table のみに保持
- 本文での参照は task 名 (heading text or 略称) で行う
- table の `依存` 列のみ絶対番号を許可 (table 内なので renumber 同期可能)
- 「Tier N (順位 X/Y)」表記の `(順位 X/Y)` 部分は除去 → `Tier N` のみ残す

却下した選択肢:
- 選択肢 A (renumber script の自動化): 保守コスト新規発生、問題のすり替え
- 選択肢 C (現状維持): convergence loop の一因、線形増加は止まらない

本 commit は ADR と CLAUDE.md リンク追加のみ。本文 cross-ref の一括変換と
新規 entry template は次の commit で実施。

References:
- docs/adr/adr-013, adr-022, adr-028
- .claude/feedback-reports/86.md Tier 3 #3 (起案動機の起源)
- PR #91 a15b263 (stale reference 起因の Minor 指摘実例)

* refactor(docs): remove inline rank references from todo files

ADR-033 のガイドライン適用。docs/todo.md / todo2.md / todo3.md の本文中
`順位 N` cross-reference をすべて task 名参照に変換。

変更対象:
- docs/todo.md: 戦略 section の 12 箇所、Tier 4/5 entry の (順位 X/Y) 表記、
  週次レビュー entry の cross-ref
- docs/todo2.md: ADR-032 series の Phase ごと優先度表記、reviewer facet /
  各 Tier 1/2/3 entry の (順位 X/Y) と inline 参照
- docs/todo3.md: Stop hook lint:md / AI 生成一時スクリプト / cli-pr-monitor
  polling 延長 / post-pr-review rate-limit / .failed marker / Recovery 経路 /
  REJECT-ESCALATE entry の inline 参照を全て task 名に変換

維持した absolute number:
- docs/todo.md の推奨実行順序サマリー table の `順位` 列 (source of truth)
- table の `依存` 列の絶対番号 (table 内なので renumber と同期可能)
- table 内 cell の歴史的 `旧順位 N` 表記 (完了済み task の参照)

未変換の例外:
- docs/todo2.md line 696 の採番管理 ADR 元 entry の `(順位 24/26)` は
  本 PR の Commit 3 で entry 全体を削除するため未変換 (削除待ち)

検証:
```sh
grep -nE '順位 [0-9]+' docs/todo.md docs/todo2.md docs/todo3.md
# 期待: docs/todo.md table 列のみ + docs/todo2.md 採番管理 ADR 元 entry のみ
```

References:
- docs/adr/adr-033-todo-numbering-simplification.md (本 ADR の決定)

* docs(todo): remove completed numbering simplification ADR entry

ADR-033 land により採番管理簡素化 task が完了したため、運用ルール
(memory feedback_todo_no_history.md) に従い元 entry を削除する。

変更:
- docs/todo2.md: "todo.md 採番管理の簡素化 ADR 起案 (PR #86 T3-3)" entry
  全体 (40 行) を削除
- docs/todo.md summary table:
  - 旧順位 27 (採番管理 ADR) 行を削除
  - 旧順位 28 (ADR-030 Phase E/F) → 順位 27 に renumber
  - 旧順位 29 (ADR-030 takt-test-vc) → 順位 28 に renumber、依存表記の
    `順位 28 Phase F` も `順位 27 Phase F` に追従
  - サマリー header 日付ラベルを "Bundle 1 完了後" → "ADR-033 land 後" に更新

検証:
- 本文 cross-ref は Commit 2 で除去済のため、本 commit の renumber は
  table 行と `依存` 列のみで完結 (ADR-033 の効果を本 PR 内で実証)
- markdownlint pass (PostToolUse hook で各 Edit ごとに検証)

* fix(adr): apply CodeRabbit findings on PR #92

PR #92 で受けた CodeRabbit Minor findings 2 件を child commit として修正。
両 finding は self-consistent: Finding 1 の改善 regex が Finding 2 の placeholder
残存を機械的に検出可能になる。

1. Minor: ADR-033 line 111 の検証 regex が数値以外の placeholder を見逃す
   - 旧: `順位 [0-9]+` (数値のみマッチ)
   - 新: `順位 [0-9A-Za-z_-]+` (英字 placeholder `順位 X` / `順位 N` も検出)
   - 期待コメントも更新し、placeholder 検出意図を明記

2. Minor: docs/todo3.md line 511 (REJECT-ESCALATE entry の動機セクション) で
   `.claude/ filter (順位 X = T2-1+T3-2 Bundle)` という placeholder が残存
   - 旧: `.claude/ filter (順位 X = T2-1+T3-2 Bundle)`
   - 新: `.claude/ filter + ADR-030 制約明記 task (PR #91 T2-1 + T3-2 Bundle)`
   - ADR-033 「本文はタスク名参照」方針に準拠

検証 (改善 regex で 0 件達成):
```sh
grep -nE "順位 [0-9A-Za-z_-]+" docs/todo.md docs/todo2.md docs/todo3.md   | grep -vE "推奨実行順序サマリー|^[^:]+:[0-9]+:\| [0-9]+ \|"
# 結果: 0 行
```
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
aloekun added a commit that referenced this pull request Apr 30, 2026
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 層を完成
aloekun added a commit that referenced this pull request Apr 30, 2026
…Bundle V 登録 (#96)

* feat(cli-pr-monitor): 重複起動 file lock + ポーリング間隔延長 (Phase 3 / 順位 12)

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 層を完成

* fix(review): apply CodeRabbit fixes for #96

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 します。

* fix(lock): CodeRabbit 指摘 (#96 round 2) を反映

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。
aloekun added a commit that referenced this pull request Apr 30, 2026
cli-pr-monitor の重複起動防止 lock file (PR #88 / #96 で導入) が
.gitignore 漏れで PR snapshot に混入する事故を防ぐ。
aloekun added a commit that referenced this pull request Apr 30, 2026
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 (変更パッケージ)
aloekun added a commit that referenced this pull request Apr 30, 2026
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 化候補。
aloekun added a commit that referenced this pull request Apr 30, 2026
* 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 可能)
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