feat: Linux バイナリビルド + クラウド setup script (WP-15) - #307
Conversation
`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>
|
Warning Review limit reached
Next review available in: 31 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughLinux 実行向けにシェル起動、タイムアウト終了、hook コマンド解決、テスト用コマンドをOS依存化しました。Linuxバイナリのnightly公開とクラウドセットアップを追加し、空ロックの競合処理も更新しています。 ChangesLinux portability and deployment
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
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
🤖 PR Monitor 分析 (GitHub Actions バックストップ)
Applicable Findings (Critical / High / Major)該当なし(レビュー未着手のため findings 0 件) Applicable Findings (Medium 以下)該当なし Filtered (not applicable)該当なし 差分概要 (軽量サマリー)
次のアクション
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
🤖 PR Monitor 分析 (GitHub Actions バックストップ)
Applicable Findings (Critical / High / Major)該当なし(findings 0 件) Applicable Findings (Medium 以下)該当なし Filtered (not applicable)該当なし 差分概要 (軽量サマリー)
次のアクション
|
There was a problem hiding this comment.
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
📒 Files selected for processing (15)
.claude/hooks-config.toml.github/workflows/release-binaries.yml.gitignoredocs/harness-improvement-plan.mdpush-runner-config.tomlscripts/cloud-setup.shsrc/check-ci-coderabbit/src/main.rssrc/cli-pr-monitor/src/classifier_runner.rssrc/cli-pr-monitor/src/lock.rssrc/cli-push-runner/src/stages/diff.rssrc/cli-push-runner/src/stages/lint_screen/classifier.rssrc/cli-push-runner/src/stages/push.rssrc/cli-push-runner/src/stages/quality_gate.rssrc/hooks-stop-quality/src/main.rssrc/lib-subprocess/src/lib.rs
🤖 PR Monitor 分析 (GitHub Actions バックストップ)
Applicable Findings (Critical / High / Major)該当なし Applicable Findings (Medium 以下)
Filtered (not applicable)該当なし 次のアクション
|
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>
7a1a058 to
e3988ac
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
🤖 PR Monitor 分析 (GitHub Actions バックストップ)
Applicable Findings (Critical / High / Major)該当なし Applicable Findings (Medium 以下)
Filtered (not applicable)該当なし(今回の指摘はいずれも ADR 抵触・スコープ外・Windows専用起因の的外れ指摘には該当しない) 次のアクション
|
|
Tip For best results, initiate chat on the files or code changes.
マージ前には、PR 上で必須 CI が動かない点を補うため、少なくとも |
🤖 PR Monitor 分析 (GitHub Actions バックストップ)
Applicable Findings (Critical / High / Major)該当なし Applicable Findings (Medium 以下)
Filtered (not applicable)該当なし(今回の指摘はいずれも ADR 抵触・スコープ外・Windows専用起因の的外れ指摘には該当しない) 次のアクション
|
) 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>
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>
## 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 の退役手順で本ファイルごと削除する前提のため見送る。
…#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>
…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>
…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>
… 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>
Summary
nightly) へ単一 tarball として公開するrelease-binaries.ymlを追加.claude/配置 → settings 生成 → jj / takt 導入までを行うscripts/cloud-setup.shを追加cmd /c決め打ちをshell_command(Windows=cmd /c/ 他=sh -c) に集約し、taskkillのみだった timeout kill に unix 分岐を追加.exe/ backslash 依存を{{CLAUDE_DIR}}/{{EXE_SUFFIX}}の実行時展開で解消 (WP-13 からの引き継ぎ)cli-pr-monitorの lock 同時取得レースを修正 (空ロックファイルを stale と誤判定して全員が takeover していた).gitignoreが.claude/*.exeのみで拡張子なし Linux バイナリを無視しない問題を修正Context
使い捨てのクラウドセッション (claude.ai/code) で 19 crate をビルドせずにハーネスを即時有効化するのが目的 (WP-15)。
スコープ補正: 計画本文のステップは「release workflow + setup script」だけだったが、着手時調査で Linux では実行時に壊れる可搬性欠陥が残っていることが判明した。
lib-subprocessのcmd /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 で取得できるため、
ghCLI 認証は不要。トークン受け渡し構成を setup script に持ち込まずに済んでいる。Validation
cargo test --workspace全 pass /clippy --workspace --all-targets --all-features -- -D warningsclean /pnpm build:all16 成果物デプロイ /lint:docs/lint:mdpasscargo test --workspace全 pass /cargo test -- --ignored --test-threads=1全 pass (jj 0.42.0 導入後) / clippy cleanadditionalContextJSON を出力 / PreToolUse が再帰強制削除コマンドを exit 2 でブロックしecho helloは通す / tree-sitter の comment-lint が違反を検出sh -c経由で clippy・cargo test とも PASScloud-setup.shの jj 取得: 実 URL・実展開ロジックで jj 0.42.0 の導入を実走確認verdict=APPROVE(simplicity / security とも、2026-07-20 18:19)/review-local(qwen3-coder:30b, 2 ラン): findings 4 件、いずれも機構の誤読と判定し全件 rejectedcloud-setup.sh実走。nightlyrelease は本 PR マージ後に初回生成されるため、確認はマージ後References
実装済に更新){{EXE_SUFFIX}}/ forward-slash 絶対パスの前例.claude/に同居する不変条件pnpm install --frozen-lockfileで担保Summary by CodeRabbit
新機能
バグ修正
ドキュメント