From 9ba95bf7eee581ac9aeb714dfd3b82ede703d83d Mon Sep 17 00:00:00 2001 From: aloekun Date: Tue, 12 May 2026 14:45:47 +0900 Subject: [PATCH 1/2] =?UTF-8?q?docs(todo,analysis):=20post-merge-feedback?= =?UTF-8?q?=20PR=20#145=20=E6=8E=A1=E7=94=A8=202=20=E4=BB=B6=E7=99=BB?= =?UTF-8?q?=E9=8C=B2=20+=20Phase=20D=20D-1=20=E5=AE=8C=E4=BA=86=E5=8F=8D?= =?UTF-8?q?=E6=98=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #145 (D-1) merge 後の post-merge-feedback で採用された 2 件 (Tier 3 #1 + #3) を todo 系列に登録し、analysis.md の Phase D section に D-1 land 状況と副産物を反映する。 順位 116 (Tier 3 XS、PR #145 T3-#1): - ADR-040 step_timeout 説明に sublinear / KV cache locality clarification 追記 - 実測値 600s 採択 / 保守上限 720s / sublinear 性の KV cache 根拠 順位 117 (Tier 3 S、PR #145 T3-#3): - coding-style.md § Cross-File Reference Lifecycle に ephemeral → permanent 知識移管 edit order を追記 - 3 ステップ原則 (① permanent target 先行作成・validate → ② 参照追加 → ③ 参照元削除) analysis.md Phase D section 更新: - D-1 land 状況 (PR #145、298 行、ZERO findings APPROVE) を table の状態列に反映 - 順位 115 を D-2 前 critical path として明示 - D-1 dogfood outcome として 4 項目記録 (skip 理由 / workflow gap 発見 / ADR-040 fix / migration codify) 順位 115 (Tier 1) の rationale に「post-merge-feedback Tier 1 #1 で再 validate 済」を追記。 --- docs/local-llm-offload-analysis.md | 24 +++++++++----- docs/todo-summary.md | 4 ++- docs/todo8.md | 51 ++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/docs/local-llm-offload-analysis.md b/docs/local-llm-offload-analysis.md index ef9e46b..a8dffb9 100644 --- a/docs/local-llm-offload-analysis.md +++ b/docs/local-llm-offload-analysis.md @@ -229,19 +229,27 @@ Phase A 実装後、PR #141 (P-3 = 187 行 mixed diff) を replay → **`prompt_ `src/cli-push-runner/src/stages/lint_screen.rs` 改修: graceful fallback (exit 0) 時にも classifier stderr を `.takt/lint-screen-report.md` の `## Diagnostic` section に取込。Phase A 診断 warn log が **real pipeline 経由で visible** になる状態を確保。新 struct `ClassifierOutput { stdout, stderr }`、新 helper `render_diagnostic`、新規 smoke test 4 件 (TP / FP / edge case / parse-error path) で contract を seal。lint_screen tests 14/14 pass + workspace 全 cargo test pass。 -##### 🔄 Phase D: Clean dogfood validation (real pipeline 経由、未着手、計画 land 済) +##### 🔄 Phase D: Clean dogfood validation (real pipeline 経由、進行中) -Phase C fix + Phase D 前提整備 (順位 109) 完了で **real pipeline 経由 dogfood の必要十分条件が揃った**。次の 3 通常 PR を **session-only opt-in (b) (`push-runner-config.toml` の `[lint_screen] enabled = true` を session 内で manual 切替、commit しない)** で dogfood、`.takt/lint-screen-report.md` の `## Summary` + `## Diagnostic` で metrics を実観測。fallback rate < 50% / num_ctx 起因 0% を real pipeline で再確認できれば Phase E に進む。 +Phase C fix + Phase D 前提整備 (順位 109) 完了で **real pipeline 経由 dogfood の必要十分条件が揃った**。**しかし D-1 着手時に session-only opt-in workflow が jj auto-snapshot と本質的に衝突する gap が判明** (順位 115 として env var override を backlog 登録、post-merge-feedback Tier 1 #1 で再 validate)。次の 3 通常 PR を **env var override (`LINT_SCREEN_ENABLED=true`) 経由 (順位 115 land 後)** で dogfood、`.takt/lint-screen-report.md` の `## Summary` + `## Diagnostic` で metrics を実観測。fallback rate < 50% / num_ctx 起因 0% を real pipeline で再確認できれば Phase E に進む。 -**Phase D 対象 PR 構成 (2026-05-12 確定)**: +**Phase D 対象 PR 構成 (2026-05-12 確定 / D-1 land 後更新)**: -| Order | 構成 (todo-summary.md priority list より) | Effort | 推定 diff 行 | Diff Profile | Phase C 既存 PR との対比 | +| Order | 構成 (todo-summary.md priority list より) | Effort | 推定 diff 行 | Diff Profile | 状態 | |---|---|---|---|---|---| -| **D-1** | 順位 112 + 113 + 114 = ADR amendments bundle (ADR-038 eprintln scope / ADR-027 metrics override / 新規 ADR Local LLM context size) | S+ | ~180-200 | docs only | #139 (414 行 docs-only) と類似 | -| **D-2** | 順位 101 + 106 + 103 = lint rule code touch (rule⑧ edge case test / self-exclusion assertion / lint runner field comment) | S+S+S | ~150-200 | Rust test/comment mix | #141 (487 行 Rust test) と類似 | -| **D-3** | 順位 102 = `paths` filter を lint runner に実装 (impl + test + 既存 rule migration) | M | ~250-350 | Rust impl + test | #141 を超える規模、num_ctx 32768 上限テスト | +| **D-1** ✅ | 順位 112 + 113 + 114 = ADR amendments bundle (ADR-038 eprintln scope / ADR-027 metrics override / 新規 ADR Local LLM context size) + 順位 115 backlog 化 | S+ | 298 (insert 228 / delete 70) | docs + 1 Rust comment | **PR #145 land 済 (2026-05-12)**、lint_screen dogfood は skip (workflow gap) | +| **115** ⏳ | `LINT_SCREEN_ENABLED` env var override (D-1 で発見した workflow gap 解消) | S | ~80-120 (Rust impl + test) | Rust impl | **D-2 着手前に land 必須**、post-merge-feedback Tier 1 #1 | +| **D-2** | 順位 101 + 106 + 103 = lint rule code touch (rule⑧ edge case test / self-exclusion assertion / lint runner field comment) | S+S+S | ~150-200 | Rust test/comment mix | 順位 115 land 後、env var workflow で初の実 dogfood | +| **D-3** | 順位 102 = `paths` filter を lint runner に実装 (impl + test + 既存 rule migration) | M | ~250-350 | Rust impl + test | D-2 完了後、num_ctx 32768 上限テスト | -**size ramp-up 設計**: small → mid → mid-large の漸増で、small PR 単体での fallback 観測と large PR で num_ctx 限界に近づく挙動を両方カバー。 +**size ramp-up 設計**: small → mid → mid-large の漸増で、small PR 単体での fallback 観測と large PR で num_ctx 限界に近づく挙動を両方カバー。**ただし D-1 は workflow gap により lint_screen dogfood をスキップ、実質 metrics 観測は D-2 / D-3 の 2 PR**。 + +**D-1 dogfood outcome (skip 理由 + 副産物)**: + +- lint_screen dogfood は実施せず (workflow gap) +- 副産物として **workflow gap を systemic に発見 + 順位 115 を Tier 1 backlog 登録 + post-merge-feedback Tier 1 #1 で再 validate** +- ADR-040 内部不整合 (3.33x label vs `(num_ctx/8192)*180s` formula = 4x) は takt review 1 iter で検出 → fix で解消、post-merge-feedback Tier 3 #1 で sublinear clarification 採用 (順位 116) +- lib.rs L128-139 → ADR-040 移管 edit order を post-merge-feedback Tier 3 #3 で codify 採用 (順位 117) **Phase D 計測手順** (各 PR 共通): diff --git a/docs/todo-summary.md b/docs/todo-summary.md index a3332d2..2aefa80 100644 --- a/docs/todo-summary.md +++ b/docs/todo-summary.md @@ -77,7 +77,9 @@ | 108 | 💎 Tier 3 | **CLAUDE.md に「Tier 2 偽装検知 + 却下パターン」table (PR #141 T3-#3 採用)** | todo6.md | S | なし (`~/.claude/CLAUDE.md` に memory `feedback_no_unenforced_rules` の policy をユーザー可視 table として公開、Tier 2 と称した必須化ルール提案を新セッションでも一貫して却下できる構造、memory ファイル閉鎖を補完) | | 110 | 💎 Tier 3 | **pure function test pattern template を `testing.md` に追記 (PR #142 T2-#3 採用)** | todo6.md | S | なし (Phase A の `overflow_hint()` をモデル例とし「境界値 / None / 閾値未満」3 パターンの test テンプレを `~/.claude/rules/common/testing.md` に追記、副作用分離の促進、Rust lib 全般で再利用) | | 111 | 💎 Tier 3 | **`docs-governance.md` に todo5/todo6 routing rule 明文化 (PR #142 T3-#1 採用)** | todo6.md | S | なし (Phase/bundle 関連 → todo6、global rules/lint → todo5 等の routing rule を `~/.claude/rules/common/docs-governance.md` に追記、PR #142 で実証された file pointer bifurcation の構造的予防、CR Minor #2 と同根) | -| 115 | 🚀 Tier 1 | **`LINT_SCREEN_ENABLED` env var override を cli-push-runner に追加 (Phase D D-1 workflow gap)** | todo8.md | S | D-2 を block (D-2 着手前に land 必須)。Phase D guide §1 の session-only opt-in が jj auto-snapshot と本質的に衝突するため、env var で TOML override する path を追加し commit-free な dogfood を成立させる。Phase D D-1 (PR #145 想定) 着手時に systemic に発見 | +| 115 | 🚀 Tier 1 | **`LINT_SCREEN_ENABLED` env var override を cli-push-runner に追加 (Phase D D-1 workflow gap)** | todo8.md | S | D-2 を block (D-2 着手前に land 必須)。Phase D guide §1 の session-only opt-in が jj auto-snapshot と本質的に衝突するため、env var で TOML override する path を追加し commit-free な dogfood を成立させる。Phase D D-1 (PR #145 land 済) 着手時に systemic に発見、post-merge-feedback Tier 1 #1 で再 validate 済 | +| 116 | 💎 Tier 3 | **ADR-040 `step_timeout` 説明に sublinear / KV cache locality clarification 追記 (PR #145 T3-#1 採用)** | todo8.md | XS | なし (L42-48 で「sublinear (3.33x)」と「per-invoke latency が概ね線形」が並存し reference table 600s と formula 720s が乖離。実測値 600s 採択 + 保守上限 720s + sublinear 性の KV cache locality 根拠を 2-3 行追記して整合化、永続 ADR の数値正確性確保) | +| 117 | 💎 Tier 3 | **`coding-style.md § Cross-File Reference Lifecycle` に ephemeral → permanent 知識移管 edit order 追記 (PR #145 T3-#3 採用)** | todo8.md | S | なし (PR #145 で lib.rs L128-139 → ADR-040 移管 + Phase C/D empirical data 移管の 2 観測。既存ルール (参照方向制約) と complementary な「① permanent target 先行作成・validate → ② 参照追加 → ③ 参照元削除」3 ステップ原則を `~/.claude/rules/common/coding-style.md` に codify、次回 ephemeral 計画書 retire 時の checklist として再利用) | **戦略**: Tier 1 を 2〜3 セッションで片付け → Tier 2 で ADR-032 の前提 + rate-limit + convergence cost 削減を進める → Tier 3 で ADR-032 を land + ドキュメント整備。Tier 4-5 は cleanup / 外部展開で daily efficiency への直接効果は小さい。 diff --git a/docs/todo8.md b/docs/todo8.md index bf95b12..2f39c8e 100644 --- a/docs/todo8.md +++ b/docs/todo8.md @@ -41,3 +41,54 @@ - D-2 / D-3 で session-only opt-in workflow が成立する --- + +### ADR-040 `step_timeout` 説明に sublinear / KV cache locality clarification 追記 (PR #145 T3-#1 採用) + +> **動機**: ADR-040 L42-48 の `step_timeout` 説明は「sublinear (3.33x)」と記述したが、本文中に「per-invoke latency が num_ctx に対して概ね線形に拡大する経験則」も併記しており、両者の関係が不明瞭。派生プロジェクトが reference table から 32K = 600s を読む際、なぜ formula `(num_ctx/8192)*180` で導出される 720s と乖離するかが直感的に分からない。clarification として「実測値 600s を正規値として採択、computed 720s は保守上限の目安、sublinear 性の根拠は KV cache locality 効果 (大規模 context で per-token efficiency 向上)」の 2-3 行追記が必要。 +> +> **本タスクの位置づけ**: PR #145 post-merge-feedback Tier 3 #1 採用 (Severity Low / Frequency Low / Effort XS / Adoption Risk None)。永続 ADR の数値整合性確保。 +> +> **参照**: `.claude/feedback-reports/145.md` Tier 3 #1、`docs/adr/adr-040-local-llm-context-size.md` L42-48 + +#### 作業計画 + +- [ ] ADR-040 § `step_timeout` 比例係数の根拠 に 2-3 行追記: + - 実測値 600s を正規採択、computed 720s は保守上限見積もり + - sublinear 性 (3.33x vs context 4x) の根拠 = KV cache locality 効果 (推定) + - 派生プロジェクトでの derivation 時は実測 cargo test 経過時間の 2x margin を採用 +- [ ] 本エントリ削除 + todo-summary.md 行削除 + +#### 完了基準 + +- ADR-040 の reference table と本文の formula が矛盾なく解釈可能になる +- 派生プロジェクトの porting 時に sublinear の根拠が永続記録から逆引きできる + +--- + +### `coding-style.md § Cross-File Reference Lifecycle` に「ephemeral → permanent 知識移管 edit order」追記 (PR #145 T3-#3 採用) + +> **動機**: PR #145 で lib.rs L128-139 dogfood evolution コメントを ADR-040 に migrate した際、edit 順序が曖昧だった (ADR-040 を先に作るべきか、lib.rs 側の参照削除を先にすべきか)。同パターンが (1) lib.rs コメント → ADR-040、(2) Phase C/D empirical data → ADR-040 で 2 回観測。既存の Cross-File Reference Lifecycle ルール は「参照方向の制約」(permanent → ephemeral 禁止) に特化しており、移管作業の edit order checklist は complementary で重複なし。次回同型の永続化作業 (ephemeral 計画書 retire 時の permanent value 移管 等) で再発防止策として codify する。 +> +> **本タスクの位置づけ**: PR #145 post-merge-feedback Tier 3 #3 採用 (Severity Low / Frequency Medium / Effort S / Adoption Risk None)。 +> +> **参照**: `.claude/feedback-reports/145.md` Tier 3 #3、`~/.claude/rules/common/coding-style.md` § Cross-File Reference Lifecycle + +#### 提案する 3 ステップ原則 + +1. **permanent target 先行作成・validate**: 移管先の permanent artifact (ADR / stable docs) を先に作成し、内容の正確性 (cross-reference の妥当性 / 数値整合性 / markdownlint pass) を確認 +2. **参照追加**: ephemeral 側 (lib.rs コメント / config コメント / scratch markdown 等) から permanent への参照 link を追加 (1-2 行) +3. **参照元削除**: ephemeral 側の冗長な内容を削除し、参照 link のみ残す。同一 commit で 3 step すべてを実施 + +#### 作業計画 + +- [ ] `~/.claude/rules/common/coding-style.md` § Cross-File Reference Lifecycle 末尾に「ephemeral → permanent 知識移管 edit order」 subsection を追加 +- [ ] 3 ステップ原則を inline で記述、PR #145 (lib.rs L128-139 → ADR-040) を実例として cite +- [ ] 派生プロジェクト (techbook-ledger / auto-review-fix-vc) への deploy 計画も検討 +- [ ] 本エントリ削除 + todo-summary.md 行削除 + +#### 完了基準 + +- 次回 permanent 化作業時に edit order が決定論的に決まる +- ephemeral 計画書 retire 時の permanent value 移管プロセスが checklist 化される + +--- From 184907d357a9f94f553b9f34ab1eb43ea84213b5 Mon Sep 17 00:00:00 2001 From: aloekun Date: Tue, 12 May 2026 17:03:53 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat(hooks-post-tool-linter):=20D-2=20lint?= =?UTF-8?q?=20rule=20code=20touch=20(=E9=A0=86=E4=BD=8D=20101/103/106)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase D dogfood の 2 本目 (D-2)。lint rule の test gap + struct doc + self-exclusion guards を bundled。 順位 101 (PR #140 T1-#1 採用): rule⑧ depth-1 root MD edge case test 2 件追加 - md_no_docs_relative_detects_root_level_back_reference (CLAUDE.md fixture) - md_no_docs_relative_detects_root_readme_back_reference (README.md fixture) - 「fire = true positive」整理を doc comment と test 名で明示 - root-level MD からの ../docs/ 参照は repo 外を指す broken link で真陽性が正しい挙動 順位 103 (PR #140 T1-#3 採用): CustomRule struct に lint runner field reference doc comment 追加 - サポート field 一覧 (id/pattern/severity/message/extensions/why/fix/example) を /// で記述 - planned field (paths、順位 102) も併記し設計-実装 gap を構造的に予防 - custom-lint-rules.toml 冒頭コメントに main.rs CustomRule struct への動線追加 順位 106 (PR #141 T2-#1 採用): self-exclusion invariant test に 2 false-green guards 追加 - assert!(path.exists()) — deployed TOML 削除 / 移動時の silent pass 抑止 - assert!(extensions contains "toml") — rule scope 変更時の silent degradation 抑止 - 各 assertion message に silent degradation のリスクと由来 (順位 106 / PR #141 T2-#1) を inline で記述 cargo test pass: hooks-post-tool-linter 95 tests (新規 2 + 既存 +1 強化、ZERO regression)。 PR #145 = D-1 と同様、lint_screen dogfood は順位 115 (env var override) land 前のため skip。 --- .claude/custom-lint-rules.toml | 4 ++ docs/todo-summary.md | 3 - docs/todo6.md | 84 -------------------------- src/hooks-post-tool-linter/src/main.rs | 81 ++++++++++++++++++++++++- 4 files changed, 84 insertions(+), 88 deletions(-) diff --git a/.claude/custom-lint-rules.toml b/.claude/custom-lint-rules.toml index 735ad92..aa56065 100644 --- a/.claude/custom-lint-rules.toml +++ b/.claude/custom-lint-rules.toml @@ -3,6 +3,10 @@ # このファイルは .claude/ 直下に配置し、hooks-post-tool-linter が読み込みます。 # プロジェクトごとに異なるルールセットを定義できます。 # +# サポート field の semantic / parse 仕様は lint runner 側 (`src/hooks-post-tool-linter/src/main.rs`) +# の `CustomRule` struct の doc comment を参照してください (順位 103 で追加された rule author 向け +# reference)。planned field (`paths` 等) も同 doc comment に記載されています。 +# # ── ルール定義 ── # # [[rules]] diff --git a/docs/todo-summary.md b/docs/todo-summary.md index 2aefa80..c999672 100644 --- a/docs/todo-summary.md +++ b/docs/todo-summary.md @@ -67,12 +67,9 @@ | 97 | 🔧 Tier 2 | **`with_num_ctx(X)` override 値 serialization 検証テスト (PR #136 T2-#1 採用)** | todo6.md | S | なし (PR #136 で追加した builder method の wiring を mockito で seal、Phase d で num_ctx tweak する局面の silent degrade 防止、CodeRabbit が見逃した test gap を post-merge-feedback agent が独立発見) | | 99 | 💎 Tier 3 | **ADR-038 に PR #138 learning 2 件を追記 (cost-aware 実装層選択 + attention dilution pitfall) (PR #138 T3-#1+#2 採用)** | todo6.md | S | なし (lint_screen が takt facet → Rust stage に pivot した cost 根拠 + Phase b' v2 の diff header full 追加で agreement 75%→50% 33pt 低下した attention dilution 観測の 2 件を ADR に codify、次回 LLM 系 feature 開発時の prior assumption に) | | 100 | 💎 Tier 3 | **`development-workflow.md` に 「同一ファイル複数編集の 1 task 統合」 + 「partial completion + 後続 PR 追補明記」 を追補 (PR #139 T3-#1 採用)** | todo6.md | XS | なし (PR #119/#120/#121 sub-PR 分割 + PR #139 partial completion で systemic に観測された 2 暗黙知を `~/.claude/rules/common/development-workflow.md` に codify、`feedback_no_unenforced_rules.md` 例外 = 既存実践の明文化のため非機械強制でも採用相当) | -| 101 | 🚀 Tier 1 | **rule⑧ depth-1 非-docs MD edge case test 追加 (PR #140 T1-#1 採用)** | todo6.md | S | なし (rule⑧ で depth-1 root MD ファイル (例: `./CLAUDE.md`) から `../docs/` 参照が false positive にならないかが未検証、3 ソース (PR diff/prepush/session) で観測された test gap、md_no_docs_relative_* test group に追加) | | 102 | 🚀 Tier 1 | **`paths` filter を lint runner に実装 (PR #140 T1-#2 採用)** | todo6.md | M | なし (rule⑧ で `extensions = ["md"]` のみで pattern semantics に依存して self-limit したが、path-sensitive な lint rule 追加時に同設計-実装 gap が systemic に再発、`src/hooks-post-tool-linter/src/filter.rs` 等で paths filter サポート + 単体 test 同 commit) | -| 103 | 🚀 Tier 1 | **lint runner サポートフィールドを code comment で明示化 (PR #140 T1-#3 採用)** | todo6.md | S | なし (`src/hooks-post-tool-linter/src/main.rs` の CustomRule struct 定義近傍に「サポート field: extensions / pattern / severity / message / why、planned: paths」を code comment 追加、TOML コメントのみだと rule author が lint runner 実装を参照する動線が無いため次の設計-実装 gap を構造的に予防) | | 104 | 💎 Tier 3 | **ADR-007 amendment: semantic self-limitation 安全条件 + lint rule 最小テストチェックリスト (PR #140 T3-#1 採用)** | todo6.md | S | なし (rule⑧ で `paths` filter 不在を pattern semantics で代替した判断の rationale を ADR-007 に追記。「semantic self-limitation OK な条件」と「explicit `paths` filter 必須な条件」、lint rule 最小テストチェックリスト = pattern detection / case-insensitive / false positive skip の 3 項目最低化、3 ソース観測) | | 105 | 💎 Tier 3 | **グローバル CLAUDE.md に lint runner サポートフィールド一覧表 (PR #140 T3-#2 採用)** | todo6.md | XS | なし (`~/.claude/CLAUDE.md` に `pattern` / `extensions` / `severity` (planned: `paths`) の field 一覧を表形式で追加、派生プロジェクト (techbook-ledger / auto-review-fix-vc) で rule porting 時の理解統一、順位 103 の code comment と相補) | -| 106 | 🔧 Tier 2 | **self-exclusion test に `path.exists()` ガード + extensions assertion 追加 (PR #141 T2-#1 採用)** | todo6.md | S | なし (PR #141 で land した `no_ephemeral_todo_self_exclusion_invariant_holds_on_deployed_toml` test の false-green ガード、`run_custom_rules` が path 不在で空 Vec を返す silent pass の防止 + extensions list から "toml" 削除時の silent degradation 防止、3 ソース独立指摘で Medium Severity) | | 107 | 💎 Tier 3 | **`development-workflow.md` に PR #125→#141 anti-pattern 事例補強 (PR #141 T3-#2 採用)** | todo6.md | XS | なし (`~/.claude/rules/common/development-workflow.md` の「タスク完了削除手順」に「マージ後 N 日間 todo.md 残存 → 後続 phase で手動発見」事例を追記、memory `feedback_verify_task_not_already_done` を central rule にも反映、`feedback_todo_no_history` と合わせて「マージ → 即削除」サイクルを強調) | | 108 | 💎 Tier 3 | **CLAUDE.md に「Tier 2 偽装検知 + 却下パターン」table (PR #141 T3-#3 採用)** | todo6.md | S | なし (`~/.claude/CLAUDE.md` に memory `feedback_no_unenforced_rules` の policy をユーザー可視 table として公開、Tier 2 と称した必須化ルール提案を新セッションでも一貫して却下できる構造、memory ファイル閉鎖を補完) | | 110 | 💎 Tier 3 | **pure function test pattern template を `testing.md` に追記 (PR #142 T2-#3 採用)** | todo6.md | S | なし (Phase A の `overflow_hint()` をモデル例とし「境界値 / None / 閾値未満」3 パターンの test テンプレを `~/.claude/rules/common/testing.md` に追記、副作用分離の促進、Rust lib 全般で再利用) | diff --git a/docs/todo6.md b/docs/todo6.md index d00876b..115e1ce 100644 --- a/docs/todo6.md +++ b/docs/todo6.md @@ -329,38 +329,6 @@ config.rs + push-runner-config.toml + review-simplicity.md + ADR で family_tag --- -### rule⑧ depth-1 非-docs MD edge case test 追加 (PR #140 T1-#1 採用) - -> **動機**: PR #140 で追加した rule⑧ (`no-docs-relative-back-to-docs`) は `extensions = ["md"]` のみで pattern semantics に依存して self-limit する設計。**depth-1 非-docs MD ファイル** (例: project root の `CLAUDE.md` / `README.md` / `.claude/README.md` 等) から `../docs/` を参照するケースが false positive にならないかが未検証で、3 ソース (PR diff / prepush takt review / post-merge-feedback session) で同一の test gap が独立指摘された。 -> -> **本タスクの位置づけ**: PR #140 post-merge-feedback Tier 1 #1 採用 (Severity Medium / Frequency Medium = 3 ソース観測 / Effort S / Adoption Risk None)。 -> -> **参照**: `.claude/feedback-reports/140.md` Tier 1 #1、`src/hooks-post-tool-linter/src/main.rs` の `md_no_docs_relative_*` test group、ADR-007 (custom lint rule layer 線引き) -> -> **実行優先度**: 🚀 **Tier 1** — Effort S。md_no_docs_relative_* test group に edge case を 1-2 件追加。 - -#### 設計決定の余地 - -- **意図する挙動の確定が先**: depth-1 root MD (例: `./CLAUDE.md`) から `../docs/` を参照するケースは現実には稀だが、もし発生したら **fire (false positive)** になるか、それとも skip すべきか? -- **判断基準案**: pattern `(?i)\]\(\.\./docs/` は path semantics で「現在 file が docs 配下にいる前提」だが、root file から参照すると `../docs/` = repo の親階層 (= リポジトリ外) を指すため **常に broken link**。よって **fire = 正解** と整理可能 (false positive ではなく true positive、ユーザーが意図しても broken link になる) -- 上記整理を test 名に反映 (`md_no_docs_relative_detects_root_level_back_reference` 等) - -#### 作業計画 - -- [ ] depth-1 root MD ファイル fixture (`CLAUDE.md` / `.claude/README.md` 等) からの `../docs/` 参照を test に追加 -- [ ] 「fire = true positive」整理を test 名と comment で明示 -- [ ] 既存 5 test と合わせて 6-7 test 構成に整理 -- [ ] markdownlint / cargo test pass 確認 -- [ ] 順位 102 (`paths` filter 実装) と組み合わせると本 test の意図が変わる可能性、land 順序を確認 -- [ ] 本 todo6.md エントリ削除 + todo-summary.md 行削除 - -#### 完了基準 - -- depth-1 非-docs MD からの `../docs/` 参照が test で明示的に扱われる (fire = true positive 整理込み) -- 全 cargo test pass - ---- - ### `paths` filter を lint runner に実装 (PR #140 T1-#2 採用) > **動機**: rule⑧ (PR #140) の TOML コメントで「`paths` filter は lint runner 未実装、`extensions` のみ」と明記し pattern semantics で self-limit したが、これは設計-実装 gap の workaround。今後 path-sensitive な lint rule (例: `tests/` 内のみ / `src/cli-*/` のみ等) を追加するたびに同じ workaround を強いる systemic pattern が予測される。`src/hooks-post-tool-linter/src/filter.rs` (or 等価な path filter モジュール) で `paths = [...]` glob filter をサポートする。 @@ -394,34 +362,6 @@ config.rs + push-runner-config.toml + review-simplicity.md + ADR で family_tag --- -### lint runner サポートフィールドを code comment で明示化 (PR #140 T1-#3 採用) - -> **動機**: PR #140 で rule⑧ を実装する際、lint runner の `CustomRule` struct がサポートする field 一覧 (現状: `extensions`, `pattern`, `severity`, `message`, `why`, `fix`, `example`) は `src/hooks-post-tool-linter/src/main.rs` のソースを読まないと分からない。TOML コメント (custom-lint-rules.toml の冒頭) のみだと **rule author が lint runner 実装を参照する動線がない**。code comment で明示すると次の rule 追加時に同じ設計-実装 gap を構造的に予防できる。 -> -> **本タスクの位置づけ**: PR #140 post-merge-feedback Tier 1 #3 採用 (Severity Low / Frequency Medium / Effort S / Adoption Risk None)。 -> -> **参照**: `.claude/feedback-reports/140.md` Tier 1 #3、`src/hooks-post-tool-linter/src/main.rs` `CustomRule` struct 定義 (line ~76-87)、PR #140 rule⑧ TOML コメント - -#### 設計決定の余地 - -- **配置先**: `CustomRule` struct 直前の doc comment (`///`) で「サポート field の semantics と将来 planned field」を一覧化 -- **記述粒度**: 各 field の serde attribute 表記 + 要旨 1 行ずつ。`paths` (planned) も含めて next-author が動線を辿れるように - -#### 作業計画 - -- [ ] `CustomRule` struct 直前に doc comment を追加 (現サポート field 一覧 + planned: `paths`) -- [ ] custom-lint-rules.toml 冒頭コメントから「ソース main.rs 参照」のリンク (line 番号 or symbol 名) を追加 -- [ ] 順位 102 (`paths` filter 実装) と同 PR で land すると `planned: paths` を `supported: paths` に書き換える単一 commit で完結 -- [ ] markdownlint / cargo doc 確認 -- [ ] 本 todo6.md エントリ削除 + todo-summary.md 行削除 - -#### 完了基準 - -- `CustomRule` struct の doc comment にサポート field 一覧が記載される -- TOML コメント側からも main.rs への動線が明示される - ---- - ### ADR-007 amendment: semantic self-limitation 安全条件 + lint rule 最小テストチェックリスト (PR #140 T3-#1 採用) > **動機**: rule⑧ (PR #140) で `paths` filter 不在を pattern semantics で代替した判断は妥当だったが、**どんな条件下で semantic self-limitation が安全か** / **explicit filter が必須な条件は何か** が ADR-007 に明文化されていない。3 ソース (PR diff / prepush / session) でこの documentation 不足を独立指摘。同時に lint rule 最小テストチェックリスト (pattern detection / case-insensitive / false positive skip の 3 項目) も ADR レベルで確立すると future rule author の prior が安定化する。 @@ -497,30 +437,6 @@ config.rs + push-runner-config.toml + review-simplicity.md + ADR で family_tag --- -### self-exclusion test に `path.exists()` ガード + extensions assertion 追加 (PR #141 T2-#1 採用) - -> **動機**: PR #141 で land した `no_ephemeral_todo_self_exclusion_invariant_holds_on_deployed_toml` test は、(a) `.claude/custom-lint-rules.toml` の path が存在することと (b) rule の `extensions` に `"toml"` が含まれることに **暗黙的に依存** している。どちらかが将来の変更で壊れると、`run_custom_rules` は空 Vec を返してテスト pass = silent false-green になり、self-exclusion invariant の保護が無効化される。CR Nitpick (PR #141) では (b) のみ指摘 (ユーザー判断で本 PR では不採用)、post-merge-feedback agent は 3 ソース (PR diff / Session / Prepush:simplicity) 独立指摘で (a)+(b) 両方を Medium Severity で再提案。 -> -> **本タスクの位置づけ**: PR #141 post-merge-feedback Tier 2 #1 採用 (Severity Medium / Frequency Low / Effort S / Adoption Risk None)。CR Nitpick の **拡張版** (path.exists ガード追加で完全性向上)。 -> -> **参照**: `.claude/feedback-reports/141.md` Tier 2 #1、`src/hooks-post-tool-linter/src/main.rs` の `no_ephemeral_todo_self_exclusion_invariant_holds_on_deployed_toml` test (PR #141 で追加) -> -> **実行優先度**: 🔧 **Tier 2** — Effort S。2 assertion を test 冒頭に追加。 - -#### 作業計画 - -- [ ] test 冒頭に `assert!(path.exists(), ...)` + `assert!(rule.extensions.contains(&"toml".to_string()), ...)` を追加 -- [ ] エラーメッセージで silent degradation のリスクを説明 -- [ ] cargo test 全 pass を確認 -- [ ] 本エントリ削除 + todo-summary.md 行削除 - -#### 完了基準 - -- path 不在 / extensions に "toml" 不在のどちらでも test が fail で停止する -- 既存 6 件の rule⑥ test と互換性維持 - ---- - ### `development-workflow.md` に PR #125→#141 anti-pattern 事例補強 (PR #141 T3-#2 採用) > **動機**: memory `feedback_verify_task_not_already_done.md` (PR #141 セッションで追加) は session-scoped で「PR #125 → #141 で 4 日間 stale todo 残存 → P-3 起動時に手動発見」事例を含むが、`~/.claude/rules/common/development-workflow.md` の central rule 側には反映されていない。`feedback_todo_no_history.md` と合わせて central 化することで、memory file 閉鎖の structural risk を軽減する。 diff --git a/src/hooks-post-tool-linter/src/main.rs b/src/hooks-post-tool-linter/src/main.rs index b1f76e9..62545c3 100644 --- a/src/hooks-post-tool-linter/src/main.rs +++ b/src/hooks-post-tool-linter/src/main.rs @@ -73,6 +73,26 @@ struct CustomRulesConfig { rules: Option>, } +/// `custom-lint-rules.toml` の `[[rules]]` エントリ。 +/// +/// **サポート field 一覧** (rule author 向け reference、`.claude/custom-lint-rules.toml` 冒頭コメントと整合): +/// +/// | field | 必須 | semantics | +/// |---|---|---| +/// | `id` | ✅ | ルール一意 identifier | +/// | `pattern` | ✅ | 検出する正規表現 (case-insensitive にしたい場合は `(?i)` prefix を pattern 内に明示) | +/// | `severity` | ✅ | `"error"` or `"warning"` | +/// | `message` | ✅ | 違反時のメッセージ | +/// | `extensions` | ✅ | 対象拡張子の list (例: `["rs", "toml"]`)。空配列を使うと全 file が対象になる anti-pattern なので避ける | +/// | `why` | optional | ルールの根拠 (ADR 参照 / PR 由来等)。省略可だが post-merge-feedback 由来は明記推奨 | +/// | `fix` | optional | `CustomRuleFix` (strategy + steps) | +/// | `example` | optional | `CustomRuleExample` (bad + good) | +/// +/// **planned field** (実装時に本コメントも併せて更新する): +/// +/// - `paths` (順位 102): glob pattern による file path filter。現状は `extensions` のみで file scope を絞り、 +/// path semantics は pattern 自体で self-limit する設計 (ADR-007 amendment 参照、順位 104)。 +/// `paths` 実装時には `extensions` × `paths` の AND 結合で評価する。 #[derive(Deserialize, Clone)] struct CustomRule { id: String, @@ -1609,6 +1629,43 @@ extensions = ["ts", "js"] assert!(violations.is_empty()); } + /// 順位 101 (PR #140 T1-#1 採用): depth-1 root MD ファイル (例: `./CLAUDE.md`、`./README.md`) から + /// `../docs/` を参照すると、リポジトリの親ディレクトリ (= リポジトリ外) を指してしまい必ず broken link になる。 + /// pattern `(?i)\]\(\.\./docs/` は path-aware ではないが、root-level MD では `../docs/` が + /// 必然的に意味を持たない参照になるため **fire = true positive** として正しい挙動。 + #[test] + fn md_no_docs_relative_detects_root_level_back_reference() { + let dir = tempfile::tempdir().unwrap(); + let file = write_file( + dir.path(), + "CLAUDE.md", + "See [Phase D plan](../docs/local-llm-offload-analysis.md) for context.\n", + ); + let rules = compile_test_rules(vec![md_no_docs_relative_back_to_docs_rule()]); + let violations = run_custom_rules(file.to_str().unwrap(), &rules); + assert_eq!( + violations.len(), + 1, + "rule⑧ should fire on root-level MD `../docs/` reference (= reaches outside repo, broken link)" + ); + } + + /// 順位 101 (PR #140 T1-#1 採用) 補強: README.md 等の root-level fixture でも同じ挙動が成立することを確認。 + /// 上の `_detects_root_level_back_reference` は CLAUDE.md fixture でカバー、本テストは別 fixture 名で + /// 「root-level MD 全般で fire」が安定することを assert する (false negative 防止)。 + #[test] + fn md_no_docs_relative_detects_root_readme_back_reference() { + let dir = tempfile::tempdir().unwrap(); + let file = write_file( + dir.path(), + "README.md", + "Project setup guide: [setup](../docs/setup.md)\n", + ); + let rules = compile_test_rules(vec![md_no_docs_relative_back_to_docs_rule()]); + let violations = run_custom_rules(file.to_str().unwrap(), &rules); + assert_eq!(violations.len(), 1); + } + fn no_ephemeral_todo_reference_rule() -> CustomRule { let stem = "todo"; let pattern = format!(r"(?i)docs/{stem}[0-9]*\.md"); @@ -1702,7 +1759,29 @@ extensions = ["ts", "js"] .join("..") .join(".claude") .join("custom-lint-rules.toml"); - let rules = compile_test_rules(vec![no_ephemeral_todo_reference_rule()]); + + assert!( + path.exists(), + "deployed custom-lint-rules.toml not found at {:?} — \ + self-exclusion invariant test would silent-pass on missing file \ + (run_custom_rules returns empty Vec when path is missing). \ + check if `.claude/custom-lint-rules.toml` was moved / deleted. \ + (順位 106 PR #141 T2-#1 false-green guard 1)", + path + ); + + let rule = no_ephemeral_todo_reference_rule(); + assert!( + rule.extensions.iter().any(|e| e == "toml"), + "rule⑥ extensions list does not contain \"toml\" — \ + self-exclusion invariant test would silent-pass on rule scope change \ + (run_custom_rules early-returns when extension is not listed). \ + extensions actual: {:?}. \ + (順位 106 PR #141 T2-#1 false-green guard 2)", + rule.extensions + ); + + let rules = compile_test_rules(vec![rule]); let violations = run_custom_rules(path.to_str().unwrap(), &rules); assert!( violations.is_empty(),