Skip to content

feat(cli-pr-monitor): PastTime newtype + proptest (Bundle W、順位 19 依存解除) - #199

Merged
aloekun merged 2 commits into
masterfrom
bundle-w-pasttime-proptest
Jun 8, 2026
Merged

feat(cli-pr-monitor): PastTime newtype + proptest (Bundle W、順位 19 依存解除)#199
aloekun merged 2 commits into
masterfrom
bundle-w-pasttime-proptest

Conversation

@aloekun

@aloekun aloekun commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Summary

Bundle W (順位 34 PBT + 順位 35 PastTime newtype) を Tier 1 として独立 land。Tier 1 → Tier 2 の priority inversion (順位 19 への dead-lock 状の依存) を解除した上で実装。

Changes

  • src/cli-pr-monitor/src/lock.rs: PastTime { epoch_secs, captured_now } newtype を導入。from_iso8601_now / from_parts で「parse 成功 + then <= now」を construction に閉じ込め、age_secs() が常に非負となる invariant を構造的に保証。旧 parse_age_secs は削除し read_fresh_lock を refactor (module 内 closure、public API 変更なし)
  • src/cli-pr-monitor/Cargo.toml: [dev-dependencies]proptest = "1" を追加
  • src/cli-pr-monitor/src/lock.rs (test): #[cfg(test)] mod proptests に 5 properties + PastTime unit test 4 件を追加
  • docs/todo-summary.md / docs/todo4.md: 順位 34/35 の row / 詳細 entry を削除、関連 entry (順位 36/38) の依存記述を Bundle W land 済を反映するよう update

Properties (proptest)

# Property 検証対象
P1 past_time_age_is_correct_when_in_past then <= nowage_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

依存解除の根拠

旧 sequencing 「順位 13 (rate-limit 自動検出) / 順位 19 (REJECT-ESCALATE) land 後着手」は obsolete:

順位 19 自身は 2026-06-06 Status update で「5-10 PR baseline 観測フェーズ」に移行 = 待機解除タイミング未定で Tier 1 を gate し続けるとデッドロックに近い扱いとなるため independent 化。

Scope 外 (out-of-scope, follow-up)

  • 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 登録予定
  • B / C (本セッション議論): A (本 PR) merge 後に別タスクとして登録:
    • B: 順位 19 自体の Tier 再評価 (retire 含む方向性)
    • C: docs/todo-summary.md table の Tier 1→2 priority inversion を機械検知する layer (lint or weekly-review facet)

Test plan

  • cargo build -p cli-pr-monitor: 成功
  • cargo test -p cli-pr-monitor lock::: 23 passed (proptest 5 + PastTime unit 4 + 既存 14)、0.04s
  • cargo clippy -p cli-pr-monitor --all-targets -- -D warnings: clean
  • pre-push review (takt simplicity facet): APPROVE (1 iteration, 3m 2s)
  • CI green (CodeRabbit + GitHub Actions)
  • merge 後に B / C タスクを docs/todo*.md に登録

Summary by CodeRabbit

リリースノート

  • バグ修正

    • ロック機構のタイムスタンプ検証を強化し、無効なタイムスタンプの処理を改善しました。
  • ドキュメント

    • 開発タスク追跡ドキュメントを更新し、優先順序と依存関係の状態を反映しました。
  • テスト

    • タイムスタンプ解析と検証ロジックのプロパティベーステストを追加しました。

aloekun added 2 commits June 7, 2026 17:44
…rom_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
@coderabbitai

coderabbitai Bot commented Jun 7, 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: 403e03b0-4d20-4d1a-a26b-48cc6a5770bc

📥 Commits

Reviewing files that changed from the base of the PR and between 426217c and b51a3a0.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • docs/todo-summary.md
  • docs/todo4.md
  • src/cli-pr-monitor/Cargo.toml
  • src/cli-pr-monitor/src/lock.rs

📝 Walkthrough

Walkthrough

ロック鮮度判定を新しいPastTime型に置き換え、future timestamp を構造的に拒否して非負性を保証する。proptest によるプロパティテストを追加してエッジケースを検証し、プロジェクト計画ドキュメントは完了タスク・変更依存関係を反映する。

Changes

PastTime型とロック安全性の向上

Layer / File(s) Summary
PastTime型の定義と基盤ヘルパー
src/cli-pr-monitor/src/lock.rs (lines 164-218)
PastTime型を新設し、from_iso8601_nowfrom_partsコンストラクタで future timestamp を構造的に拒否。age_secs()は非負返値を保証し、current_unix_secsヘルパがSystemTimeから unix 秒への安全な変換を提供する。
read_fresh_lockでのPastTime統合
src/cli-pr-monitor/src/lock.rs (lines 151-152)
ロック stale 判定の経過秒計算を旧parse_age_secsから新PastTime::from_iso8601_nowに置き換え、age_secs()の非負返値を前提とした安全な stale 判定を実行する。
PastTime型のユニットテストと性質ベーステスト
src/cli-pr-monitor/src/lock.rs (lines 552-665)
from_partsfrom_iso8601_nowの受理・拒否ケースを単体テストで検証。proptest によりparse_iso8601PastTime::from_partsの包括的性質テストを実装し、panic 非発生・pre-epoch 年拒否・有効日付受理を確認する。
テスト基盤整備とシナリオ調整
src/cli-pr-monitor/Cargo.toml (line 16),
src/cli-pr-monitor/src/lock.rs (lines 537-542)
Cargo.toml にproptest = "1"を追加し、性質ベーステストの依存を確保。acquire_atの I/O エラー時にUnavailableを返すテストシナリオを、親パスが通常ファイルであるケースとして明確化する。
ドキュメント更新(タスク進行状況と依存関係)
docs/todo-summary.md (lines 26-28),
docs/todo4.md (lines 13-17, 110-114, 145)
推奨実行順序サマリーテーブルを Tier 1 完了後の状態に更新し、cargo-mutants 統合・concurrency stress・weekly 統合の依存関係を Bundle W land 完了・残依存縮小に合わせて修正。todo4.md の現在進行中エントリを proptest 導入から cargo-mutants 統合へ切り替え、ADR-031 のステータスと依存を反映する。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • aloekun/claude-code-hook-test#96: 本 PR がPastTime型とproptestを実装する一方、PR #96src/cli-pr-monitor/src/lock.rsのロック stale 判定・ISO8601 age 計算ロジックとテストの初期導入を行い、直接の前提関係にある。
  • aloekun/claude-code-hook-test#133: PR #133docs/todo-summary.mdの分割・リンク参照を導入した後、本 PR がそのテーブル内容とtodo4.md参照を新しいタスク順序に合わせて編集する。
🚥 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 The title clearly and specifically summarizes the main changes: introduction of PastTime newtype and proptest testing, along with the dependency resolution for Bundle W.
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.

@aloekun
aloekun merged commit 1db7c59 into master Jun 8, 2026
1 check passed
@aloekun
aloekun deleted the bundle-w-pasttime-proptest branch June 8, 2026 03:33
aloekun added a commit that referenced this pull request Jun 8, 2026
…機械検知 (順位 196 完了)

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 マーカーをカバー。

## テスト & dogfood

- 17 unit tests pass (table parse / tier 抽出 / rank 抽出 / 「なし」 prefix /
  resolved 検出 / inversion 検出 / Tier 3→Tier 2 非 inversion / missing-rank skip /
  Tier 2→Tier 3 inversion)
- 現 docs/todo-summary.md に対し 0 violations (false positive なし)
- false positive 抑制の 1 ラウンド: 順位 150 (Tier 2) の依存欄が「なし (順位 102 + 順位 118
  と整合)」で 順位 118 (Tier 3) 参照を検出していたため、`なし` 行を skip する
  `has_no_dependency_prefix` を追加

## pre-push reviewer non-blocking findings 対応 (F-2 + F-3)

- **F-2**: production code (lib code) の `parse_tier` / `extract_referenced_ranks` が
  per-row `Regex::new()` 再 compile していた問題を `LazyLock<Regex>` で module 初期化
  時の 1 回 compile に修正 (Rust 1.92 標準 lib)。既存 codebase の OnceLock 利用 (cli-pr-monitor)
  と整合する std::sync::* family
- **F-3**: `check_content_skips_when_referenced_rank_missing_from_table` テストの fixture
  に resolved-marker (`(retire 済)`) が含まれており、将来 fixture に rank 19 行を追加した
  際に test 経路が missing-rank → resolved-marker へ silent shift する fragility が
  あった。fixture を `"順位 19 land 後推奨"` (resolved-marker 非含有) に変更し、
  missing-rank 経路を厳密に exercise する形に修正。意図は doc コメントで明文化

## 統合

- `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 昇格 / 却下を判定)

## 完了確認

- `cargo build -p cli-docs-lint`: 成功
- `cargo test -p cli-docs-lint`: 48 passed (17 priority_inversion + 既存 31)
- `cargo clippy -p cli-docs-lint --all-targets -- -D warnings`: clean
- `pnpm lint:docs`: OK (preamble + cross-ref + priority-inversion)

Refs: 順位 196 (本 PR で削除済、本 commit で実装完了)
aloekun added a commit that referenced this pull request Jun 8, 2026
…機械検知

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 昇格 / 却下を判定)
aloekun added a commit that referenced this pull request Jun 8, 2026
…nd で代替経路実現済、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)
aloekun added a commit that referenced this pull request Jun 8, 2026
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 行を追加
aloekun added a commit that referenced this pull request Jun 8, 2026
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 行を追加
aloekun added a commit that referenced this pull request Jun 9, 2026
…-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)
aloekun added a commit that referenced this pull request Jun 11, 2026
…undle W 派生) (#203)

順位 197 採用 (PR #199 post-merge-feedback T2-1)。Bundle W (cli-pr-monitor::lock の
PastTime newtype + proptest pattern) を hooks-session-start に展開し、orphan reaper
の age 計算における saturating_sub silent semantic mismatch を型層で排除する。

## 変更

- src/hooks-session-start/Cargo.toml: [dev-dependencies] に proptest = "1" を追加
- src/hooks-session-start/src/past_time.rs (新規): PastTime newtype + 4 unit tests + 5 proptests (P1-P5)
- src/hooks-session-start/src/main.rs:236: now_unix.saturating_sub(start_unix) を
  PastTime::from_parts(start_unix, now_unix) に差し替え、future timestamp を None で reject
- src/hooks-session-start/src/main.rs: future timestamp の caller 挙動を直接検証する
  integration test find_orphans_skips_future_start_time_without_silent_age_zero を追加

## 設計判断

- アプローチ A (Bundle W 完全移植) を採用 (todo10.md MVP)
- 共通 lib 化 (アプローチ C) は parser 実装差異 (fractional 秒対応の有無) のため別 task に分離
- PastTime は別 module file (past_time.rs) に切り出し、main.rs への touch を最小化
- past_time.rs は新規 158 行、main.rs への touch は mod 宣言 + 1 行差し替え + 1 integration test の最小限

## 構造的予防 vs 挙動変更

future timestamp の最終挙動 (skip) は変わらない。改善は silent age=0 fallback への regression
不能化:

- saturating_sub 時代: future timestamp → silently age=0 → "young" 判定 (silent fallback)
- PastTime: future timestamp → from_parts が None → 明示 skip (型層で fallback 不能)
- proptest P2 (past_time_rejects_future) が silent age=0 への regression を構造的に阻む

.failed marker を future timestamp で生成するかは別の設計判断 (本 PR scope 外)。

## File-length lint

main.rs は本 PR 着手前から 1588 行 (>800 行上限) で existing-grandfathered。順位 147 ファイル長 lint の
touch-trigger ratchet 設計上、main.rs に何かを touch すれば必ず発火する。本 PR では touch を最小化
(mod 宣言 + 数行差し替え + 1 test) し、新規 code は past_time.rs に切り出した。main.rs split は別 task。

## テスト結果

- cargo test -p hooks-session-start: 68 passed (新規 9 件 = 4 unit + 5 proptest + 1 integration)
- cargo clippy --all-targets -- -D warnings: clean
- cargo build --workspace: OK

## 参照

- PR #199 (Bundle W = cli-pr-monitor::lock PastTime land)
- .claude/feedback-reports/199.md Tier 2 #1
- ADR-030 (orphan reaper L2 recovery)
- src/cli-pr-monitor/src/lock.rs (PastTime + proptest reference)
aloekun added a commit that referenced this pull request Jun 12, 2026
…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 に集約)。
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