Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/todo-summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@
| 78 | 💎 Tier 3 | **ADR-NNN (採番未確定、land 時に確定): Rust timestamp arithmetic safety + CLAUDE.md security 拡充 (PR #115 T3-1 採用) ★ Bb-3 follow-up** | todo5.md | S | なし (config が user-editable system boundary のとき `sanitize()` 値域検証を必須化し dependent arithmetic に `// SAFETY: <sanitize-fn> により上限保証` コメントを要求するパターンを ADR + CLAUDE.md に codify、Rust 固有の checked_add + MAX_SAFE capping + time-dependent test の 3 層を明文化。2026-05-16 entry 登録時の旧予約 ADR-038 → ADR-041 振り直し → 順位 139 (PR #168 follow-up) ADR-041 取得に伴い 2026-05-22 再 placeholder 化、land 時 PR で空き番号確定 — 順位 135 codified placeholder policy の実例運用) |
| 79 | 💎 Tier 3 | **`docs-governance.md` § Retirement Workflow に「残タスクの lifecycle 整合」要件明記 (PR #117 T3-1 採用)** | todo5.md | XS | なし (PR #117 で順位 15 を Bb-3 で吸収済として削除した際、現 Step 2「残タスクを priority table に登録」が priority table から除外するケース = 完了/deprioritize/defer を未定義だった実証。除外時の commit/PR で 3 値のいずれかを明示する要件を追加して将来の同型 ambiguity を構造的に防ぐ) |
| 81 | 🚀 Tier 1 | **cli-pr-monitor: CR 投稿エラー (`Failed to post review comments`) auto-retry 拡張 (PR #120 T1-2 採用) ★ Bundle f (defer)** | todo5.md | M | 1 観測のみで systemic 性未確認 (§A-2 P-5 PR で defer 判断、ADR-018 §追記 2026-05-08 で re-trigger 条件 = 2 件以上の同型観測を規定) |
| 91 | 🔧 Tier 2 | **`[lint_screen]` config parse テスト (PR #132 T2-#4 採用) ★ Bundle i** | todo6.md | S | なし (PR #132 で追加した push-runner-config.toml の `[lint_screen]` section に対する toml::from_str テスト、CodeRabbit nitpick 起点、silent field rename 防止) |
| 92 | 🔧 Tier 2 | **scale-aware eval fixtures (200+ 行) — Phase d 投入前の必須 infrastructure (PR #132 T2-#5 採用) ★ Bundle i** | todo6.md | M | 順位 91 と同 PR 推奨 (Bundle i コア、PR #132 smoke で観測した mistral:7b 大規模 diff JSON 不完全 (`missing field 'screen_decision'`) を fixture 化、Phase d 着手前の改善 ループ reference point 確保) |
| 92 | 🔧 Tier 2 | **scale-aware eval fixtures (200+ 行) — Phase d 投入前の必須 infrastructure (PR #132 T2-#5 採用)** | todo6.md | M | なし (PR #132 smoke で観測した mistral:7b 大規模 diff JSON 不完全 (`missing field 'screen_decision'`) を fixture 化、Phase d 着手前の改善 ループ reference point 確保。順位 91 = Bundle i ペアタスクは既存 test カバー判明で 2026-05-24 削除済) |
| 93 | 💎 Tier 3 | **`coding-style.md` Cross-File Reference Lifecycle に partial fix 例を追記 (PR #132 T3-#8 採用)** | todo6.md | XS | なし (PR #94 / #111 / #132 で反復した「変更差分外ファイルへの partial fix 再発」パターンを anti-pattern 例として codify、独立並列実施可) |
| 95 | 🔧 Tier 2 | **`docs/todo*.md` preamble file count 自動照合スクリプト (PR #133 T2-#4 採用) ★ Bundle j** | todo6.md | S | なし (PR #133 で todo6.md「六つ」/ todo7.md「七つ」が実 8 ファイルと乖離した実例。todo*.md 分割が今後も繰り返す pattern (todo3 → 4 → 5 → 6 → 7) のため CI 層で自動検証) |
| 96 | 🔧 Tier 2 | **Markdown cross-reference validator CI step (PR #133 T2-#3 採用) ★ Bundle j** | todo6.md | M | 順位 10 (ADR-032 PR-broken-link) と方向性が近接、fold-in 検討の余地あり。順位 94 (regex 規約) + 順位 95 (count 照合) と組み合わせて docs/ 整合性の多層検証 |
Expand All @@ -73,6 +72,7 @@
| 139 | 💎 Tier 3 | **ADR-041: Test Isolation Patterns for Multi-Condition Guards (PR #168 T3-#2 採用) — 本 PR で land** | todo8.md | M | なし (PR #120 W-001 初発見 + PR #168 sentinel pattern 実装の 2 PR 横断で Frequency Medium、`feedback_no_unenforced_rules.md` 例外 = 既存実践の明文化 + project-specific 実装例 (poll.rs) + PR #120 W-001 history codify、順位 84 = code-review.md global checklist の補完 layer、順位 135 codified placeholder 番号 policy 適用 — 当初 ADR-NNN placeholder で entry 登録 → land 時 PR で ADR-041 確定取得、順位 78 を ADR-NNN に再 placeholder 化) |
| 140 | 💎 Tier 3 | **順位 135「codified placeholder policy」を正式 ADR に昇格 (PR #169 T3-#2 採用)** | todo8.md | S | なし (順位 135 entry を retire し、ADR-NNN (採番未確定、land 時に確定): ADR Numbering Strategy として永続化。PR #111/#132/#169 の 3+ PR で適用実証済 — PR #169 で「ADR-038 → 041 → NNN」3 段振り直し dogfood が land、ephemeral todo entry 限りでは派生プロジェクトへの transferability 不足、`feedback_no_unenforced_rules.md` 例外 = 既存実践 (3 PR で実証) の明文化 + 後続 entry が同 policy を参照する際の永続 reference 確保) |
| 141 | 🚀 Tier 1 | **CR rate-limit detection bug 修正 — fix_push_time 固定 + 早期 merge 判断 signal (PR #169 観測由来)** | todo8.md | S | なし (PR #169 セッションで systemic 観測 = wakeup ごとの push_time 更新で CR walkthrough overlay の updated_at が「過去扱い」になり parse_rate_limit の event_time >= push_time filter で除外される構造バグ、`feedback_pipeline_over_rules` 適用 = パイプライン側機械的修正で Claude 判断介入を排除、wall clock 短縮 = rate-limit 検出時に mergeable CLEAN なら 5-10 分で人間判断 (38 分 reset 待ちを bypass)、既存 auto-retry path は維持 (ユーザーが「待つ」選択時は通常 flow)、Bundle a Sub-PR 2 / Bundle f scope 外の独立 layer) |
| 142 | 💎 Tier 3 | **ADR-041 補強 — "State Preservation Invariant" pattern section 追加 (PR #170 T3-#1 採用)** | todo8.md | S | なし (PR #168/169/170 で連続観測の write-once 不変式 (once-set-never-overwritten) パターンを ADR-041 に追記、`state.fix_push_time.or_else(...)` 形式の 3 点セット test pattern (既存値あり / 新値提供 / preservation 確認) を明文化、参照実装 = poll.rs `finalize_*_preserves_existing_fix_push_time` + monitor.rs `resume_returns_fix_push_time_from_state_when_set`、ADR-041 既存 section (Multi-Condition Guards) とは別 pattern class、`feedback_no_unenforced_rules.md` 例外 = 既存実践 (3 PR で実証) の明文化 + 派生プロジェクト transferability 確保) |

**戦略**: Tier 1 を 2〜3 セッションで片付け → Tier 2 で ADR-032 の前提 + rate-limit + convergence cost 削減を進める → Tier 3 で ADR-032 を land + ドキュメント整備。Tier 4-5 は cleanup / 外部展開で daily efficiency への直接効果は小さい。

Expand Down
28 changes: 0 additions & 28 deletions docs/todo6.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,6 @@

## 現在進行中

### `[lint_screen]` config parse テスト (PR #132 T2-#4 採用) ★ Bundle i

> **動機**: PR #132 (Phase c MVP) で `push-runner-config.toml` に新 section `[lint_screen]` を追加したが、`config.rs` の test module には parse テストが不在。CodeRabbit nitpick で指摘 (`config_parses_with_diff` 相当が `[lint_screen]` には未存在)。serde TOML は field name の完全一致を要求するため、parse テストがないと将来の field rename / 追加で silent `None` fallback が発生し、機能が無音で停止するリスクがある。
>
> **本タスクの位置づけ**: PR #132 post-merge-feedback Tier 2 #4 採用 (Frequency Medium / Effort S / Adoption Risk None)。
>
> **参照**: `.claude/feedback-reports/132.md` Tier 2 #4、`src/cli-push-runner/src/config.rs` (テスト module の `config_parses_with_diff` を template に踏襲)、PR #132 commit `73903d72` (lint_screen config 追加)
>
> **実行優先度**: 🔧 **Tier 2** — Effort S。順位 92 と同 PR (Bundle i) 推奨。

#### 作業計画

- [ ] `src/cli-push-runner/src/config.rs` の `#[cfg(test)] mod tests` に `config_parses_with_lint_screen_section` を追加
- [ ] 全 7 field (`enabled`, `exe_path`, `model`, `endpoint`, `timeout_secs`, `max_diff_lines`, `output_path`) の deserialize 検証
- [ ] `enabled = false` でも `Option<LintScreenConfig>` が `Some(...)` で構築されることを assert (= section があれば parse される、なくなれば None)
- [ ] 一部 field 省略時に default (`None`) になることを assert (`config.lint_screen.unwrap().exe_path.is_none()` 等)

#### 完了基準

- 上記テストが pass
- 将来 `LintScreenConfig` に field 追加 / rename した時に test 側で気付ける構造になる

#### 詰まっている箇所

なし

---

### scale-aware eval fixtures (200+ 行) — Phase d 投入前の必須 infrastructure (PR #132 T2-#5 採用) ★ Bundle i

> **動機**: PR #132 smoke dogfood で 868 行の現実 PR diff を mistral:7b に流したところ、JSON 出力が不完全 (`missing field 'screen_decision'`) になり fallback path が作動した。Phase b' eval fixtures (10-30 行/件) では出ない failure mode で、Phase d 本番 PR 投入時に頻発するリスクが顕在化していた。fixture 化することで再現可能化し、 §8.D prompt v3 / v4 改善ループの reference point として固定する。
Expand Down
43 changes: 43 additions & 0 deletions docs/todo8.md
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,49 @@ CR 仕様変更時は graceful degradation: 検出が壊れたら shortcut signa

---

### ADR-041 補強 — "State Preservation Invariant" pattern section 追加 (PR #170 T3-#1 採用)

> **動機**: PR #170 post-merge-feedback analyzer が **PR #168/169/170 で write-once 不変式 (once-set-never-overwritten) のテストカバレッジ漏れが連続観測** されたことを Frequency Medium で識別。ADR-041 (Test Isolation Patterns for Multi-Condition Guards) の既存 section は early-return guard (sentinel pattern + 直交 precondition) のみで、`state.fix_push_time.or_else(...)` のような **write-once 不変式は別 pattern class** として未収録。順位 141 で takt-fix が自動追加した 3 件の preservation test (poll.rs `finalize_*_preserves_existing_fix_push_time` / monitor.rs `resume_returns_fix_push_time_from_state_when_set`) が、ADR-041 の延長として補強される自然な pattern であることが post-merge analyzer により独立識別された。
>
> **本タスクの位置づけ**: PR #170 post-merge-feedback Tier 3 #1 採用。`feedback_no_unenforced_rules.md` の例外 = 既存実践 (3 PR で実証) + project-specific 参照実装の明文化 + 派生プロジェクト transferability 確保。Severity Low / **Frequency Medium (PR #168/169/170 の 3 PR 横断)** / Effort S / Adoption Risk None。
>
> **参照**: `.claude/feedback-reports/170.md` Tier 3 #1、`docs/adr/adr-041-test-isolation-patterns.md` (本セッション順位 139 で land 済、本 task で補強)、`src/cli-pr-monitor/src/stages/poll.rs` (preservation test 2 件)、`src/cli-pr-monitor/src/stages/monitor.rs` (preservation test 1 件)、PR #168/169/170 history
>
> **実行優先度**: 💎 **Tier 3** — Effort S。既存 ADR への追記のみ (新規 ADR / コード変更なし)。

#### 設計決定 (案)

analyzer report の `[ADR-041 追加 section 案]` をベースに、`docs/adr/adr-041-test-isolation-patterns.md` の「## 適用範囲」セクションの前に新 section `## 補足: State Preservation Invariant パターン (once-set-never-overwritten)` を挿入する。内容構成:

- **パターン定義**: `state.fix_push_time = state.fix_push_time.or_else(|| ctx.fix_push_time.map(String::from));` 形式の write-once 不変式コード例
- **3 点セット test**:
1. `state.fix_push_time = Some("old_time")` — 既存値あり (preservation される側)
2. `ctx.fix_push_time = Some("new_time")` — 新値を提供 (上書きを試みる側)
3. `assert_eq!(state.fix_push_time, Some("old_time"))` — old value が retain されたことを確認
- **Anti-pattern**: 全テスト fixture を `fix_push_time: None` で統一すると "don't overwrite" branch (preservation path) が実行されず coverage = 0
- **適用タイミング**: 新 field を追加し、その field が `or_else` / `if existing.is_none() { ... }` 等の write-once 意味論を持つ場合、**field 追加と同一 PR で** 上記 3 点セット test を追加する
- **参照実装**: PR #170 で land された 3 件 (`finalize_initial_review_park_preserves_existing_fix_push_time` / `finalize_review_recheck_park_preserves_existing_fix_push_time` / `resume_returns_fix_push_time_from_state_when_set`)
- **由来**: PR #170 simplicity-review F-2 + post-merge analyzer session で観測

#### 作業計画

- [ ] `docs/adr/adr-041-test-isolation-patterns.md` に新 section `## 補足: State Preservation Invariant パターン (once-set-never-overwritten)` を挿入 (上記 6 項目)
- [ ] `## 適用範囲` セクション内の対象記述に「write-once 不変式を持つ pure function 系 state 更新」を追記 (既存 = 2+ 条件の OR/AND 早期 return を持つ pure function 系 test、追加 = write-once 不変式パターン)
- [ ] `## 改訂履歴` に「2026-05-23: PR #170 T3-#1 採用、State Preservation Invariant section 追加」を追記
- [ ] 本 todo8.md entry を削除 (本 ADR 補強で内容が ADR に migrate されるため、`feedback_todo_no_history` 適用)

#### 完了基準

- ADR-041 に State Preservation Invariant section が追加され、3 点セット test pattern + 参照実装 + Anti-pattern + 適用タイミングが記述される
- 次回 write-once 不変式 field を追加する PR で、本 ADR section を直接 cite して 3 点セット test を実装できる
- 順位 142 entry が todo8.md から削除される

#### 詰まっている箇所

なし。記述のみで実装変更不要。順位 141 と異なり ADR 本体への追記のみで完結する。

---

## 既知課題 (記録のみ、本セッションで未対応)

### post-merge-feedback workflow が長時間 stale marker を残す問題 (PR #119 marker observed 2026-05-15)
Expand Down
32 changes: 32 additions & 0 deletions src/hooks-post-tool-comment-lint-rust/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,38 @@ mod tests {
assert_eq!(violations.len(), MAX_VIOLATIONS);
}

/// 順位 57 (PR #105 T2-1 採用): `collect_all_violations` の `truncate(MAX_VIOLATIONS)`
/// contract test。`find_violations` (comment-forbidden 系) と
/// `find_function_length_violations` (function-too-long 系) の **両 source 混在** で
/// 合計が MAX_VIOLATIONS を超えるとき、最終 vec が MAX_VIOLATIONS に cap されることを
/// 機械強制する。
///
/// 既存 cap test (`max_violations_capped` / `function_length_violations_capped`) は
/// 各 source 単体の cap のみを検証しており、`collect_all_violations` で
/// `truncate(MAX_VIOLATIONS)` を削除した場合 (= 両 source 合算が cap を超える regression)
/// は検知できない gap がある。本 test は将来の lint 追加時にもこの contract を seal する。
#[test]
fn collect_all_violations_truncates_combined_total_to_max() {
let mut source = String::new();
source.push_str("fn long_function() {\n");
for i in 0..(MAX_FUNCTION_LINES + 5) {
source.push_str(&format!(" let _x{} = {};\n", i, i));
}
source.push_str("}\n");
for i in 0..(MAX_VIOLATIONS + 5) {
source.push_str(&format!("// trailing comment {}\n", i));
}

let violations = collect_all_violations("test.rs", &source, None);

assert_eq!(
violations.len(),
MAX_VIOLATIONS,
"combined violations from both sources must be truncated to MAX_VIOLATIONS (= {})",
MAX_VIOLATIONS
);
}

#[test]
fn is_rust_file_accepts_rs() {
assert!(is_rust_file("main.rs"));
Expand Down
53 changes: 53 additions & 0 deletions src/hooks-post-tool-linter/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1156,6 +1156,59 @@ mod tests {
assert_eq!(violations.len(), MAX_CUSTOM_VIOLATIONS);
}

/// 順位 125 (PR #151 T2-#1 横展開) defensive seal: `build_violation_json` の
/// `content[..m.start()]` は `regex::Match::start()` が char boundary を保証するため
/// panic 安全だが、multi-byte content でも line 算出
/// (`.bytes().filter(|b| *b == b'\n').count() + 1`) が正しく動作することを
/// empirical に seal する。
///
/// fixture lines (1-indexed):
/// - L1: Japanese text (3 bytes/char) — non-match
/// - L2: emoji (4 bytes) — non-match
/// - L3: ASCII match (expect line=3)
/// - L4: combining character (e + U+0301) — non-match
/// - L5: ASCII match (expect line=5)
///
/// 将来 `content[..m.start()]` を `char_indices()` 等に書き換えた際の line off-by-one
/// regression を catch する。
#[test]
fn run_custom_rules_line_number_correct_with_multibyte_content() {
use std::io::Write;
let dir = tempfile::tempdir().unwrap();
let file = dir.path().join("multibyte_fixture.ts");
{
let mut f = std::fs::File::create(&file).unwrap();
writeln!(f, "// 日本語コメント").unwrap();
writeln!(f, "// 🦀 rust").unwrap();
writeln!(f, "console.log('after multibyte');").unwrap();
writeln!(f, "// caf\u{00e9}").unwrap();
writeln!(f, "console.log('second');").unwrap();
}

let rules = compile_test_rules(vec![make_test_rule(
"no-console-log",
r"console\.log\(",
&["ts"],
)]);
let violations = run_custom_rules(file.to_str().unwrap(), &rules);

assert_eq!(
violations.len(),
2,
"two console.log violations expected after multi-byte content"
);
let v1: serde_json::Value = serde_json::from_str(&violations[0]).unwrap();
let v2: serde_json::Value = serde_json::from_str(&violations[1]).unwrap();
assert_eq!(
v1["location"]["line"], 3,
"first violation should be on line 3 (after multi-byte L1 + L2)"
);
assert_eq!(
v2["location"]["line"], 5,
"second violation should be on line 5 (after combining char L4)"
);
}

#[test]
fn run_custom_rules_outer_break_skips_subsequent_rules() {
use std::io::Write;
Expand Down