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
43 changes: 43 additions & 0 deletions .claude/custom-lint-rules.toml
Original file line number Diff line number Diff line change
Expand Up @@ -704,3 +704,46 @@ rs = [
"no_hardcoded_jj_revset_range_skips_parameterized_format",
"no_hardcoded_jj_revset_range_skips_other_branch_literal",
]

# ─── ルール⑬: config コメント内の workstream 連番名を禁止 ───

[[rules]]
id = "no-workstream-seq-names-in-config"
pattern = '\bPR-[0-9]+\b'
severity = "warning"
message = "config コメントに workstream 連番名 (PR-N) が書かれています。恒久的な参照 (GitHub PR 番号 `#NNN` / 日付 / ADR) に置き換えてください"
why = "`PR-N` (N = 数字) 形式の workstream 連番は、その作業パッケージが終わると何を指すか復元できない ephemeral な識別子であり、rule⑥ `no-ephemeral-todo-reference` と同じ「消える参照」クラス。実際に `.claude/hooks-config.toml` のコメントへ書かれ、後から対応 PR を特定できなくなっていた (順位 216)。GitHub PR 番号 `#NNN` は不変なので除外する (pattern が `PR-` + 数字に限定されるため `#NNN` 形式は構造的に非対象)。なお本ルール自身の説明で数字を使わず `PR-N` と書くのは、rule⑥ が `docs/todoN.md` と書くのと同じ自己検出回避"
# plain `json` は対象外。本ルールは config **コメント**内の ephemeral 参照を対象とするが、
# JSON はコメント構文を持たないため、fire するとしたら data 文字列であり message と食い違う。
extensions = ["toml", "yaml", "yml", "jsonc"]

[rules.fix]
strategy = "ephemeral な連番を恒久参照に置換、または削除"
steps = [
"その `PR-N` が指す実 PR を特定できるなら GitHub PR 番号 `#NNN` に置換",
"特定できないなら日付 / ADR 番号など復元可能な参照に置換",
"計画の記述で参照先が未定なら連番を落として内容だけ残す (例: 「次 PR (PR-N) で移行予定」→「将来移行予定」)",
]

[rules.example]
bad = '# 次 PR (PR-N、N = 数字) で allow-list 方式に移行予定'
good = '# 将来 allow-list 方式へ移行予定 (由来: PR #216)'

[rules.incident]
pr = 216
bad_fixture = "no-workstream-seq-names-in-config.toml"
good_fixture = "no-workstream-seq-names-in-config.toml"
adr = "adr-007"

[rules.test_coverage]
# rule⑬ は toml / yaml / yml (主要拡張子) + jsonc / json (非主要)。
# positive は主要拡張子ごとに 1 件、negative は `PR #216` (GitHub 形式) を網羅。
other_ext_tests = ["no_workstream_seq_names_in_config_detects_jsonc_comment"]

[rules.test_coverage.main_ext_tests]
toml = [
"no_workstream_seq_names_in_config_detects_toml_comment",
"no_workstream_seq_names_in_config_skips_github_pr_reference",
]
yaml = ["no_workstream_seq_names_in_config_detects_yaml_comment"]
yml = ["no_workstream_seq_names_in_config_detects_yml_comment"]
4 changes: 2 additions & 2 deletions .claude/hooks-config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ stale_check_enabled = true
[session_start.weekly_review_reminder]
# 試験運用元 (本リポジトリ) では明示的に enable して reminder を実発火させる運用。
# ADR-031 は 2026-06-01 で本採用化済み、Phase E 観測で「reminder 未発火 = silent」だった
# 問題に対処するため試験運用元での enable に切替 (2026-06-23、PR-1)。
# 問題に対処するため試験運用元での enable に切替 (2026-06-23)。
# 派生プロジェクト deploy 時は default OFF (ADR-039 § 1 opt-in 契約) を維持。
# 次 PR (PR-3) で `[features].enabled` allow-list 方式に移行予定 = 本 `enabled = true` は暫定。
# 将来 `[features].enabled` allow-list 方式へ移行予定 = 本 `enabled = true` は暫定。
enabled = true
# 7 日は**恒久設定**であり暫定値ではない (2026-08-13 ユーザー判断で確定)。週次レビューは
# 毎週実行すること自体に意味があり、「weekly」を冠する運用の reminder が月周期で鳴るなら
Expand Down
33 changes: 31 additions & 2 deletions docs/claude-code-web-tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@
| 180 | T2 | — | `escape_markdown_pipe(&str)` を pub 追加 + `format_table` の user field に適用 + 5 variant test(markdown table 破壊の防止 / prompt injection の緩和 = defense-in-depth の一層) | `src/lib-report-formatter/src/lib.rs` | XS-S | 外部依存ゼロの純 lib。既存 private `truncate()` と escape ロジック重複、DRY 整理(共通化 or 役割分担)を検討 | |
| 228 | T2 | ✅ | `evaluate_rate_limit_shortcut` の cr_clean 判定(`new_comments` / `actionable_comments` / `unresolved_threads` 3 field × None/Some 境界)の回帰テスト | `src/cli-pr-monitor/src/stages/poll/rate_limit_signal.rs`(末尾 tests) | S | pure 関数、silent-clean 誤認保護。同 crate の `#[ignore]` 統合テストは無関係 | test(check-ci): rate-limit shortcut の cr_clean 判定をテストで固定する |
| 178 | T2 | — | `state.rs` の behavioral invariant test を ADR-041 pattern(sentinel 事前投入 + mutation 不在 assert)で 3-5 件追加 | `src/cli-pr-monitor/src/state.rs` | S | **todo 提案の invariant #1/#2 は実挙動と不一致**。`update_state_from_check_result` の実挙動を読んで実在する invariant を再選定する | |
| 239 | T2 | ✅ | `filter_transcripts` の `read_dir` 非決定順を timestamp ソートで決定論化 + 回帰テスト | `src/cli-merge-pipeline/src/feedback/transcript.rs`(`filter_transcripts` + tests) | M | temp-dir に複数 jsonl 生成 → 順序 assert で完結。実 hook 発火不要 | fix(merge-pipeline): transcript の読み取り順を timestamp ソートで決定論化する |

### Batch 2: 新規実装を伴う(○、要設計判断)

Expand All @@ -111,7 +110,6 @@ cargo test で検証完結するが、新規 module / lint rule / 軽微リフ
| 順位 | Tier | 無人可 | 内容 | 対象ファイル | 工数 | 注意 | PRタイトル |
|---|---|---|---|---|---|---|---|
| 340 | T2 | — | `decide.rs` の rate_limit × positive-evidence 複合境界テスト + `main.rs` の rate_limit threading テスト | `src/check-ci-coderabbit/src/{decide,main}.rs` | S | (a) は純関数で容易。(b) は `main.rs` の呼び出し側を I/O 無しでテスト可能にする小さな合成関数抽出リファクタが要る | |
| 216 | T2 | ✅ | `no-workstream-seq-names-in-config` lint rule 追加(config comment 内 `PR-[0-9]+` を検出、`#NNN` は除外) | `.claude/custom-lint-rules.toml` + `src/hooks-post-tool-linter/src/custom_rules/rule_tests_extras.rs` + `tests/incident_eval.rs` + `tests/fixtures/incidents/{bad,good}/` + (dogfood) `.claude/hooks-config.toml` | S | 確立 12 rule / 11 incident パターン踏襲。Rust regex lookaround 不要(`\bPR-[0-9]+\b`)。dogfood は数行の text 編集 | feat(post-tool-linter): config の workstream 連番名を lint する |
| 272 | T1 | — | cli-docs-lint に ADR 重複採番検出 + CLAUDE.md 索引整合チェック(新規 validator module) | `src/cli-docs-lint/src/adr_consistency.rs`(新規)+ `main.rs`(CheckMode dispatch 拡張) | S-M | 中核(validator + fixture test)は cargo test で完結。「pnpm lint:docs 経由の発火確認」は Web 外だが成功条件ではない。CLAUDE.md は docs_dir の親なので TempDir で fake 構造を組む | |
| 334 | T1 | — | docs/todo\*.md 本文の順位番号表記を検出する custom lint rule(ADR-033 仕組み化、`paths=["docs/todo*.md"]` scope、table 行除外) | `.claude/custom-lint-rules.toml` + fixtures(216 と同基盤) | M | 検証経路は 216 と同じ cargo test。**regex FP 精緻化**(preamble の「順位 220 以降」等)+ **本文 dogfood cleanup の規模**を着手前に grep 見積り(todo 記載 S だが M 見込み) | |
| 179 | T2 | — | rate-limit retry 境界(max_retries=0/1/3)で retry 継続 vs `action_required` 遷移の off-by-one を pin する parameterized テスト | `src/cli-pr-monitor/src/stages/poll/rate_limit.rs`(判定 L52)+ `config.rs`(L143-155) | S-M | **todo の「rstest 使用済」は誤り**(Cargo.lock に不在)。新 dev-dep 追加 or plain 複数 `#[test]` で代替を着手時判断。gh subprocess を踏まない早期 return 経路で構成する | |
Expand Down Expand Up @@ -178,8 +176,39 @@ cargo test で検証完結するが、新規 module / lint rule / 軽微リフ

これで docs-only の採用枠は 0 件になった。ただし本ファイルは retire しない(→ § ライフサイクル)。

### 2026-08-14

| 順位 | 節 | 判定 | 根拠 |
|---|---|---|---|
| 239 | 採用タスク (2) Batch 1 | マージ済みのため削除 | 夜間 PR [#391](https://github.com/aloekun/claude-code-hook-test/pull/391) が 2026-08-14 にマージ。実体も確認済み — `src/cli-merge-pipeline/src/feedback/transcript.rs` に `jsonl_paths.sort_by_key(\|path\| transcript_ordering_key(path))` が存在し、`docs/todo-summary2.md` の順位 table からも削除済み |
| 216 | 採用タスク (2) Batch 2 | 本 PR で完成させたため削除 | 夜間 PR [#394](https://github.com/aloekun/claude-code-hook-test/pull/394) は fixture 2 ファイルのみで rule 本体が無く**未完了だった**(→ [§ 未完了のままマージされた順位](#未完了のままマージされた順位))。本 PR で rule 定義・rule test 5 件・E2E case・dogfood を実装し、完了基準(`.toml`/`.yaml`/`.yml`/`.jsonc` の `PR-` + 数字を warning 検出、`PR #NNN` は非検出)を満たしたうえで削除した |

---

## 未完了のままマージされた順位

夜間 PR がマージされても**タスクが完了しているとは限らない**。マージは「その PR の内容を取り込む」判断であって「台帳の完了基準を満たした」判定ではなく、両者を突き合わせる機構が現状どこにも無い。ここには実際に起きた事例と、そこから設けた機構を記録する。

**現在 live な事例は無い**(順位 216 は 2026-08-14 に完成させて削除済み。→ [§ 棚卸し履歴](#棚卸し履歴))。本節を残すのは、下記の失敗モードと対処が再発防止の根拠として参照され続けるため。

### 事例: 順位 216(2026-08-14)

| PR | 入った成果物 | 欠けていた成果物 |
|---|---|---|
| [#394](https://github.com/aloekun/claude-code-hook-test/pull/394) | `tests/fixtures/incidents/{bad,good}/no-workstream-seq-names-in-config.toml`(2 ファイル・計 6 行) | `.claude/custom-lint-rules.toml` の rule 定義、rule test、`tests/incident_eval.rs` の E2E case、dogfood |

**なぜ CI を通ったのか**: custom lint rule の 3 つの機械チェックは**すべて「rule → fixture/test」の向き**にしか働かなかった。

- `rule_test_coverage_check`: toml の各 rule に対応 test が実在するか
- `incident_fixture_coverage_check`: incident 由来 rule に bad/good fixture が実在するか
- `cases_cover_every_incident_rule`: `CASES.len()` == `[rules.incident]` の個数

いずれも rule の存在を起点に検査するため、**rule を伴わない孤児 fixture は 3 チェックすべてを素通り**した。「新規 lint rule は 3 つの cargo test 群で機械強制される」という [§ 採用タスク (2)](#採用タスク-2-cargo-test-検証タスククロスプラットフォーム対応後2026-07-23) の記述は、**rule を書いた場合にのみ成立**していた。

**対処**: 逆向きの `orphan_fixture_check`(`src/hooks-post-tool-linter/src/custom_rules/coverage.rs`)を追加し、「fixture があるなら必ず rule がある」を fail-closed で強制した。この検査があれば #394 は CI で落ちてマージできなかった。

**残る一般的リスク**: 上記は lint rule クラスに固有の対処であり、「マージ ≠ 完了」という失敗モード自体は他のタスククラスに残る。完了基準を機械可読にして削除前に検証する仕組み(push 前セルフレビューでの決定論的な台帳自動削除 + 実装確認)を別途構築する。

## 昇格検査履歴

週次レビューの昇格候補チェック(→ [§ 定期更新(週次)](#定期更新週次) 2)で **「検査したが本台帳の採用基準を満たさない」と判定した順位**を記録する。次回以降の検査はここに載っている順位を除外し、**残り全件**を判定する。
Expand Down
1 change: 0 additions & 1 deletion docs/todo-summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@
| 207 | 💎 Tier 3 | **`~/.claude/rules/common/patterns.md` § Experimental Feature 設計時の参照必須 に「mechanical lint は ADR-039 scope 外」境界 case 追加 (PR #204 post-merge-feedback T3-2 採用)** | todo10.md | S | なし (PR #204 で project-local adr-039 § 1.b 追加した知見の global 投影、派生プロジェクトでの同型 over-application 防止、4 条件 (non-blocking / 決定論 / scope 限定 / recovery hint 明確) + 該当例 (file-length / file-size lint) と非該当例 (post-merge-feedback / weekly-review / local-llm) を境界 case として明示、順位 200/202/205 と同 pattern = project-local 知見の global codification、`feedback_global_config_backup` 適用必須) |
| 211 | 💎 Tier 3 | **`~/.claude/rules/common/testing.md` に「単複・閾値・時制で出力形式が変わる関数は N=0 / N=1 / N≥2 の 3 境界 variant 必須」guideline 追加 (PR #210 post-merge-feedback T3-2 採用)** | todo10.md | XS | なし (PR #210 で `drain_pipe_capped_reporting_n_plus_1_truncates_one_appends_summary` が当初 `"1 lines truncated"` 誤期待値で takt-fix auto-fix された実観測、N-1/N/N+1 境界値と直交する「N=1 単複境界 + ゼロ近傍」次元の guideline 化、順位 110 pure function test pattern と相補、派生プロジェクトに global 経由で自動波及、`feedback_global_config_backup` 適用必須) |
| 215 | 💎 Tier 3 | **`~/.claude/rules/common/coding-style.md` に「Defensive State Reset in State Machines」section 追加 (PR #214 post-merge-feedback T3-1 採用)** | todo18.md | S | なし (PR #214 round 2 で `finalize_initial_review_park` の `state.pr` / `state.repo` / `state.started_at` を `read_state()` 後に無条件上書きする pattern が CR Major #4 の fix として land、CR Major #1 (`head_commit`) + CR Major #2 (`review_recheck_count`) と同型の defensive reset が既に 3 field 適用済 = `review_recheck.rs` 内で複数 instance あり、将来の reviewer が「redundant」と誤判定して削除すると prior cycle の stale state が混入して silent bug 化、global rules への docs 追記で派生プロジェクト (techbook-ledger / auto-review-fix-vc) に自動波及、simplicity-review LLM が同 file を読むため "enforced via review" として機能し memory `feedback_no_unenforced_rules` 例外を満たす、`feedback_global_config_backup` 適用必須) |
| 216 | 🔧 Tier 2 | **`no-workstream-seq-names-in-config` lint rule 追加 — config comment 内 `PR-[0-9]+` ephemeral workstream sequence 検出 (PR #216 post-merge-feedback T1-1 採用) ★ Bundle 216-217** | todo18.md | S | なし (PR #216 で `hooks-config.toml` comment に "PR-1" / "PR-3" workstream sequence を書き込んだ違反を post-merge-feedback T1-1 で捕捉、rule⑥ `no-ephemeral-todo-reference` は `docs/todo*.md` file path のみ catch するが workstream sequence names は対象外、pattern `(?i)\bPR-[0-9]+\b` で `.toml`/`.yaml`/`.yml`/`.jsonc`/`.json` comment を対象、`#NNN` GitHub PR 表記は exception で除外、rule⑫ と同 pattern (TOML `test_coverage` meta field + main.rs test) で Effort S、順位 217 と同根 = 1 PR bundle 推奨。**Tier 列との不整合補足**: analyzer feedback report の `Tier 1: Hooks/Linter 改善` カテゴリは project Tier 1 (🚀 high-impact urgent) ではなく memory `feedback_tier_classification` の re-classification rule (mechanical enforcement = T2 / docs 修正 = T3) に従い project Tier 2 (🔧 tooling improvements) に再分類) |
| 217 | 💎 Tier 3 | **`~/.claude/rules/common/coding-style.md` § Cross-File Reference Lifecycle に config file comments の permanent artifact 扱い + workstream sequence 禁止例追加 (PR #216 post-merge-feedback T3-1 採用) ★ Bundle 216-217** | todo18.md | XS | なし (順位 216 lint rule と同根、本 task は文書層 = author 理解促進、機械層 (216) との 2 層防御、coding-style.md 既存 § Cross-File Reference Lifecycle は markdown 内 cross-ref を主想定で config file comments の permanent artifact 扱いが暗黙的、workstream sequence names (`PR-1`/`PR-3` 等) 禁止例 + GitHub PR numbers (`#NNN`) 代替を明示、派生プロジェクト (techbook-ledger / auto-review-fix-vc) へ global 経由で自動波及、`feedback_global_config_backup` 適用必須、順位 216 と 1 PR bundle 推奨) |
| 218 | 💎 Tier 3 | **ADR-039 § Bounded Lifetime + `~/.claude/rules/common/patterns.md` に provisional `enabled` 変更時の todo entry 必須化を追加 (PR #216 post-merge-feedback T3-2 採用)** | todo18.md | XS | なし (PR #216 で `weekly_review_reminder.enabled = true` の provisional 状態を config comment のみで tracking した silent aging risk を post-merge-feedback T3-2 で捕捉、ADR-039 6-point design checklist に「provisional `enabled` 変更時は `docs/todo*.md` に移行 tracking entry を作成」を追加、`~/.claude/rules/common/patterns.md` § Experimental Feature 設計時の参照必須 にも同旨 note 追加、Frequency Low (初観測) + Adoption Risk None で早期 codify、派生プロジェクト (techbook-ledger / auto-review-fix-vc) へ global 経由で自動波及、`feedback_global_config_backup` 適用必須) |
| 219 | 💎 Tier 3 | **`~/.claude/rules/common/development-workflow.md` § 設計 doc/実装の同期チェック に「commit description 言及 ≠ 実装完了」明文化 (PR #216 post-merge-feedback T3-3 採用)** | todo18.md | XS | なし (本 PR で「commit description で順位 N 言及 = 実装完了」naïve assumption から analyzer が 6 entry 削除計画を立てたが、grep で実体確認した結果 5 entry が正解 (順位 215 救出) の実観測、「PR commit description で順位 N や feature X を言及していても、実際のファイル変更を `jj diff` / `grep` で確認するまで completion 判定しない」guideline を追加、Frequency Low (初観測) + Severity Medium (analyzer / Claude 誤判定リスクが今後も継続)、派生プロジェクト (techbook-ledger / auto-review-fix-vc) へ global 経由で自動波及、`feedback_global_config_backup` 適用必須) |
Loading
Loading