From 357511b607db6bc9082cf997f428cf38cba71509 Mon Sep 17 00:00:00 2001 From: aloekun Date: Tue, 26 May 2026 19:30:42 +0900 Subject: [PATCH 1/4] =?UTF-8?q?docs(todo):=20PR=20#174=20post-merge-feedba?= =?UTF-8?q?ck=20=E6=8E=A1=E7=94=A8=204=20=E4=BB=B6=E3=82=92=20todo9.md=20/?= =?UTF-8?q?=20summary=20table=20=E3=81=AB=E7=99=BB=E9=8C=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #174 (Bundle 1) post-merge-feedback で採用判定された 4 件を docs/todo9.md に 新規エントリとして追加し、docs/todo-summary.md table に行追加。 - 順位 155 (T1 #1): cli-pr-monitor fix chain 末尾に空 commit 検査 + jj abandon step 追加 (PR #174 で観測した kqvluqyv 事例の構造化予防、Effort S) - 順位 156 (T2 #1): effective_patterns() の空 variant test 4 件直交化 (CR Minor finding 起点の 4 filter path 独立検証化、Effort S) - 順位 157 (T2 #2): Bundle 1 dogfood checklist 実行 — __test.ps1 block + override env 確認 (ADR-039 bounded lifetime data point #1、Effort XS) - 順位 158 (T3 #1): ADR-039 に bounded lifetime decision trigger 必須化パターン 追記 (PR #174 の 3-5 PR dogfood 実例を reusable rule に昇格、Effort S) --- docs/todo-summary.md | 4 ++ docs/todo9.md | 165 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+) diff --git a/docs/todo-summary.md b/docs/todo-summary.md index fd7234f..b4b1f2e 100644 --- a/docs/todo-summary.md +++ b/docs/todo-summary.md @@ -77,6 +77,10 @@ | 152 | 🔧 Tier 2 | **todo entry 削除時の事前 land 確認手順 — 順位 136 hook 拡張 or 独立 follow-up (PR #173 T2-1 採用、2026-05-26)** | todo9.md | XS-S | 順位 136 (working copy staleness + 既実装 grep) と同型機械強制、lifecycle 補完 = 順位 136 (add/edit 時) + 本タスク (delete 時)。PreToolUse hook で `docs/todo*.md` 削除時に対応 land commit を `jj log` で grep 検証、land 確認なら allow + 証跡出力、未確認なら warning (block しない)。順位 136 hook 統合 (~+15 行) or 独立 (~40 行) のいずれか、ADR-042 § Decision matrix 適用 (mechanizable + FP 低 + Adoption Risk None) | | 153 | 🔧 Tier 2 | **`review-harness-whole` facet 追加 — 観点 ① 独立 facet 化 (順位 8 follow-up、Phase B+1、2026-05-26 ユーザー合意) ★ 週次拡張** | todo9.md | S | 順位 8 Phase B land + 2-3 週 dogfood 後に着手判断 (extract 不要なら close)、順位 146-151 Bundle 既存ルール仕組み化の継続的発見源、architecture-whole から ① 観点を extract して context 圧迫回避 | | 154 | 🔧 Tier 2 | **`review-todo-whole` facet + aggregate 前 file size pre-step — 観点 ⑤ ⑦ 拡張 (順位 8 follow-up、Phase B+1、2026-05-26 ユーザー合意) ★ 週次拡張** | todo9.md | M | 順位 136 land + Phase B 2-3 週 dogfood 完了後着手、順位 95 / 147 と scope 整理必要 (CI 即時 vs 週次 batch)、ADR-031 3 層分離原則で file size は LLM 不要の Rust pre-step に分離 | +| 155 | 🚀 Tier 1 | **cli-pr-monitor fix chain 末尾に空 commit 検査 + `jj abandon` step 追加 (PR #174 T1-#1 採用)** | todo9.md | S | なし (PR #174 で `kqvluqyv` 空 commit が PR diff 汚染した実証ベース、`master..@` 範囲を `jj log` で sweep して機械強制、既存 `CleanupEmptyFixCommit` action の補完層) | +| 156 | 🔧 Tier 2 | **`effective_patterns()` の空 variant test 4 件直交化 (PR #174 T2-#1 採用)** | todo9.md | S | なし (4 filter path = element-level trim / element-level empty / collection-level empty / valid set retention を独立 test 化、1 path regression で 1 test failed の早期切り分け、`feedback_test_dry_antipattern.md` 適用) | +| 157 | 🔧 Tier 2 | **Bundle 1 dogfood checklist 実行 — `__test.ps1` block + override env 確認 (PR #174 T2-#2 採用、ADR-039 bounded lifetime data point #1)** | todo9.md | XS | なし (PR #174 PR body の未消化 dogfood、Bundle 2 PR merge 前の前提条件として消化、結果は Bundle 2 PR body に記録) | +| 158 | 💎 Tier 3 | **ADR-039 に「bounded lifetime に明示的 decision trigger (PR 数 / 日付 / 条件) 必須化」パターン追記 (PR #174 T3-#1 採用)** | todo9.md | S | なし (PR #174 が "3-5 PR の dogfood 後に判定" を code + config + PR body で明示した実例の reusable rule 化、formless な「未来の判定」を ADR レベルで禁止、PR #174 を実例 cite) | **戦略**: 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/todo9.md b/docs/todo9.md index 278c086..036125b 100644 --- a/docs/todo9.md +++ b/docs/todo9.md @@ -447,6 +447,171 @@ --- +### cli-pr-monitor fix chain 末尾に空 commit 検査 + `jj abandon` step を追加 (PR #174 T1-#1 採用) + +> **動機**: PR #174 で post-pr-monitor の `CleanupEmptyFixCommit` action 後に、別の空 commit (`kqvluqyv`) が祖父コミット位置に残存し、後続の Bundle 1 Minor fix push 時に PR diff を汚染する事象を観測。cleanup ロジックが「fix chain で直近 create された空 commit」のみ対象にしており、過去の空 commit を見逃す構造的欠陥が明らかになった。手動 `jj abandon` で 1 件解消したが、機械強制すべき。 +> +> **本タスクの位置づけ**: PR #174 post-merge-feedback Tier 1 #1 採用 (Severity Medium / Frequency Low / Effort S / Adoption Risk None)。cli-pr-monitor の cleanup phase に「`jj log --no-graph` で空 description の commit を検出 → 全て abandon」step を追加し、空 commit による PR diff 汚染を構造的に予防する。 +> +> **参照**: `.claude/feedback-reports/174.md` Tier 1 #1、PR #174 で観測した `kqvluqyv` 事例 (Bundle 1 fix loop 中に手動 abandon)、`src/cli-pr-monitor/src/` +> +> **実行優先度**: 🚀 **Tier 1** — Effort S。cli-pr-monitor fix chain への追加 step 1 件、機械強制で重複事故を防止。 + +#### 設計決定 (案) + +- 配置: `src/cli-pr-monitor/src/` の fix chain cleanup phase 末尾 (既存 `CleanupEmptyFixCommit` の後) +- 動作: + 1. `jj log -r 'master..@' --no-graph -T 'change_id ++ "\n" ++ description.first_line() ++ "\n---\n"'` で PR 範囲 commit を列挙 + 2. `description = ""` (空) の commit を filter + 3. 該当 commit を `jj abandon ` で順次 abandon +- scope 限定: `master..@` 範囲のみ (= PR に含まれる範囲)。master 以下は対象外 +- 既存 `CleanupEmptyFixCommit` との関係: 既存は直近 fix commit のみ対象、本 step は全範囲 sweep の補完層 +- fail-open: jj log / abandon の失敗時は warning ログのみで cleanup を継続 (push を block しない) + +#### 作業計画 + +- [ ] cli-pr-monitor の cleanup phase 実装箇所を特定 (`CleanupEmptyFixCommit` action の呼び出し元) +- [ ] 空 commit 列挙ロジック (jj log + description filter) を追加 +- [ ] abandon ループ + error handling 実装 +- [ ] test 拡充: 空 commit 0 件 / 1 件 / 複数件 / 非空 commit のみ / mixed +- [ ] `pnpm build:cli-pr-monitor` で release 生成 + dogfood (次の PR で同様の状況を作って動作確認) +- [ ] 本エントリ削除 + todo-summary.md 行削除 + +#### 完了基準 + +- post-pr-monitor の cleanup phase 完了時に PR 範囲内の空 commit が全て abandon される +- 既存 `CleanupEmptyFixCommit` action と non-regression +- dogfood で空 commit 自動 cleanup が動作確認される + +#### 詰まっている箇所 + +なし。Effort S、既存 cleanup phase への追加 step で副作用最小。 + +--- + +### `effective_patterns()` の空 variant test 4 件を直交化 (PR #174 T2-#1 採用) + +> **動機**: PR #174 で CR Minor finding (outside-diff-range) で「空文字要素を含む patterns で default fallback しない edge case」が指摘され、Minor fix push で `trim + filter blank` を追加した。fix 時に追加した test 3 件 (`only_blank_entries` / `filters_blank_entries_and_keeps_valid_ones` / `trims_whitespace_in_pattern_values`) は機能網羅しているが、各 filter path (element-level trim / element-level empty filter / collection-level empty filter) の直交検証が部分的。将来の `effective_patterns()` 変更時に 1 path だけ regression した場合、複数 test が同時失敗して原因切り分けが困難になる懸念。 +> +> **本タスクの位置づけ**: PR #174 post-merge-feedback Tier 2 #1 採用 (Severity Medium / Frequency Low / Effort S / Adoption Risk None)。4 variant (`all_blank` / `mixed_blank_and_valid` / `whitespace_padded` / `mixed_filter_to_empty`) を独立 test として直交化し、各 filter path の単独失敗で原因が即特定できる構造にする。memory `feedback_test_dry_antipattern.md` (テストで DRY を適用しない) の実例運用。 +> +> **参照**: `.claude/feedback-reports/174.md` Tier 2 #1、PR #174 CR Minor finding (outside-diff-range)、`src/cli-push-runner/src/stages/scratch_file_warning.rs` (現状の 3 test) +> +> **実行優先度**: 🔧 **Tier 2** — Effort S。test 拡充 1 ファイル、~30 行追加。 + +#### 設計決定 (案) + +- 配置: `src/cli-push-runner/src/stages/scratch_file_warning.rs` の `#[cfg(test)] mod tests` 内 +- 4 variant の直交 test: + 1. **all_blank**: `patterns = ["", " ", "\t"]` → 全要素 blank、collection-level empty filter で default fallback + 2. **mixed_blank_and_valid**: `patterns = ["", "__*", " ", "_tmp_*"]` → element-level filter で blank 除去、残りが valid set + 3. **whitespace_padded**: `patterns = [" __* "]` → element-level trim で whitespace 除去、残った "__*" が valid + 4. **mixed_filter_to_empty**: `patterns = [" ", "\t"]` → element-level filter で全消去、collection-level empty で default +- 既存 3 test との関係: 既存 test の一部 (例: `only_blank_entries`, `filters_blank_entries_and_keeps_valid_ones`) は variant 1, 2 と機能重複するが、命名を「filter path 直交」に揃えるリネーム or 統合を判断 +- DRY anti-pattern を避け、各 variant は独立 fixture で記述 (memory `feedback_test_dry_antipattern.md`) + +#### 作業計画 + +- [ ] 既存 3 test の filter path カバレッジを確認 (どの path を test しているか map) +- [ ] 4 直交 variant の test 関数を追加 (既存と命名衝突しないよう調整) +- [ ] 既存 test と新規 test の重複検出 → 既存を残しつつ補完 or 既存をリネーム +- [ ] cargo test で 4 件 pass 確認 +- [ ] 本エントリ削除 + todo-summary.md 行削除 + +#### 完了基準 + +- 4 filter path を各 1 test で独立検証 +- 1 path のみ regression した場合に 1 test だけ failed する構造 (memory rule 「early-return guard test 分離」と同思想) +- 既存 test と non-regression + +#### 詰まっている箇所 + +なし。Effort S、test 拡充のみで実装変更不要。 + +--- + +### Bundle 1 dogfood checklist 実行 — `__test.ps1` block + override env 確認 (PR #174 T2-#2 採用、ADR-039 bounded lifetime data point #1) + +> **動機**: PR #174 で実装した `scratch_file_warning` stage は ADR-039 § 3 Bounded lifetime 準拠で「3-5 PR の dogfood 後に default-ON 昇格 or 却下を判定」する設計。PR #174 の PR body に未消化の dogfood checklist が残っており (`__test.ps1` を意図的に作って push し block 動作確認 / override env でバイパス確認)、これが ADR-039 bounded lifetime の初回データポイント。次の PR (Bundle 2 等) merge 前の前提条件として消化が必要。 +> +> **本タスクの位置づけ**: PR #174 post-merge-feedback Tier 2 #2 採用 (Severity Low / Frequency Low / Effort XS / Adoption Risk None)。manual operation で完結、Bundle 1 自身の運用検証 + ADR-039 bounded lifetime 体系の初回稼働確認。 +> +> **参照**: `.claude/feedback-reports/174.md` Tier 2 #2、PR #174 PR body の Test Plan unchecked items、`docs/adr/adr-039-experimental-feature-standard-pattern.md` § 3 Bounded lifetime、`src/cli-push-runner/src/stages/scratch_file_warning.rs` (`SCRATCH_FILE_WARNING_OVERRIDE` env) +> +> **実行優先度**: 🔧 **Tier 2** — Effort XS。手動 dogfood 1 セット、~10 分。 + +#### 設計決定 (案) + +- 手順: + 1. ローカル working dir に `__test_dummy.ps1` (or `.txt`) を作成 (中身は無害な dummy) + 2. `jj describe -m "test: scratch hook dogfood"` 等で commit + 3. `pnpm push` を実行 → scratch_file_warning stage が block する (EXIT_SCRATCH_FILE_WARNING = 6) を確認 + 4. `$env:SCRATCH_FILE_WARNING_OVERRIDE = "1"; pnpm push` で override → 通過確認 + 5. dogfood 完了後、`__test_dummy.ps1` ファイル削除 + commit abandon で working dir clean +- 記録: dogfood 結果 (block message / override 動作 / false positive 有無) を Bundle 2 PR body に「ADR-039 bounded lifetime data point #1」として記載 +- 注意: 本 dogfood は本リポジトリで実施。派生プロジェクトへの deploy 後の dogfood は別タスク (派生プロジェクト側の bounded lifetime data point として記録) + +#### 作業計画 + +- [ ] `__test_dummy.ps1` を working dir に作成 +- [ ] `jj describe + pnpm push` で block 動作確認 +- [ ] `$env:SCRATCH_FILE_WARNING_OVERRIDE = "1"; pnpm push` で override 動作確認 +- [ ] cleanup: `__test_dummy.ps1` 削除 + commit abandon +- [ ] 結果を Bundle 2 PR body に記録 +- [ ] 本エントリ削除 + todo-summary.md 行削除 + +#### 完了基準 + +- block 動作: scratch_file_warning stage が `__test_dummy.ps1` を検出し EXIT 6 で push を block する +- override 動作: env var 設定後に同 stage を通過、push が成功する +- ADR-039 bounded lifetime data point #1 が記録される + +#### 詰まっている箇所 + +なし。Effort XS、manual operation で完結。 + +--- + +### ADR-039 に「bounded lifetime に明示的 decision trigger 必須化」パターンを追記 (PR #174 T3-#1 採用) + +> **動機**: PR #174 で `scratch_file_warning` stage の bounded lifetime を「3-5 PR の dogfood 後に default-ON 昇格 or 却下を判定」と code comment + config TOML + PR body で明示した。この「明示的 decision trigger (PR 数 / 日付 / 条件)」を ADR-039 § 3 Bounded lifetime に reusable pattern として codify することで、将来の experimental feature 実装時にも同型の trigger 明文化が確実になる (現状は ADR-039 既存 text が trigger 形式を任意としており、formless な「未来の判定」で永遠の試験運用化リスク残存)。 +> +> **本タスクの位置づけ**: PR #174 post-merge-feedback Tier 3 #1 採用 (Severity Low / Frequency Medium / Effort S / Adoption Risk None)。Frequency Medium = 実験的機能は複数 PR にまたがって登場する性質。`feedback_no_unenforced_rules.md` 例外 = ADR (= 設計判断 doc) への追加で機械強制ではなく reviewer / Claude の judgment 補助、PR #174 で実装実例あり (= 既存実践の明文化)。 +> +> **参照**: `.claude/feedback-reports/174.md` Tier 3 #1、PR #174 (`push-runner-config.toml` の `[scratch_file_warning]` section コメント + `scratch_file_warning.rs` module doc の "3-5 PR dogfood" 記述)、`docs/adr/adr-039-experimental-feature-standard-pattern.md` § 3 Bounded lifetime +> +> **実行優先度**: 💎 **Tier 3** — Effort S。ADR への 1 paragraph 追記。 + +#### 設計決定 (案) + +- 配置: `docs/adr/adr-039-experimental-feature-standard-pattern.md` § 3 Bounded lifetime の既存 bullet list 末尾に「decision trigger の明示化要件」を追加 +- 追記内容案 (3-5 行): + - 試験期限を「期限 OR 条件 OR PR 数」のいずれかで明示的 trigger 化する (任意の "未来" 判定は禁止) + - 例: 「3-5 PR の dogfood 後に判定」(PR #174 `scratch_file_warning` の実例)、「6 ヶ月経過で却下とみなす」(既存 text)、「false positive 5 件以上で却下」等 + - decision trigger は config / code comment / PR body のいずれかで永続記録 (ephemeral 計画書だけでは retire 時に dead pointer 化リスク) + - 既存試験運用 ADR (014/023/025/029/030/031/033/034/036/037/038) の bounded lifetime も再評価し、formless な記述があれば後続 PR で追補 +- PR #174 を実例参照として ADR-039 内に 1-line cite 推奨 + +#### 作業計画 + +- [ ] ADR-039 § 3 Bounded lifetime の現行 text を確認 +- [ ] 「decision trigger 明示化要件」3-5 行を追記 +- [ ] PR #174 を実例として 1-line 引用 +- [ ] markdownlint clean 確認 +- [ ] 本エントリ削除 + todo-summary.md 行削除 + +#### 完了基準 + +- ADR-039 § 3 Bounded lifetime に「明示的 decision trigger 必須化」要件が明文化される +- 将来の experimental feature 実装時に formless な bounded lifetime 記述を ADR 違反として指摘可能になる +- PR #174 が実例として ADR から逆引きできる + +#### 詰まっている箇所 + +なし。Effort S、ADR への追記のみで副作用最小。 + +--- + ## 既知課題 (記録のみ、本セッションで未対応) (現時点で本ファイルへの既知課題は無し。docs/todo8.md 末尾の post-merge-feedback workflow stale marker 問題を参照。) From a800ff54df1d28fb986798a4b191cd3487112a1f Mon Sep 17 00:00:00 2001 From: aloekun Date: Tue, 26 May 2026 19:42:03 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat(cli-push-runner):=20Bundle=202=20?= =?UTF-8?q?=E2=80=94=20=E9=A0=86=E4=BD=8D=202=20bookmark=20check=20stage?= =?UTF-8?q?=20+=20=E9=A0=86=E4=BD=8D=20156/158=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 順位 2 (PR #85 T1-3) jj bookmark 未設定 early-exit を実装。jj 環境では新規ブランチ で bookmark を作成し忘れる落とし穴があり、PR #85 で初回 `pnpm push` が `Nothing changed` で終了し 158s かけた quality_gate + takt review が無駄になった 事象の構造的予防。 ## 実装 (順位 2) - src/cli-push-runner/src/stages/bookmark_check.rs (新規): 新 stage - src/cli-push-runner/src/stages/mod.rs: module register - src/cli-push-runner/src/main.rs: - Stage -1 として run_bookmark_check を pipeline 最早期に追加 - EXIT_BOOKMARK_MISSING = 7 を新設 - run_pipeline が 50 行制限を超えたため run_pre_checks を抽出して責務分離 設計: - 配置: scratch_file_warning より前の最早期 stage (push 自体不可能な状態を最優先で fail-fast) - 検出: `jj bookmark list` で非 trunk bookmark の有無を確認 (lib_jj_helpers::is_trunk_bookmark で filter) - 失敗時挙動: error 終了 + 推奨コマンド (`jj bookmark create -r @`) 提示 - fail-open: jj 実行失敗時は warning ログのみで push を続行 (jj 不調で push 自体を止めない) - 設計上の non-config: jj git push が bookmark 必須仕様のため、本 stage をバイパスする 正当な use case なし。`[bookmark_check]` config section は追加せず常に有効 ## Bundled docs - 順位 156: effective_patterns() の空 variant test 4 件直交化。既存 3 test を filter path 直交命名 (all_blank / mixed_blank_and_valid / whitespace_padded) にリネーム + 4 番目 variant (mixed_filter_to_empty) を新規追加。1 path regression で 1 test だけ failed する構造 (memory feedback_test_dry_antipattern.md 適用)。 - 順位 158: ADR-039 § 3 Bounded lifetime に「明示的 decision trigger 必須化」要件を 追記。PR #174 を実例 (Bundle 1 = 3-5 PR dogfood trigger) として cite、formless な 「未来の判定」記述を ADR レベルで禁止する reusable pattern として codify。 ## Tests cargo test --manifest-path src/cli-push-runner/Cargo.toml: 133 passed 新規 test: bookmark_check 6 件 + effective_patterns 1 件 (variant #4) --- ...9-experimental-feature-standard-pattern.md | 14 ++ src/cli-push-runner/src/main.rs | 37 +++- .../src/stages/bookmark_check.rs | 166 ++++++++++++++++++ src/cli-push-runner/src/stages/mod.rs | 2 + .../src/stages/scratch_file_warning.rs | 17 +- 5 files changed, 223 insertions(+), 13 deletions(-) create mode 100644 src/cli-push-runner/src/stages/bookmark_check.rs diff --git a/docs/adr/adr-039-experimental-feature-standard-pattern.md b/docs/adr/adr-039-experimental-feature-standard-pattern.md index 967b6c2..c0fc254 100644 --- a/docs/adr/adr-039-experimental-feature-standard-pattern.md +++ b/docs/adr/adr-039-experimental-feature-standard-pattern.md @@ -65,6 +65,20 @@ - **継続**: 期限内に判定が出ない場合、計画書側に新たな期限と判定基準を記述 (1 回まで延長可) - bounded lifetime を欠いた試験運用は「永遠の試験運用」化し、累積複雑度の温床になる +#### 明示的 decision trigger の必須化 (PR #174 採用、Bundle 1 実例) + +試験期限は「期限 OR 条件 OR PR 数」のいずれかで **明示的 decision trigger 化** する。任意の "未来の判定" (= 形式不明の「いずれ判断する」記述) は禁止し、reviewer / 後継 Claude session が判定タイミングを一意に決定できる構造にする: + +- **PR 数ベース**: 「N-M PR の dogfood 後に判定」 (例: PR #174 `scratch_file_warning` = 「3-5 PR の dogfood 後に default-ON 昇格 or 却下を判定」) +- **期限ベース**: 「YYYY-MM-DD 経過で却下とみなす」「6 ヶ月経過しても採用判定未達なら却下」 +- **条件ベース**: 「false positive 5 件以上で却下」「latency p95 が X ms 超で却下」 + +decision trigger は **config (TOML コメント) / code comment (module doc) / PR body** のいずれかで永続記録する。ephemeral 計画書 (`docs/-analysis.md` 等) だけに記述すると retire 時に dead pointer 化するため不可 ([coding-style.md § Cross-File Reference Lifecycle](../../CLAUDE.md) 参照)。 + +参考実装: PR #174 では `push-runner-config.toml` の `[scratch_file_warning]` section コメント + `scratch_file_warning.rs` module doc の 2 箇所で trigger を明示 (永続性確保 + 検索容易性)。 + +既存試験運用 ADR (014/023/025/029/030/031/033/034/036/037/038) の bounded lifetime 記述に formless な箇所があれば、後続 PR で個別に追補する。 + ## 帰結 ### 利点 diff --git a/src/cli-push-runner/src/main.rs b/src/cli-push-runner/src/main.rs index 6c3b46a..072324d 100644 --- a/src/cli-push-runner/src/main.rs +++ b/src/cli-push-runner/src/main.rs @@ -1,6 +1,7 @@ //! Push Runner — takt ベースの pre-push パイプライン //! //! pnpm push から呼び出され、以下のステージを実行する: +//! Stage -1: bookmark_check — 非 trunk bookmark の存在を確認 (順位 2) //! Stage 0: scratch_file_warning — `__*` 等の scratch ファイル混入を検査 (順位 1) //! Stage 1: quality_gate — TOML で定義されたコマンド群をグループ間で並列実行 //! Stage 1.5: diff — jj diff を取得しファイルに書き出し(reviewers が Read で参照) @@ -17,6 +18,7 @@ //! 4 - 設定エラー //! 5 - diff 取得失敗 //! 6 - scratch_file_warning 検出 (override env で bypass 可能) +//! 7 - bookmark_check 非 trunk bookmark 未設定 mod config; mod log; @@ -28,8 +30,8 @@ use std::time::Instant; use config::load_config; use log::log_info; use stages::{ - run_diff, run_lint_screen, run_push, run_quality_gate, run_scratch_file_warning, run_takt, - DiffResult, + run_bookmark_check, run_diff, run_lint_screen, run_push, run_quality_gate, + run_scratch_file_warning, run_takt, DiffResult, }; const EXIT_SUCCESS: i32 = 0; @@ -39,6 +41,7 @@ const EXIT_PUSH_FAILURE: i32 = 3; const EXIT_CONFIG_ERROR: i32 = 4; const EXIT_DIFF_FAILURE: i32 = 5; const EXIT_SCRATCH_FILE_WARNING: i32 = 6; +const EXIT_BOOKMARK_MISSING: i32 = 7; /// diff stage を実行し lint-screen を呼び出す。 /// Ok(skip_takt) で成功、 Err(exit_code) で pipeline 中断。 @@ -63,6 +66,26 @@ fn run_diff_and_lint_screen(config: &config::Config) -> Result { Ok(false) } +/// quality_gate より前の事前チェック (bookmark / scratch file) を実行する。 +/// 失敗時は exit code を Err で返し、pipeline を中断する。 +fn run_pre_checks(config: &config::Config) -> Result<(), i32> { + if !run_bookmark_check() { + log_info( + "パイプライン中断: 非 trunk bookmark が見つかりません。\ + `jj bookmark create -r @` で bookmark を作成して再実行してください。", + ); + return Err(EXIT_BOOKMARK_MISSING); + } + if !run_scratch_file_warning(config.scratch_file_warning.as_ref()) { + log_info( + "パイプライン中断: scratch ファイル検出。.gitignore 修正 / ファイル削除 / \ + SCRATCH_FILE_WARNING_OVERRIDE=1 のいずれかで再実行してください。", + ); + return Err(EXIT_SCRATCH_FILE_WARNING); + } + Ok(()) +} + fn run_pipeline() -> i32 { let start = Instant::now(); @@ -76,17 +99,13 @@ fn run_pipeline() -> i32 { let has_diff = config.diff.is_some(); log_info(&format!( - "パイプライン開始: scratch → quality_gate → {} takt ({}) → push", + "パイプライン開始: bookmark → scratch → quality_gate → {} takt ({}) → push", if has_diff { "diff →" } else { "" }, config.takt.workflow, )); - if !run_scratch_file_warning(config.scratch_file_warning.as_ref()) { - log_info( - "パイプライン中断: scratch ファイル検出。.gitignore 修正 / ファイル削除 / \ - SCRATCH_FILE_WARNING_OVERRIDE=1 のいずれかで再実行してください。", - ); - return EXIT_SCRATCH_FILE_WARNING; + if let Err(code) = run_pre_checks(&config) { + return code; } if !run_quality_gate(&config.quality_gate) { diff --git a/src/cli-push-runner/src/stages/bookmark_check.rs b/src/cli-push-runner/src/stages/bookmark_check.rs new file mode 100644 index 0000000..8080e1d --- /dev/null +++ b/src/cli-push-runner/src/stages/bookmark_check.rs @@ -0,0 +1,166 @@ +//! Bookmark check stage — 順位 2 (PR #85 T1-3) +//! +//! `jj git push` は bookmark が必要だが、jj 環境では新規ブランチで bookmark を +//! 作成し忘れる落とし穴がある (PR #85 で初回 `pnpm push` が bookmark 未設定 → +//! `Nothing changed` で終了し、158s かけた quality_gate + takt review が無駄に +//! なった実証ベース)。本 stage は pipeline 最早期 (`scratch_file_warning` の前) +//! で `jj bookmark list` を確認し、非 trunk bookmark が無ければ即 error 終了して +//! 後続 stage の無駄実行を防ぐ。 +//! +//! Stage 配置: `run_pipeline` の最早期 (scratch_file_warning の前)。bookmark 不在 +//! は push 自体が不可能な状態のため、最優先で fail-fast する。 +//! +//! fail-open: `jj bookmark list` 実行失敗 (timeout / 起動失敗) 時は warning ログ +//! のみで push を続行する。jj 不調で push 自体を止めない設計。 +//! +//! 設計上の non-config: `jj git push` は bookmark を必須とする仕様で、本 stage を +//! バイパスする正当な use case は存在しない。よって `[bookmark_check]` config +//! section は追加せず、常に有効。 + +use std::process::Command; + +use lib_jj_helpers::is_trunk_bookmark; + +use crate::log::{log_info, log_stage}; + +const JJ_TIMEOUT_SECS: u64 = 30; + +/// `jj bookmark list` で非 trunk なローカル bookmark の存在を確認し、 +/// push を続行してよいか (= 非 trunk bookmark が 1 件以上存在) を返す。 +/// +/// fail-open: jj 実行失敗時は warning ログのみで true を返し、push 自体は止めない。 +pub(crate) fn run_bookmark_check() -> bool { + let raw = match run_jj_bookmark_list() { + Ok(output) => output, + Err(e) => { + log_info(&format!( + "bookmark_check: jj bookmark list 失敗、検査を skip して push を続行します: {}", + e + )); + return true; + } + }; + let bookmarks = parse_non_trunk_bookmarks(&raw); + if bookmarks.is_empty() { + log_stage("bookmark", "ローカル bookmark (非 trunk) が見つかりません"); + log_info( + " push 不可: `jj git push` は bookmark が必要です。\n \ + 対処: `jj bookmark create -r @` で bookmark を作成して再実行してください\n \ + 例: `jj bookmark create feat/my-feature -r @`", + ); + return false; + } + log_stage( + "bookmark", + &format!( + "非 trunk bookmark 検出 ({} 件): {}", + bookmarks.len(), + bookmarks.join(", ") + ), + ); + true +} + +fn parse_non_trunk_bookmarks(raw: &str) -> Vec { + raw.lines() + .filter(|line| !line.starts_with(' ') && !line.starts_with('\t')) + .filter_map(|line| line.split(':').next()) + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty() && !is_trunk_bookmark(s)) + .collect() +} + +fn run_jj_bookmark_list() -> Result { + use std::process::Stdio; + + let mut child = Command::new("jj") + .args(["bookmark", "list"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .map_err(|e| format!("jj bookmark list 起動失敗: {}", e))?; + + let stdout_handle = + crate::runner::drain_pipe(child.stdout.take().expect("stdout must be piped")); + let stderr_handle = + crate::runner::drain_pipe(child.stderr.take().expect("stderr must be piped")); + + let status = crate::runner::wait_with_timeout("jj bookmark list", &mut child, JJ_TIMEOUT_SECS) + .map_err(|e| format!("jj bookmark list wait 失敗: {}", e))?; + + let stdout = stdout_handle.join().unwrap_or_default(); + let stderr = stderr_handle.join().unwrap_or_default(); + + match status { + None => Err(format!("jj bookmark list タイムアウト ({}s)", JJ_TIMEOUT_SECS)), + Some(s) if s.success() => Ok(stdout), + Some(_) => Err(stderr.trim().to_string()), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_non_trunk_typical_output() { + let output = "\ +feat/xyz: abc1234 add feature + @origin: abc1234 add feature +main: def5678 initial + @origin: def5678 initial +"; + assert_eq!(parse_non_trunk_bookmarks(output), vec!["feat/xyz"]); + } + + #[test] + fn parse_non_trunk_multiple_feature_bookmarks() { + let output = "\ +feat/a: 111 desc +feat/b: 222 desc +main: 333 desc +"; + assert_eq!(parse_non_trunk_bookmarks(output), vec!["feat/a", "feat/b"]); + } + + #[test] + fn parse_non_trunk_only_trunk_returns_empty() { + let output = "main: abc123 desc\nmaster: def456 desc\n"; + assert!(parse_non_trunk_bookmarks(output).is_empty()); + } + + #[test] + fn parse_non_trunk_empty_output_returns_empty() { + assert!(parse_non_trunk_bookmarks("").is_empty()); + } + + #[test] + fn parse_non_trunk_skips_indented_remote_lines() { + let output = "\ +feat/xyz: abc1234 desc + @origin: abc1234 desc + @upstream: abc1234 desc +"; + assert_eq!(parse_non_trunk_bookmarks(output), vec!["feat/xyz"]); + } + + #[test] + fn parse_non_trunk_filters_out_master_and_main() { + let output = "\ +feat/branch1: abc desc +master: def desc +feat/branch2: ghi desc +main: jkl desc +"; + assert_eq!( + parse_non_trunk_bookmarks(output), + vec!["feat/branch1", "feat/branch2"] + ); + } + + #[test] + fn parse_non_trunk_handles_single_feature_bookmark() { + let output = "feat/single: abc desc\n"; + assert_eq!(parse_non_trunk_bookmarks(output), vec!["feat/single"]); + } +} diff --git a/src/cli-push-runner/src/stages/mod.rs b/src/cli-push-runner/src/stages/mod.rs index 9dace47..b5d5021 100644 --- a/src/cli-push-runner/src/stages/mod.rs +++ b/src/cli-push-runner/src/stages/mod.rs @@ -1,3 +1,4 @@ +mod bookmark_check; mod diff; mod lint_screen; mod push; @@ -6,6 +7,7 @@ mod quality_gate; mod scratch_file_warning; mod takt; +pub(crate) use bookmark_check::run_bookmark_check; pub(crate) use diff::{run_diff, DiffResult}; pub(crate) use lint_screen::run_lint_screen; pub(crate) use push::run_push; diff --git a/src/cli-push-runner/src/stages/scratch_file_warning.rs b/src/cli-push-runner/src/stages/scratch_file_warning.rs index 2ccf9a9..2fd5bfa 100644 --- a/src/cli-push-runner/src/stages/scratch_file_warning.rs +++ b/src/cli-push-runner/src/stages/scratch_file_warning.rs @@ -478,16 +478,16 @@ mod tests { } #[test] - fn effective_patterns_default_when_only_blank_entries() { + fn effective_patterns_all_blank_falls_back_to_default() { let config = ScratchFileWarningConfig { enabled: Some(true), - patterns: Some(vec!["".to_string(), " ".to_string()]), + patterns: Some(vec!["".to_string(), " ".to_string(), "\t".to_string()]), }; assert_eq!(effective_patterns(Some(&config)), vec!["__*".to_string()]); } #[test] - fn effective_patterns_filters_blank_entries_and_keeps_valid_ones() { + fn effective_patterns_mixed_blank_and_valid_keeps_only_valid() { let config = ScratchFileWarningConfig { enabled: Some(true), patterns: Some(vec![ @@ -504,11 +504,20 @@ mod tests { } #[test] - fn effective_patterns_trims_whitespace_in_pattern_values() { + fn effective_patterns_whitespace_padded_is_trimmed() { let config = ScratchFileWarningConfig { enabled: Some(true), patterns: Some(vec![" __* ".to_string()]), }; assert_eq!(effective_patterns(Some(&config)), vec!["__*".to_string()]); } + + #[test] + fn effective_patterns_mixed_filter_to_empty_falls_back_to_default() { + let config = ScratchFileWarningConfig { + enabled: Some(true), + patterns: Some(vec![" ".to_string(), "\t".to_string()]), + }; + assert_eq!(effective_patterns(Some(&config)), vec!["__*".to_string()]); + } } From 8ed077fd2c717849b28a60aacc512cef6d4ea625 Mon Sep 17 00:00:00 2001 From: aloekun Date: Tue, 26 May 2026 19:46:03 +0900 Subject: [PATCH 3/4] =?UTF-8?q?docs(todo):=20Bundle=202=20=E5=AE=8C?= =?UTF-8?q?=E4=BA=86=E3=81=AB=E4=BC=B4=E3=81=84=20=E9=A0=86=E4=BD=8D=202?= =?UTF-8?q?=20/=20156=20/=20158=20=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/todo-summary.md | 3 -- docs/todo2.md | 33 ------------------ docs/todo9.md | 81 -------------------------------------------- 3 files changed, 117 deletions(-) diff --git a/docs/todo-summary.md b/docs/todo-summary.md index b4b1f2e..af81126 100644 --- a/docs/todo-summary.md +++ b/docs/todo-summary.md @@ -11,7 +11,6 @@ | 順位 | Tier | タスク | ファイル | 工数 | 依存 | |---|---|---|---|---|---| -| 2 | 🚀 Tier 1 | `cli-push-runner` jj bookmark 未設定 early-exit (PR #85 T1-3) | todo2.md | S | なし | | 5 | 🚀 Tier 1 | **AI 生成一時スクリプト pattern の pre-push 検出 (PR #88 T1-2)** | todo3.md | Small | 順位 1 と関連 (要擦り合わせ) | | 6 | 🚀 Tier 1 | ADR-032 PR-pre: GitHub Branch Protection 整備 | todo2.md | 設定のみ | なし (依存タスクは完了済) | | 8 | 🔧 Tier 2 | 週次レビュー (ADR-031) Phase B 実装 — 7 観点責務 mapping 確定 (① ハーネス遵守 + ⑥ テストロジック を MVP 優先、2026-05-26 ユーザー合意) | todo.md | 中-高 | 順位 20 compensating check 前提 + 順位 136 land 先行推奨 (観点 ⑤ 責務分離) | @@ -78,9 +77,7 @@ | 153 | 🔧 Tier 2 | **`review-harness-whole` facet 追加 — 観点 ① 独立 facet 化 (順位 8 follow-up、Phase B+1、2026-05-26 ユーザー合意) ★ 週次拡張** | todo9.md | S | 順位 8 Phase B land + 2-3 週 dogfood 後に着手判断 (extract 不要なら close)、順位 146-151 Bundle 既存ルール仕組み化の継続的発見源、architecture-whole から ① 観点を extract して context 圧迫回避 | | 154 | 🔧 Tier 2 | **`review-todo-whole` facet + aggregate 前 file size pre-step — 観点 ⑤ ⑦ 拡張 (順位 8 follow-up、Phase B+1、2026-05-26 ユーザー合意) ★ 週次拡張** | todo9.md | M | 順位 136 land + Phase B 2-3 週 dogfood 完了後着手、順位 95 / 147 と scope 整理必要 (CI 即時 vs 週次 batch)、ADR-031 3 層分離原則で file size は LLM 不要の Rust pre-step に分離 | | 155 | 🚀 Tier 1 | **cli-pr-monitor fix chain 末尾に空 commit 検査 + `jj abandon` step 追加 (PR #174 T1-#1 採用)** | todo9.md | S | なし (PR #174 で `kqvluqyv` 空 commit が PR diff 汚染した実証ベース、`master..@` 範囲を `jj log` で sweep して機械強制、既存 `CleanupEmptyFixCommit` action の補完層) | -| 156 | 🔧 Tier 2 | **`effective_patterns()` の空 variant test 4 件直交化 (PR #174 T2-#1 採用)** | todo9.md | S | なし (4 filter path = element-level trim / element-level empty / collection-level empty / valid set retention を独立 test 化、1 path regression で 1 test failed の早期切り分け、`feedback_test_dry_antipattern.md` 適用) | | 157 | 🔧 Tier 2 | **Bundle 1 dogfood checklist 実行 — `__test.ps1` block + override env 確認 (PR #174 T2-#2 採用、ADR-039 bounded lifetime data point #1)** | todo9.md | XS | なし (PR #174 PR body の未消化 dogfood、Bundle 2 PR merge 前の前提条件として消化、結果は Bundle 2 PR body に記録) | -| 158 | 💎 Tier 3 | **ADR-039 に「bounded lifetime に明示的 decision trigger (PR 数 / 日付 / 条件) 必須化」パターン追記 (PR #174 T3-#1 採用)** | todo9.md | S | なし (PR #174 が "3-5 PR の dogfood 後に判定" を code + config + PR body で明示した実例の reusable rule 化、formless な「未来の判定」を ADR レベルで禁止、PR #174 を実例 cite) | **戦略**: 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/todo2.md b/docs/todo2.md index a72d283..7147149 100644 --- a/docs/todo2.md +++ b/docs/todo2.md @@ -362,39 +362,6 @@ Phase 観測 (4-6 週) Phase 2 (任意、段階的緩和) ``` -### `cli-push-runner` jj bookmark 未設定 early-exit (PR #85 T1-3) - -> **動機**: PR #85 の初回 `pnpm push` で bookmark 未設定 → `jj git push` が `Nothing changed` で終了し、158s かけて走った Quality Gate + takt review がすべて無駄になった。jj 環境特有の落とし穴で、決定論的に防止可能。 -> -> **本タスクの位置づけ**: `cli-push-runner` でパイプライン開始時 (quality_gate より前) に bookmark 存在チェックを追加し、未設定なら early-exit で Quality Gate を回避する。 -> -> **参照**: `.claude/feedback-reports/85.md` Tier 1 #3 -> -> **実行優先度**: 🚀 **Tier 1** — S 工数、daily efficiency への直接効果 (失敗 push 1 回あたり 2-3 分 + takt review token 消費を節約)。 - -#### 設計決定 (案) - -- 検出位置: `cli-push-runner` の早期 stage (quality_gate より前、ADR-022 の責務分離原則に従う) -- 検出方法: `jj bookmark list` で現在の `@` に紐付く bookmark の有無を確認 -- 失敗時挙動: bookmark 未設定なら error 終了 + 推奨コマンド (`jj bookmark create -r @`) を提示 - -#### 作業計画 - -- [ ] `src/cli-push-runner/src/main.rs` または stages/ に bookmark check stage 追加 -- [ ] エラーメッセージで具体的な解決手順を提示 -- [ ] dogfood: 意図的に bookmark なし状態で `pnpm push` し、early-exit を確認 -- [ ] 派生プロジェクトへ deploy -- [ ] 本 todo2.md エントリを削除 - -#### 完了基準 - -- bookmark 未設定で `pnpm push` が即座に error 終了 (Quality Gate 不実行) -- 解決手順がエラーメッセージに含まれる - -#### 詰まっている箇所 - -なし - ### `cli-pr-monitor` プロセス正常終了の integration test (PR #85 T2-2) > **動機**: PR #85 の `pnpm create-pr` 完了後、`cli-pr-monitor.exe` がバックグラウンドで残留し手動 `taskkill` が必要だった。termination シグナル処理またはタイムアウトの問題の可能性があり、本セッションで初めて顕在化。回帰テストで継続的に検出できるようにする。 diff --git a/docs/todo9.md b/docs/todo9.md index 036125b..8c3a33e 100644 --- a/docs/todo9.md +++ b/docs/todo9.md @@ -489,47 +489,6 @@ --- -### `effective_patterns()` の空 variant test 4 件を直交化 (PR #174 T2-#1 採用) - -> **動機**: PR #174 で CR Minor finding (outside-diff-range) で「空文字要素を含む patterns で default fallback しない edge case」が指摘され、Minor fix push で `trim + filter blank` を追加した。fix 時に追加した test 3 件 (`only_blank_entries` / `filters_blank_entries_and_keeps_valid_ones` / `trims_whitespace_in_pattern_values`) は機能網羅しているが、各 filter path (element-level trim / element-level empty filter / collection-level empty filter) の直交検証が部分的。将来の `effective_patterns()` 変更時に 1 path だけ regression した場合、複数 test が同時失敗して原因切り分けが困難になる懸念。 -> -> **本タスクの位置づけ**: PR #174 post-merge-feedback Tier 2 #1 採用 (Severity Medium / Frequency Low / Effort S / Adoption Risk None)。4 variant (`all_blank` / `mixed_blank_and_valid` / `whitespace_padded` / `mixed_filter_to_empty`) を独立 test として直交化し、各 filter path の単独失敗で原因が即特定できる構造にする。memory `feedback_test_dry_antipattern.md` (テストで DRY を適用しない) の実例運用。 -> -> **参照**: `.claude/feedback-reports/174.md` Tier 2 #1、PR #174 CR Minor finding (outside-diff-range)、`src/cli-push-runner/src/stages/scratch_file_warning.rs` (現状の 3 test) -> -> **実行優先度**: 🔧 **Tier 2** — Effort S。test 拡充 1 ファイル、~30 行追加。 - -#### 設計決定 (案) - -- 配置: `src/cli-push-runner/src/stages/scratch_file_warning.rs` の `#[cfg(test)] mod tests` 内 -- 4 variant の直交 test: - 1. **all_blank**: `patterns = ["", " ", "\t"]` → 全要素 blank、collection-level empty filter で default fallback - 2. **mixed_blank_and_valid**: `patterns = ["", "__*", " ", "_tmp_*"]` → element-level filter で blank 除去、残りが valid set - 3. **whitespace_padded**: `patterns = [" __* "]` → element-level trim で whitespace 除去、残った "__*" が valid - 4. **mixed_filter_to_empty**: `patterns = [" ", "\t"]` → element-level filter で全消去、collection-level empty で default -- 既存 3 test との関係: 既存 test の一部 (例: `only_blank_entries`, `filters_blank_entries_and_keeps_valid_ones`) は variant 1, 2 と機能重複するが、命名を「filter path 直交」に揃えるリネーム or 統合を判断 -- DRY anti-pattern を避け、各 variant は独立 fixture で記述 (memory `feedback_test_dry_antipattern.md`) - -#### 作業計画 - -- [ ] 既存 3 test の filter path カバレッジを確認 (どの path を test しているか map) -- [ ] 4 直交 variant の test 関数を追加 (既存と命名衝突しないよう調整) -- [ ] 既存 test と新規 test の重複検出 → 既存を残しつつ補完 or 既存をリネーム -- [ ] cargo test で 4 件 pass 確認 -- [ ] 本エントリ削除 + todo-summary.md 行削除 - -#### 完了基準 - -- 4 filter path を各 1 test で独立検証 -- 1 path のみ regression した場合に 1 test だけ failed する構造 (memory rule 「early-return guard test 分離」と同思想) -- 既存 test と non-regression - -#### 詰まっている箇所 - -なし。Effort S、test 拡充のみで実装変更不要。 - ---- - ### Bundle 1 dogfood checklist 実行 — `__test.ps1` block + override env 確認 (PR #174 T2-#2 採用、ADR-039 bounded lifetime data point #1) > **動機**: PR #174 で実装した `scratch_file_warning` stage は ADR-039 § 3 Bounded lifetime 準拠で「3-5 PR の dogfood 後に default-ON 昇格 or 却下を判定」する設計。PR #174 の PR body に未消化の dogfood checklist が残っており (`__test.ps1` を意図的に作って push し block 動作確認 / override env でバイパス確認)、これが ADR-039 bounded lifetime の初回データポイント。次の PR (Bundle 2 等) merge 前の前提条件として消化が必要。 @@ -572,46 +531,6 @@ --- -### ADR-039 に「bounded lifetime に明示的 decision trigger 必須化」パターンを追記 (PR #174 T3-#1 採用) - -> **動機**: PR #174 で `scratch_file_warning` stage の bounded lifetime を「3-5 PR の dogfood 後に default-ON 昇格 or 却下を判定」と code comment + config TOML + PR body で明示した。この「明示的 decision trigger (PR 数 / 日付 / 条件)」を ADR-039 § 3 Bounded lifetime に reusable pattern として codify することで、将来の experimental feature 実装時にも同型の trigger 明文化が確実になる (現状は ADR-039 既存 text が trigger 形式を任意としており、formless な「未来の判定」で永遠の試験運用化リスク残存)。 -> -> **本タスクの位置づけ**: PR #174 post-merge-feedback Tier 3 #1 採用 (Severity Low / Frequency Medium / Effort S / Adoption Risk None)。Frequency Medium = 実験的機能は複数 PR にまたがって登場する性質。`feedback_no_unenforced_rules.md` 例外 = ADR (= 設計判断 doc) への追加で機械強制ではなく reviewer / Claude の judgment 補助、PR #174 で実装実例あり (= 既存実践の明文化)。 -> -> **参照**: `.claude/feedback-reports/174.md` Tier 3 #1、PR #174 (`push-runner-config.toml` の `[scratch_file_warning]` section コメント + `scratch_file_warning.rs` module doc の "3-5 PR dogfood" 記述)、`docs/adr/adr-039-experimental-feature-standard-pattern.md` § 3 Bounded lifetime -> -> **実行優先度**: 💎 **Tier 3** — Effort S。ADR への 1 paragraph 追記。 - -#### 設計決定 (案) - -- 配置: `docs/adr/adr-039-experimental-feature-standard-pattern.md` § 3 Bounded lifetime の既存 bullet list 末尾に「decision trigger の明示化要件」を追加 -- 追記内容案 (3-5 行): - - 試験期限を「期限 OR 条件 OR PR 数」のいずれかで明示的 trigger 化する (任意の "未来" 判定は禁止) - - 例: 「3-5 PR の dogfood 後に判定」(PR #174 `scratch_file_warning` の実例)、「6 ヶ月経過で却下とみなす」(既存 text)、「false positive 5 件以上で却下」等 - - decision trigger は config / code comment / PR body のいずれかで永続記録 (ephemeral 計画書だけでは retire 時に dead pointer 化リスク) - - 既存試験運用 ADR (014/023/025/029/030/031/033/034/036/037/038) の bounded lifetime も再評価し、formless な記述があれば後続 PR で追補 -- PR #174 を実例参照として ADR-039 内に 1-line cite 推奨 - -#### 作業計画 - -- [ ] ADR-039 § 3 Bounded lifetime の現行 text を確認 -- [ ] 「decision trigger 明示化要件」3-5 行を追記 -- [ ] PR #174 を実例として 1-line 引用 -- [ ] markdownlint clean 確認 -- [ ] 本エントリ削除 + todo-summary.md 行削除 - -#### 完了基準 - -- ADR-039 § 3 Bounded lifetime に「明示的 decision trigger 必須化」要件が明文化される -- 将来の experimental feature 実装時に formless な bounded lifetime 記述を ADR 違反として指摘可能になる -- PR #174 が実例として ADR から逆引きできる - -#### 詰まっている箇所 - -なし。Effort S、ADR への追記のみで副作用最小。 - ---- - ## 既知課題 (記録のみ、本セッションで未対応) (現時点で本ファイルへの既知課題は無し。docs/todo8.md 末尾の post-merge-feedback workflow stale marker 問題を参照。) From b10a83bac6481478a8b47ee467c00844f653a96e Mon Sep 17 00:00:00 2001 From: aloekun Date: Tue, 26 May 2026 21:05:08 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix(cli-push-runner):=20Bundle=202=20CR=20M?= =?UTF-8?q?inor=202=20=E4=BB=B6=E5=AF=BE=E5=BF=9C=20(main.rs=20message=20p?= =?UTF-8?q?unctuation=20+=20todo9.md=20=E9=A0=86=E4=BD=8D=20155=20design?= =?UTF-8?q?=20clarification)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/todo9.md | 5 +++-- src/cli-push-runner/src/main.rs | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/todo9.md b/docs/todo9.md index 8c3a33e..565410e 100644 --- a/docs/todo9.md +++ b/docs/todo9.md @@ -461,9 +461,10 @@ - 配置: `src/cli-pr-monitor/src/` の fix chain cleanup phase 末尾 (既存 `CleanupEmptyFixCommit` の後) - 動作: - 1. `jj log -r 'master..@' --no-graph -T 'change_id ++ "\n" ++ description.first_line() ++ "\n---\n"'` で PR 範囲 commit を列挙 - 2. `description = ""` (空) の commit を filter + 1. `jj log -r 'master..@' --no-graph -T 'change_id ++ "\u{1f}" ++ if(empty, "EMPTY", "CONTENT") ++ "\n"'` で PR 範囲 commit を列挙 (`empty` は jj template の commit 自体が空か判定する keyword) + 2. 各行を `\u{1f}` (Unit Separator) で分割し、2 列目が `EMPTY` の commit を filter 3. 該当 commit を `jj abandon ` で順次 abandon + - 注意: `description.first_line()` は description の 1 行目を返すため「全 description 空」と「複数行 description で 1 行目だけ空」を区別できない。実装では jj template の `empty` keyword (= commit が file change を含まないか) を直接使うか、`if(description, "DESCRIBED", "UNDESCRIBED")` で description 有無を判定する設計に固定する - scope 限定: `master..@` 範囲のみ (= PR に含まれる範囲)。master 以下は対象外 - 既存 `CleanupEmptyFixCommit` との関係: 既存は直近 fix commit のみ対象、本 step は全範囲 sweep の補完層 - fail-open: jj log / abandon の失敗時は warning ログのみで cleanup を継続 (push を block しない) diff --git a/src/cli-push-runner/src/main.rs b/src/cli-push-runner/src/main.rs index 072324d..33edd98 100644 --- a/src/cli-push-runner/src/main.rs +++ b/src/cli-push-runner/src/main.rs @@ -78,8 +78,8 @@ fn run_pre_checks(config: &config::Config) -> Result<(), i32> { } if !run_scratch_file_warning(config.scratch_file_warning.as_ref()) { log_info( - "パイプライン中断: scratch ファイル検出。.gitignore 修正 / ファイル削除 / \ - SCRATCH_FILE_WARNING_OVERRIDE=1 のいずれかで再実行してください。", + "パイプライン中断: scratch ファイル検出。`.gitignore` 修正 / ファイル削除 / \ + `SCRATCH_FILE_WARNING_OVERRIDE=1` のいずれかで再実行してください。", ); return Err(EXIT_SCRATCH_FILE_WARNING); }