Skip to content

feat(hooks): takt persona-without-model lint rule (順位 39 / D-4)#150

Open
aloekun wants to merge 3 commits into
masterfrom
feat/takt-persona-model-lint
Open

feat(hooks): takt persona-without-model lint rule (順位 39 / D-4)#150
aloekun wants to merge 3 commits into
masterfrom
feat/takt-persona-model-lint

Conversation

@aloekun
Copy link
Copy Markdown
Owner

@aloekun aloekun commented May 12, 2026

Summary

Phase D D-4 として 順位 39 (PR #98 post-merge-feedback Tier 1 #1) の takt-workflow-persona-without-model lint rule を実装。Bundle Z #B-α と同じ決定論的防止層 (ADR-007 正規表現層)。

当初 D-4 = 順位 47 (> vs >= boundary lint) を予定していたが、着手前検証 (memory rule feedback_verify_task_not_already_done) で PR #126 (b677b9d4f54d) で既に land 済 を発見し、D-5 から 順位 39 を D-4 に繰上げ、D-5 を 順位 56 + 119 bundle に再構成。詳細は docs/local-llm-offload-analysis.md L295-299 参照。

Commit 構成

  1. 12d11c96 — docs(todo,analysis): D-4 を 順位 39 に re-pivot + stale 順位 47 entry 削除
  2. 0c2cc07d — feat(hooks-post-tool-linter): takt-workflow-persona-without-model lint rule

変更内容 (commit 2)

新規 lint rule ⑨ (.claude/custom-lint-rules.toml)

  • id: takt-workflow-persona-without-model
  • pattern: multi-line regex で persona: 直後の field 行が model: 以外なら fire
  • enumeration 方式で takt yaml の sibling field 列挙 (Rust regex lookahead 非対応の pragmatic 対処)
  • extensions=[yaml] + paths=[.takt/workflows/*.yaml] で範囲限定 (順位 102 / PR feat(hooks-post-tool-linter): paths filter + Phase D 初 real lint_screen dogfood (順位 102) #148 の paths filter 活用)
  • severity = error / fix strategy = "model: <sonnet|haiku|opus> を追加"

Clean baseline (3 yaml site に model: sonnet 追加)

  • .takt/workflows/post-pr-review.yaml:24 (judge block in loop_monitor)
  • .takt/workflows/post-pr-review.yaml:119 (supervise step、Bundle Y2 完全性)
  • .takt/workflows/pre-push-review.yaml:27 (judge block in loop_monitor)

Unit tests (6 件、src/hooks-post-tool-linter/src/main.rs)

  • 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
  • deployed_takt_workflows_have_clean_baseline_for_persona_model_rule (regression test)

Phase D D-4 dogfood metrics

LINT_SCREEN_ENABLED=true で push を実行。.takt/lint-screen-report.md の結果:

Metric 観測値
screen_decision informational
findings 件数 0
fallback_reason なし (clean run)
## Diagnostic section 不在 = num_ctx 32768 で overflow 発生せず
pre-push pipeline 総 latency 645s
takt review iter 1 (3m 28s)、simplicity + security 両 APPROVED
kill-switch (fallback > 50%) fallback 0/1 = 0% → 基準内

D-4 観測の意義:

  1. informational verdict の初観測: D-3 は auto_fix + 1 false positive、D-4 は informational + 0 findings = lint-screen が「指摘なし」と判定する経路を実証
  2. clean run + num_ctx 余裕: ~530 行 (Cargo.lock 等含む) Rust + toml + yaml mix diff で overflow なし
  3. diff profile multiplicty: Rust impl (lint rule + tests) + TOML (rule entry) + YAML (3 clean baseline) の heterogeneous diff を lint_screen が「指摘なし」と正しく判断
  4. Round 2 (D-4〜D-7) 累積 PR data 進捗: 2/4 PR 観測完了 (D-3 + D-4)、残り D-5/D-6/D-7

Test plan

  • cargo test -p hooks-post-tool-linter --bin hooks-post-tool-linter で 108 tests pass (前 102 + 新 6)
  • cargo fmt --check で新規追加コードは rustfmt clean
  • pnpm build:all で deploy 用 exe 更新成功
  • LINT_SCREEN_ENABLED=true pnpm push で pre-push pipeline 完走 (simplicity + security 両 APPROVED、1 iteration 3m 28s)
  • .takt/workflows/*.yaml 全 file が clean baseline 維持 (deployed_takt_workflows_have_clean_baseline_for_persona_model_rule test で機械検証)

Out of scope

  • 派生プロジェクト (techbook-ledger / auto-review-fix-vc) への deploy: 別途 pnpm deploy:hooks 実行で対応
  • D-5/D-6/D-7 着手: 本 PR land 後の別 PR で順次対応

関連

  • 順位 39 spec: docs/todo4.md L265
  • Round 2 計画: docs/local-llm-offload-analysis.md §1 Phase D Round 2 table
  • ADR-007: hooks/カスタムリンターの正規表現層 / AST 層線引き

Summary by CodeRabbit

  • New Features

    • 追加:ワークフロー設定における persona: フィールドに対して model: の明示的指定を検証する新しいカスタムリントルール
  • Tests

    • 追加:新しいリントルールの動作検証テストスイート
  • Documentation

    • 更新:プロジェクト進行状況と Phase D・E の計画情報
    • 更新:完了済みタスク情報の削除

Review Change Stack

aloekun added 2 commits May 13, 2026 04:08
順位 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 追加観測の方針継続。
…t 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)。
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f3e461f3-ec38-4de3-a445-c6300469ee52

📥 Commits

Reviewing files that changed from the base of the PR and between 0c2cc07 and 1ec1568.

📒 Files selected for processing (2)
  • .claude/custom-lint-rules.toml
  • src/hooks-post-tool-linter/src/main.rs
 ________________________________________________________________
< CodeHamster is my sidekick. She powers the GPU with her wheel. >
 ----------------------------------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
📝 Walkthrough

Walkthrough

新しいカスタムlintルール takt-workflow-persona-without-model を追加し、taktワークフローの persona: 宣言に対して明示的な model: フィールド指定を強制します。既存の2つのワークフロー (post-pr-review.yamlpre-push-review.yaml) を更新してこのルールに準拠させ、新しいルール動作を検証する包括的なテストスイートを提供します。

Changes

Persona Model Lint Rule and Enforcement

Layer / File(s) Summary
Custom lint rule definition and documentation
.claude/custom-lint-rules.toml
新しいルール ⑨ takt-workflow-persona-without-model を定義します。.takt/workflows/*.yaml 内で persona: の直後に model: 以外のフィールドが続く場合を検出し、`model: <sonnet
Workflow configuration updates with explicit model specifications
.takt/workflows/post-pr-review.yaml, .takt/workflows/pre-push-review.yaml
post-pr-review.yamlpre-push-review.yamlloop_monitors judge および supervise step に対して、persona: supervisor を持つ箇所に model: sonnet を追加します。
Comprehensive test suite for persona-model requirement
src/hooks-post-tool-linter/src/main.rs
新しいカスタムルール用テストヘルパーと多数のテストケースを追加します。judge ブロック内での persona:instruction: 違反検出、supervise step 内での persona:policy: 違反検出、model: 直後の合格判定、複数違反検出、非yaml拡張子での無視、本番ワークフロー群のベースライン検証を確認します。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • aloekun/claude-code-hook-test#91: 同じ linter 実装とテスト群を修正し、multiline 対応カスタムルール正規表現ハンドリングとテストカバレッジを追加するため関連があります。
🚥 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 プルリクエストのタイトルは、追加される新しいカスタムリント規則「takt-workflow-persona-without-model」の主要な変更を明確に説明しており、PR目標と一致しています。
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.

✨ 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.

Tip

CodeRabbit can scan for known vulnerabilities in your dependencies using OSV Scanner.

OSV Scanner will automatically detect and report security vulnerabilities in your project's dependencies. No additional configuration is required.

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.

Actionable comments posted: 2

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

2036-2042: ⚡ Quick win

テスト用ルールの正規表現ハードコードは設定とのドリフト源です

ここで正規表現を複製すると、.claude/custom-lint-rules.toml 更新時にテストが古いまま残るリスクがあります。id = "takt-workflow-persona-without-model" を TOML から読み出してテストに使う形へ寄せるのが安全です。

差分イメージ
-    fn takt_workflow_persona_without_model_rule() -> CustomRule {
-        make_test_rule(
-            "takt-workflow-persona-without-model",
-            r"...",
-            &["yaml"],
-        )
-    }
+    fn deployed_rule_by_id(id: &str) -> CustomRule {
+        let path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
+            .join("..").join("..").join(".claude").join("custom-lint-rules.toml");
+        let content = std::fs::read_to_string(&path).expect("read custom-lint-rules.toml");
+        let config: CustomRulesConfig = toml::from_str(&content).expect("parse custom rules");
+        config.rules
+            .unwrap_or_default()
+            .into_iter()
+            .find(|r| r.id == id)
+            .expect("rule id not found")
+    }
🤖 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 2036 - 2042, The test
currently hardcodes the rule id and regex in
takt_workflow_persona_without_model_rule; change it to read the rule definition
(at least the id, preferably the regex) from the custom rules TOML so the test
stays in sync: modify takt_workflow_persona_without_model_rule to lookup the
entry with id "takt-workflow-persona-without-model" from the parsed
.claude/custom-lint-rules.toml (reuse existing TOML parsing logic if available),
then call make_test_rule using the retrieved id and pattern instead of the
inline string/regex.
🤖 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.

Inline comments:
In @.claude/custom-lint-rules.toml:
- Line 318: The regex pattern variable (pattern) used to detect a persona:
followed by certain keys is missing several valid keys (e.g., output_contracts,
pass_previous_response, required_permission_mode, parallel), causing false
negatives; update the pattern string so the alternation after persona: includes
those missing keys (and any other current keys used in persona blocks) so that
lines like "persona: <name>" followed by those keys are matched; ensure the
updated pattern still uses the same (?m) and anchors and preserves whitespace
handling used in the original pattern.

In `@docs/local-llm-offload-analysis.md`:
- Line 5: The document asserts future progress on "2026-05-13" (e.g., references
to "Phase d Round 2", "着手", "着手中", "追加済み前提" under the "状態" / Phase sections) as
if already completed; change those statements into planned language or separate
planned vs actual sections: replace assertive verbs like "着手/追加済み" with
"予定/着手予定" (or add "(予定)") for every occurrence of the date string "2026-05-13",
add a clear "更新基準日: 2026-05-12" header, and explicitly split content into "実績"
(completed items) and "計画/予定" (future items) so the Phase d/Phase d Round 2 and
Phase c+ mentions are not presented as accomplished.

---

Nitpick comments:
In `@src/hooks-post-tool-linter/src/main.rs`:
- Around line 2036-2042: The test currently hardcodes the rule id and regex in
takt_workflow_persona_without_model_rule; change it to read the rule definition
(at least the id, preferably the regex) from the custom rules TOML so the test
stays in sync: modify takt_workflow_persona_without_model_rule to lookup the
entry with id "takt-workflow-persona-without-model" from the parsed
.claude/custom-lint-rules.toml (reuse existing TOML parsing logic if available),
then call make_test_rule using the retrieved id and pattern instead of the
inline string/regex.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 53779dbf-24fe-4e94-9d71-06ae9c33a798

📥 Commits

Reviewing files that changed from the base of the PR and between 0ec4bd5 and 0c2cc07.

📒 Files selected for processing (7)
  • .claude/custom-lint-rules.toml
  • .takt/workflows/post-pr-review.yaml
  • .takt/workflows/pre-push-review.yaml
  • docs/local-llm-offload-analysis.md
  • docs/todo-summary.md
  • docs/todo7.md
  • src/hooks-post-tool-linter/src/main.rs
💤 Files with no reviewable changes (2)
  • docs/todo-summary.md
  • docs/todo7.md

Comment thread .claude/custom-lint-rules.toml Outdated
Comment thread docs/local-llm-offload-analysis.md
…ds 追加 (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