Skip to content

feat(hooks-session-start): PastTime newtype + proptest 移植 (順位 197 / Bundle W 派生) - #203

Merged
aloekun merged 1 commit into
masterfrom
hooks-session-start-past-time
Jun 11, 2026
Merged

aloekun merged 1 commit into
masterfrom
hooks-session-start-past-time

Conversation

@aloekun

@aloekun aloekun commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Summary

順位 197 採用 (PR #199 post-merge-feedback T2-1)。Bundle W (cli-pr-monitor::lockPastTime 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 (新規 158 行): 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: 整合性検証用 integration test find_orphans_skips_future_start_time_without_silent_age_zero を追加
  • docs/todo10.md + docs/todo-summary.md: 順位 197 エントリ削除

設計判断

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

構造的予防 vs 挙動変更

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

経路 future timestamp の挙動
saturating_sub 時代 silently age=0 → "young" 判定 (silent fallback)
PastTime 移植後 from_partsNone → 明示 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 (PR #202 land) のファイル長 lint は touch-trigger ratchet 設計のため、main.rs への何らかの touch で必ず発火する。本 PR では:

  • main.rs への touch を最小化 (mod 宣言 + 1 行差し替え + 1 test)
  • 新規 code は past_time.rs (新 module file) に切り出し
  • main.rs split (~800 行未満化) は別 task として scope 外

テスト結果

cargo test -p hooks-session-start
test result: ok. 68 passed; 0 failed (新規 9件 = 4 unit + 5 proptest + 1 integration)

cargo clippy --all-targets -- -D warnings
warnings なし

cargo build --workspace
OK

参照

Test plan

  • cargo test -p hooks-session-start で 68 tests pass
  • cargo clippy --all-targets -- -D warnings clean
  • cargo build --workspace OK
  • proptest 実行時間 < 1 秒 (デフォルト 256 cases)
  • 既存 orphan reaper test (find_orphans_*) の regression なし

Summary by CodeRabbit

リリースノート

  • 新機能

    • セッション孤立検出の時刻計算が改善され、未来の時刻が適切に処理されるようになりました。
  • テスト

    • プロパティベーステストが追加され、時刻計算の安全性が検証されるようになりました。
  • ドキュメント

    • タイムスタンプ不変性に関する新しい設計決定記録(ADR)が追加されました。

…undle W 派生)

順位 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)
@coderabbitai

coderabbitai Bot commented Jun 11, 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: d6fc6e91-5cce-4f07-a832-2d484ec6143b

📥 Commits

Reviewing files that changed from the base of the PR and between b737a53 and ba11006.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • docs/todo-summary.md
  • docs/todo10.md
  • src/hooks-session-start/Cargo.toml
  • src/hooks-session-start/src/main.rs
  • src/hooks-session-start/src/past_time.rs
💤 Files with no reviewable changes (2)
  • docs/todo-summary.md
  • docs/todo10.md

📝 Walkthrough

Walkthrough

orphan 判定における時刻計算を型安全な PastTime 新型で置き換え、未来の startTime を構造的に拒否。proptest で年齢計算整合性と future 拒否を検証。

Changes

PastTime 型と Orphan 判定統合

Layer / File(s) Summary
PastTime 型定義とテスト実装
src/hooks-session-start/src/past_time.rs
新規 PastTime 型は then <= now を from_parts で検証し、age_secs で経過秒を返す。単体テストで過去・同値は受理、未来・off-by-one は拒否を確認。proptest スイートで from_parts の年齢整合性、未来拒否、parse_iso8601_to_unix の panic 非発生・pre-epoch 拒否・有効範囲受理を検証。
main.rs への統合と orphan ロジック更新
src/hooks-session-start/src/main.rs
past_time モジュールのインポート、parse_iso8601_to_unix を pub(crate) に公開。find_orphan_post_merge_feedback_runs 内で saturating_sub を PastTime::from_parts に置き換え、未来の startTime を orphan 候補から除外。future startTime が age=0 で誤検出されないテストを追加。
開発依存の追加
src/hooks-session-start/Cargo.toml
proptest = "1" を dev-dependencies に追加し、プロパティテスト環境を整備。
タスク一覧と ADR ドキュメント更新
docs/todo-summary.md, docs/todo10.md
proptest 追加タスク(順位 197)を削除。新規 ADR Timestamp invariant safety の codify タスク(saturating_sub 禁止、過去性の型表現、proptest による実行可能契約)に置き換え。

Sequence Diagram

sequenceDiagram
  participant Reaper as Orphan Reaper
  participant Parser as parse_iso8601_to_unix
  participant PastTime
  participant Judge as find_orphan_post_merge_feedback_runs
  Reaper->>Parser: startTime (ISO8601 string)
  Parser-->>Reaper: start_unix (i64)
  Reaper->>PastTime: from_parts(start_unix, now_unix)
  alt then <= now
    PastTime-->>Reaper: Some(PastTime)
    Reaper->>PastTime: age_secs()
    PastTime-->>Reaper: elapsed_secs (i64)
    Reaper->>Judge: include in orphan candidates
  else then > now (future)
    PastTime-->>Reaper: None
    Reaper->>Judge: exclude from orphan candidates
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • aloekun/claude-code-hook-test#199: PR #199 の時間計算 saturating_sub と ISO8601 取り扱いを、main PR の PastTime 導入(then <= now 構造保証)と proptest 回帰(未来拒否・panic 非発生)により実装。
  • aloekun/claude-code-hook-test#154: orphan reaper と thresholded age 計算を対象にした先行実装。main PR は同じ orphan reaper ロジックに型安全性を追加。
🚥 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タイトルは「PastTime newtype + proptest 移植」という主要な変更内容を明確に示しており、変更セットの中心的な目的と完全に関連している。
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 e2ae73e into master Jun 11, 2026
1 check passed
@aloekun
aloekun deleted the hooks-session-start-past-time branch June 11, 2026 07:38
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