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
14 changes: 14 additions & 0 deletions docs/adr/adr-039-experimental-feature-standard-pattern.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/<topic>-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 で個別に追補する。

## 帰結

### 利点
Expand Down
3 changes: 2 additions & 1 deletion docs/todo-summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 先行推奨 (観点 ⑤ 責務分離) |
Expand Down Expand Up @@ -77,6 +76,8 @@
| 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 の補完層) |
| 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 に記録) |

**戦略**: 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
33 changes: 0 additions & 33 deletions docs/todo2.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <name> -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 シグナル処理またはタイムアウトの問題の可能性があり、本セッションで初めて顕在化。回帰テストで継続的に検出できるようにする。
Expand Down
85 changes: 85 additions & 0 deletions docs/todo9.md
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,91 @@

---

### 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 ++ "\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 <change_id>` で順次 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 しない)

#### 作業計画

- [ ] 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 で副作用最小。

---

### 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 で完結。

---

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

(現時点で本ファイルへの既知課題は無し。docs/todo8.md 末尾の post-merge-feedback workflow stale marker 問題を参照。)
37 changes: 28 additions & 9 deletions src/cli-push-runner/src/main.rs
Original file line number Diff line number Diff line change
@@ -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 で参照)
Expand All @@ -17,6 +18,7 @@
//! 4 - 設定エラー
//! 5 - diff 取得失敗
//! 6 - scratch_file_warning 検出 (override env で bypass 可能)
//! 7 - bookmark_check 非 trunk bookmark 未設定

mod config;
mod log;
Expand All @@ -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;
Expand All @@ -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 中断。
Expand All @@ -63,6 +66,26 @@ fn run_diff_and_lint_screen(config: &config::Config) -> Result<bool, i32> {
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 <name> -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();

Expand All @@ -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) {
Expand Down
Loading