Skip to content

feat(weekly-review): クローズ済み PR の残存ブランチを決定論的に検出し削除を提案する - #377

Merged
aloekun merged 1 commit into
masterfrom
feat/stale-branch-scan
Aug 9, 2026
Merged

feat(weekly-review): クローズ済み PR の残存ブランチを決定論的に検出し削除を提案する#377
aloekun merged 1 commit into
masterfrom
feat/stale-branch-scan

Conversation

@aloekun

@aloekun aloekun commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Summary

  • クローズ済み / マージ済み PR の残存ブランチを検出する決定論 exe cli-stale-branch-scan を追加 (pnpm stale-branch-scan)。todo 順位 395
  • 削除は提案までで止める — 出力は人間が貼れる git push origin --delete -- <branch> であって、exe も skill も実行しない (ADR-022 / ADR-028)
  • weekly-review skill に Step 1.0 (takt 起動前の同期実行) と Phase 3 (findings とは別枠での提示) を追加。skill は別リポジトリのため本 PR には含まれない
  • ADR-031 に § 残存ブランチ検出 と、決定論 scan を L2 (takt) と L3 (skill) のどちらに置くかの判断基準を記録
  • test 36 件 (+ cwd 依存 1 件は --ignored)。取得失敗・上限到達・欠損フィールドはすべて Err に倒し、0 件と報告しない

Context

Why: クローズ済み PR #365 のブランチを手で消したことで ADR-072 決定 3 の除外マーカーが失われ、同じ順位が再選択された (PR #373)。決定 3 自体は設計どおりで、ブランチの存在が着手済みマーカーである以上、浮いたブランチを定期的に片付ける場が要る。

設計判断 — takt workflow には置けなかった: todo は「facet 相乗り or workflow 内の決定論 scan」を想定していたが、weekly-review.yaml は全 provider に network_access: false を課している (他 3 パイプラインは true)。検出には git ls-remote / gh が要るため、1 つの scan のためにフラグを反転すると whole-tree review 6 facet すべての隔離が同時に緩む/monthly-reviewcli-telemetry-report を呼ぶのと同じ ADR-031 L3 (skill 側の決定論層) に置いた。

判定規則: 紐づく PR が全て closed/merged → 削除候補 / open が 1 本でもある → 対象外 (close 後の再オープンが実在する) / PR が 1 件も無い → 対象外 (PR 未作成の作業中ブランチと区別できない) / trunk は常に対象外 / claude/nightly-* は除外しない (除外するとその順位が二度と選ばれない) / state が未知の値 → open 扱い (保護側)

実装中に設計が破綻した点: 当初は PR を全件取得していたが、実走で即 fail-closed が発火した — 本リポジトリは PR が 300 件を超えており、書いた時点で既に上限に張り付いていた。上限を上げるのは対症療法 (総 PR 数は単調増加) なので、入力領域をブランチ単位に変更した (--head <branch> で 1 本ずつ)。remote ブランチ数は運用上小さく有界。

Scope decision: 1 PR。exe 単体では weekly-review から呼ばれず、ADR/todo/pnpm 配線なしでは機能しない。PR diff 1,128 行で warning 閾値 800 超・block 閾値 1500 未満。

Validation

  • cargo test --workspace: 2,006 pass / 0 fail。本 crate は 36 pass + --ignored 1 pass
  • cargo clippy --workspace --all-targets -- -D warnings: clean
  • pnpm lint:md (127 files) / pnpm lint:docs: 0 error
  • pnpm push pre-push review: simplicity / security とも approved (3 iterations)
  • 実走: 削除候補 0 件、open PR 3 本 (test(hooks-session-start): stale_check_enabled の TOML パーステスト追加 (順位284) #320 / test(hooks-session-start): stale_check_enabled の TOML パーステスト追加 (順位 284) #324 / feat: 順位 203 の無人実装 (nightly-todo) #373) を正しく対象外と判定。todo 記載の「現状」と一致
  • レビュー指摘 5 件をすべて実測で検証:
    • git check-ref-format; ` | $() が ref 名に使えることを確認 → allowlist + 全描画経路の無害化
    • --force有効な ref 名であることを確認 → 削除コマンドに -- 区切りを挿入
    • develop / trunk が未保護だったことを確認 → lib_jj_helpers::is_trunk_bookmark へ寄せる
    • 本リポジトリの config が section override だけで trunk を決めていることを確認 → top-level → section fallback を実装し、実ファイルに対する回帰テストを追加
    • 外部コマンドに timeout が無い → lib_subprocess::wait_with_timeout_basic で 60s
  • weekly-review 経由の実走は未実施: 次回 /weekly-review 起動時に Step 1.0 が呼ばれることを確認する

References

Summary by CodeRabbit

  • 新機能

    • リモートブランチと関連プルリクエストを調査し、削除候補を検出するスキャン機能を追加しました。
    • 削除は自動実行せず、安全な手動削除コマンドとして提案します。
    • 作業中・PR未存在・保護対象のブランチは削除候補から除外します。
    • 取得失敗や不明な状態では、安全側に処理します。
  • ドキュメント

    • 週次レビューにおける検出ルールと運用方針を更新しました。

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a187466f-c1cd-4017-9d60-4f2444f9f5ed

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

remote ブランチと PR の全状態を取得する Rust CLI を追加しました。closed または merged のみのブランチを削除候補として Markdown 出力します。削除は実行しません。L3 から同期実行できるようにビルドと週次パイプラインを更新しました。

Changes

残存ブランチ検出

Layer / File(s) Summary
ブランチ分類と判定
src/cli-stale-branch-scan/src/classify.rs
PR 状態、trunk、複数 PR、未知 state を処理します。closed または merged のみのブランチを削除候補に分類します。
remote と PR の収集
src/cli-stale-branch-scan/src/collect.rs
git ls-remotegh pr list --state all --head を実行します。JSON、上限、timeout、終了状態を検証します。
CLI と決定的レポート
src/cli-stale-branch-scan/src/main.rs
引数と trunk 設定を処理します。候補、open PR 付き、PR なしの各 section を出力します。危険なブランチ名では削除コマンドを省略します。
ビルドと週次パイプライン統合
src/cli-stale-branch-scan/Cargo.toml, Cargo.toml, package.json, docs/adr/adr-031-weekly-review-pipeline.md, docs/harness-improvement-plan.md, docs/todo-summary2.md, docs/todo21.md
新しい crate を workspace と build:all に追加します。pnpm stale-branch-scan を L3 で実行する構成と関連記録を更新します。

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Operator as 実行環境
  participant ScanCLI as cli-stale-branch-scan
  participant Git as git ls-remote
  participant GH as gh pr list
  participant Report as Markdown レポート
  Operator->>ScanCLI: pnpm stale-branch-scan
  ScanCLI->>Git: remote ブランチを取得
  ScanCLI->>GH: ブランチごとの PR 状態を取得
  ScanCLI->>Report: 削除候補と保護対象を出力
  Report-->>Operator: 手動削除コマンドを提示
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed タイトルは、クローズ済みPRに関連する残存ブランチの決定論的な検出と削除提案という主変更を正確かつ簡潔に表しています。
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/stale-branch-scan

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🤖 PR Monitor 分析 (GitHub Actions バックストップ)

  • トリガー: issue_comment (created) / 実行 run
  • CI: pending (rust ubuntu-latest / rust windows-latest とも実行中、結果未確定)
  • レビュー状況: CodeRabbit は "Review in progress" (未着、定型の処理中通知のみ)。人間レビュー・レビューコメントともに 0 件
  • Verdict: user_decision (レビュー指摘 0 件だが CI 未完了・レビュー未着のため確定判断は時期尚早。次回チェックでの再評価を推奨)

Applicable Findings (Critical / High / Major)

該当なし (レビュー指摘 0 件)

Applicable Findings (Medium 以下)

該当なし

Filtered (not applicable)

該当なし

次のアクション

  • CodeRabbit レビューおよび CI (rust ubuntu-latest / windows-latest) の完了を待ち、結果が揃った時点で再分析する
  • diff 概要: 新規 crate src/cli-stale-branch-scan (main.rs 521行 / collect.rs 256行 / classify.rs 291行 追加) を Cargo workspace に追加し、ADR-031 (weekly-review pipeline) のドキュメントを拡張。docs/todo21.md・todo-summary2.md の整理も含む、機能追加中心の変更
  • mergeStateStatus が BLOCKED (CI 未完了によるものと推測) のため、CI 完了後に再度マージ可否を確認する

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/cli-stale-branch-scan/src/main.rs (2)

169-178: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

config の読み取り失敗を stderr へ通知することを検討してください。

L170 と L176 は、ファイル欠如と TOML parse 失敗の両方を None へ落とします。fail-closed にしない判断は doc (L164-168) のとおり妥当です。

ただし parse 失敗は設定の破損を示します。現状は無音で TRUNK_BOOKMARKS のみの保護へ縮退します。default_branchTRUNK_BOOKMARKS 外の名前を使うリポジトリでは、その trunk が削除提案に載ります。

parse 失敗時のみ stderr へ 1 行出すと、レポートの決定性を保ったまま原因が分かります。

🤖 Prompt for 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.

In `@src/cli-stale-branch-scan/src/main.rs` around lines 169 - 178, Update
parse_configured_trunk_branch to emit one concise diagnostic line to stderr when
TOML deserialization fails, while still returning None and preserving the
existing fallback behavior. Keep missing-file handling in
configured_trunk_branch silent, and ensure valid configurations remain
unchanged.

272-277: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

remote も同じ allowlist を通すことを検討してください。

L273 は remote を削除コマンド文字列へそのまま埋め込みます。branchis_safe_branch_name で検査しますが、remote は検査しません。L213 の header も同様です。

現状 remote は CLI 引数であり、実行者が制御します。したがって今は脅威ではありません。ただし将来 skill や workflow が外部由来の値を --remote へ渡すと、branch と同じコピペ実行リスクが生じます。

出力の全経路が 1 つの allowlist を通る形にすると、この差分が残りません。doc コメント (L244-246) が述べる方針とも一致します。

🤖 Prompt for 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.

In `@src/cli-stale-branch-scan/src/main.rs` around lines 272 - 277, Validate the
remote value with the same allowlist used by is_safe_branch_name before
embedding it in generated delete commands and the header output. Update the
command-generation flow around command_cell and the header construction so every
output path handles unsafe remote values consistently, preserving the existing
warning behavior and documented safety policy.
🤖 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 `@docs/adr/adr-031-weekly-review-pipeline.md`:
- Line 210: ADR-031のブランチ削除コマンド記述を、実装仕様に合わせて `git push origin --delete --
<branch>` の形式へ更新してください。対象は「削除は提案までで止める」節のコマンド表記のみとし、その他の説明は変更しないでください。
- Around line 132-145: Implement the missing /weekly-review L3 skill flow, using
the /monthly-review skill and its cli-telemetry-report execution path as the
reference. Run pnpm stale-branch-scan synchronously before starting takt,
include its output with the findings for the approval selection, and stop before
approval if the scan fails. Ensure the skill supports resuming from pending JSON
as described by the ADR.

In `@src/cli-stale-branch-scan/src/collect.rs`:
- Around line 159-171: Update fetch_pull_requests_for_branch so errors from
run("gh", &args) are also wrapped with the branch name before propagation, while
preserving the existing branch-context mapping for parse_pr_list errors.

---

Nitpick comments:
In `@src/cli-stale-branch-scan/src/main.rs`:
- Around line 169-178: Update parse_configured_trunk_branch to emit one concise
diagnostic line to stderr when TOML deserialization fails, while still returning
None and preserving the existing fallback behavior. Keep missing-file handling
in configured_trunk_branch silent, and ensure valid configurations remain
unchanged.
- Around line 272-277: Validate the remote value with the same allowlist used by
is_safe_branch_name before embedding it in generated delete commands and the
header output. Update the command-generation flow around command_cell and the
header construction so every output path handles unsafe remote values
consistently, preserving the existing warning behavior and documented safety
policy.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8491bb38-80c5-4a59-bca7-23b0cd9418b1

📥 Commits

Reviewing files that changed from the base of the PR and between 499befc and c0e29c0.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • Cargo.toml
  • docs/adr/adr-031-weekly-review-pipeline.md
  • docs/harness-improvement-plan.md
  • docs/todo-summary2.md
  • docs/todo21.md
  • package.json
  • src/cli-stale-branch-scan/Cargo.toml
  • src/cli-stale-branch-scan/src/classify.rs
  • src/cli-stale-branch-scan/src/collect.rs
  • src/cli-stale-branch-scan/src/main.rs
💤 Files with no reviewable changes (1)
  • docs/todo-summary2.md

Comment thread docs/adr/adr-031-weekly-review-pipeline.md
Comment thread docs/adr/adr-031-weekly-review-pipeline.md Outdated
Comment thread src/cli-stale-branch-scan/src/collect.rs
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🤖 PR Monitor 分析 (GitHub Actions バックストップ)

  • トリガー: pull_request_review (submitted) / 実行 run
  • CI: 実質 green — CodeRabbit review completed (pass) / rust (ubuntu-latest) pass / rust (windows-latest) pass。analyze は本 workflow 自身のジョブで pending 表示は自己参照による想定内の状態
  • レビュー状況: CodeRabbit が pull_request_review を1件提出済み (COMMENTED, 2026-08-09T12:50:57Z、actionable 3 件 + nitpick 2 件)。人間レビューは0件、reviewDecision 未確定 (承認なし)
  • Verdict: user_decision

Applicable Findings (Critical / High / Major)

該当なし

Applicable Findings (Medium 以下)

# File (Line) Reviewer Issue Recommended Action
1 src/cli-stale-branch-scan/src/collect.rs:171 (Minor) CodeRabbit fetch_pull_requests_for_branchrun("gh", ...) の失敗はブランチ名の文脈なしで伝播する (parse_pr_list 側の失敗のみ map_err でブランチ名が付く) run() の結果にも同様に map_err でブランチ名を付けてから parse_pr_list に渡す
2 src/cli-stale-branch-scan/src/main.rs:169-178 (Trivial) CodeRabbit parse_configured_trunk_branch の TOML parse 失敗が無音で None にフォールバックし原因調査ができない (ファイル欠如時の無音フォールバックは design doc 上意図的) parse 失敗時のみ stderr に1行診断を出す (欠如ファイルの無音扱いは変更しない)
3 src/cli-stale-branch-scan/src/main.rs:272-277 (Trivial) CodeRabbit 削除コマンド生成で branchis_safe_branch_name の allowlist を通るが remote は未検査 (現状は CLI 引数で実行者制御のため脅威ではない、と CodeRabbit 自身も明記) 将来 remote に外部由来の値が渡るケースに備え、同じ allowlist を通す

Filtered (not applicable)

# File (Line) Issue Filter Reason
1 docs/adr/adr-031-weekly-review-pipeline.md:145 (Major) /weekly-review skill 側の L3 実装 (pnpm stale-branch-scan 同期実行) がリポジトリに無いとの指摘 Scope mismatch: docs/adr/ は read-only zone (analyze-coderabbit.md Step 2)。加えて false positive の疑いが強い — SKILL.md はこのリポジトリでは追跡対象外 (/monthly-review skill も同様に repo 未追跡、ADR-062 参照)。CodeRabbit は repo 外で管理される skill ファイルの存在を検知できていない
2 docs/adr/adr-031-weekly-review-pipeline.md:210 (Minor) ADR記載の削除コマンドが git push origin --delete <branch> (実装は main.rs で -- 区切りあり) で実装と表記が不一致 Scope mismatch: docs/adr/ は read-only zone (analyze-coderabbit.md Step 2)。事実としては軽微な記述不一致があるため次のアクションに記載

次のアクション

  • Medium以下の適用可能指摘3件 (collect.rs のエラー文脈付与、main.rs の parse失敗ログ、remote allowlist 検査) は severity が Medium 以下のため自動 fix 対象外。まとめて対応するか人間判断で要検討
  • ADR-031:210 の削除コマンド表記 (git push origin --delete <branch>) が実装済みの -- 区切りと不一致。docs/adr は read-only zone のため filter されているが、次回 ADR 編集時に併せて修正すると齟齬がなくなる
  • mergeStateStatus が BLOCKED (reviewDecision 未確定、承認レビュー0件)。CI は全て green のため、あとは人間の承認待ちの状態

todo 順位 395。クローズ済み PR #365 のブランチを手で消したことで ADR-072 決定 3 の
除外マーカーが失われ、同じ順位が再選択された (PR #373)。決定 3 自体は設計どおりで、
ブランチの存在が着手済みマーカーである以上、浮いたブランチを定期的に片付ける場が要る。

**takt workflow には置けない。** 検出には git ls-remote / gh = ネットワークが要るが、
weekly-review.yaml は全 provider に network_access: false を課している (他 3 パイプラインは
true)。1 つの scan のためにこれを反転すると whole-tree review 6 facet すべての隔離が
緩むため、/monthly-review が cli-telemetry-report を呼ぶのと同じく skill 側 (ADR-031 の
L3 = 決定論層) に置いた。置き場所の判断基準 (ネットワークが要るか) を ADR-031 へ記録。

判定規則:
- 紐づく PR がすべて closed/merged → 削除候補
- open が 1 本でもある → 対象外 (close 後に別 PR を開く / reopen が実在する)
- PR が 1 件も無い → 対象外 (PR 未作成の作業中ブランチと区別できない)
- trunk は常に対象外 / claude/nightly-* は除外しない (除外すると順位が二度と選ばれない)
- state が未知の値 → open 扱い (保護側。誤って削除提案に載せない)

**PR は全件引かずブランチごとに --head で引く。** 総 PR 数は単調増加する一方、remote
ブランチ数は運用上小さく有界。全件方式は実装中に実際に破綻した (本リポジトリは PR が
300 件を超えており、書いた時点で既に上限に張り付いていた)。

**削除はしない。** 出力は人間がそのまま貼れる git push --delete までで、exe は実行
しない (ADR-022 / ADR-028)。ブランチ削除は外部可視かつ着手済みマーカーの破棄でもある。

**出力に wall-clock を含めない。** 同じ状態なら同じ出力にして週次 diff を取れるようにし、
「今週新たに浮いたブランチ」だけを読めるようにした。実行時刻は呼び手が記録する。

- 新規 crate src/cli-stale-branch-scan (classify = 純粋判定 / collect = fail-closed I/O)
- unit test 25 件。取得失敗・上限到達・欠損フィールドはすべて Err に倒し、0 件と報告しない
- pnpm stale-branch-scan / build:cli-stale-branch-scan (build:all にも登録)
- ADR-031 に § 残存ブランチ検出 と L2/L3 の置き場所判断基準を追記

実走: 本リポジトリで削除候補 0 件、open PR 3 本 (#320/#324/#373) を正しく対象外と判定。

weekly-review skill (別リポジトリ claude-code-skills) 側の Step 1.0 / Phase 3 追記は
本 PR に含まれない。編集は済んでいるがコミットは未実施。

--- pre-push review 対応 ---

security REJECT (High, SEC-NEW-cli-stale-branch-scan-main-L154): git の ref 名規則は
`;` バッククォート `|` `$()` を許す (git check-ref-format で実測)。push 権限を持つ誰か
(夜間/cloud harness の自動化を含む) が細工したブランチ名は、本レポートが設計として
出す「そのまま貼れる削除コマンド」経由でコピペ実行時に任意コマンドを実行し得る。
安全文字の allowlist を導入し、外れる名前には削除コマンドを生成しない。

**指摘の修正範囲は狭かったので広げた。** 同じブランチ名は削除提案表の 1 列目と
参考表 2 つにも出るため、コマンド欄だけ塞いでもバッククォート (コードスパン脱出) と
`|` (表の列構造破壊) が残る。描画の全経路を単一の branch_cell() へ通し、危険文字を
`?` へ潰したうえで印を付ける形にした。出口ごとに個別対策を足すと出口が増えたときに
同じ穴が空くため、安全文字集合の定義は 1 箇所に保つ。

simplicity (SIM-NEW-cli-stale-branch-scan-classify-L283): trunk 名を独自 hardcode して
おり lib_jj_helpers::TRUNK_BOOKMARKS からずれて develop/trunk を守れていなかった。
is_trunk_bookmark 呼び出しに変更し、push-runner-config.toml の default_branch も
追加の保護対象として読む。

test 30 件 (危険文字が 3 表いずれにも生で出ないことの回帰固定を含む)。
workspace 全体 2,000 pass / clippy clean / lint 0 error。

--- pre-push review 2 巡目 ---

simplicity needs_fix (High, SIM-NEW-cli-stale-branch-scan-main-L121): configured_trunk_branch()
が top-level default_branch しか読まないが、本リポジトリの push-runner-config.toml は
top-level をコメントアウトし [pr_size_check] / [docs_only_routing] の section override
だけで trunk 名を決めている (ADR-051 の cross-config coupling)。master は TRUNK_BOOKMARKS
に含まれるため masked だが、標準外の trunk 名を section override だけで設定している
リポジトリでは None に落ち、trunk が削除候補として貼れるコマンド付きで出得た。
cli-push-runner の effective_default_branch() と同じ top-level → section fallback へ修正。

**実 config に対する回帰テストを追加した** (--ignored)。既存 5 件は合成 TOML で分岐を
固めるだけで、指摘の起点だった「このリポジトリの実 config が section override 構成で
ある」事実を突いていない。実ファイルの構成が変わって解決不能になっても合成テストは
気づけないため、実ファイルから "master" が解決できることを値まで assert する。

test 36 件 (うち 1 件は cwd 依存の --ignored)。workspace 全体 2,005 pass / clippy clean。

--- CodeRabbit レビュー対応 (#377、3 件) ---

- ADR-031 に L3 skill の所在を明記。skill は本リポジトリではなく skills repo
  ($CLAUDE_SKILLS_REPO) にあり ~/.claude/skills/ へ deploy する構成 (ADR-062 の
  /monthly-review と同じ)。ADR がそれを書いていなかったため「ADR は L3 を定義して
  いるが実装が無い」と読めていた。各層の実体がどこにあるかの表を追加し、skill 側の
  変更は PR diff に現れない帰結も明記した。
- ADR-031 の削除コマンドを実装に合わせて `--delete -- <branch>` へ修正。あわせて
  「貼れるコマンドである以上ブランチ名は攻撃面」という設計理由 (-- 区切りと
  allowlist の 2 つの手当て) を ADR 側にも残した。実装だけが知っている状態を解消。
- gh 自体の失敗にもブランチ名を付ける。map_err が parse_pr_list の結果にしか
  掛かっておらず、起動失敗 / timeout / 非ゼロ exit ではどのブランチで止まったか
  分からなかった。最大 100 ブランチを順に回すため fail-closed 停止後の切り分けが
  効かない。実行層を closure で受ける形にし、失敗経路をネットワーク無しで固定する
  回帰テストを 3 件追加。

test 40 件 (うち --ignored 1)。workspace 全体 2,009 pass / clippy clean / lint 0 error。
@aloekun
aloekun force-pushed the feat/stale-branch-scan branch from c0e29c0 to 17a14cd Compare August 9, 2026 13:19
@aloekun

aloekun commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

CodeRabbit レビュー対応 (3 件すべて修正)

# 指摘 対応
1 L3 skill の実装がリポジトリに無い (🟠 Major) ADR-031 に各層の実体の所在を追記
2 削除コマンドの記法が実装とずれている (🟡 Minor) ADR-031 を --delete -- <branch> へ修正
3 run の失敗にブランチ名が付かない (🟡 Minor) 両経路に文脈を付け、回帰テスト 3 件を追加

1. L3 skill の所在について

指摘は事実として正しく、原因は ADR-031 が skill の所在を書いていなかったことです。本プロジェクトの skill は本リポジトリではなく skills repo ($CLAUDE_SKILLS_REPO) に置き ~/.claude/skills/ へ deploy する構成で、これは ADR-062/monthly-review (cli-telemetry-report 経路) と同じです。本 ADR に固有の判断ではありません。

ADR-031 に各層の実体がどこにあるかの表を追加しました。あわせて skill 側の変更は本リポジトリの PR diff に現れないという帰結と、L2/L3 をまたぐ変更では両リポジトリの同期を人間が確認する必要がある (自動照合の仕組みは無く /skill-sync-check が手動手段) ことも明記しています。

本 PR に対応する skill 側の変更 (Step 1.0 = takt 起動前の pnpm stale-branch-scan 同期実行 / Phase 3 = findings とは別枠での提示) は編集・deploy 済みですが、別リポジトリのためこの PR には含まれません。

2. 削除コマンドの記法

ご指摘のとおり実装は -- 区切りを入れており、ADR が追随していませんでした。修正に加えてなぜ -- が要るのかも ADR 側へ残しています:

  • git check-ref-format で実測したところ、ref 名には ; / バッククォート / | / $() が使え、--force のような - 始まりも有効
  • したがって (a) -- 区切りを必ず挟む (b) 安全文字 allowlist から外れる名前には貼れるコマンドを生成しない、の 2 つが要る
  • ブランチ名は 3 つの表すべてに出るため、無害化は描画の全経路で共通関数を通す

理由を実装だけが知っている状態を解消しました。

3. run 失敗時のブランチ名

そのとおりで、map_errparse_pr_list の結果にしか掛かっていませんでした。fetch_pull_requests_for は最大 100 ブランチを順に回すため、起動失敗 / timeout / 非ゼロ exit のいずれでも停止位置が分からず、fail-closed 停止後の切り分けが効かない状態でした。timeout (60s) を入れた直後だっただけに実害の出やすい抜けです。

実行層を closure で受ける形 (fetch_pull_requests_for_branch_with) にして両経路へ文脈を付け、失敗経路をネットワーク無しで固定する回帰テスト 3 件を追加しました (gh 失敗 / parse 失敗 / 成功経路の対照)。

検証

  • cargo test --workspace: 2,009 pass / 0 fail (本 crate 40 件、うち cwd 依存 1 件は --ignored)
  • cargo clippy --workspace --all-targets -- -D warnings: clean
  • pnpm lint:md (127 files) / pnpm lint:docs: 0 error
  • pnpm push pre-push review: simplicity / security とも approved

非ブロッキング指摘について (todo 登録の候補)

pre-push review が、TrunkConfig::effective_default_branchcli-push-runner の同名ロジックを手で複製している点を非ブロッキングで挙げています。将来 tie-break 規則が変わったときに片方だけ古くなり trunk 保護が silent に drift するリスクがあり、共有先の lib-* crate が存在しないため抽出は新規作業になります。本 PR では扱わず、todo 登録の候補として記録します。

@aloekun
aloekun merged commit ef09f8a into master Aug 9, 2026
3 checks passed
@aloekun
aloekun deleted the feat/stale-branch-scan branch August 9, 2026 14:43
aloekun added a commit that referenced this pull request Aug 10, 2026
あわせて post-merge feedback (#376/#377/#380/#381/#382) の採用分 10 件を順位 402-411 へ
登録した。2026-08-10 に採用候補を系統別へ分類し、ユーザーが採否を決定したもの。

採用: 系統 A (観測の完全性) 3 件 / 系統 B (重複実装の予防) 3 件 /
      系統 C (shell・config パースの安全性) 3 件
却下: 系統 D (workflow セキュリティ標準化) / 系統 E (PAT 失効監視) — 様子見
形を変えて採用: 系統 F — 「CLAUDE.md に rustfmt 非適用の方針を書く」ではなく
      「cargo fmt を PreToolUse でブロックする」(順位 411)

系統 F の変更理由 (ユーザー判断): 規約は CLAUDE.md に書いた時点で毎セッション読まれ
コンテキストを圧迫するが、PreToolUse hook は発火するまでコストがゼロで、ブロックと
同時に正しいコマンドをフィードバックできる。読み手は規約を覚えていなくても正しい経路へ
到達する。ADR-042 の mechanizable 判定を満たすため機構側が正しい。

**この非対称は現行 ADR-042 に無い**。同 ADR の判断基準は「機械判定できるか」「投資対効果」
が中心で、「規約は常時コンテキストを消費し hook は発火時のみ」という観点が明示されて
いない。ルール追加を検討するたびに効く一般則なので、順位 411 の作業範囲に ADR-042 への
追記を含めた。
aloekun added a commit that referenced this pull request Aug 10, 2026
あわせて post-merge feedback (#376/#377/#380/#381/#382) の採用分 10 件を順位 402-411 へ
登録した。2026-08-10 に採用候補を系統別へ分類し、ユーザーが採否を決定したもの。

採用: 系統 A (観測の完全性) 3 件 / 系統 B (重複実装の予防) 3 件 /
      系統 C (shell・config パースの安全性) 3 件
却下: 系統 D (workflow セキュリティ標準化) / 系統 E (PAT 失効監視) — 様子見
形を変えて採用: 系統 F — 「CLAUDE.md に rustfmt 非適用の方針を書く」ではなく
      「cargo fmt を PreToolUse でブロックする」(順位 411)

系統 F の変更理由 (ユーザー判断): 規約は CLAUDE.md に書いた時点で毎セッション読まれ
コンテキストを圧迫するが、PreToolUse hook は発火するまでコストがゼロで、ブロックと
同時に正しいコマンドをフィードバックできる。読み手は規約を覚えていなくても正しい経路へ
到達する。ADR-042 の mechanizable 判定を満たすため機構側が正しい。

**この非対称は現行 ADR-042 に無い**。同 ADR の判断基準は「機械判定できるか」「投資対効果」
が中心で、「規約は常時コンテキストを消費し hook は発火時のみ」という観点が明示されて
いない。ルール追加を検討するたびに効く一般則なので、順位 411 の作業範囲に ADR-042 への
追記を含めた。

## 計画書 (harness-improvement-plan.md) の WP-18 節を再編成

**WP-18 で生んだ問題と、WP-18 の運用で日常的に踏む問題を WP-18 の外へ押し出さない**
(2026-08-10 ユーザー方針) ため、残作業を 3 区分へ分けて完了条件を明示した。

従来は観測と派生タスクが 1 表に混在し、WP-18 の完了に何が要るのかが読み取れなかった。

- (1) 観測待ち — 機構は整備済みで事象か期限を待つもの
- (2) 運用問題の対処 — WP-18 が生んだ (基準 1) / WP-18 の運用で踏む潜在バグ (基準 2)。
      順位 397 / 398-400 / 401 / 410。**完了条件に含める**
- (3) WP-18 外の派生 — 順位 396 / 411 / 402-409 等。完了条件に含めない

(3) を完了条件から外すのは、§ 7 の退役条件が「全 WP が完了または見送り」である以上、
リポジトリ全体の一般則を WP-18 に紐づけると計画書が永久に退役できなくなるため。
ただし**優先度が低いという意味ではない** — 順位 396 (flaky テスト) と 411 (cargo fmt
ブロック) はいずれも高優先度で、WP-18 とは独立に早期着手する旨を明記した。

あわせて古い記述を実測に合わせた:

- 見出しの「実装・スモークは 2026-08-08 までにほぼ完了」→ 決定 16 という新規実装が
  2026-08-10 に入ったため「観測中 + 運用問題の対処中」へ
- 「前 2 者は順位 394 後の run で判定できる」→ 順位 394 は完了済みで実際の前提は決定 16。
  同一ファイル内の自己矛盾だった
- WP-17 残課題節にも同じ「順位 394 後の run」が残っていたため同期。あわせて
  「代替解は draft 廃止」が誤りだったことも記録した

## todo 側

- 順位 396 を Tier 2 → **Tier 1** へ格上げ (ユーザー判断)。単発の Severity では Tier 2
  相当だが、flaky テストは「また flake だろう」という読み替えを生み実バグの見落とし
  経路になるため。両 OS matrix (ADR-065) の信号品質を守る意味で早期に潰す
- 順位 411 に早期着手の根拠を追記 (cargo fmt は反射的に実行されやすい)
- **却下を negative result として記録**: 系統 D / E は様子見。trunk 保護の drift 対処
  2 件は却下 (予防側は順位 405 で押さえた / 共有 lib 化は network isolation 設計と
  抵触しうる)。**再採用条件は「同型の drift が今後も再発する場合」**と明記した
aloekun added a commit that referenced this pull request Aug 10, 2026
あわせて post-merge feedback (#376/#377/#380/#381/#382) の採用分 10 件を順位 402-411 へ
登録した。2026-08-10 に採用候補を系統別へ分類し、ユーザーが採否を決定したもの。

採用: 系統 A (観測の完全性) 3 件 / 系統 B (重複実装の予防) 3 件 /
      系統 C (shell・config パースの安全性) 3 件
却下: 系統 D (workflow セキュリティ標準化) / 系統 E (PAT 失効監視) — 様子見
形を変えて採用: 系統 F — 「CLAUDE.md に rustfmt 非適用の方針を書く」ではなく
      「cargo fmt を PreToolUse でブロックする」(順位 411)

系統 F の変更理由 (ユーザー判断): 規約は CLAUDE.md に書いた時点で毎セッション読まれ
コンテキストを圧迫するが、PreToolUse hook は発火するまでコストがゼロで、ブロックと
同時に正しいコマンドをフィードバックできる。読み手は規約を覚えていなくても正しい経路へ
到達する。ADR-042 の mechanizable 判定を満たすため機構側が正しい。

**この非対称は現行 ADR-042 に無い**。同 ADR の判断基準は「機械判定できるか」「投資対効果」
が中心で、「規約は常時コンテキストを消費し hook は発火時のみ」という観点が明示されて
いない。ルール追加を検討するたびに効く一般則なので、順位 411 の作業範囲に ADR-042 への
追記を含めた。

## 計画書 (harness-improvement-plan.md) の WP-18 節を再編成

**WP-18 で生んだ問題と、WP-18 の運用で日常的に踏む問題を WP-18 の外へ押し出さない**
(2026-08-10 ユーザー方針) ため、残作業を 3 区分へ分けて完了条件を明示した。

従来は観測と派生タスクが 1 表に混在し、WP-18 の完了に何が要るのかが読み取れなかった。

- (1) 観測待ち — 機構は整備済みで事象か期限を待つもの
- (2) 運用問題の対処 — WP-18 が生んだ (基準 1) / WP-18 の運用で踏む潜在バグ (基準 2)。
      順位 397 / 398-400 / 401 / 410。**完了条件に含める**
- (3) WP-18 外の派生 — 順位 396 / 411 / 402-409 等。完了条件に含めない

(3) を完了条件から外すのは、§ 7 の退役条件が「全 WP が完了または見送り」である以上、
リポジトリ全体の一般則を WP-18 に紐づけると計画書が永久に退役できなくなるため。
ただし**優先度が低いという意味ではない** — 順位 396 (flaky テスト) と 411 (cargo fmt
ブロック) はいずれも高優先度で、WP-18 とは独立に早期着手する旨を明記した。

あわせて古い記述を実測に合わせた:

- 見出しの「実装・スモークは 2026-08-08 までにほぼ完了」→ 決定 16 という新規実装が
  2026-08-10 に入ったため「観測中 + 運用問題の対処中」へ
- 「前 2 者は順位 394 後の run で判定できる」→ 順位 394 は完了済みで実際の前提は決定 16。
  同一ファイル内の自己矛盾だった
- WP-17 残課題節にも同じ「順位 394 後の run」が残っていたため同期。あわせて
  「代替解は draft 廃止」が誤りだったことも記録した

## todo 側

- 順位 396 を Tier 2 → **Tier 1** へ格上げ (ユーザー判断)。単発の Severity では Tier 2
  相当だが、flaky テストは「また flake だろう」という読み替えを生み実バグの見落とし
  経路になるため。両 OS matrix (ADR-065) の信号品質を守る意味で早期に潰す
- 順位 411 に早期着手の根拠を追記 (cargo fmt は反射的に実行されやすい)
- **却下を negative result として記録**: 系統 D / E は様子見。trunk 保護の drift 対処
  2 件は却下 (予防側は順位 405 で押さえた / 共有 lib 化は network isolation 設計と
  抵触しうる)。**再採用条件は「同型の drift が今後も再発する場合」**と明記した

--- CodeRabbit レビュー対応 (#384、5 件すべて修正) ---

1. WP-18 完了条件でスモーク未確定の扱いが不明確 (Major)
   (c) だけを非必須と書き (a)(b) の扱いが無かった。(a)(b) は事象待ちで**自力で発生させ
   られない**ため、条件に含めると WP を閉じられない。3 件すべてを非ブロッカーとし、
   理由と移管先・期限を表で明記した。(a)(b) は 2026-11-06 時点で未観測なら
   「機会が来なかった」として見送り ADR-067 の bounded lifetime へ委ねる。

2. 順位 411 の要約が詳細計画と不一致 (Minor)
   summary は「正しいコマンドを提示」だが、cargo fmt に**代替コマンドは存在しない**
   (手で直すのが正)。「正しい対処を提示」へ変更し、詳細側にもその旨を明記した。

3. 順位 398 の完了判定を対象 PR に束縛すべき (Major)
   「report 生成を完了根拠にする」案が不十分だった。copy_feedback_report は
   find_latest_run_dir で最新 run を選ぶだけで **pr_number と照合していない**ため、
   別 PR の report を現在の PR の {pr_number}.md へコピーし得る。また takt の終了は
   timeout や失敗でも起こるので終了した事実は report 完成を証明しない。実装を読んで
   裏付けたうえで、完了判定には「成功終了」と「対象 PR のものであること」の両方が
   要る旨を追記した。本セッションで実際に context.json が別 PR を指していた事象とも
   同型である。

4. 旧語彙 lint の extensions から yaml が漏れている (Minor)
   拡張子は eq_ignore_ascii_case の文字列一致で **yml と yaml は別物**。本リポジトリは
   .github/workflows/*.yml と .coderabbit.yaml の両方を持つため、yaml を落とすと
   後者が未検査になる。両方を対象に加え、理由も併記した。

5. cargo fmt の検出対象が未定義 (Major)
   完全一致だけでは cargo fmt --all / cargo +stable fmt / rustup run stable cargo fmt /
   cargo-fmt が素通りする。作業計画の先頭に「検出範囲を先に決める」を追加し、完了基準に
   「完全一致に限定する場合は素通りする形態を明記する」ことを求める形にした。

いずれも実物 (takt.rs の実装 / linter の拡張子判定 / リポジトリ内の .yml と .yaml の
共存) を確認したうえで妥当と判断している。
aloekun added a commit that referenced this pull request Aug 10, 2026
あわせて post-merge feedback (#376/#377/#380/#381/#382) の採用分 10 件を順位 402-411 へ
登録した。2026-08-10 に採用候補を系統別へ分類し、ユーザーが採否を決定したもの。

採用: 系統 A (観測の完全性) 3 件 / 系統 B (重複実装の予防) 3 件 /
      系統 C (shell・config パースの安全性) 3 件
却下: 系統 D (workflow セキュリティ標準化) / 系統 E (PAT 失効監視) — 様子見
形を変えて採用: 系統 F — 「CLAUDE.md に rustfmt 非適用の方針を書く」ではなく
      「cargo fmt を PreToolUse でブロックする」(順位 411)

系統 F の変更理由 (ユーザー判断): 規約は CLAUDE.md に書いた時点で毎セッション読まれ
コンテキストを圧迫するが、PreToolUse hook は発火するまでコストがゼロで、ブロックと
同時に正しいコマンドをフィードバックできる。読み手は規約を覚えていなくても正しい経路へ
到達する。ADR-042 の mechanizable 判定を満たすため機構側が正しい。

**この非対称は現行 ADR-042 に無い**。同 ADR の判断基準は「機械判定できるか」「投資対効果」
が中心で、「規約は常時コンテキストを消費し hook は発火時のみ」という観点が明示されて
いない。ルール追加を検討するたびに効く一般則なので、順位 411 の作業範囲に ADR-042 への
追記を含めた。

## 計画書 (harness-improvement-plan.md) の WP-18 節を再編成

**WP-18 で生んだ問題と、WP-18 の運用で日常的に踏む問題を WP-18 の外へ押し出さない**
(2026-08-10 ユーザー方針) ため、残作業を 3 区分へ分けて完了条件を明示した。

従来は観測と派生タスクが 1 表に混在し、WP-18 の完了に何が要るのかが読み取れなかった。

- (1) 観測待ち — 機構は整備済みで事象か期限を待つもの
- (2) 運用問題の対処 — WP-18 が生んだ (基準 1) / WP-18 の運用で踏む潜在バグ (基準 2)。
      順位 397 / 398-400 / 401 / 410。**完了条件に含める**
- (3) WP-18 外の派生 — 順位 396 / 411 / 402-409 等。完了条件に含めない

(3) を完了条件から外すのは、§ 7 の退役条件が「全 WP が完了または見送り」である以上、
リポジトリ全体の一般則を WP-18 に紐づけると計画書が永久に退役できなくなるため。
ただし**優先度が低いという意味ではない** — 順位 396 (flaky テスト) と 411 (cargo fmt
ブロック) はいずれも高優先度で、WP-18 とは独立に早期着手する旨を明記した。

あわせて古い記述を実測に合わせた:

- 見出しの「実装・スモークは 2026-08-08 までにほぼ完了」→ 決定 16 という新規実装が
  2026-08-10 に入ったため「観測中 + 運用問題の対処中」へ
- 「前 2 者は順位 394 後の run で判定できる」→ 順位 394 は完了済みで実際の前提は決定 16。
  同一ファイル内の自己矛盾だった
- WP-17 残課題節にも同じ「順位 394 後の run」が残っていたため同期。あわせて
  「代替解は draft 廃止」が誤りだったことも記録した

## todo 側

- 順位 396 を Tier 2 → **Tier 1** へ格上げ (ユーザー判断)。単発の Severity では Tier 2
  相当だが、flaky テストは「また flake だろう」という読み替えを生み実バグの見落とし
  経路になるため。両 OS matrix (ADR-065) の信号品質を守る意味で早期に潰す
- 順位 411 に早期着手の根拠を追記 (cargo fmt は反射的に実行されやすい)
- **却下を negative result として記録**: 系統 D / E は様子見。trunk 保護の drift 対処
  2 件は却下 (予防側は順位 405 で押さえた / 共有 lib 化は network isolation 設計と
  抵触しうる)。**再採用条件は「同型の drift が今後も再発する場合」**と明記した

--- CodeRabbit レビュー対応 (#384、5 件すべて修正) ---

1. WP-18 完了条件でスモーク未確定の扱いが不明確 (Major)
   (c) だけを非必須と書き (a)(b) の扱いが無かった。(a)(b) は事象待ちで**自力で発生させ
   られない**ため、条件に含めると WP を閉じられない。3 件すべてを非ブロッカーとし、
   理由と移管先・期限を表で明記した。(a)(b) は 2026-11-06 時点で未観測なら
   「機会が来なかった」として見送り ADR-067 の bounded lifetime へ委ねる。

2. 順位 411 の要約が詳細計画と不一致 (Minor)
   summary は「正しいコマンドを提示」だが、cargo fmt に**代替コマンドは存在しない**
   (手で直すのが正)。「正しい対処を提示」へ変更し、詳細側にもその旨を明記した。

3. 順位 398 の完了判定を対象 PR に束縛すべき (Major)
   「report 生成を完了根拠にする」案が不十分だった。copy_feedback_report は
   find_latest_run_dir で最新 run を選ぶだけで **pr_number と照合していない**ため、
   別 PR の report を現在の PR の {pr_number}.md へコピーし得る。また takt の終了は
   timeout や失敗でも起こるので終了した事実は report 完成を証明しない。実装を読んで
   裏付けたうえで、完了判定には「成功終了」と「対象 PR のものであること」の両方が
   要る旨を追記した。本セッションで実際に context.json が別 PR を指していた事象とも
   同型である。

4. 旧語彙 lint の extensions から yaml が漏れている (Minor)
   拡張子は eq_ignore_ascii_case の文字列一致で **yml と yaml は別物**。本リポジトリは
   .github/workflows/*.yml と .coderabbit.yaml の両方を持つため、yaml を落とすと
   後者が未検査になる。両方を対象に加え、理由も併記した。

5. cargo fmt の検出対象が未定義 (Major)
   完全一致だけでは cargo fmt --all / cargo +stable fmt / rustup run stable cargo fmt /
   cargo-fmt が素通りする。作業計画の先頭に「検出範囲を先に決める」を追加し、完了基準に
   「完全一致に限定する場合は素通りする形態を明記する」ことを求める形にした。

いずれも実物 (takt.rs の実装 / linter の拡張子判定 / リポジトリ内の .yml と .yaml の
共存) を確認したうえで妥当と判断している。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant