Skip to content

feat(hooks-post-tool-linter): no-time-field-strict-greater rule (順位 47 / §A-2 P-2)#126

Merged
aloekun merged 1 commit into
masterfrom
feature/p2-time-field-strict-greater
May 7, 2026
Merged

feat(hooks-post-tool-linter): no-time-field-strict-greater rule (順位 47 / §A-2 P-2)#126
aloekun merged 1 commit into
masterfrom
feature/p2-time-field-strict-greater

Conversation

@aloekun
Copy link
Copy Markdown
Owner

@aloekun aloekun commented May 7, 2026

Summary

  • §A-2 Phase 5 dogfood P-2 (順位 47) — PR feat(check-ci-coderabbit): --list-findings モード追加 (Bundle a Sub-PR 1) #101 で実証された latent drift (parse_findings 系 3 関数で時刻フィールドの > vs >= 混在) を構造的に防止する custom lint rule を追加
  • Bundle Z #B-α と同じ「決定論的防止層」哲学 (ADR-007 正規表現層)
  • master 上 classifier (P-0 で有効化) が本 PR の post-pr-monitor で classification を起動 → §A-2 計測ログに dogfood 結果を記録予定

Changes

.claude/custom-lint-rules.toml

新ルール no-time-field-strict-greater 追加:

[[rules]]
id = "no-time-field-strict-greater"
pattern = '\b(created_at|submitted_at|updated_at|comment_event_time|event_time|comment_created_at|published_at|posted_at|commented_at)\s*>\s*[a-zA-Z_]'
severity = "warning"
extensions = ["rs"]

検出対象: 9 種の時刻フィールド (created_at / submitted_at / updated_at / comment_event_time / event_time / comment_created_at / published_at / posted_at / commented_at) の strict > 比較で、RHS が alphabetic identifier の場合。

Direction-specific 設計 (> のみ flag):

  • < の strict 比較 (例: is_stalecreated_at < threshold) は意図的な exclusive 境界として正しい用途があるため scope 外
  • < 側の boundary 問題は観測されたら別 rule で対応

src/hooks-post-tool-linter/src/main.rs

11 unit tests + 3 helper 関数 で transition matrix を網羅:

Test 入力 期待
detects_created_at_gt_push_time created_at > push_time 1 violation
detects_submitted_at_gt_since submitted_at > since 1 violation
detects_updated_at_gt_threshold updated_at > threshold 1 violation
detects_comment_event_time comment_event_time > now 1 violation
skips_inclusive_comparison created_at >= push_time 0 violations
skips_strict_less_than created_at < threshold 0 violations
skips_le_inclusive created_at <= cutoff 0 violations
skips_numeric_rhs created_at > 0 0 violations
skips_doc_comment_with_inclusive /// created_at >= push_time 0 violations
skips_unrelated_field count > limit 0 violations
only_targets_rs TOML file with created_at > push_time 0 violations

Helper 関数 (test fixture を runtime 構築):

  • build_rs_source_with_op(field, op, rhs) -> String
  • build_doc_comment_source(field, op, rhs) -> String
  • build_toml_with_field(field, op, rhs) -> String

理由: literal created_at > push_time を test source 内に書くと 新ルール自身が test source を flag して self-trigger ループに陥る。format!() 経由で runtime 構築すると Edit-time lint の regex (literal adjacency 必須) を回避できる (= dogfood で実証された "lint rule self-violation" の structural pattern)。

.claude/hooks-post-tool-linter.exe

pnpm build:hooks-post-tool-linter で release build を再生成し配備

既存 codebase の lint 適用結果

  • 0 violations (grep -rn '\b(created_at|...)_at\s*>\s*[a-zA-Z_]' で 0 件)
  • 既存の created_at >= push_time (doc コメント) は >= のため対象外
  • 既存の is_stalecreated_at < threshold< のため対象外 (Direction-specific 設計の効果)

§A-2 dogfood (P-2) 観測ポイント

本 PR の post-pr-monitor で classifier が CodeRabbit findings を分類する 2 回目の dogfood サイクル (P-1 では findings ゼロで起動せず)。lint rule 追加 PR は CR から style / regex 厳密性 / edge case 指摘が出やすく、findings が複数発生する想定。

計測対象は P-1 と同じ:

  1. agreement rate: classifier の action 一致率 — 目標 ≥80%
  2. classifier latency: finding あたり秒数 — 目標 ≤5s/件
  3. fallback rate: human_review fallback 比率 — 目標 ≤20%
  4. normalized_issue 言語制約違反率: 英語 8 文字以上連続検出 — 目標 ≤10%

Self-violation 知見の副産物

本 PR で 新 lint rule が test source に self-trigger するパターンを実体験。Edit-time lint hook (PostToolUse) は new_string (Edit の追加文字列) を scan するため、test fixture に literal pattern を書くと Edit が即 reject される。回避策として test fixture を format!() で runtime 構築する DSL pattern を確立。これは新 lint rule 追加時の standard pattern として再利用可能 (今後の T1 lint task で同様のテクニックが必要)。

Test plan

  • cargo test -p hooks-post-tool-linter rs_time_field_strict_greater — 11/11 pass
  • cargo test -p hooks-post-tool-linter — 75/75 pass、regression なし
  • pre-push-review APPROVE (1 iter / 3m 4s)
  • release build + deploy
  • 既存 codebase で > strict 違反 0 件確認
  • (post-merge) classifier 起動確認 + §A-2 計測ログ記録

関連

  • 順位 47 — docs/todo5.md 詳細エントリ
  • §A-2 P-2 — docs/local-llm-offload-analysis.md
  • ADR-007 — custom lint rule の正規表現/AST 層線引き
  • Bundle Z #B-α (ADR-036) — 決定論的防止層哲学

Summary by CodeRabbit

リリースノート

  • Tests

    • タイムスタンプ比較に関連する新しいリントルールのテストカバレッジを追加しました。
  • Chores

    • 開発ツールにカスタムリント規則を追加し、コード品質チェックを強化しました。

…7 / §A-2 P-2)

§A-2 Phase 5 dogfood P-2。PR #101 で実証された latent drift (parse_findings
系 3 関数で created_at > push_time vs >= の混在) を構造的に防止する custom
lint rule を追加。Bundle Z #B-α と同じ「決定論的防止層」哲学。

ルール仕様:
- pattern: \b(created_at|submitted_at|updated_at|comment_event_time|
  event_time|comment_created_at|published_at|posted_at|commented_at)
  \s*>\s*[a-zA-Z_]
- direction: `>` のみ flag (`<` は is_stale 等で意図的 exclusive 境界
  の正しい用途があるため scope 外)
- target: .rs files only

検証:
- 11 unit tests (4 detect 系 + 7 skip 系) + 1 helper 関数 で transition
  matrix を網羅
- 全 75 tests passed
- 既存 codebase で 0 violations (created_at >= は doc コメントのみ、
  is_stale の created_at < は `<` で対象外)

build + deploy 済 (.claude/hooks-post-tool-linter.exe)。
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

📝 Walkthrough
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed プルリクエストのタイトルは、新しいカスタムリント規則 no-time-field-strict-greater の追加という主要な変更を明確に要約しており、変更セットの中心内容と完全に関連している。
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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/hooks-post-tool-linter/src/main.rs (1)

1360-1366: ⚡ Quick win

9 フィールドのうち 5 フィールドに検出テストがありません。

TOML の交代パターンには 9 フィールドが列挙されていますが、陽性検出テスト (violations.len() == 1 を assert するもの) は created_atsubmitted_atupdated_atcomment_event_time の 4 フィールドのみです。以下の 5 フィールドには検出テストがなく、TOML の交代式への誤字があってもテストスイートでは捕捉できません。

  • event_time
  • comment_created_at
  • published_at
  • posted_at
  • commented_at
🧪 追加テストの例 (残り 5 フィールド)
+    #[test]
+    fn rs_time_field_strict_greater_detects_event_time() {
+        let dir = tempfile::tempdir().unwrap();
+        let file = write_file(
+            dir.path(),
+            "parse.rs",
+            &build_rs_source_with_op("event_time", ">", "cutoff"),
+        );
+        let rules = compile_test_rules(vec![rs_time_field_strict_greater_rule()]);
+        let violations = run_custom_rules(file.to_str().unwrap(), &rules);
+        assert_eq!(violations.len(), 1);
+    }
+
+    #[test]
+    fn rs_time_field_strict_greater_detects_comment_created_at() {
+        let dir = tempfile::tempdir().unwrap();
+        let file = write_file(
+            dir.path(),
+            "parse.rs",
+            &build_rs_source_with_op("comment_created_at", ">", "since"),
+        );
+        let rules = compile_test_rules(vec![rs_time_field_strict_greater_rule()]);
+        let violations = run_custom_rules(file.to_str().unwrap(), &rules);
+        assert_eq!(violations.len(), 1);
+    }
+
+    #[test]
+    fn rs_time_field_strict_greater_detects_published_at() {
+        let dir = tempfile::tempdir().unwrap();
+        let file = write_file(
+            dir.path(),
+            "parse.rs",
+            &build_rs_source_with_op("published_at", ">", "threshold"),
+        );
+        let rules = compile_test_rules(vec![rs_time_field_strict_greater_rule()]);
+        let violations = run_custom_rules(file.to_str().unwrap(), &rules);
+        assert_eq!(violations.len(), 1);
+    }
+
+    #[test]
+    fn rs_time_field_strict_greater_detects_posted_at() {
+        let dir = tempfile::tempdir().unwrap();
+        let file = write_file(
+            dir.path(),
+            "parse.rs",
+            &build_rs_source_with_op("posted_at", ">", "now"),
+        );
+        let rules = compile_test_rules(vec![rs_time_field_strict_greater_rule()]);
+        let violations = run_custom_rules(file.to_str().unwrap(), &rules);
+        assert_eq!(violations.len(), 1);
+    }
+
+    #[test]
+    fn rs_time_field_strict_greater_detects_commented_at() {
+        let dir = tempfile::tempdir().unwrap();
+        let file = write_file(
+            dir.path(),
+            "parse.rs",
+            &build_rs_source_with_op("commented_at", ">", "boundary"),
+        );
+        let rules = compile_test_rules(vec![rs_time_field_strict_greater_rule()]);
+        let violations = run_custom_rules(file.to_str().unwrap(), &rules);
+        assert_eq!(violations.len(), 1);
+    }

Also applies to: 1380-1521

🤖 Prompt for 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.

In `@src/hooks-post-tool-linter/src/main.rs` around lines 1360 - 1366, The test
suite is missing positive-detection tests for five time-field alternatives
listed in the regex used by rs_time_field_strict_greater_rule (event_time,
comment_created_at, published_at, posted_at, commented_at); add unit tests
alongside the existing positive cases (the tests that assert violations.len() ==
1) to cover each of these five fields so any typo in the TOML pattern will be
caught. Locate rs_time_field_strict_greater_rule and the test block(s) that
currently assert violations.len() == 1 for created_at, submitted_at, updated_at,
comment_event_time and add similar tests that feed sample Rust input containing
each of event_time, comment_created_at, published_at, posted_at, and
commented_at (using the same test harness and expectations) to ensure each
alternative triggers a violation.
🤖 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.

Nitpick comments:
In `@src/hooks-post-tool-linter/src/main.rs`:
- Around line 1360-1366: The test suite is missing positive-detection tests for
five time-field alternatives listed in the regex used by
rs_time_field_strict_greater_rule (event_time, comment_created_at, published_at,
posted_at, commented_at); add unit tests alongside the existing positive cases
(the tests that assert violations.len() == 1) to cover each of these five fields
so any typo in the TOML pattern will be caught. Locate
rs_time_field_strict_greater_rule and the test block(s) that currently assert
violations.len() == 1 for created_at, submitted_at, updated_at,
comment_event_time and add similar tests that feed sample Rust input containing
each of event_time, comment_created_at, published_at, posted_at, and
commented_at (using the same test harness and expectations) to ensure each
alternative triggers a violation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1d4e3e97-31ae-466d-96fe-e93eb72cfea1

📥 Commits

Reviewing files that changed from the base of the PR and between be5ee6f and 39f0d95.

📒 Files selected for processing (2)
  • .claude/custom-lint-rules.toml
  • src/hooks-post-tool-linter/src/main.rs

@aloekun aloekun merged commit b677b9d into master May 7, 2026
1 check passed
@aloekun aloekun deleted the feature/p2-time-field-strict-greater branch May 7, 2026 14:39
aloekun added a commit that referenced this pull request May 7, 2026
…P-1/P-2 ledger (順位 7 / §A-2 P-3) (#127)

* feat(hooks-post-tool-linter): PowerShell (?i) flag validation + §A-2 P-1/P-2 ledger 記録 (順位 7 / §A-2 P-3)

§A-2 Phase 5 dogfood P-3 (順位 7)。PR #91 で発覚した PowerShell case-insensitive
regex の構造的落とし穴 (no-empty-powershell-catch / no-silent-error-action で
(?i) 欠落 → CR Major 指摘) を機械強制で再発防止。

順位 7 (Tier 1, T1-1):
- find_powershell_rules_missing_case_insensitive_flag() 関数追加: extensions に
  ps1 を含む rule で pattern に (?i) が無いものを ID リストで返す
- load_custom_rules() で起動時 warn (本番運用層)
- 7 unit tests (異種 violator / valid / mixed-ext / case-insensitive ext /
  multi-violator / non-ps1 ignore) + 1 deployed-config 全 rule 検証 test (CI 検出層)
- 既存 no-ephemeral-todo-reference rule の pattern に (?i) を追加 (deployed
  validation を pass、Windows file path 大文字小文字混在対応の副次効果あり)
- ~/.claude/rules/common/code-review.md § Custom lint rule patterns 追加
  (PR #91 a15b263 fix の経緯 + 順位 7 機械強制の事後参照)

§A-2 計測ログ (P-1/P-2) 記録:
- P-1 (PR #125): findings: 0 (CR APPROVE no comments)、classifier 未起動 →
  dogfood 不発
- P-2 (PR #126): findings: 1 (CR Nitpick、review body <details> block 内、
  check-ci-coderabbit 抽出漏れ)、手動 synthetic finding で classifier 実行 →
  action=human_review / action_confidence=0.0 / fallback=length_contract、
  agreement: 1/1 (100%)、latency: 6.4s/件 (>5s 目標)、fallback: 1/1
- 既知 system gap: check-ci-coderabbit が review body の <details> Nitpick を
  抽出しない (post-pr-monitor が classifier に渡す入力経路に欠落)

build + deploy 済 (.claude/hooks-post-tool-linter.exe)。
全 82 tests pass、regression なし。

* docs(todo): 順位 7 (PowerShell (?i) flag 自動検証) 完了に伴い削除
aloekun added a commit that referenced this pull request May 13, 2026
* docs(todo,analysis): D-4 を 順位 39 に re-pivot + stale 順位 47 entry 削除

順位 47 (>vs>= boundary lint) は PR #126 (commit b677b9d, 2026-05-08) で
no-time-field-strict-greater rule として既に land 済 (custom-lint-rules.toml
line 208-243) を D-4 着手前検証で発見。memory rule feedback_verify_task_not_already_done
を適用して stale todo entry を削除し、D-4 を Round 2 計画から 順位 39 (takt workflow
`model` 必須化 lint rule) に繰上げ、D-5 を 順位 56 + 119 bundle に再構成。

変更:
- docs/local-llm-offload-analysis.md: D-4/D-5 row 更新 + 想定リスク に re-pivot 経緯追記
- docs/todo7.md: stale 順位 47 entry (38 行) 削除
- docs/todo-summary.md: 順位 47 row 削除

Phase E 着手前提 (5 PR 累積 dogfood) は変わらず、D-4〜D-7 で 4 PR 追加観測の方針継続。

* feat(hooks-post-tool-linter): takt-workflow-persona-without-model lint rule (順位 39 / Phase D D-4)

PR #98 post-merge-feedback Tier 1 #1 採用。takt workflow yaml で persona: を持つ step
に model: 未指定の場合を検出する custom lint rule (ルール⑨)。Bundle Z #B-α と同じ
決定論的防止層 (ADR-007 正規表現層)。

実装:
- .claude/custom-lint-rules.toml: 新規 rule takt-workflow-persona-without-model
  - pattern: multi-line regex で persona: 直後の field 行が model: 以外なら fire
  - field enumeration 方式 (Rust regex lookahead 非対応の pragmatic 対処)
  - extensions=[yaml] + paths=[.takt/workflows/*.yaml] で範囲限定 (順位 102 PR #148 で
    実装した paths filter を利用)
- .takt/workflows/post-pr-review.yaml: 2 site に model: sonnet 追加で clean baseline
  - line 23: judge block (loop_monitor) の persona: supervisor 直下
  - line 117: supervise step の persona: supervisor 直下 (Bundle Y2 完全性)
- .takt/workflows/pre-push-review.yaml: 1 site に model: sonnet 追加で clean baseline
  - line 26: judge block (loop_monitor) の persona: supervisor 直下
- src/hooks-post-tool-linter/src/main.rs: unit tests 6 件追加
  - takt_workflow_persona_detects_judge_block_violation
  - takt_workflow_persona_detects_supervise_step_violation
  - takt_workflow_persona_skips_when_model_directly_follows (clean baseline)
  - takt_workflow_persona_detects_multiple_violations_in_same_file
  - takt_workflow_persona_skips_non_yaml_extension (extensions filter)
  - deployed_takt_workflows_have_clean_baseline_for_persona_model_rule (regression test)

Phase D D-4 dogfood: real diff として lint_screen の dogfood data 蓄積を目的。
Round 2 計画 (D-3 + D-4〜D-7 = 累積 5 PR) の 2 PR 目。

cargo test: 108 passed (前 102 + 新 6)。

* fix(hooks-post-tool-linter): takt persona-without-model rule に 4 fields 追加 (PR #150 CR Major)

CodeRabbit Major finding (#150 (comment)):
persona 直後の field 列挙 から output_contracts / pass_previous_response /
required_permission_mode / parallel が漏れていた = false negative リスク。
4 fields を pattern alternation に追加し regression test も同梱。

変更:
- .claude/custom-lint-rules.toml: ルール⑨ pattern alternation に 4 fields 追加
- src/hooks-post-tool-linter/src/main.rs: 同期更新 + 新 test
  takt_workflow_persona_detects_required_permission_mode_violation で
  pre-push-review.yaml fix step (persona: coder → required_permission_mode:)
  pattern が検出されることを assert

cargo test: 109 passed (前 108 + 新 1、CR Major regression test)

CR Minor (line 5、未来日進捗) は project local time (JST) と CR UTC の時差解釈
ギャップに起因 (CR 時点 = 2026-05-12 20:04 UTC = 2026-05-13 05:04 JST)。
2026-05-13 は session-start hook が認識する local 今日であり、'着手' は本 PR で
発生した事実。project 内既存 PR land 日付も全て local time (timezone qualifier 無し)
で記載しており、本 PR の追記のみ qualifier 付与は逆に不整合を生む。
別 reply で resolved として close する。
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