Skip to content

feat: Linux バイナリビルド + クラウド setup script (WP-15) - #307

Merged
aloekun merged 10 commits into
masterfrom
wp-15-linux-binaries
Jul 20, 2026
Merged

feat: Linux バイナリビルド + クラウド setup script (WP-15)#307
aloekun merged 10 commits into
masterfrom
wp-15-linux-binaries

Conversation

@aloekun

@aloekun aloekun commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Summary

  • master push で Linux バイナリをビルドし rolling prerelease (nightly) へ単一 tarball として公開する release-binaries.yml を追加
  • Release からの取得 → .claude/ 配置 → settings 生成 → jj / takt 導入までを行う scripts/cloud-setup.sh を追加
  • Linux で実行時に壊れる可搬性欠陥を修正: 唯一の shell spawn 点だった cmd /c 決め打ちを shell_command (Windows=cmd /c / 他=sh -c) に集約し、taskkill のみだった timeout kill に unix 分岐を追加
  • デプロイ時 config の .exe / backslash 依存を {{CLAUDE_DIR}} / {{EXE_SUFFIX}} の実行時展開で解消 (WP-13 からの引き継ぎ)
  • Linux 実測で発見した cli-pr-monitor の lock 同時取得レースを修正 (空ロックファイルを stale と誤判定して全員が takeover していた)
  • .gitignore.claude/*.exe のみで拡張子なし Linux バイナリを無視しない問題を修正

Context

使い捨てのクラウドセッション (claude.ai/code) で 19 crate をビルドせずにハーネスを即時有効化するのが目的 (WP-15)。

スコープ補正: 計画本文のステップは「release workflow + setup script」だけだったが、着手時調査で Linux では実行時に壊れる可搬性欠陥が残っていることが判明した。lib-subprocesscmd /c はリポジトリ唯一の shell spawn 点で、Linux では quality_gate / push / merge の全 step が無言で失敗扱いになる。check-ci-coderabbit の timeout kill は Windows 分岐しか無く、Linux では wait_with_output が無限ハングする。受け入れ基準「cargo test と push pipeline の dry-run が通る」はこれらを直さないと満たせないため、前提として本 PR に含めた。

musl 不採用: lib-ollama-client は ureq + rustls で openssl 非依存だが、hooks-post-tool-comment-lint-rust の tree-sitter が C コンパイルを含むため musl-tools のセットアップが必要になる。実行先が Ubuntu 系で glibc 2.35 なら十分と判断し、ubuntu-22.04 の gnu ターゲットに固定した。

計画の要確認事項への回答: public リポジトリの Release asset は素の HTTPS で取得できるため、gh CLI 認証は不要。トークン受け渡し構成を setup script に持ち込まずに済んでいる。

Validation

  • Windows: cargo test --workspace 全 pass / clippy --workspace --all-targets --all-features -- -D warnings clean / pnpm build:all 16 成果物デプロイ / lint:docs / lint:md pass
  • Linux (WSL Ubuntu 24.04, 実 Linux): cargo test --workspace 全 pass / cargo test -- --ignored --test-threads=1 全 pass (jj 0.42.0 導入後) / clippy clean
  • Linux での hooks 実発火: SessionStart が additionalContext JSON を出力 / PreToolUse が再帰強制削除コマンドを exit 2 でブロックし echo hello は通す / tree-sitter の comment-lint が違反を検出
  • Linux での push pipeline: quality_gate の rust-lint-test が sh -c 経由で clippy・cargo test とも PASS
  • cloud-setup.sh の jj 取得: 実 URL・実展開ロジックで jj 0.42.0 の導入を実走確認
  • pre-push review: verdict=APPROVE (simplicity / security とも、2026-07-20 18:19)
  • /review-local (qwen3-coder:30b, 2 ラン): findings 4 件、いずれも機構の誤読と判定し全件 rejected
  • 未実施: 実クラウドセッションでの cloud-setup.sh 実走。nightly release は本 PR マージ後に初回生成されるため、確認はマージ後

References

Summary by CodeRabbit

  • 新機能

    • Linux向けバイナリを自動ビルドし、Nightlyリリースから取得できるようになりました。
    • クラウド環境でハーネスや関連設定を一括セットアップするスクリプトを追加しました。
  • バグ修正

    • Windows・Linux・macOS間のコマンド実行互換性を改善しました。
    • 処理のタイムアウト時に子プロセスを適切に終了できるようになりました。
    • ロック取得中の誤った同時実行を防止しました。
  • ドキュメント

    • Linux対応とセットアップ手順の進捗・受け入れ基準を更新しました。

aloekun and others added 8 commits July 20, 2026 16:36
`run_cmd_shell_*` の唯一の spawn 点が `Command::new("cmd").args(["/c", ...])` に
固定されており、Linux では spawn が ENOENT で失敗して quality_gate / push /
merge の全 step が無言で失敗扱いになる状態だった。

OS を判定して `cmd /c` (Windows) / `sh -c` (それ以外) を返す `shell_command` を
新設し、`run_cmd_shell_with` をこれ経由に変更。`sh` を選んだのは step の cmd が
`pnpm test` 等の単純なコマンド行で bash 固有構文を使わないためで、bash 不在の
最小コンテナ (クラウドの使い捨て環境) でも同じ経路が通ることを優先した。

テストの fixture も cmd.exe 構文 (`ping -n` / `for /L`) を OS 別 const
(LONG_RUNNING_CMD / EMIT_60_LINES_CMD) に出し分け、所要時間・出力行数を両 OS で
揃えて timeout / truncation の検証カバレッジが片側に落ちないようにした。

Windows で cargo test -p lib-subprocess 33/33 pass を実測。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
cmd.exe 構文 (`for /L` / `type nul` / `exit /b` / `A & B` / `ping -n`) をテストに
直書きしていた箇所が cfg 未ガードのまま残っており、Linux では cargo test が
panic または assert 失敗する状態だった。

対象:
- cli-push-runner diff.rs: cmd 直起動を lib_subprocess::shell_command へ集約し、
  100 行出力 / 0 バイト出力 / stderr+非 0 終了 / stdout+stderr 併出の 4 fixture を
  OS 別 const 化
- cli-push-runner push.rs / quality_gate.rs: cap (40 行) を超える出力の fixture を
  OS 別 const 化。行数と拒否行の文面を両 OS で揃え、片側だけ主題を検証しなくなる
  ことを防ぐ
- cli-pr-monitor classifier_runner.rs: cfg 未ガードの `Command::new("cmd")` 2 箇所
  (Linux では expect で panic) を shell_command 経由に置換

既に #[cfg(windows)] でガード済みのテスト (runner.rs / poll/iteration.rs /
lint_screen/classifier.rs / t7_cwd_independence.rs) は Linux では skip される。
pump_child_io の deadlock 保護と stdout/stderr 分離が Linux leg で無検証になる点は
WP-16 (CI matrix) で扱う。

Windows で cli-push-runner 256/256・cli-pr-monitor 254/254 pass を実測。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
タイムアウト時の kill が #[cfg(target_os = "windows")] の taskkill のみで、
非 Windows 側の分岐が存在しなかった。Linux では deadline 到達で timeout_flag は
立つものの子は生き続け、直後の wait_with_output が子の自然終了までブロックする。
gh がハングした場合に 30 秒 timeout が一切機能せず、CI 監視が永久停止する。

kill_process_by_id を新設し Windows=taskkill /F・Unix=kill -9 に分岐。外部コマンド
経由にしたのは libc 依存を増やさないため。あわせて child_id が Linux ビルドで
unused_variables warning になる問題も解消する (本リポジトリの品質ゲートは
clippy -D warnings のため warning は実質ビルド失敗と等価)。

関数長ゲート (50 行) に触れたため run_gh からタイマースレッド起動部を
spawn_timeout_killer として抽出。done_flag の立て忘れが PID 再利用時に無関係の
プロセスを殺しうる点を doc に明記した。

Windows で cargo test -p check-ci-coderabbit 94/94 pass を実測。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…WP-15)

WP-13 がスコープ外として本 WP に委ねたデプロイ時 config の cmd.exe 依存を解消する。

file-length step は `.\.claude\hooks-post-tool-comment-lint-rust.exe` という
backslash 相対パス + .exe 決め打ちで、sh 経由になる Linux では解決できなかった。
一方 cmd.exe は forward-slash の相対パス (`.claude/foo.exe`) をコマンドとして
解決できない。実測の結果、両シェルが共通で通るのは **forward-slash の絶対パス**
だけだったため (ADR-005 が settings.local.json で確認済みの性質と同じ)、
hooks-stop-quality 側に 2 つのプレースホルダー展開を追加した:

  {{CLAUDE_DIR}} → .claude/ の絶対パス (forward-slash 正規化)
  {{EXE_SUFFIX}} → .exe (Windows) / 空文字 (それ以外)

解決不能時はプレースホルダーを残す (展開済みの壊れたパスで走らせるより、
{{CLAUDE_DIR}} を含むエラーで失敗させたほうが原因が自明になるため)。

push-runner-config.toml の [lint_screen] exe_path は明示指定をやめた。code 側の
default が既に OS で cfg 分岐しており、config に .exe 付きの値を書くと Linux で
解決に失敗するため。

実測:
- cargo test -p hooks-stop-quality 31+4+5 pass (展開の 3 test を新規追加)
- 展開後コマンドを cmd.exe 経由で実走させ exit 0 を確認

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
使い捨てのクラウドセッションが 19 crate をビルドせずにハーネスを有効化できるよう、
master push で x86_64-unknown-linux-gnu 向けの全成果物をビルドし、固定タグ
`nightly` の prerelease へ公開する。

設計判断:
- **単一 tarball**: バイナリを個別 asset にすると run 失敗時に release へ新旧が
  混在した不整合セットが残る。1 asset なら差し替えが実質アトミックで、取得側も
  1 回のダウンロードで済む。
- **rolling tag の上書き**: 使い捨て環境向けなのでバージョン解決を setup script に
  持ち込みたくない。再現性は tarball 内 BUILD_INFO の commit SHA で担保する。
- **バイナリ一覧を cargo metadata から導出**: package.json / deploy-hooks.ts に
  列挙をコピーすると crate 追加時に片方だけ更新され無言で欠落する (実際
  deploy-hooks.ts の allowlist は 11 個で、settings template が参照する hook exe を
  3 つ取りこぼしている)。workspace の bin target を機械的に集めて drift を断つ。
- **ubuntu-22.04 固定**: glibc は後方互換が無いため、実行環境より古い glibc
  (2.35) でビルドする。
- **公開前に cargo test**: 壊れたバイナリを rolling release に載せると、クラウド側は
  setup が成功したまま原因不明の挙動不良を起こす。

ローカル実測: cargo metadata からの bin 抽出が 16 個を返し package.json の
build:all と一致することを確認。YAML の構文妥当性も確認済み。

Windows leg + hooks smoke test を含む CI matrix は WP-16 で扱う。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
使い捨ての Linux 環境 (claude.ai/code) で 19 crate をビルドせずにハーネスを
有効化する scripts/cloud-setup.sh を新設。rolling release から Linux バイナリを
取得 → .claude/ 配置 → settings 生成 → jj / takt 導入までを行う。

設計判断:
- **認証を要求しない**: public リポジトリの Release asset は素の HTTPS で取れる。
  gh CLI 認証を setup に持ち込むとトークン受け渡しの構成が必要になり、クラウドの
  許可リスト型ネットワーク設定と合わせて失敗点が増える (plan の要確認事項に対する回答)。
- **hooks が無言で無効化される経路を fail-closed に**: バイナリ欠落や settings 生成
  失敗を skip して「setup 成功」と報告すると、セッションはハーネス無しで進み誰も
  気付かない (ADR-005 が対処した事故と同じ形)。必須要素は即 exit 1。
- **必須バイナリ一覧を settings.local.json.template から導出**: 「どの exe が無いと
  hooks が発火しないか」の正解はテンプレート自身が持つ。script に一覧をコピーすると
  hook 追加時に片方だけ更新され無言で穴が開く。実測で 9 個の hook exe を正しく抽出。
- **jj は 0.42.0 固定**: ADR-011 / 015 / 045 が 0.42 系の挙動に依存している。
  takt は pnpm install --frozen-lockfile で ADR-017 の固定を機械的に担保。

あわせて .gitignore を修正。従来 `.claude/*.exe` のみだったため Linux の拡張子なし
バイナリが無視されず、クラウドセッションで jj が成果物を snapshot してしまう状態
だった。名前ベースで無視し、唯一衝突する hooks-config.toml だけ negate で戻す。

Ollama 依存機能の graceful skip はコード監査で確認済み: lint_screen は
`enabled = false` かつ戻り値が () で block 不可能、classifier は exe 側が
fallback JSON + exit 0、runner 側が全失敗経路を空 Vec に潰す二重の fail-open。
実 Ollama を叩く eval は #[ignore] + env opt-in の二重ガードで `cargo test
-- --ignored` でも skip される。よってクラウド Linux では設定変更なしで完走する。

bash -n による構文検査と、テンプレート解析ロジックの実測を実施。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Linux 実測 (WSL Ubuntu 24.04) で concurrent_acquire_only_one_wins が失敗し、
8 スレッド中 6 つが同時に lock を取得していた。

原因は create と内容書き込みの非原子性。`OpenOptions::create_new` 自体は atomic
だが、成功直後のファイルは**空**で存在する。この窓で他スレッドが読むと TOML
parse に失敗し、実装がそれを一律「壊れている = stale」と扱って全員 takeover して
いた。doc comment は「read-then-write の TOCTOU race を排除する設計」と述べていたが、
排除できていたのは create の競合だけで、空ファイル窓は塞がっていなかった。

Windows でこのテストが通っていたのはスケジューリングの差で窓が狭かっただけで、
設計上の欠陥は同じ。ADR-045 の並列 workspace 運用では複数 pr-monitor が同時起動
しうるため、レートリミット浪費の防波堤が実質機能していなかったことになる。

修正は parse 失敗を内容で 2 分する:
- **空** → 保持者が書き込み中 = Busy (同時取得を防ぐ)
- **非空だが不正** → 本当に破損 = 従来どおり takeover

空側にも 5s の齢制限を掛ける。create 直後に保持者が crash すると空ファイルが残り、
制限が無いと以降の取得が永久に阻まれるため。既存の corrupt_lock_is_taken_over
(非空の不正内容) の意味は変えていない。

実測: Windows 255/255・Linux 252/252 pass。incident 再現テスト
empty_lock_file_is_treated_as_busy_not_stale を追加。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…P-15)

lint_screen/classifier.rs のテストは全ケースが cmd.exe / PowerShell を子プロセスに
使うため個別に #[cfg(windows)] が付いていたが、mod 自体は無条件だった。結果 Linux
では `use super::*` が unused となり、本リポジトリの品質ゲート
(clippy --workspace --all-targets --all-features -- -D warnings) が
error: unused import で落ちていた (Linux 実測で発覚)。

mod ごと #[cfg(all(test, windows))] に変更し、冗長になった個別 cfg 5 箇所を削除。
Linux 側で pump_child_io の deadlock 保護が無検証になる点は doc に明記し、
WP-16 (CI matrix) へ引き継ぐ。

実測: Linux で clippy clean・cargo test --workspace 全 pass。Windows 256/256 pass。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@aloekun, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 31 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6168fad9-3ece-4273-9055-b0a580cf0ea9

📥 Commits

Reviewing files that changed from the base of the PR and between 7a1a058 and e3988ac.

📒 Files selected for processing (3)
  • docs/harness-improvement-plan.md
  • src/check-ci-coderabbit/src/main.rs
  • src/cli-pr-monitor/src/lock.rs
📝 Walkthrough

Walkthrough

Linux 実行向けにシェル起動、タイムアウト終了、hook コマンド解決、テスト用コマンドをOS依存化しました。Linuxバイナリのnightly公開とクラウドセットアップを追加し、空ロックの競合処理も更新しています。

Changes

Linux portability and deployment

Layer / File(s) Summary
Cross-platform shell execution and tests
src/lib-subprocess/src/lib.rs, src/cli-push-runner/..., src/cli-pr-monitor/src/classifier_runner.rs
シェル起動をWindowsのcmd /cとPOSIXのsh -cに分岐し、関連テストの実行コマンドをOS別定数へ整理しました。
Cross-platform timeout termination
src/check-ci-coderabbit/src/main.rs
タイムアウト時の子プロセス終了をPID経由に統一し、Windowsではtaskkill、その他ではkill -9を使用します。
Hook command placeholder resolution
.claude/hooks-config.toml, src/hooks-stop-quality/src/main.rs, push-runner-config.toml
{{CLAUDE_DIR}}{{EXE_SUFFIX}}を実行前に展開し、OS別の実行ファイル設定とテストを更新しました。
Nightly Linux binary release
.github/workflows/release-binaries.yml, .gitignore, docs/harness-improvement-plan.md
Linuxバイナリをテスト・ビルドし、BUILD_INFO、tarball、checksumをnightly prereleaseへ公開するworkflowを追加しました。
Cloud harness setup automation
scripts/cloud-setup.sh
nightly成果物の取得、必須バイナリ検証、settings生成、jjとNode依存の導入、任意機能の状態報告を順番に実行します。
Empty lock write-window handling
src/cli-pr-monitor/src/lock.rs
書き込み直後の空lockをBusyとして扱い、同時takeoverを防ぐ回帰テストを追加しました。

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

Sequence Diagram(s)

sequenceDiagram
  participant GitHubActions
  participant GitHubRelease
  participant CloudSetup
  participant ClaudeDir
  GitHubActions->>GitHubActions: test and build Linux binaries
  GitHubActions->>GitHubRelease: upload nightly archive and checksum
  CloudSetup->>GitHubRelease: download nightly archive
  CloudSetup->>ClaudeDir: extract and validate hook binaries
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 Linuxバイナリのビルド公開とcloud setup script追加という主要変更を適切に表しており、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 wp-15-linux-binaries

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

Copy link
Copy Markdown
Contributor

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

  • トリガー: issue_comment (created) / 実行 run
  • CI: CodeRabbit チェックは pass 表示だが、これは check-run 自体の完了ステータス。実体は review limit reached(利用上限到達、次回利用可能まで 15 分、コメント投稿 2026-07-20T10:06:43Z 起点)でレビュー本体は未実施。他のビルド/テスト系ワークフロー run は本ブランチ (wp-15-linux-binaries) 上に存在せず、この PR に対する必須チェックも無し (no required checks reported)。
  • レビュー状況: CodeRabbit = レビュー未着手(rate limit、findings 0件)。人間レビューアーによる review / インラインコメントは無し。会話コメントは CodeRabbit の rate-limit 通知 1 件のみで、過去の本 workflow 分析コメントは存在しない(重複ガード対象外、初回分析)。
  • Verdict: user_decision(レビュー自体が未完了のため「問題なし」の確定判断ができない。CodeRabbit 再試行 or 手動 @coderabbitai review 待ちの判断は人間に委ねる)

Applicable Findings (Critical / High / Major)

該当なし(レビュー未着手のため findings 0 件)

Applicable Findings (Medium 以下)

該当なし

Filtered (not applicable)

該当なし

差分概要 (軽量サマリー)

  • ブランチ: wp-15-linux-binariesmaster / +992 / −282 / 15 files
  • 変更ファイル: .claude/hooks-config.toml(stop_quality の cmd をクロスプラットフォーム対応のプレースホルダー化)、.github/workflows/release-binaries.yml(新規、Linux バイナリビルド + rolling release 公開)、.gitignoredocs/harness-improvement-plan.mdpush-runner-config.tomlscripts/cloud-setup.sh(新規)、および src/ 配下の Rust ソース 8 ファイル(check-ci-coderabbit, cli-pr-monitor, cli-push-runner, hooks-stop-quality, lib-subprocess
  • 変更の性質: ワークフロー追加 + 設定変更 + 実行コード変更が混在しており docs-only ではない(ADR-035 非該当)

次のアクション

  • CodeRabbit の review limit 解除(2026-07-20T10:21Z 頃以降)を待つか、@coderabbitai review で手動再トリガーし、findings が出てから本バックストップまたはローカルセッションで再分析する。
  • .rs ファイル(19 crate 相当のロジック変更)を含むため、自動レビューが揃うまでマージは見送り、人間または pre-push/post-PR パイプラインでのビルド・テスト確認を優先する。
  • 本 PR 用の CI 必須チェックが未設定(no required checks reported)である点は、release-binaries.yml 追加の意図と整合しているか(新規 workflow は push 時トリガーで PR では走らない設計か)を確認する。

@aloekun

aloekun commented Jul 20, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

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

@github-actions

Copy link
Copy Markdown
Contributor

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

  • トリガー: issue_comment (created) / 実行 run
  • CI: CodeRabbit チェックは PENDING("Review in progress")。本 PR に対する他の必須/ビルド系ワークフロー run は無し(wp-15-linux-binaries ブランチ上の run 一覧は空)。
  • レビュー状況: CodeRabbit = レビュー未完了(pulls/307/reviews は空配列、findings 0件)。人間レビューアーによる review / インラインコメントも無し。会話コメントには前回の本 workflow 分析(10:08:36Z 投稿)以降、新規のやり取りとして PR 作者による手動再トリガー @coderabbitai review(10:23:38Z)と CodeRabbit の受理応答(10:23:49Z、「Review triggered」だが「既にレビュー済みコミットは再レビューしない/このコマンドは自動レビューが一時停止している場合のみ有効」という注記付き)が追加されている。CI 状態も前回時点の pass(rate limit 表示)から現在は PENDING に変化しており、実質的なレビュー内容(findings)はまだ生成されていない。
  • Verdict: user_decision(レビュー本体が未完了のため、承認・要修正いずれの確定判断もできない。CodeRabbit の処理完了待ち、または限定的な再レビュー動作の意図を確認するかは人間の判断に委ねる)

Applicable Findings (Critical / High / Major)

該当なし(findings 0 件)

Applicable Findings (Medium 以下)

該当なし

Filtered (not applicable)

該当なし

差分概要 (軽量サマリー)

  • ブランチ: wp-15-linux-binariesmaster / 15 files 変更(前回分析時点と同一のファイルセット、新規コミットは無し)
  • 変更ファイル: .claude/hooks-config.toml.github/workflows/release-binaries.yml(新規)、.gitignoredocs/harness-improvement-plan.mdpush-runner-config.tomlscripts/cloud-setup.sh(新規)、および src/ 配下の Rust ソース 8 ファイル(check-ci-coderabbit, cli-pr-monitor, cli-push-runner, hooks-stop-quality, lib-subprocess
  • 変更の性質: ワークフロー追加 + 設定変更 + 実行コード変更が混在しており docs-only ではない(ADR-035 非該当)

次のアクション

  • CodeRabbit の再レビューが実際に findings を生成するか(「既にレビュー済みコミットは再レビューしない」という注記があるため、新規コミットが無い今回の手動トリガーでは実質的に空振りになる可能性がある)を次回チェック時に確認する。
  • 必須チェックが未設定(release-binaries.yml は PR では走らない設計と見られる)ため、.rs ファイルを含む実行コード変更については人間または pre-push/post-PR パイプラインでのビルド・テスト確認を優先する。
  • レビューが完了し findings が出るか、もしくは一定時間 CodeRabbit が PENDING のまま進展しない場合は、@coderabbitai review の再試行要否も含め人間が判断する。

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

🧹 Nitpick comments (2)
scripts/cloud-setup.sh (1)

52-52: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

readonly とコマンド置換を分離して終了ステータスの隠蔽を防ぐ。

readonly$(...) によるコマンド置換を同じ行で行うと、サブシェル内でのエラー(cd の失敗など)の終了コードが readonly コマンドの成功(0)によって上書きされ、set -e で即座に捕捉できなくなります。

このスクリプトでは、仮に失敗しても直後の cd "${REPO_ROOT}" で確実にエラーとなるため実害はありませんが、シェルスクリプトのベストプラクティス(Shellcheck SC2155)として分離しておくことをお勧めします。

♻️ 提案するリファクタリング
-readonly REPO_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
+REPO_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
+readonly REPO_ROOT
🤖 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 `@scripts/cloud-setup.sh` at line 52, In scripts/cloud-setup.sh, separate the
REPO_ROOT command substitution from the readonly declaration: assign the
cd-derived path first, then apply readonly to REPO_ROOT on a separate command,
preserving the existing value and behavior.

Source: Linters/SAST tools

src/cli-pr-monitor/src/lock.rs (1)

137-207: 🩺 Stability & Availability | 🔵 Trivial | 🏗️ Heavy lift

書き込み窓をなくすなら hard_link より OS バックの排他制御へ
現状の 5 秒猶予で空ファイルは吸収できていますが、部分書き込みや遅いストレージの窓はまだ残ります。hard_link はロック用途としては不向きなので、窓を根本的に消したいなら OS バックの file lock か temp file → atomic publish の方式を検討したいです。

🤖 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-pr-monitor/src/lock.rs` around lines 137 - 207, Replace the
create-and-write locking flow associated with read_fresh_lock and
holder_still_writing with OS-backed exclusive file locking or a
temporary-file-then-atomic-publish strategy, so readers never observe empty or
partially written lock contents. Remove the LOCK_WRITE_WINDOW_SECS-based
grace-period handling once the atomic publication or exclusive lock guarantees
the write cannot be observed mid-operation.
🤖 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 `@src/check-ci-coderabbit/src/main.rs`:
- Around line 156-168: Update run_gh so the result of child.wait_with_output()
is stored before propagating errors, ensuring done_flag.store(true, ...)
executes on both success and failure paths. Preserve the existing timeout-killer
cleanup and return the captured wait/output error afterward.

In `@src/cli-pr-monitor/src/lock.rs`:
- Around line 151-156: Update file_age_secs so a future mtime caused by clock
skew is treated as age zero rather than returning None, preserving the “created
just now” interpretation for empty locks. Keep None for genuine metadata,
timestamp, or conversion failures, and leave holder_still_writing’s handling
unchanged.

---

Nitpick comments:
In `@scripts/cloud-setup.sh`:
- Line 52: In scripts/cloud-setup.sh, separate the REPO_ROOT command
substitution from the readonly declaration: assign the cd-derived path first,
then apply readonly to REPO_ROOT on a separate command, preserving the existing
value and behavior.

In `@src/cli-pr-monitor/src/lock.rs`:
- Around line 137-207: Replace the create-and-write locking flow associated with
read_fresh_lock and holder_still_writing with OS-backed exclusive file locking
or a temporary-file-then-atomic-publish strategy, so readers never observe empty
or partially written lock contents. Remove the LOCK_WRITE_WINDOW_SECS-based
grace-period handling once the atomic publication or exclusive lock guarantees
the write cannot be observed mid-operation.
🪄 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: 208fad05-f225-448d-aff9-9d669c3381fe

📥 Commits

Reviewing files that changed from the base of the PR and between 2cecf45 and 7a1a058.

📒 Files selected for processing (15)
  • .claude/hooks-config.toml
  • .github/workflows/release-binaries.yml
  • .gitignore
  • docs/harness-improvement-plan.md
  • push-runner-config.toml
  • scripts/cloud-setup.sh
  • src/check-ci-coderabbit/src/main.rs
  • src/cli-pr-monitor/src/classifier_runner.rs
  • src/cli-pr-monitor/src/lock.rs
  • src/cli-push-runner/src/stages/diff.rs
  • src/cli-push-runner/src/stages/lint_screen/classifier.rs
  • src/cli-push-runner/src/stages/push.rs
  • src/cli-push-runner/src/stages/quality_gate.rs
  • src/hooks-stop-quality/src/main.rs
  • src/lib-subprocess/src/lib.rs

Comment thread src/check-ci-coderabbit/src/main.rs
Comment thread src/cli-pr-monitor/src/lock.rs
@github-actions

Copy link
Copy Markdown
Contributor

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

  • トリガー: pull_request_review (submitted) / 実行 run
  • CI: CodeRabbit = pass (Review completed) / analyze = pending (本レポートを生成している本 run 自身であり、応答投稿後に完了する想定。他の必須ビルド/テスト系ワークフローは本 PR 上に存在しない)
  • レビュー状況: coderabbitai[bot] が 2026-07-20T10:31:54Z に COMMENTED でレビュー提出、Actionable comments 2件 (Minor) + Nitpick 2件 (Trivial)。人間レビューは無し (reviewDecision 未設定)。過去の PR Monitor コメント (10:08, 10:25 投稿) 以降に本レビューが新規投稿されたため、重複ガードは非該当として分析続行。
  • Verdict: user_decision (Critical/High/Major の applicable finding は無く、Medium 以下 (Minor/Trivial) の applicable finding のみ)

Applicable Findings (Critical / High / Major)

該当なし

Applicable Findings (Medium 以下)

# File (Line) Reviewer Issue Recommended Action
1 src/check-ci-coderabbit/src/main.rs (156-168) coderabbitai (Minor) run_ghchild.wait_with_output() がエラーを返し ? で早期リターンすると done_flag.store(true, ...) が実行されず、監視スレッドが最大30秒生き残る。deadline到達時に PID 再利用があれば無関係プロセスを kill しうる let result = child.wait_with_output(); done_flag.store(true, ...); let output = result.map_err(...)?; の順に変更し、エラー経路でも done_flag を確実にセットする
2 src/cli-pr-monitor/src/lock.rs (151-156) coderabbitai (Minor) file_age_secs はクロックスキュー (mtime が未来) の場合 None を返し、holder_still_writing がこれを stale 扱いにする。これは本PRがまさに修正対象としている同時取得レース (WP-15) を別経路 (クラウド/コンテナのクロックスキュー) から再発させうる duration_sinceErr の場合は Some(0) (作成直後扱い) を返すよう変更
3 scripts/cloud-setup.sh (52) coderabbitai (Trivial/Low value) readonly REPO_ROOT="$(cd ... && pwd)" はコマンド置換と readonly を同一行で行っており、サブシェル失敗時の終了コードが隠蔽されうる (Shellcheck SC2155)。直後の cd で実質エラーにはなるため実害は小さい 代入と readonly 宣言を2行に分離 (任意対応)
4 src/cli-pr-monitor/src/lock.rs (137-207) coderabbitai (Trivial/Heavy lift) hard_link ベースのロック機構は部分書き込み/低速ストレージでの書き込み窓が残るため、根本対応として OS バックの exclusive file lock か temp file → atomic publish への置き換えを提案 設計変更を伴う大きめの提案のため、今回の PR スコープでは対応不要。将来課題としてバックログ化を検討

Filtered (not applicable)

該当なし

次のアクション

aloekun and others added 2 commits July 20, 2026 19:41
table 行と本文に実装内容・設計判断・実測結果を反映。

記録した主な内容:
- スコープ補正の理由 (受け入れ基準を満たす前提として可搬性修正が先に必要だった)
- forward-slash 絶対パスのみが cmd.exe と sh の双方で通るという実測知見
- 単一 tarball / rolling tag / cargo metadata 由来のバイナリ一覧という 3 つの設計判断
- musl 不採用の根拠 (tree-sitter の C コンパイルに musl-tools が必要 vs 実行先が
  Ubuntu 系で glibc 2.35 なら十分)
- 要確認事項への回答 (public release は素の HTTPS で取得でき gh 認証は不要)
- Ollama graceful skip がコード監査で無条件に成立すること
- WSL Ubuntu 24.04 での実測結果一式
- Linux 実測で副次発見した lock 同時取得レース = WP-16 (CI matrix) の価値の実例
- `完了` 条件と Linux 側の未検証領域 (#[cfg(windows)] ガードのテスト)

pnpm lint:docs / lint:md とも pass。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
いずれも本 PR で導入したコードの実バグ。Minor だが、どちらも「本 PR が塞いだ
はずの問題を別経路から再発させる」性質のため採用した。

① check-ci-coderabbit run_gh: wait_with_output が Err を返す経路で done_flag を
   立てずに `?` で抜けていた。タイマースレッドが deadline (30s) まで生き残り、
   PID 再利用時に無関係のプロセスを kill しうる。spawn_timeout_killer の doc に
   「立て忘れると…無関係のプロセスを殺しうる」と自分で書いた契約を、同じ関数の
   error path で破っていた形。結果を変数に受けてから done_flag → `?` の順に修正。

② cli-pr-monitor file_age_secs: SystemTime::duration_since は mtime が未来
   (クロックスキュー) だと Err を返し、`.ok()?` で None に潰れていた。
   holder_still_writing はこれを「齢不明」として stale 扱いに倒すため、
   create_new 直後の空 lock が takeover され、本 PR で塞いだ同時取得レースが
   クロックスキュー経由で再発しうる。クラウド / コンテナではスキューは珍しくない。
   未来 mtime は「たった今作られた」= 齢 0 と解釈する安全側に修正。
   検証可能にするため純粋関数 age_secs_between に切り出し、未来 mtime と通常
   経路の 2 test を追加した (mtime 操作なしで固定できる形にした)。

実測: Windows で cargo test --workspace 全 pass / clippy clean。
Linux (WSL Ubuntu 24.04) でも 39 スイート全 pass / clippy clean。
cli-pr-monitor は 255→257 (lock test 2 件追加)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aloekun
aloekun force-pushed the wp-15-linux-binaries branch from 7a1a058 to e3988ac Compare July 20, 2026 10:45
@aloekun

aloekun commented Jul 20, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

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

@github-actions

Copy link
Copy Markdown
Contributor

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

  • トリガー: issue_comment (created) / 実行 run
  • CI: CodeRabbit = pass (Review completed)。他の必須ビルド/テスト系チェックは本 PR 上に存在せず (release-binaries.yml は master への push トリガーであり PR では走らない設計)。
  • レビュー状況: coderabbitai[bot] が 2026-07-20T10:31:54Z に COMMENTED でレビュー提出 (Actionable/Minor 2件 + Nitpick/Trivial 2件)。その後 10:42:06Z のコミット e3988ace("fix: CodeRabbit 指摘 2 件を修正")で Minor 2件は両方修正済み・回帰テスト追加済みであることをソース確認済み (CodeRabbit 側も両インラインコメントに ✅ Addressed in commits 768f4b9 to e3988ac を付与)。10:52:33Z に再度 @coderabbitai review が要求されたが、応答は「既レビュー済みコミットの再レビューはしない」旨のみで新規指摘なし。人間レビューは無し (reviewDecision 未設定)。前回分析コメント (10:34:55Z 投稿) 以降、上記の修正コミットと再レビュー要求という新しい情報があるため重複ガード非該当として分析続行。
  • Verdict: user_decision (Critical/High/Major の該当なし。残存する適用可能指摘は CodeRabbit 申告の Trivial 2件のみで、いずれも「低優先度」「重い変更」と申告者自身が明記)

Applicable Findings (Critical / High / Major)

該当なし

Applicable Findings (Medium 以下)

# File (Line) Reviewer Issue Recommended Action
1 scripts/cloud-setup.sh (52) coderabbitai (Trivial / 💤 Low value) readonly REPO_ROOT="$(cd ... && pwd)" で readonly 宣言とコマンド置換を同一行にしており、Shellcheck SC2155 的にサブシェル失敗時の終了コードが隠蔽され得る(本スクリプトでは直後の cd で確実に失敗するため実害は限定的、と申告者自身が明記) 対応するなら REPO_ROOT="$(...)"readonly REPO_ROOT を別行に分離。優先度低のため次回改修時でも可
2 src/cli-pr-monitor/src/lock.rs (137-207) coderabbitai (Trivial / 🏗️ Heavy lift) 現行の hard_link + 猶予窓 (grace window) 方式は本 PR が意図的に採用した設計 (WP-15 の同時取得レース修正そのもの) だが、部分書き込みや低速ストレージ環境では窓が理論上残る。申告者自身が「窓を根本的に消したいなら」という条件付きの将来検討として提示 現行方式のままで運用継続可。窓を完全に無くしたい場合は OS バックの exclusive file lock か temp-file→atomic publish への置き換えを別 issue として検討

Filtered (not applicable)

該当なし(今回の指摘はいずれも ADR 抵触・スコープ外・Windows専用起因の的外れ指摘には該当しない)

次のアクション

  • Trivial 2件は申告者自身が低優先度/重い変更と明記しており、現状のままマージしても支障は小さいと判断できる。マージ前に対応するか、フォローアップ課題として先送りするかは人間の判断に委ねる。
  • 本 PR には build/test 系の必須 CI が設定されていない点は変わらず。.rs ロジック変更 (lock.rs のレース修正含む) を含むため、マージ前に人間または pre-push/post-PR パイプラインでのビルド・テスト実行(cargo test 等)を推奨する。
  • Minor 2件は修正・テスト追加済みで再発防止も図られているため、追加対応は不要。

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@github-actions[bot] 分析内容を確認しました。Minor 指摘 2 件が修正・回帰テスト追加済みであり、残件も Trivial のため、現時点でブロッキングなレビュー指摘はありません。

マージ前には、PR 上で必須 CI が動かない点を補うため、少なくとも cargo test --workspace(必要に応じて release build も)を独立して成功確認するのが適切です。残る 2 件は、現在の設計判断を維持したまま後続課題として扱えます。

@github-actions

Copy link
Copy Markdown
Contributor

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

  • トリガー: issue_comment (created) / 実行 run
  • CI: CodeRabbit = pass (Review completed)。他の必須ビルド/テスト系チェックは本 PR 上に存在せず (release-binaries.yml は master への push トリガーであり PR では走らない設計)。状態は前回分析時 (10:57:05Z) から変化なし。
  • レビュー状況: coderabbitai[bot] の唯一のレビュー (2026-07-20T10:31:54Z 提出) から変化なし。Minor 2件はコミット e3988ace で修正・回帰テスト追加済み (CodeRabbit 側も両インラインコメントに ✅ Addressed を付与済み)。残る Trivial 2件は前回分析時と同一。今回のトリガーはコミット @github-actions[bot] の前回分析に対する coderabbitai[bot] の追認リプライ (10:57:26Z、「ブロッキングなレビュー指摘はありません」「cargo test --workspace の独立確認を推奨」という内容のみで新規指摘なし)。人間レビューは無し (reviewDecision 未設定)。
  • Verdict: user_decision (Critical/High/Major の該当なし。残存する適用可能指摘は CodeRabbit 申告の Trivial 2件のみで、いずれも申告者自身が「低優先度」「重い変更」と明記)

Applicable Findings (Critical / High / Major)

該当なし

Applicable Findings (Medium 以下)

# File (Line) Reviewer Issue Recommended Action
1 scripts/cloud-setup.sh (52) coderabbitai (Trivial / 💤 Low value) readonly REPO_ROOT="$(cd ... && pwd)" で readonly 宣言とコマンド置換を同一行にしており、Shellcheck SC2155 的にサブシェル失敗時の終了コードが隠蔽され得る(本スクリプトでは直後の cd で確実に失敗するため実害は限定的、と申告者自身が明記) 対応するなら REPO_ROOT="$(...)"readonly REPO_ROOT を別行に分離。優先度低のため次回改修時でも可
2 src/cli-pr-monitor/src/lock.rs (137-207) coderabbitai (Trivial / 🏗️ Heavy lift) 現行の hard_link + 猶予窓 (grace window) 方式は本 PR が意図的に採用した設計 (WP-15 の同時取得レース修正そのもの) だが、部分書き込みや低速ストレージ環境では窓が理論上残る。申告者自身が「窓を根本的に消したいなら」という条件付きの将来検討として提示 現行方式のままで運用継続可。窓を完全に無くしたい場合は OS バックの exclusive file lock か temp-file→atomic publish への置き換えを別 issue として検討

Filtered (not applicable)

該当なし(今回の指摘はいずれも ADR 抵触・スコープ外・Windows専用起因の的外れ指摘には該当しない)

次のアクション

  • 前回分析 (10:57:05Z) 以降、実質的な新情報 (新規レビュー・新規指摘・CI 状態変化) は無い。今回のトリガーとなった coderabbitai[bot] の追認リプライも既存分析の内容確認のみで、対応必要な新規事項は含まれない。
  • Trivial 2件は据え置き可能。マージ前に対応するか、フォローアップ課題として先送りするかは人間の判断に委ねる。
  • 本 PR には build/test 系の必須 CI が設定されていない点は変わらず。.rs ロジック変更 (lock.rs のレース修正含む) を含むため、マージ前に人間または pre-push/post-PR パイプラインでの cargo test --workspace 等の独立確認を推奨する (CodeRabbit 側も同趣旨を追認済み)。

@aloekun
aloekun merged commit cc51b5e into master Jul 20, 2026
1 check passed
@aloekun
aloekun deleted the wp-15-linux-binaries branch July 20, 2026 11:07
aloekun added a commit that referenced this pull request Jul 20, 2026
)

PR #307 マージ後の release-binaries.yml 初回実行 (ubuntu-22.04) が
kill_switch_env_skips_check の Broken pipe で失敗し、master が赤になっていた。
release が生成されず cloud-setup.sh の取得対象が不在の状態だったため先行修正する。

原因は test helper の stdin 書き込みが子の即時終了と競合すること。
hooks-stop-tool-call-leak の main は kill-switch (STOP_TOOL_CALL_LEAK_OVERRIDE) と
enabled = false の 2 経路で **stdin を読む前に return** する。読み手が消えたパイプへ
write_all すると Unix では EPIPE になるが、旧実装は `.expect()` で panic していた。
Windows では小さな payload がバッファに収まり成功しがちなため顕在化していなかった。

BrokenPipe のみ正常として飲み込む helper
`write_stdin_tolerating_early_exit` を導入。他の I/O エラーは従来どおり panic させる。
これらの test の主題は「skip されること」であって「stdin が消費されること」ではない。

**検証**: WSL では修正前も 60/60 pass してしまい競合を再現できなかったため、
write の前に 300ms の遅延を注入して EPIPE を確定的に起こす検証を行った。
同一条件で修正前は CI と同じ `Os { code: 32, kind: BrokenPipe }` で FAIL、
修正後は PASS することを実測した (この遅延は検証専用でコミットには含めない)。
あわせて Windows 34+7 pass、Linux 39 スイート全 pass、clippy 両 OS clean。

「WSL で通っても CI で落ちる」実例であり、WP-16 (CI matrix) の必要性を裏づける。

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
aloekun added a commit that referenced this pull request Jul 20, 2026
pr-monitor.yml バックストップが CodeRabbit の投稿 (ack / rate-limit 通知含む) ごとに
「🤖 PR Monitor 分析」コメントを再投稿していた問題を修正 (PR #287 で 5 件、#304 で 3 件、
#307 で 5 件実観測)。原因は重複ガードが LLM prompt 内 (助言層) にしかなく、トリガー事象
(新規コメントの存在) 自身が prompt の skip 条件を無効化するトートロジーだったこと。

- jobs.analyze.if: に決定論ガードを追加 (ADR-042: 助言層 -> 決定論層):
  - issue_comment は CR walkthrough/summary マーカーを含み、かつ rate-limit placeholder
    でないもののみ起動する positive allowlist。ack / rate-limit 通知 / command invocation
    を一括除外 (denylist より確実)。マーカーは live PR #304/#307 の生 body で実検証。
  - CLOSED/MERGED PR では起動しない (issue.state / pull_request.state == 'open')。
- prompt 手順 2 を「新規コメントの有無」から「分析価値のある新情報の有無」へ書換え、
  ack/rate-limit/自身の分析コメントは新情報に数えない旨を明示。決定論層の二層目に降格。
- 先頭設計メモに経緯を記録。

検証: YAML parse + paren balance を node で確認、pnpm lint:docs OK。
残 (post-merge): workflow_dispatch スモーク + 実 PR dogfood 確認 (todo17.md 順位 319)。

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
aloekun added a commit that referenced this pull request Jul 20, 2026
## R5: WP-15 `完了` 条件 (1) の達成記録

PR #307 マージ時の release-binaries.yml run は build job が失敗しており
(master が赤で #308 の E2E 修正が必要だった)、成功したのは #308 マージ後の
run (commit 541adde)。この経緯も含めて記録した。

生成物は本セッションで再実測している: WSL Ubuntu 24.04 から素の curl で
tarball (9,721,643 bytes) + .sha256 を**認証なしで**取得 → sha256sum -c 一致
→ 展開して 16 バイナリ + BUILD_INFO 確認 → release バイナリそのもので hooks
実発火 (pre-tool-validate が破壊的削除コマンドを exit 2 でブロックし無害な
echo を exit 0 で通す / session-start が additionalContext JSON を出力)。

これで WP-15 ④ の「public リポジトリの Release asset は素の HTTPS で取得
できるため gh CLI 認証は不要」という設計判断が実 URL・実 asset で裏づけられた。
旧 PR #309 の docs コミットの主張を引き写すのではなく、自分で再実測した結果を
記録している。

## 追補の実装状況

R1〜R4 の実装内容、破棄の実施結果、検証の実測値を反映。E2E カバレッジは
正直に申告した: 担保できたのは全 gate のユニット検証 / checker 実エントリ
ポイントの実データ実走 / 修正前後の差分実測の 3 点で、cli-pr-monitor 側の
統合経路 (park → PARK signal) と wakeup → 再 trigger 経路は、CR レート制限が
本セッション中に自然発生しなかったため未実測である旨を明記した。

なお本ファイルは master 時点で既に 59,798 bytes と file_size_check の 50KB
閾値を超過しており (non-blocking 警告)、本変更で 73,781 bytes になった。
分割は § 9 の退役手順で本ファイルごと削除する前提のため見送る。
aloekun added a commit that referenced this pull request Jul 21, 2026
…#311)

* docs(plan): WP-15 追補 — 監視 fail-open 修正のゼロ再構築方針 (旧 PR #309 全破棄)

旧 PR #309 (fix/monitor-fail-open-signals) は、4 コミット目の初版が本番 config で
一度も実行されない誤修正 + 実エントリポイントを迂回して pass するテストであり、
takt の High REJECT → fix step の自動書き直しを経た合成物となった。続修より
ゼロ再構築が速いとのユーザー判断 (2026-07-20) に基づき、再利用なしの全破棄を
決定。健全に見えるコミットも含めて引き継がない (中途半端な状態の引き継ぎと、
それによる実装の制約を排除するため)。

追補には、新実装が単独セッションで着手できるよう以下を自己完結で記録した:
- 破棄対象と手順 (PR close / branch 削除 / local abandon — 本コミット時点で未実施)
- 要件 R1-R5 (What のみ。実装方式は新実装の裁量、旧コードは参照しない)
- コード実読で検証済みの根本原因チェーン 6 段 (再調査不要)
- 検証要件 (両 OS 全スイート / incident 実データでの checker 単体実測 /
  E2E カバレッジの正直な申告 / 経路同一性を確認してから実測を主張する規律)
- 旧作業の教訓 (本番経路での発火をテストで固定・実エントリポイント・実データ fixture)

本コミットはドキュメント更新のみ。#309 の close・破棄・新実装には着手していない。
lint:docs / lint:md pass。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(check-ci): CR rate-limit 書式の第3世代対応と未知書式 fallback (WP-15 追補 R2)

CodeRabbit が rate-limit comment の待機時間書式を 3 度目に変更 (PR #309、
2026-07-20 実観測) し、`parse_rate_limit` が None を返して rate-limit 検知が
沈黙した。ADR-034 が予告していた再発事案 (PR #182/#184 に次ぐ 2 度目の
書式変更起因 regression)。

- 第 3 世代書式 `**Next review available in:** **57 minutes**` の抽出を追加。
  ラベルと数値の間に markdown 強調と `:` が挟まるため区切りを `[:*\s]*` で
  吸収し、CR が強調記法を変えても壊れないようにした。
- **書式追随を前提にしない fail-closed 化**: marker (`rate limited by
  coderabbit.ai`) が一致したのに待機時間をどの既知書式でも読めない場合、
  従来は None = 「rate-limit ではない」に倒れていた。marker 一致を制限の
  根拠として採用し、待機時間だけを既定 30 分で埋める方式に変更。既定値が
  実 reset より短ければ wakeup 後に再検出されて再 park されるだけで、
  retry は max_retries で有界。
- 既定値適用時は checker が stderr に警告 (cli-pr-monitor がログ転送) し、
  「30 分」を CR の申告値と誤読させない + 書式再変更の検知シグナルを兼ねる。

fixture は PR #309 の実 comment body を出典付きで埋め込み (ADR-049)。この
comment は walkthrough header marker を同一 body に併せ持つため、clean
walkthrough と誤認しない排他も併せて固定した。

ADR-034 の既知 format 一覧に第 3 世代行と fallback 方針を追記し、更新手順の
stale なファイル参照 (main.rs → markers.rs / rate_limit.rs) を修正。

* fix(check-ci): decide() に rate_limit を渡し silent success を排除 (WP-15 追補 R1/R4)

CodeRabbit がレート制限でレビューを開始できないまま、監視が「レビュー済み・
指摘なし」と報告する silent success を、判定ロジック側で塞ぐ。

## 根本原因 (2026-07-20 コード実読 + PR #307/#309 実観測)

CR はレート制限中も commit check を pass にする (外部 SaaS 挙動)。checker は
これを review_state に採用する一方、parse_rate_limit の結果は出力 JSON に
添付されるだけで decide() には渡っていなかった。結果、decide() は
「review_state = success かつ指摘ゼロ」= 完了と読み、stop_monitoring_success
を返していた。monitor 側の terminal 短絡は rate-limit branch より先に発火する
ため、park / 再 trigger 機構は一度も呼ばれない。

症状は monitor 側に出るが、原因は action の算出そのものにある。旧 PR #309 は
症状側 (monitor 2 箇所) への多層パッチで High REJECT を受けたため、本実装では
算出点である decide() に一本化した。

## 変更

- decide() / build_summary() に rate_limit を渡す。
- R1: rate-limit 検出中かつ「レビュー実施の陽性証拠」が無ければ
  continue_monitoring を返し、判断を monitor の rate-limit branch (park /
  再 trigger、既存・有界) に委ねる。has_actionable 分岐より前に置くことで、
  過去サイクル由来の未解決スレッドで action_required に抜ける穴も塞ぐ。
- R4: rate-limit を検出できなかった場合の backstop として、陽性証拠が無い限り
  stop_monitoring_success を出さない。CR が marker 文言自体を変えても silent
  success には戻らず、最悪 max_duration までの監視継続 (timed_out) に倒れる。
- build_summary は rate-limit 中に「CodeRabbit指摘なし」と断定せず
  「レート制限中 (レビュー未実施)」を出す。

## 陽性証拠の定義

review_state (commit status) は制限中でも pass になるため証拠に使わない。
push_time で絞られた「今サイクルの CR 出力そのもの」= walkthrough_clean /
actionable_comments が読めた (Some(0) 含む) / new_comments > 0 のみを採用する。
unresolved_threads は push_time で絞られず過去サイクルの残骸を含み得るので
除外した。

## 残存リスク

陽性証拠を一切残さない clean レビュー経路が CR 側に存在した場合、監視が
max_duration まで走って timed_out 報告になる。silent success より安全側だが
遅くなる。既存 fixture の範囲では walkthrough_clean か actionable_comments の
いずれかが必ず立つことを確認済み。

既存 decide/summary テスト 100 件は無改修で pass (新 gate が確立済み挙動を
乱していないことの確認)。incident 再現テストは PR #309 の実観測値から構成。

* fix(pr-monitor): 判定文が未確定要素を無視して断定しないよう修正 (WP-15 追補 R3)

監視レポートの人間向け判定文が、findings が空というだけで「問題は見つかり
ませんでした」と断定していた。PR #307/#309 では「未解決スレッド2件」を表示
しながら同一レポート内で「問題は見つかりませんでした」と結論する矛盾が実観測
されている。

findings が空であることは「見るべきものが無かった」の十分条件ではない。
レート制限でレビューが走っていない場合も、未解決スレッドが残っている場合も
空になり得る。

- rate-limit 検出中は保留判定文を出す (R1 で checker から rate_limit が
  届くようになったため、monitor 側で判別可能になった)。
- 未解決スレッドが残っている間は「問題なし」「重大な問題なし」のいずれも
  出さず、件数を添えて保留する。重大な指摘がある場合は「修正が必要」を優先。
- 判定順を「未確定 → 重大 → 未解決 → 軽微 → 問題なし」に整理し、未確定要素を
  findings の有無より先に評価する。断定文へ落ちる経路を構造的に塞ぐ形。

compute_verdict は未確定判定 (verdict_for_unsettled_review) と findings 判定
(verdict_for_findings) に分割し、「断定文はどの guard を通過して初めて出せる
のか」を関数境界で表現した (50 行ガイドラインにも整合)。

既存の verdict テスト 13 件は無改修で pass。

* docs(plan): WP-15 完了条件 (1) 達成の記録と追補の実装状況を反映 (R5)

## R5: WP-15 `完了` 条件 (1) の達成記録

PR #307 マージ時の release-binaries.yml run は build job が失敗しており
(master が赤で #308 の E2E 修正が必要だった)、成功したのは #308 マージ後の
run (commit 541adde)。この経緯も含めて記録した。

生成物は本セッションで再実測している: WSL Ubuntu 24.04 から素の curl で
tarball (9,721,643 bytes) + .sha256 を**認証なしで**取得 → sha256sum -c 一致
→ 展開して 16 バイナリ + BUILD_INFO 確認 → release バイナリそのもので hooks
実発火 (pre-tool-validate が破壊的削除コマンドを exit 2 でブロックし無害な
echo を exit 0 で通す / session-start が additionalContext JSON を出力)。

これで WP-15 ④ の「public リポジトリの Release asset は素の HTTPS で取得
できるため gh CLI 認証は不要」という設計判断が実 URL・実 asset で裏づけられた。
旧 PR #309 の docs コミットの主張を引き写すのではなく、自分で再実測した結果を
記録している。

## 追補の実装状況

R1〜R4 の実装内容、破棄の実施結果、検証の実測値を反映。E2E カバレッジは
正直に申告した: 担保できたのは全 gate のユニット検証 / checker 実エントリ
ポイントの実データ実走 / 修正前後の差分実測の 3 点で、cli-pr-monitor 側の
統合経路 (park → PARK signal) と wakeup → 再 trigger 経路は、CR レート制限が
本セッション中に自然発生しなかったため未実測である旨を明記した。

なお本ファイルは master 時点で既に 59,798 bytes と file_size_check の 50KB
閾値を超過しており (non-blocking 警告)、本変更で 73,781 bytes になった。
分割は § 9 の退役手順で本ファイルごと削除する前提のため見送る。

* docs(check-ci): wait_time_parsed の doc が実装と食い違う記述を修正

セルフレビュー (pre-push-review simplicity facet) の指摘。

`wait_time_parsed` の doc は「監視側はこれを見て『実測』と『既定値』を区別
して報告する」と書いていたが、実装では monitor 側への配線を見送っており
(cli-pr-monitor の RateLimitState は本 field を持たない)、記述と実装が
食い違っていた。既定値適用を運用者に伝える経路は実際には checker の stderr
警告 (monitor がログ転送) が担っている。

doc を実態に合わせ、あわせて「なぜ typed 化を見送ったか」(全 struct literal の
更新コストに対し得られるのが park summary の文言精度という副次的利得)と
「値がどこから参照できるか」(monitor が保持する checker の生 JSON) を明記した。

なお本指摘は、PR 全体 (master..@) を対象にセルフレビューを再実行して初めて
検出された。push 時のパイプラインは [diff] stage が tip コミットのみを
レビュアーに渡すため (push-runner-config.toml の `command = "jj diff -r @"`)、
models.rs を含む祖先コミットがレビュー対象外だった。この構造的欠陥は
docs/todo-summary2.md 順位 288 (Tier 1、Severity High で 3 連続再発) として
既知で、本 PR で 4 回目の再発となった。修正は独立 PR で行う。

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
aloekun added a commit that referenced this pull request Aug 3, 2026
…bit #353)

PR #353 の CodeRabbit 指摘 3 件への対応。うち 2 件は本 PR が持ち込んだ実バグだった。

## 指摘 2 (Minor): terminal 化 3 経路が head_commit を None で上書きする

`finalize_pending_review` / `finalize_waiting_reset` / `finalize_posted_retrigger` が
`state.head_commit = pr_info.head_commit.clone()` と**無条件代入**していた。
`util::get_pr_head_commit` は gh 失敗を None に潰すため、API 障害のたびに保存済み OID が
消える。その状態で次回 `--monitor-only` を実行すると `should_continue_state` が None を
見て継続を拒否し、時刻窓アンカーが「今」へリセットされて間に届いた CR コメントを
取りこぼす — 本 PR が動機に挙げた incident class (#237/#307/#309) と同型。

CodeRabbit が「共通の根本原因」と指摘したとおり 3 経路に同じ欠陥があったため、
`PrMonitorState::record_head_commit(Option<&str>)` を state.rs に置き、Some のときだけ
上書きする単一実装へ集約した。取得失敗は「head が変わった」の証拠ではないので既存値の
保持が正しい (fail-safe)。回帰テスト 2 件 (None で消えない / Some で上書きする) を追加。

## 指摘 3 (Minor): テストが実 gh CLI を呼ぶ

`finalize_waiting_reset` は内部で `fetch_mergeable_status` → `run_gh_quiet` を呼ぶため、
前コミットで追加した回帰テストが実 gh プロセスを起動していた (ネットワーク依存・低速)。

`finalize_waiting_reset_with(..., fetch_mergeable: impl FnOnce(&PrInfo) -> Option<..>)`
へ本体を切り出し、production は `fetch_mergeable_status` を、テストは closure を渡す形に
した (cli-push-runner の `verify_diff_covers_pr_range` と同じ注入の流儀)。

あわせて「注入したものが実際に使われている」ことを確認するテストを追加した — `|_| None`
を渡すだけのテストでは、fetcher を無視する実装でも通ってしまい注入の正しさを判別できない。
Cell フラグで呼び出し自体を観測する。

## 指摘 1 (Minor): 計画書の stale な park 検証残

「WP-15 追補残: レート制限 park の実観測」は park 廃止で (a) が moot になっていた。
見出しを「レート制限時の保留保証 (GitHub Actions 経路)」へ改め、(a) の終了と (b) の
引き継ぎ先を明記した (ADR-018 amendment / ADR-064 ステータス欄と同じ内容を計画書側にも
反映し、3 箇所の記述を揃える)。

検証: cargo test --workspace 1905 件 pass (新規 4 件)、clippy --workspace -D warnings 緑、
pnpm lint:docs / lint:md 0 error。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
aloekun added a commit that referenced this pull request Aug 3, 2026
…d 回帰復元 (CodeRabbit #353)

PR #353 のレビュー指摘への対応。CodeRabbit 3 件 + pre-push simplicity 1 件で、
**4 件中 3 件は本 PR が持ち込んだ実バグ / カバレッジ欠落**だった。

## CodeRabbit 指摘 2 (Minor): terminal 化 3 経路が head_commit を None で上書きする

`finalize_pending_review` / `finalize_waiting_reset` / `finalize_posted_retrigger` が
`state.head_commit = pr_info.head_commit.clone()` と**無条件代入**していた。
`util::get_pr_head_commit` は gh 失敗を None に潰すため、API 障害のたびに保存済み OID が
消える。その状態で次回 `--monitor-only` を実行すると `should_continue_state` が None を
見て継続を拒否し、時刻窓アンカーが「今」へリセットされて間に届いた CR コメントを
取りこぼす — 本 PR が動機に挙げた incident class (#237/#307/#309) と同型。

CodeRabbit が「共通の根本原因」と指摘したとおり 3 経路に同じ欠陥があったため、
`PrMonitorState::record_head_commit(Option<&str>)` を state.rs に置き、Some のときだけ
上書きする単一実装へ集約した。取得失敗は「head が変わった」の証拠ではないので既存値の
保持が正しい (fail-safe)。回帰テスト 2 件 (None で消えない / Some で上書きする) を追加。

## CodeRabbit 指摘 3 (Minor): テストが実 gh CLI を呼ぶ

`finalize_waiting_reset` は内部で `fetch_mergeable_status` → `run_gh_quiet` を呼ぶため、
前コミットで追加した回帰テストが実 gh プロセスを起動していた (ネットワーク依存・低速)。

`finalize_waiting_reset_with(..., fetch_mergeable: impl FnOnce(&PrInfo) -> Option<..>)`
へ本体を切り出し、production は `fetch_mergeable_status` を、テストは closure を渡す形に
した (cli-push-runner の `verify_diff_covers_pr_range` と同じ注入の流儀)。

あわせて「注入したものが実際に使われている」ことを確認するテストを追加した — `|_| None`
を渡すだけのテストでは、fetcher を無視する実装でも通ってしまい注入の正しさを判別できない。
Cell フラグで呼び出し自体を観測する。

## pre-push simplicity 指摘 (SIM-NEW-rate_limit-L122, Medium): fail-closed 回帰の消失

tests.rs のファイル分割時に `finalize_posted_retrigger_action_required_when_write_state_fails`
だけが欠落していた。この関数は 2 つの sibling (`finalize_waiting_reset` /
`finalize_pending_review`) と**逆に fail-closed** で、理由は `@coderabbitai review` 投稿と
いう副作用を伴うため state 永続化の成否で重複投稿を防ぐ必要があるから。fail-open 側 2 件の
回帰テストは移行されていたのに、意図的に非対称な唯一の経路のテストが落ちていた。復元し、
非対称の理由を doc comment に明記した。

## CodeRabbit 指摘 1 (Minor): 計画書の stale な park 検証残

「WP-15 追補残: レート制限 park の実観測」は park 廃止で (a) が moot になっていた。
見出しを「レート制限時の保留保証 (GitHub Actions 経路)」へ改め、(a) の終了と (b) の
引き継ぎ先を明記した (ADR-018 amendment / ADR-064 ステータス欄と同じ内容を計画書側にも
反映し、3 箇所の記述を揃える)。

検証: cargo test --workspace 1906 件 pass (新規 5 件)、clippy --workspace -D warnings 緑、
pnpm lint:docs / lint:md 0 error。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
aloekun added a commit that referenced this pull request Aug 3, 2026
… 3) (#353)

* refactor(pr-monitor): CronCreate park モデルを撤去し single-shot 化 (WP-17 PR 3)

cli-pr-monitor を「1 回 check して必ず terminal 報告で終了する」モデルへ移行する。
旧 Bb-1/Bb-2 の park モデル (未確定なら state に wakeup 時刻を書き、[PR_MONITOR_PARK]
envelope で Claude に CronCreate 予約を依頼し、発火時の再 invoke で継続する) を撤去した。

## なぜ廃止するか (ADR-018 amendment)

- wakeup はローカルセッションの寿命に依存する。PR #237 でセッション終了による
  CronCreate 失効 = 監視の取りこぼしを実観測した。
- WP-17 で GitHub Actions 経路 (pr-monitor workflow の Phase A/B) が常設になった。
  CodeRabbit のレビュー到着・後続コメントがそのままトリガーになるため、ローカルの
  時限 wakeup は冗長になった。

## 挙動の変化

- 未確定 (review 未完) → 旧: park + wakeup 予約 / 新: terminal `pending_review` 報告
  (「後続は GitHub Actions 経路が処理」を明示。ADR-064 の陽性証拠原則どおり
  pending を silent success に見せない)
- rate-limit reset 待ち → 旧: park / 新: terminal `rate_limited` 報告 (ADR-064 (b) の
  保留判定文を維持)。reset 経過後の即時 retrigger 投稿と comment dedup は残す
- fresh push → 旧: checker を呼ばず initial park / 新: 即 1 回 check して報告

## 残したもの (park の付随物ではないため)

- **時刻窓アンカーの継続** (`should_continue_state`): 同一 PR + 同一 head なら
  state.started_at / fix_push_time を維持する。これを落とすと再実行のたびに
  `--push-time` が「今」になり、push 後に届いた CR コメントが新着判定から漏れる。
  旧 `should_resume_wakeup` から wakeup 時刻経過の条件だけを外した形
- rate_limit_retries / rate_limit_last_retriggered_at (dedup と retry 上限は
  invocation 跨ぎで引き続き意味を持つ)
- **順位 141 の mergeable shortcut** (`[RATE_LIMIT_BUT_MERGEABLE]` signal):
  「rate-limit 中でも既に mergeable なら即 merge を選べる」独立機能で、park の
  付随物ではない。初版 diff は rate_limit_signal.rs の削除に巻き込んで silent に
  消しており、pre-push simplicity review が REJECT
  (SIM-NEW-src-cli-pr-monitor-rate_limit-L138: 他の撤去物は全て ADR/計画書に
  列挙されているのに shortcut だけ無言で消えている = 意図の疑い) → fix step が
  rate_limit.rs へテストごと復元した (実測検証済み)。terminal 化した
  `finalize_waiting_reset` から引き続き発火する。signal 文面の選択肢 B は
  single-shot 後の実態 (Actions 経路 / --monitor-only 再実行) に合わせて更新
- **rate-limit 系 terminal での head_commit 保存**: 2 回目の pre-push review が、
  `finalize_waiting_reset` / `finalize_posted_retrigger` で head_commit 保存が
  落ちている回帰を REJECT で検出 (SIM-NEW-rate_limit-L154: 保存が無いと次回
  `--monitor-only` の継続判定が fresh 初期化に倒れ、時刻窓リセットで CR コメントを
  取りこぼす — 本 PR 自身が動機に挙げた incident class の再導入)。fix step が
  両関数への保存 + 回帰テスト 2 件を追加した (実測検証済み)

## 撤去したもの

- state: next_wakeup_at_unix / wakeup_reason / review_recheck_count (旧 state file に
  残っていても unknown field として無視される — 前方互換テストを追加)
- poll: review_recheck.rs / review_recheck_signal.rs (park scheduling と PARK envelope
  整形の全量)。rate_limit_signal.rs はファイルとしては削除したが、上記のとおり
  shortcut 部分は rate_limit.rs へ移設して維持
- config: [review_recheck] セクションと sanitize / overflow 検査 (wakeup 時刻の加算
  演算ごと消滅)、monitor.max_duration_secs (ループ上限)。旧 config は unknown
  field/section として無視される — 前方互換テストを追加

検証: cargo test -p cli-pr-monitor 226 件 pass、workspace 全緑、
clippy --workspace -D warnings 緑。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(hooks-session-start): pr_monitor catch-up nudge を撤去 (WP-17 PR 3)

pr_monitor catch-up nudge (Bb-3) は「park 中にセッションが終了して CronCreate が失効
した場合の救済」だった。park/wakeup モデルの廃止 (同 PR の cli-pr-monitor 変更) に伴い
救済対象の状態が存在しなくなるため、機構ごと撤去する。

- hooks-session-start: pr_monitor.rs module 削除 + main.rs の配線解除
- .claude/hooks-config.toml: telemetry registry から `pr_monitor_catchup` id を除去
  (過去の firings jsonl には残るが現役 id ではない)。あわせて、どのコードも読んで
  いなかった daemon 時代 (ADR-009) の死に設定 [post_pr_monitor] セクションを撤去
  (現行の監視設定はリポジトリルートの pr-monitor-config.toml が正)
- cli-telemetry-report: doc comment の例示 id を現役のものへ差し替え

検証: cargo test --workspace 1893 件 pass、clippy --workspace -D warnings 緑。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(adr): ADR-018 amendment (park モデル廃止) + ADR-064/034 整合 + 計画書 (WP-17 PR 3)

- ADR-018 追記 (2026-08-03): park モデル廃止の決定・根拠・維持したもの (時刻窓アンカー
  の state 継続 / rate-limit dedup) を記録。ADR-064 検証残の移し替え ((a) park 実観測は
  moot / (b) 判定文の保留保証は Actions 経路の検証残へ) を明記
- ADR-064 ステータス欄: 同じ移し替えを検証残の側からも記載 (両方に記録して検証の穴を
  残さない — 計画書の着手前決定 2)
- ADR-034: Bundle b の CronCreate park モデル記述に「廃止済み、歴史記録として読む」
  注記を追加
- 計画書: PR 3 を実施中に更新し、実装で確定した設計判断 (should_continue_state を残す
  理由) と「本 PR の PR がスモーク段 1 の観測対象を兼ねる」ことを記載

検証: pnpm lint:docs / lint:md 0 error。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(pr-monitor): head_commit の None 上書き防止 + テストの gh 依存除去 + fail-closed 回帰復元 (CodeRabbit #353)

PR #353 のレビュー指摘への対応。CodeRabbit 3 件 + pre-push simplicity 1 件で、
**4 件中 3 件は本 PR が持ち込んだ実バグ / カバレッジ欠落**だった。

## CodeRabbit 指摘 2 (Minor): terminal 化 3 経路が head_commit を None で上書きする

`finalize_pending_review` / `finalize_waiting_reset` / `finalize_posted_retrigger` が
`state.head_commit = pr_info.head_commit.clone()` と**無条件代入**していた。
`util::get_pr_head_commit` は gh 失敗を None に潰すため、API 障害のたびに保存済み OID が
消える。その状態で次回 `--monitor-only` を実行すると `should_continue_state` が None を
見て継続を拒否し、時刻窓アンカーが「今」へリセットされて間に届いた CR コメントを
取りこぼす — 本 PR が動機に挙げた incident class (#237/#307/#309) と同型。

CodeRabbit が「共通の根本原因」と指摘したとおり 3 経路に同じ欠陥があったため、
`PrMonitorState::record_head_commit(Option<&str>)` を state.rs に置き、Some のときだけ
上書きする単一実装へ集約した。取得失敗は「head が変わった」の証拠ではないので既存値の
保持が正しい (fail-safe)。回帰テスト 2 件 (None で消えない / Some で上書きする) を追加。

## CodeRabbit 指摘 3 (Minor): テストが実 gh CLI を呼ぶ

`finalize_waiting_reset` は内部で `fetch_mergeable_status` → `run_gh_quiet` を呼ぶため、
前コミットで追加した回帰テストが実 gh プロセスを起動していた (ネットワーク依存・低速)。

`finalize_waiting_reset_with(..., fetch_mergeable: impl FnOnce(&PrInfo) -> Option<..>)`
へ本体を切り出し、production は `fetch_mergeable_status` を、テストは closure を渡す形に
した (cli-push-runner の `verify_diff_covers_pr_range` と同じ注入の流儀)。

あわせて「注入したものが実際に使われている」ことを確認するテストを追加した — `|_| None`
を渡すだけのテストでは、fetcher を無視する実装でも通ってしまい注入の正しさを判別できない。
Cell フラグで呼び出し自体を観測する。

## pre-push simplicity 指摘 (SIM-NEW-rate_limit-L122, Medium): fail-closed 回帰の消失

tests.rs のファイル分割時に `finalize_posted_retrigger_action_required_when_write_state_fails`
だけが欠落していた。この関数は 2 つの sibling (`finalize_waiting_reset` /
`finalize_pending_review`) と**逆に fail-closed** で、理由は `@coderabbitai review` 投稿と
いう副作用を伴うため state 永続化の成否で重複投稿を防ぐ必要があるから。fail-open 側 2 件の
回帰テストは移行されていたのに、意図的に非対称な唯一の経路のテストが落ちていた。復元し、
非対称の理由を doc comment に明記した。

## CodeRabbit 指摘 1 (Minor): 計画書の stale な park 検証残

「WP-15 追補残: レート制限 park の実観測」は park 廃止で (a) が moot になっていた。
見出しを「レート制限時の保留保証 (GitHub Actions 経路)」へ改め、(a) の終了と (b) の
引き継ぎ先を明記した (ADR-018 amendment / ADR-064 ステータス欄と同じ内容を計画書側にも
反映し、3 箇所の記述を揃える)。

検証: cargo test --workspace 1906 件 pass (新規 5 件)、clippy --workspace -D warnings 緑、
pnpm lint:docs / lint:md 0 error。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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