Skip to content

fix(push-runner): pre-push レビューを PR 全体に修正 + 範囲カバレッジ検査 (順位 288/264) - #313

Merged
aloekun merged 3 commits into
masterfrom
fix/prepush-review-pr-range
Jul 21, 2026
Merged

fix(push-runner): pre-push レビューを PR 全体に修正 + 範囲カバレッジ検査 (順位 288/264)#313
aloekun merged 3 commits into
masterfrom
fix/prepush-review-pr-range

Conversation

@aloekun

@aloekun aloekun commented Jul 21, 2026

Copy link
Copy Markdown
Owner

概要

pnpm push の pre-push AI レビューが tip コミット 1 本しか見ていなかった欠陥を修正します。push-runner-config.toml[diff] commandjj diff -r @ が直書きされており、複数コミットを 1 回の push で送ると 祖先コミットがセルフレビューを一度も経ずに merge される状態でした。docs/todo-summary2.md 順位 288(🚀 Tier 1)として既知で、Severity High で PR #268/#300/#301/#3114 回再発していた事案です。順位 264(--git 形式切替)も同時に解消します。

根本原因

同じパイプライン内の pr_size_checkdocs_only_routing<base>..@(PR 範囲)を Rust 側で組み立てていましたが、[diff] だけが jj diff -r @(tip のみ)で 非対称でした。しかも docs_only_routing.rs の doc コメントにこの非対称が明記されたまま放置されていました。レビュアーは渡された diff が PR 全体かを検証できないため、範囲が狭いことによる見落としは誰にも検知されません(実際 security-review が実 diff と矛盾して "docs-only / No dependency changes" と報告していました)。

変更

  • 範囲の真実源を 1 箇所に: top-level default_branch を新設し、diff / docs_only_routing / pr_size_check の 3 stage が Config::resolve_base_branch 経由で同一の値を使う。section 側の default_branch は後方互換の override として残す。
  • config から revset を排除: [diff] command{{PR_RANGE}} プレースホルダを使い、push-runner が <base>..@ に展開する(狭い範囲を直書きできない)。
  • 範囲カバレッジ検査(fail-closed): 生成 diff が PR 範囲の全変更ファイルを含むか jj diff --summary と突き合わせ、不足なら exit 5。config の書き方に依存せず「レビュー範囲 < PR 範囲」を検知できるため、未更新の派生プロジェクト config も捕まえる。
  • --git 形式へ切替(順位 264): 範囲検査がヘッダを読む要件に加え、jj 既定形式は色を落とすと +/- が消え LLM が削除を追加と誤読する(PR docs(adr): ADR-051 クロスシステム設定 coupling パターンを起票 (順位263) #256 で実害)。
  • templates/push-runner-config.toml も同時修正(pnpm deploy:hooks で派生配布のため必須)。ADR-051(cross-system config coupling)として exe/config 同時更新義務を両 config にコメント記載。

ADR-027 の射程を実測で確定

ADR-027 が狭めたのは reviewer の観点(cross-file 探索の除去)であってコミット範囲ではなかった、という点を amendment として明文化しました。範囲拡張のコストを実測:

レビュー対象 行数 所要時間
tip のみ 37 行 4m 32s
PR 全体 1011 行 4m 43s

27 倍に広げて +11 秒。ADR-027 の速度改善は arch-review facet 除去によるもので、範囲縮小は寄与していないことが判明したため、範囲拡張を採用しました。

この PR 自身が範囲修正の価値を実証

範囲検査コード(私が書いた部分)に対し、PR 全体をレビュー対象にしたことで pre-push セルフレビューが 3 ラウンドに渡り 5 件の指摘を検出しました(すべて親コミットにあり tip-only レビューでは 1 件も見えなかった)。5 件は 5 コミット目(この PR の tip、"欠陥 5 件を修正")に集約:

  1. rename/copy 行の誤パースで正常な push を false-abort(blocking)— R <old> <new> を 1 個の壊れたパスとして扱い、rename を含む push が必ず exit 5 で停止。実 jj 出力で修正を検証済み。
  2. summary パース不能時に gate が無言で機能停止 — 空集合に丸めて素通ししていたのを、「空(変更なし)」と「行はあるがパース不能(書式変更)」を区別し後者は fail-closed。
  3. PR 範囲導出の重複post_takt_regate.rsConfig::diff_pr_range() に統一。
  4. summary status を M/A/D/R/C に限定 — catch-all で書式変化行を妥当パス化して 2 の fail-closed を不発にしていた穴。テストも意図した分岐(旧テストは別分岐でたまたま Err)を突くよう修正。
  5. 崩れた R/C 行の silent-drop 穴 — 崩れ rename 行が黙って drop され coverage をすり抜け得た。生トークンを残し fail-closed に。

実測で見つけた副次バグ(実シェルで検証)

範囲 summary の取得を当初シェル経由で実装したところ、cmd.exe はクォートを除去せず jj に渡すため Revision '"master..@"' doesn't exist で失敗しました。sh は除去するので Linux だけ通る = Windows の全 push が止まる形。ユニットテストは summary 取得を注入していたため検出できず、実シェルで叩いて初めて判明しました。direct args 呼び出しに修正済み。

既知の限定事項(意図的に未修正)

summary_line_new_pathrsplit_once(' ') は、rename 先パス自体に空白が含まれると誤分割します。ただし self-mitigating(silent pass ではなく fail-closed の missing-files に落ちる)で、正しく直すには jj の空白パス escaping 形式の調査が必要、かつ本リポジトリに空白入りパスは存在しないため、修正コスト > 得られる安全性と判断し未修正としました(security-review も non-blocking と評価)。

検証

  • Windows + WSL Linux 双方で cargo test --workspace 全 pass・clippy clean・lint:docs / lint:md 退行なし。
  • 範囲修正が実地で効くことを push で実証: diff が 37 行 → 1322 行(PR 全体)に拡大。
  • pre-push セルフレビューは最終的に両レビュアー APPROVE。

依存関係(マージ順序)

この PR は master ベースのため、#312(pipeline lock race 修正)を先にマージしてから本 PR を master に rebase すると、本 PR の CI に含まれる flaky なロックテスト(#312 が修正した race 由来)が解消されます。

🤖 Generated with Claude Code

Summary by CodeRabbit

  • 改善

    • プッシュ時のレビュー、docs-only 判定、PRサイズ検査がPR全体の変更範囲を対象にするようになりました。
    • 変更範囲が不足・判定不能な場合は処理を中断し、見落としを防止します。
    • 各チェックで同一のベースブランチと変更範囲を使用するよう統一しました。
  • ドキュメント

    • PR全体をレビュー対象とする方針、背景、検証方法をADRとTODO資料に追記・整理しました。

@coderabbitai

coderabbitai Bot commented Jul 21, 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

Run ID: eda9f5e5-d7c4-4d8e-ba28-d738fa80a145

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

PR全体のbase branch範囲をConfigで一元解決し、diff・docs-only routing・PRサイズ検査・post-takt再ゲートへ渡すよう変更した。diff生成では範囲カバレッジを検証し、不足時にfail-closedで停止する。

Changes

PR範囲レビュー統一

Layer / File(s) Summary
PR範囲解決と設定検証
src/cli-push-runner/src/config/*
default_branch<base>..@ の解決をConfigへ集約し、3 stageの範囲不一致を設定検証で拒否する。
Diff範囲展開とカバレッジ検査
push-runner-config.toml, templates/push-runner-config.toml, src/cli-push-runner/src/stages/diff.rs
{{PR_RANGE}} を使ってgit diffを生成し、jj diff --summary とdiffヘッダのパス集合を比較する。解析不能や不足時は出力せずエラーにする。
各stageへのPR範囲受け渡し
src/cli-push-runner/src/main.rs, src/cli-push-runner/src/stages/{docs_only_routing,pr_size_check,post_takt_regate}.rs
各stageとpost-takt再ゲートがConfigで解決したPR範囲を受け取って処理する。
ADRとTODOの更新
docs/adr/adr-027-push-review-simplicity-focus.md, docs/todo-summary2.md, docs/todo15.md
PR全体をレビュー対象とする方針、範囲検査の実装状況、残課題を記録する。

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

Sequence Diagram(s)

sequenceDiagram
  participant PushRunner
  participant Config
  participant DiffStage
  participant jj
  PushRunner->>Config: PR範囲を解決
  Config-->>PushRunner: <base>..@
  PushRunner->>DiffStage: PR範囲を渡してdiff実行
  DiffStage->>jj: diff --git -r <base>..@
  DiffStage->>jj: diff --summary <base>..@
  jj-->>DiffStage: 変更ファイル集合
  DiffStage-->>PushRunner: 検証済みdiffまたはエラー
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/prepush-review-pr-range

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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.

aloekun added 2 commits July 22, 2026 00:31
## 問題

`[diff] command` に `jj diff -r @` が直書きされており、AI レビュアーには **tip
コミットの diff しか渡っていなかった**。祖先コミットは pre-push のセルフレビューを
一度も経ずに merge される。

同じパイプライン内で `pr_size_check` と `docs_only_routing` は `<base>..@` (PR 範囲)
を見ており、`[diff]` だけが非対称だった。実際 PR #311 では 695 行の PR に対して
37 行だけがレビュー対象になり、レビュアーは渡された 37 行を見て正しく「docs-only」と
判定していた。**レビュアー側からは「渡された diff が PR 全体か」を検証できない**ため、
この誤りは誰にも検知されない。

docs/todo-summary2.md 順位 288 として既知で、Severity High で PR #268/#300/#301 に
続き #311 が 4 回目の再発。

## 変更

- **範囲の真実源を 1 箇所に**: top-level `default_branch` を新設し、`diff` /
  `docs_only_routing` / `pr_size_check` の 3 stage が `Config::resolve_base_branch`
  経由で同じ値を使う。従来は section ごとに独立した `default_branch` を持ち、
  「値を同期する義務」を config コメントで課していた (docs_only_routing.rs の doc に
  明記されていた) が、義務はコード上の不変条件ではないため非対称を許していた。
  section 側は後方互換の override として残す (派生プロジェクトの既存 config 対策)。
- **config から revset を排除**: `[diff] command` は `{{PR_RANGE}}` プレースホルダを
  使う。push-runner が `<base>..@` に展開するため、狭い範囲を書く余地が無くなる。
- **範囲カバレッジ検査 (fail-closed)**: 生成した diff が PR 範囲の全変更ファイルを
  含むか `jj diff --summary` と突き合わせ、不足があれば exit 5 で中断する。config の
  書き方に依存せず「レビュー範囲 < PR 範囲」を検知できるため、未更新の派生プロジェクト
  config も捕まえる。summary 取得失敗・`diff --git` ヘッダ不在 (= 収録ファイルを特定
  できない) も「網羅している」に倒さずエラーにする (ADR-043)。
- **`--git` 形式へ切替 (順位 264)**: 範囲検査がヘッダを読む要件に加え、jj 既定形式は
  色を落とすと `+`/`-` が消えて LLM レビュアーが削除を追加と誤読する (PR #256 で
  simplicity-review が todo 25 行の削除を追加と誤読し false positive REJECT、約 19 分
  浪費)。
- `templates/push-runner-config.toml` も同時修正 (deploy:hooks で派生配布されるため)。

## 実測で見つけた副次バグ

範囲 summary の取得を当初 shell 経由 (`[diff] command` と同じ経路) で実装したが、
実シェルで叩いたところ **cmd.exe はクォートを除去せず jj に渡す**ため
`Revision '"<base>..@"' doesn't exist` で必ず失敗した。sh は除去するので Linux だけ
通る = Windows の全 push が fail-closed で止まる形。ユニットテストは summary 取得を
注入していたため検出できなかった。direct args 呼び出し (docs_only_routing の既存
`run_jj_diff_summary`) を共有する形に修正し、doc に理由を残した。両 stage は
「PR 範囲の変更ファイル一覧」という同一の問いを扱うため、別実装にすると本 PR が
排除した非対称を再導入することになる。

## 検証

cargo test --workspace 全 pass / clippy clean。範囲検査は incident 形状 (PR 2 ファイル
変更に対し diff は tip の 1 ファイルのみ) の再現テストで固定し、fail-closed 経路
(summary 取得失敗 / ヘッダ不在) と過剰検知しない経路 (空 PR 範囲 / Windows パス
区切りの正規化) も併せて固定した。base branch 解決は 3 stage が同一範囲に解決される
ことと override の優先順位を machine-enforce している。
## ADR-027 amendment

ADR-027 が狭めたのは reviewer が使う criteria (cross-file 探索を要求しない)
であり、レビュー対象に含めるコミットの範囲ではなかった。この 2 つが混同され、
`[diff] command` が tip コミット限定のまま運用されて 4 回の再発を招いたため、
射程を明文化した。

「レビュー対象は PR 範囲全体」と決定した根拠も併記:

- 速度は理由にならない。同一 PR でレビュー対象を 37 行 → 1011 行 (27 倍) に
  広げても 4m32s → 4m43s の +11 秒。ADR-027 の速度改善は arch-review facet の
  除去 (219-270s/iter) によるもので、範囲縮小は寄与していなかった。
- 範囲が狭いことによる見落としはレビュアー側から検知できない (渡された diff が
  PR 全体かを検証する手段が無い)。
- CodeRabbit backstop はセルフレビューを省く理由にならない。独立した層として併用する。

## todo 更新

- 順位 264 (`--git` 切替): 完了につきエントリと table 行を削除。
- 順位 288: `[diff]` 範囲修正の部分のみ完了として記録。**残タスク** (post-merge
  feedback の全 run 集約、bookmark_check.rs の祖先未レビュー穴の検証) は明示して
  残す。エントリ全体を消すと未着手部分が失われるため削除しない。
@aloekun

aloekun commented Jul 21, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 5

🤖 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/todo15.md`:
- Around line 447-449: docs/todo15.md の該当エントリで、[diff] stage の PR
範囲化に関する作業計画と完了基準を実装済みとして更新してください。既存の実装済み記述と整合させ、完了したチェック項目を未完了一覧から削除し、残タスクとして
post-merge feedback の全 run 集約と bookmark_check.rs の祖先未レビュー穴の検証だけを残してください。

In `@src/cli-push-runner/src/config/mod.rs`:
- Around line 64-83: Update the base-branch resolution and validation flow
around Config::resolve_base_branch, load_config, and
validate_base_branch_ranges_agree so a legacy section-level default_branch is
resolved once as the shared base branch for the entire configuration. Ensure all
stages, including absent or docs-only stages, use that same effective branch,
preventing valid legacy configurations from being rejected due to inconsistent
defaults.

In `@src/cli-push-runner/src/stages/diff.rs`:
- Around line 140-153: 設定ロード時に[diff]
commandへ{{PR_RANGE}}プレースホルダーが必ず含まれるよう検証を追加し、未設定の場合は明確なエラーで失敗させてください。diff検証処理のmissing判定や成功条件は変更せず、既存の設定読み込み処理と[diff]
commandのシンボルを利用して実装してください。
- Around line 121-130: Update parse_summary_paths to return a Result and reject
any non-empty summary containing an unknown status or malformed R/C entry
instead of silently dropping it. Propagate that parse error through the caller
before evaluating expected.is_empty(), while preserving the successful
empty-summary behavior and existing valid-entry parsing.
- Around line 266-269: Move the verify_diff_covers_pr_range check in the diff
execution flow before the empty-output branch, ensuring fetch_summary is
obtained and coverage is validated even when output is empty. Preserve
report_coverage_failure and DiffResult::Error on failure, then return
DiffResult::Empty only after coverage validation succeeds.
🪄 Autofix (Beta)

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

Run ID: 3739835e-e363-4b16-9967-8d96a8549d51

📥 Commits

Reviewing files that changed from the base of the PR and between d7c2dda and 1ec2eb6.

📒 Files selected for processing (14)
  • docs/adr/adr-027-push-review-simplicity-focus.md
  • docs/todo-summary2.md
  • docs/todo15.md
  • push-runner-config.toml
  • src/cli-push-runner/src/config/docs_only_routing.rs
  • src/cli-push-runner/src/config/lint_screen.rs
  • src/cli-push-runner/src/config/mod.rs
  • src/cli-push-runner/src/config/pr_size_check.rs
  • src/cli-push-runner/src/main.rs
  • src/cli-push-runner/src/stages/diff.rs
  • src/cli-push-runner/src/stages/docs_only_routing.rs
  • src/cli-push-runner/src/stages/post_takt_regate.rs
  • src/cli-push-runner/src/stages/pr_size_check.rs
  • templates/push-runner-config.toml
💤 Files with no reviewable changes (1)
  • src/cli-push-runner/src/config/pr_size_check.rs

Comment thread docs/todo15.md
Comment on lines +447 to +449
> **(a) 実装済 (2026-07-21)**: PR #311 で 4 回目の再発 (695 行の PR に対しレビュー対象 37 行、security-review が実 diff と矛盾して "docs-only / No dependency changes" と記載) を観測し、`[diff]` stage を修正した。top-level `default_branch` を新設して `diff` / `docs_only_routing` / `pr_size_check` の 3 stage が同一解決を共有し、`[diff] command` は `{{PR_RANGE}}` プレースホルダ経由で範囲を受け取る (config に revset を直書きできない)。加えて生成 diff が PR 範囲の全変更ファイルを含むかを `jj diff --summary` と突き合わせる**範囲カバレッジ検査**を fail-closed で追加し、config の書き方に依存せず「レビュー範囲 < PR 範囲」を検知できるようにした (未更新の派生プロジェクト config も捕まる)。順位 264 (`--git` 形式切替) も同 PR で同時実施 (範囲検査が `diff --git` ヘッダを読む要件と重なるため)。**ADR-027 の射程についてのユーザー判断**: 範囲拡張のレビュー時間コストを実測したところ 37 行 4m32s → 1011 行 4m43s (**+11 秒**) で、ADR-027 の速度改善は arch-review facet 除去によるものであり diff 範囲縮小は寄与していないことが判明したため、範囲拡張を採用した。
>
> **残タスク**: 本エントリ本体の「post-merge feedback の全 run 集約」と、上記 (b) `bookmark_check.rs` の祖先未レビュー穴の検証は未着手。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

実装済みの作業計画を同期してください。

ここでは [diff] stage の PR 範囲化を「実装済み」としていますが、同じエントリ内の作業計画・完了基準には、なお未完了の作業として残っています。該当チェック項目を完了扱いに更新し、残タスクだけを記載してください。

🤖 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 `@docs/todo15.md` around lines 447 - 449, docs/todo15.md の該当エントリで、[diff] stage
の PR
範囲化に関する作業計画と完了基準を実装済みとして更新してください。既存の実装済み記述と整合させ、完了したチェック項目を未完了一覧から削除し、残タスクとして
post-merge feedback の全 run 集約と bookmark_check.rs の祖先未レビュー穴の検証だけを残してください。

Comment on lines +64 to +83
impl Config {
/// PR base branch を解決する。優先順: section override → top-level → 既定値。
///
/// section override は後方互換のために残してある (既存の派生プロジェクト config が
/// `[pr_size_check] default_branch` 等を持つため)。新規に section 側で持たせないこと。
/// override 値が stage 間で食い違わないことは [`validate_base_branch_ranges_agree`]
/// が `load_config` 時に fail-closed で保証する。
pub(crate) fn resolve_base_branch(&self, section_override: Option<&str>) -> String {
section_override
.map(str::trim)
.filter(|s| !s.is_empty())
.map(str::to_string)
.or_else(|| {
self.default_branch
.as_deref()
.map(str::trim)
.filter(|s| !s.is_empty())
.map(str::to_string)
})
.unwrap_or_else(|| DEFAULT_BASE_BRANCH.to_string())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

section override が後方互換になっていません。

例えば既存 config が [pr_size_check] default_branch = "main" だけを持つ場合、PR size は main..@、存在しない diff/docs-only stage は master..@ となり、Line 262 で設定全体が拒否されます。実際に有効な stage だけを比較するか、legacy override を共通 base branch として一度だけ解決してください。

Also applies to: 255-267

🤖 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-push-runner/src/config/mod.rs` around lines 64 - 83, Update the
base-branch resolution and validation flow around Config::resolve_base_branch,
load_config, and validate_base_branch_ranges_agree so a legacy section-level
default_branch is resolved once as the shared base branch for the entire
configuration. Ensure all stages, including absent or docs-only stages, use that
same effective branch, preventing valid legacy configurations from being
rejected due to inconsistent defaults.

Comment thread src/cli-push-runner/src/stages/diff.rs Outdated
Comment on lines +121 to +130
let expected = parse_summary_paths(&summary);
if expected.is_empty() {
if summary.trim().is_empty() {
return Ok(());
}
return Err(format!(
"summary に行はあるが 1 件もパースできませんでした ({} 行)。jj の出力書式が\
変わった可能性があります",
summary.lines().filter(|l| !l.trim().is_empty()).count()
));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

未知 status が既知 status と混在すると coverage から消えます。

M known.rsX omitted.rs が混在すると、Line 194 で後者だけが破棄され、expected が空ではないため parse エラーにもなりません。parser を Result にして、非空行が一つでも未知 status、または不正な R/C 形式なら fail-closed にしてください。

Also applies to: 168-197

🤖 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-push-runner/src/stages/diff.rs` around lines 121 - 130, Update
parse_summary_paths to return a Result and reject any non-empty summary
containing an unknown status or malformed R/C entry instead of silently dropping
it. Propagate that parse error through the caller before evaluating
expected.is_empty(), while preserving the successful empty-summary behavior and
existing valid-entry parsing.

Comment on lines +140 to +153
let missing: Vec<&String> = expected.iter().filter(|p| !covered.contains(*p)).collect();
if missing.is_empty() {
return Ok(());
}
Err(format!(
"{} ファイルが未収録 (例: {})",
missing.len(),
missing
.iter()
.take(3)
.map(|s| s.as_str())
.collect::<Vec<_>>()
.join(", ")
))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

ファイル集合の一致だけでは tip-only diff を検出できません。

祖先と tip が同じファイルを変更した場合、jj diff -r @ でも expected ⊆ covered が成立し、祖先の hunks を欠いたまま Line 142 で成功します。少なくとも [diff] command{{PR_RANGE}} が含まれることを設定ロード時に必須化してください。

🤖 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-push-runner/src/stages/diff.rs` around lines 140 - 153, 設定ロード時に[diff]
commandへ{{PR_RANGE}}プレースホルダーが必ず含まれるよう検証を追加し、未設定の場合は明確なエラーで失敗させてください。diff検証処理のmissing判定や成功条件は変更せず、既存の設定読み込み処理と[diff]
commandのシンボルを利用して実装してください。

Comment thread src/cli-push-runner/src/stages/diff.rs Outdated
Comment on lines +266 to +269
if let Err(reason) = verify_diff_covers_pr_range(&output, fetch_summary) {
report_coverage_failure(pr_range, &reason);
return DiffResult::Error;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

空出力も coverage 検査後に判定してください。

現在は tip が空なら summary を取得せず DiffResult::Empty になります。祖先に変更がある古い -r @ config では AI review を完全に skip できるため、Line 266 の検査を空出力分岐より前へ移動してください。

修正案
+    if let Err(reason) = verify_diff_covers_pr_range(&output, fetch_summary) {
+        report_coverage_failure(pr_range, &reason);
+        return DiffResult::Error;
+    }
+
     if output.is_empty() {
         ...
         return DiffResult::Empty;
     }
-
-    if let Err(reason) = verify_diff_covers_pr_range(&output, fetch_summary) {
-        report_coverage_failure(pr_range, &reason);
-        return DiffResult::Error;
-    }

Also applies to: 563-574

🤖 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-push-runner/src/stages/diff.rs` around lines 266 - 269, Move the
verify_diff_covers_pr_range check in the diff execution flow before the
empty-output branch, ensuring fetch_summary is obtained and coverage is
validated even when output is empty. Preserve report_coverage_failure and
DiffResult::Error on failure, then return DiffResult::Empty only after coverage
validation succeeds.

@github-actions

Copy link
Copy Markdown
Contributor

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

  • トリガー: pull_request_review (submitted) / 実行 run
  • CI: CodeRabbit チェックは pass (Review completed)。analyze チェックは pending (本バックストップ自身の実行)。他に失敗している check なし。mergeStateStatus は UNSTABLE (analyze 未完了によるものと推測)。
  • レビュー状況: CodeRabbit が 1 件のレビュー (COMMENTED, actionable comments 5件) を投稿。人間レビュアーによるレビューはまだ無し (reviewDecision 空)。
  • Verdict: needs_fix

Applicable Findings (Critical / High / Major)

# File (Line) Reviewer Issue Recommended Action
1 src/cli-push-runner/src/stages/diff.rs (266-269) CodeRabbit verify_diff_covers_pr_range の呼び出しが output.is_empty() の早期 return より後にあるため、tip diff が空 (-r @ 由来の古い config 等) の場合は範囲カバレッジ検査自体がスキップされ、祖先コミットの変更が無検査で DiffResult::Empty (=レビューskip) になる。本 PR が塞ごうとしている「祖先コミットが未レビューのまま merge される」欠陥を、この分岐が再導入している verify_diff_covers_pr_range の呼び出しを output.is_empty() チェックより前に移動する (CodeRabbit の diff 修正案どおり)
2 src/cli-push-runner/src/stages/diff.rs (140-153) CodeRabbit カバレッジ検査はファイル集合の一致のみを見ており、祖先とtipが同一ファイルを変更した場合 (hunk 単位では祖先の変更を欠く) を検出できない。[diff] command{{PR_RANGE}} が実際に使われているかを設定ロード時に必須検証すれば、この構造的な穴を機械的に塞げる 設定ロード時に [diff] command{{PR_RANGE}} プレースホルダが含まれることを検証し、含まれない場合は明確なエラーで fail-closed にする
3 src/cli-push-runner/src/stages/diff.rs (121-130) CodeRabbit parse_summary_paths は未知 status の行を黙って drop するため、既知 status の行が1件でも summary に混在すると expected が非空になり、パース失敗検知 (expected.is_empty()) が発火しない。結果、未知 status のファイルがカバレッジ検査から静かに漏れる parse_summary_pathsResult 化し、非空 summary に未知 status や不正な R/C 形式が1件でもあれば全体をエラーにする。呼び出し側は expected.is_empty() 判定前にこのエラーを伝播させる
4 src/cli-push-runner/src/config/mod.rs (64-83, 255-267) CodeRabbit section 側 default_branch の後方互換 override が、他 stage (docs_only_routing / diff) が top-level default に解決される場合と食い違うと validate_base_branch_ranges_agree で設定全体が拒否される。例: 既存 config が [pr_size_check] default_branch = "main" のみを持つ (top-level 未設定) 場合、main..@ vs master..@ の不一致で reject される 実際に有効な stage 同士だけを比較するか、legacy section override を「共通 base branch」として一度だけ解決するよう見直す。ADR-051 (cross-system config coupling) の射程との整合をユーザー判断で確認

Applicable Findings (Medium 以下)

# File (Line) Reviewer Issue Recommended Action
1 docs/todo15.md (447-449) CodeRabbit 同エントリ内で「実装済み」と記述した箇所と、まだ未完了扱いの作業計画・完了基準チェック項目が整合していない 完了したチェック項目を実装済み記述に合わせて更新し、残タスク (post-merge feedback の全 run 集約、bookmark_check.rs の祖先未レビュー穴の検証) のみを残す

Filtered (not applicable)

該当なし

次のアクション

  • Critical 3件 (diff.rs のカバレッジ検査順序・hunk 単位検出・未知 status ハンドリング) は本 PR の目的 (祖先コミット未レビュー merge の防止) の実効性に直結するため、優先的に修正すること。
  • config/mod.rs の後方互換性回帰 (Major) は、既存の派生プロジェクト config を壊す可能性があるため、修正方針をユーザー判断のうえで確定させること。
  • docs/todo15.md の整合性修正 (Minor) は上記と合わせてバッチで対応可能。

@aloekun
aloekun force-pushed the fix/prepush-review-pr-range branch from 1ec2eb6 to a410e29 Compare July 21, 2026 16:01
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