feat(lint-screen): Bundle k-1 (順位 123) — Markdown ハンク除外フィルター + docs Phase D Round 2 完遂反映#155
Conversation
Phase D dogfood で 5 PR 連続観測された false positive (mistral:7b が docs-only diff や `.md` ファイルに対して Rust の `unused-import` を hallucinate) を 拡張子ベースの mechanical filter で構造的に解消する。 ## 設計 - `src/cli-push-runner/src/stages/lint_screen.rs` に `filter_excluded_hunks` 関数を追加 - `EXCLUDED_EXTENSIONS = ["md", "markdown"]` を hardcode (大文字小文字無視) - `diff --git ` 行を file-diff 境界として 1 ハンク = 1 chunk に分割、各 chunk の `+++ b/<path>` (または `--- a/<path>`) から拡張子を抽出して判定 - 全ハンクが対象外拡張子 (= docs-only diff) の場合は `FilterResult::AllExcluded` を 返し、invoke を完全に skip + skip-report (`screen_decision: skipped` + 理由) を書き出す short-circuit path に分岐 ## なぜ filter 適用箇所を (b) lint_screen stage 内にしたか - (a) `.takt/review-diff.txt` 生成時に drop すると review 全体の汎用性を壊す - (c) prompt 内で「.md は無視せよ」と instruct は LLM 信頼で危険 - (b) lint_screen stage 限定で副作用最小、Bundle k 順位 123 計画通り ## なぜ `run_lint_screen` を `invoke_and_write_report` に分割したか filter 経路追加で 50 行ガイドラインを超過 (58 行)。invoke + write_report の 2 step を `invoke_and_write_report` ヘルパーに切り出して 30 行台に収めた。 ## テストカバレッジ (10 件新規) - Rust-only / mixed (Rust + .md) / pure .md / pure .markdown - 大文字 (.MD / .Markdown) も除外対象に含む - `something.mdxyz.rs` のような中途 `.md` を含む path は除外しない - `/dev/null` create / delete の片側 path 判定 - 3-file mixed diff で hunk 境界が正しく保たれる - skip-report 本文に "skipped" / "docs-only diff" / "Bundle k 順位 123" を含む ## 累積 false positive への効果見積 5 観測のうち: - D-4 CR fix (TOML) — scope 外 (TOML は除外対象に含まない) = 残存 - D-5 ×2 + D-6 (Markdown / docs-only) — **構造的解消** (3 件消滅) - D-5 初回 (Rust + docs mixed) — Rust 部分は依然 mistral:7b に渡る = scope 内 Rust FP が残る可能性 期待: Phase E 採否判定前の FP rate を 5/7 → ~2/7 に低減。残 2 件は別 root cause (Rust scope hallucinate / TOML hallucinate) のため別途分析が必要。 Refs: Bundle k 順位 123 (todo8.md 削除済)、ADR-038 Known failure mode、 docs/local-llm-offload-phase-d-outcomes.md L162 (false positive 累積観測)
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughlint_screen ステージに Markdown( ChangesMarkdown フィルター実装と Phase D ドキュメント更新
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@src/cli-push-runner/src/stages/lint_screen.rs`:
- Around line 360-375: The function chunk_has_excluded_extension currently takes
the first header line found (using find_map) which can pick the "--- a/..." old
path and miss renames; change it to prefer the new-path header "+++ b/" across
the entire chunk and only fall back to "--- a/" if no "+++ b/" is present:
search lines for a "+++ b/" prefix first, set path to that if found, otherwise
search for "--- a/"; then extract the extension and check EXCLUDED_EXTENSIONS as
before. Ensure you update the logic inside chunk_has_excluded_extension
(referencing the function name and EXCLUDED_EXTENSIONS) so renames like *.rs ->
*.md correctly use the new-path extension.
🪄 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: 59b2dfc7-e68e-4e69-8c65-38c8810db0b4
📒 Files selected for processing (5)
docs/local-llm-offload-analysis.mddocs/local-llm-offload-phase-d-outcomes.mddocs/todo-summary.mddocs/todo8.mdsrc/cli-push-runner/src/stages/lint_screen.rs
💤 Files with no reviewable changes (2)
- docs/todo8.md
- docs/todo-summary.md
…b/ (new path) を優先
CodeRabbit Major 指摘の rename 時除外漏れ bug を解消する。unified diff の慣例で
`--- a/<path>` が `+++ b/<path>` より先に出現するため、find_map の旧実装では
両者を OR で取ると常に旧パスが優先されてしまう問題があった。
## 修正前の bug
```rust
let path = chunk.lines().find_map(|line| {
line.strip_prefix("+++ b/")
.or_else(|| line.strip_prefix("--- a/"))
}).unwrap_or("");
```
`*.rs → *.md` の rename で:
- `--- a/src/a.rs` を先に処理 → `find_map` が "src/a.rs" を返す
- `+++ b/docs/a.md` に到達せず
- 拡張子 = "rs" → EXCLUDED_EXTENSIONS に含まれず → mistral:7b が `.md` 内容を見る
- = Bundle k-1 の filter contract (新パス優先) に違反
## 修正後
```rust
let new_path = chunk.lines().find_map(|line| line.strip_prefix("+++ b/"));
let old_path = chunk.lines().find_map(|line| line.strip_prefix("--- a/"));
let path = new_path.or(old_path).unwrap_or("");
```
new_path を chunk 全体から先に探す → rename でも新拡張子で判定。new_path が無い
場合 (= `+++ /dev/null` の delete) のみ old_path にフォールバック。
## テスト追加 (2 件)
- `filter_excluded_hunks_prefers_b_path_on_rename_to_markdown`: `*.rs → *.md`
rename が AllExcluded になることを assert
- `filter_excluded_hunks_keeps_rename_from_md_to_rust`: 逆方向 (`*.md → *.rs`)
rename が Kept になることを assert (symmetric 検証)
## 累積テスト
cli-push-runner stages::lint_screen 24 → 26 件 pass、clippy clean、release exe deploy 済
Refs: PR #155 CR review コメント (Major 🟠 Quick win)、Bundle k-1 順位 123、ADR-038
… diff metadata strip + silent failure baseline cleanup (#159) Bundle l 3 サブ変更を bundled で land (順位 129 + 131 + 132、todo-summary.md table 4 行整理含む)。 共通テーマ: cli-push-runner lint-screen stage の堅牢化 + I/O write 関数の silent failure 構造的防止。 順位 132 (Tier 1 / S): cli-push-runner/src/stages/lint_screen.rs に strip_diff_metadata_lines 前処理関数を追加し、similarity index NN% / index <hex> / file mode / rename 系の diff metadata 行を LLM 入力前に決定論的に除去。PR #155-#156 で 5 観測された mistral:7b の magic-number FP を構造的に解消。unit test 7 件 (各 metadata 系列 + content 保持 + substring 誤検出回避)。 順位 130 (prompt 改修 or 前処理 filter のいずれか) を本実装で前処理 filter に確定して supersede、 table から 130 行も削除。 順位 129 (Tier 1 / S): .claude/custom-lint-rules.toml に rule⑩ (no-write-result-discard、 pattern let\s+_\s*=\s+write_\w+\(、extensions=["rs"]、severity=error) を追加。 hooks-post-tool-linter に 8 件 unit test を追加 (positive x3 / negative x4 / dogfood x1)。 dogfood test (deployed_src_rust_passes_no_write_result_discard_rule) は src/**/*.rs を再帰走査して clean baseline を強制。本 PR の completeness criteria に合わせて既存違反 5 件を fix: - cli-pr-monitor/src/stages/poll.rs: 3 箇所の let _ = write_state(...) を if let Err(e) = ... { log_info(...) } に置換 (非 fatal 進行 + 観測可能性回復) - cli-merge-pipeline/src/feedback.rs: FailedMarkerGuard::drop 内の write_failed_marker と feedback::run の write_pending_marker を log 化、後者は write_pending_marker_logged helper に抽出して fn run の 50 行ガイドライン超過を回避 (lint_screen.rs の write_skip_report_logged と同 pattern) 順位 131 (Tier 2 / M): write_skip_report_logged の error path regression test を 3 件追加。 parent が regular file な path に書込を試みて create_dir_all 失敗を確定的に誘発する方式 (Unix/Windows 両方で reliable、追加 dep 不要)。write_skip_report の Err 返却 + write_skip_report_logged の no-panic + writable path success の 3 段で seal、将来 refactor での silent drop 回帰を機械検出。 副次: src/cli-push-runner/Cargo.toml に tempfile dev-dep を追加 (順位 131 test 用)、 todo8.md から 129/130/131/132 detail entry を削除 (Cross-File Reference Lifecycle 整合)。 検証: cargo test 全 workspace pass / pnpm build:all 成功 / 変更 crate 3 つ (cli-push-runner / hooks-post-tool-linter / cli-pr-monitor) で cargo clippy --all-targets -- -D warnings clean。
…ule-test coverage check + global testing.md 明文化) PR #163 (Bundle k-2) merge 後の post-merge-feedback で 2 件採用された follow-up task を todo 系列に登録。 ## 順位 137 (Tier 1, M, Frequency High) Rule-Test Coverage Check Cargo test。 `.claude/custom-lint-rules.toml` の各 rule の extensions ⇔ `src/hooks-post-tool-linter/src/main.rs` の test 関数名を mechanical に検証 する Cargo test step を追加する。 由来: PR #110/#151/#152/#155 の 4 PR 観測 (Frequency High)、PR #163 で順位 127 として passive reminder comment を追加したが、analyzer が「passive reminder では PR #152 同根再発を防止できなかった実証ベース → mechanical enforcement が必要」と判定。 必須カバレッジ scope (CodeRabbit Minor 指摘で確定): 主要 ext rs/toml/yaml/yml × 全 rule に対応 test 必須、その他 ext (jsonc/json/ts/tsx/js/jsx/py/ps1) は rule あたり positive single test 1 件以上で代替。完了基準と詰まっている箇所 の scope drift を fix。 ## 順位 138 (Tier 3, XS, Frequency High) Rule Extension Test Pattern を `~/.claude/rules/common/testing.md` に明文化。 順位 137 (mechanical Cargo test) と 2 層構成。`~/.claude/` global 配下のため 派生プロジェクト (techbook-ledger / auto-review-fix-vc) へ自動波及。 ## land 順序の推奨 順位 137 + 138 は同 PR で land 推奨 (命名規約 + 必須 ext scope 決定が atomic に整う、cross-reference が同 commit で完結)。 ## 補正事項 analyzer report は target を `src/cli-custom-linter/src/main.rs` と記載 していたが、実際の crate 名は `hooks-post-tool-linter` のため詳細 entry で補正済。 ## CodeRabbit review 反映 PR review (PR #164 round 1) で順位 137 の「完了基準」(全 (rule, ext) ペア必須) と「詰まっている箇所」(主要 ext に絞った coverage 推奨) の scope drift を 🟡 Minor で指摘。修正方針: - 完了基準を 主要 ext (rs/toml/yaml/yml) × 全 rule 必須 + その他 ext は rule あたり single positive test で代替、と明示 - 設計決定 section に必須カバレッジ scope を追加 (順位 138 testing.md と 同一 commit で codify することで cross-reference の atomicity 確保) - 詰まっている箇所 から該当議論を削除、TOML meta field vs 命名規約方式の trade-off のみ残す (= 順位 138 実装時に確定する補助 design 議論) ## docs only 実装は別 PR で着手予定。
…ule-test coverage check + global testing.md 明文化) (#164) PR #163 (Bundle k-2) merge 後の post-merge-feedback で 2 件採用された follow-up task を todo 系列に登録。 ## 順位 137 (Tier 1, M, Frequency High) Rule-Test Coverage Check Cargo test。 `.claude/custom-lint-rules.toml` の各 rule の extensions ⇔ `src/hooks-post-tool-linter/src/main.rs` の test 関数名を mechanical に検証 する Cargo test step を追加する。 由来: PR #110/#151/#152/#155 の 4 PR 観測 (Frequency High)、PR #163 で順位 127 として passive reminder comment を追加したが、analyzer が「passive reminder では PR #152 同根再発を防止できなかった実証ベース → mechanical enforcement が必要」と判定。 必須カバレッジ scope (CodeRabbit Minor 指摘で確定): 主要 ext rs/toml/yaml/yml × 全 rule に対応 test 必須、その他 ext (jsonc/json/ts/tsx/js/jsx/py/ps1) は rule あたり positive single test 1 件以上で代替。完了基準と詰まっている箇所 の scope drift を fix。 ## 順位 138 (Tier 3, XS, Frequency High) Rule Extension Test Pattern を `~/.claude/rules/common/testing.md` に明文化。 順位 137 (mechanical Cargo test) と 2 層構成。`~/.claude/` global 配下のため 派生プロジェクト (techbook-ledger / auto-review-fix-vc) へ自動波及。 ## land 順序の推奨 順位 137 + 138 は同 PR で land 推奨 (命名規約 + 必須 ext scope 決定が atomic に整う、cross-reference が同 commit で完結)。 ## 補正事項 analyzer report は target を `src/cli-custom-linter/src/main.rs` と記載 していたが、実際の crate 名は `hooks-post-tool-linter` のため詳細 entry で補正済。 ## CodeRabbit review 反映 PR review (PR #164 round 1) で順位 137 の「完了基準」(全 (rule, ext) ペア必須) と「詰まっている箇所」(主要 ext に絞った coverage 推奨) の scope drift を 🟡 Minor で指摘。修正方針: - 完了基準を 主要 ext (rs/toml/yaml/yml) × 全 rule 必須 + その他 ext は rule あたり single positive test で代替、と明示 - 設計決定 section に必須カバレッジ scope を追加 (順位 138 testing.md と 同一 commit で codify することで cross-reference の atomicity 確保) - 詰まっている箇所 から該当議論を削除、TOML meta field vs 命名規約方式の trade-off のみ残す (= 順位 138 実装時に確定する補助 design 議論) ## docs only 実装は別 PR で着手予定。
Summary
Phase D dogfood で 5 PR 連続観測された false positive (mistral:7b が docs-only diff や
.mdファイルに対して Rust のunused-importを hallucinate) を 拡張子ベースの mechanical filter で構造的に解消する。Phase E 採否判定前に systemic FP root cause を消す位置付け。src/cli-push-runner/src/stages/lint_screen.rs—filter_excluded_hunks関数 +EXCLUDED_EXTENSIONS = ["md", "markdown"](case-insensitive) を追加。diff --git行を file-diff 境界として分割、各 chunk の+++ b/<path>(なければ--- a/<path>) から拡張子判定。全ハンク除外時はFilterResult::AllExcludedで invoke を skip +screen_decision: skipped説明付き skip-report を書き出す short-circuit。analysis.mdL216 を🚧 進行中→✅ 完遂、outcomes.mdに D-7 outcome section (PR feat(post-merge-feedback): Bundle c-1 (D-7) — L1 Drop guard + L2 orphan reaper + ADR-030 spec #154 結果) を追加、Phase E 判定材料を 7 data points / 5 PR に更新。Phase D Round 2 完遂状態を文書化。このPRの2コミット構成
vnwzzzyw):docs(llm-offload): Phase D Round 2 完遂状況を analysis.md / outcomes.md に反映 (D-7 #154 結果込み)— docs-only、PR scope に混ぜることをユーザー許可済plytvxno):feat(lint-screen): Bundle k-1 (順位 123) — Markdown ハンク除外フィルターを追加— 本 PR の主旨Bundle k-1 self-dogfood 結果 (Phase D data point #8 = 累積 9 dp / 6 PR)
LINT_SCREEN_ENABLED=true で push 実行、本 PR 自身が filter のテストケースに:
auto_fixunused-importonlint_screen.rs:305(const EXCLUDED_EXTENSIONS: &[&str] = &[)## Diagnostic観察の意義:
docs/*.mdハンクは mistral:7b に渡らず、findings は Rust ファイル (lint_screen.rs) のみが対象。D-3〜D-6 で観測したuse std::io::Write;hallucinate は本 PR で再現しなかった。const EXCLUDED_EXTENSIONS: &[&str] = &[...]をunused-importと誤分類。これは Markdown 軸ではなく Rust const 構文の misclassification で、5 観測した「context 外 hallucinate」とは別 root cause。EXCLUDED_EXTENSIONSを line 374 で実使用していることを直接読み取って FP と判定 + 「自己実証 PR」とコメント。takt-fix iteration outcome (2 iterations / 8m 59s)
initial review で simplicity-review が REJECT を出した 2 件の BLOCKING:
let _ = write_skip_report(...)経路で I/O エラーを silent drop —write_report経路 (line 108-117) のif let Err(e) { log_stage(...) }pattern と不一致run_lint_screenの nesting depth 増加 — fix のためのif let Errをインラインで書くと深くなるtakt-fix の解決:
write_skip_report_loggedhelper を新規切り出し (既存のinvoke_and_write_reportextraction pattern と同形)。1 fix で両 finding を同時解消、iteration 2 で APPROVE。累積 FP rate への効果
.md系 FP (D-5 ×2 / D-6)累積: 5 件 (filter 前) → 2-3 件 (filter 後の推定)。Phase E 採否判定の精度が向上する位置付け。
テストカバレッジ (10 件新規)
something.mdxyz.rsの中途.md判定 (= 除外しない)/dev/nullcreate / delete 片側 path の処理Test plan
cargo test -p cli-push-runner stages::lint_screen24 件 passcargo test --workspace901+ 件 pass (regression なし)cargo clippy -p cli-push-runnerclean.claude/cli-push-runner.exedeployRefs
docs/local-llm-offload-phase-d-outcomes.md🤖 Self-dogfood meta-pattern: 本 PR の filter 実装が本 PR 自身の lint_screen で機能していることが finding 内容から直接観測可能。
Summary by CodeRabbit
New Features
Documentation
Tests