Skip to content

fix(merge-pipeline, stop-hook): sync_local の stale master 解消 + Stop hook の takt subsession skip + ADR-013/004 amendment - #222

Merged
aloekun merged 2 commits into
masterfrom
fix-merge-pipeline-sync-and-stop-hook-subsession
Jun 27, 2026
Merged

fix(merge-pipeline, stop-hook): sync_local の stale master 解消 + Stop hook の takt subsession skip + ADR-013/004 amendment#222
aloekun merged 2 commits into
masterfrom
fix-merge-pipeline-sync-and-stop-hook-subsession

Conversation

@aloekun

@aloekun aloekun commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Summary

新 PC で発覚した「post-merge-feedback subsession が read-only (edit: false) のはずなのに src/lib-report-formatter/src/lib.rs を stray 編集した」事故の 連鎖根本原因を 2 layer で修正 する。

ユーザー分析 (2026-06-26 セッション) で確定した root cause:

  1. バグ A (本筋): merge-pipelinesync_local()jj new master (= local bookmark 参照) で stale local master に着地 → working tree に古いコード (= cargo clippy 失敗対象) が乗る
  2. バグ B (多層防御): hooks-stop-qualitystop_hook_active のみで判定し、takt subsession の Stop event でも品質ゲートが発火 → stale tree の lint 失敗を「直せ」と subsession に指示 → edit: false 制約に反して stray edit を試みる

this PC では .jj/repo/config.tomlauto-track-bookmarks = "*" (ADR-011 推奨設定) が偶然 local master を fast-forward させてバグ A が顕在化していなかったが、構造的バグはずっと存在。今回両方を直接修正する。

変更内容

バグ A 修正: sync_localmaster@origin (= remote tracking ref) 直接参照に変更

src/cli-merge-pipeline/src/main.rssync_local():

  • 旧: format!("jj new {}", branch) → 例: jj new master
  • 新: format!("jj new {}@origin", branch) → 例: jj new master@origin

master@origin (= remote tracking ref) は jj clone 直後から自動生成され、.jj/repo/config.tomlauto-track-bookmarks 設定の有無に依存しない。ADR-011 (push 戦略) との偶発的結合 (coincidental coupling) を構造的に解消

新 helper sync_local_new_command(branch: &str) -> String を抽出して unit test 3 件で command format を pin (master / main / @origin suffix 不在検出)。

バグ B 修正: hooks-stop-quality に takt subsession skip 判定を追加

src/hooks-stop-quality/src/main.rs:

  • 新 helper takt_subsession_active(repo_root: &Path) -> bool: .takt/runs/*/meta.json を scan、いずれかが status: "running" なら true。1 件目で短絡 return、malformed JSON は defensive skip
  • 新 helper meta_status_is_running(meta_path: &Path) -> bool: 単一 meta.json の status 判定 (test 容易化のため切り出し)
  • 新 helper should_skip_quality_gate(hook_input: &HookInput) -> bool: stop_hook_active チェック + takt subsession check を組み合わせた skip 判定
  • main() 関数長 81 行 (元 75 + 本 PR の +6) を read_stdin_or_block / parse_hook_input_or_block / warn_no_steps_configured / run_quality_steps / block_on_failures の 5 helper に分解して 18 行に縮小 (順位 48 関数長 50 行ガイドライン内)

unit test 9 件追加 (runs dir 不在 / meta.json 0 件 / 全 status=completed / 一部 status=running / single running / malformed JSON / 各 status 値の境界等を網羅)。

ADR-013 amendment

docs/adr/adr-013-merge-pipeline.md:

  • 設計上の決定 table のローカル同期行を jj new master@origin に更新、注釈追記
  • 新 section 「§ sync_local の前提条件 (2026-06-26 追加、PR-W1 follow-up)」 を追加:
    • 設計上の理由 (= local bookmark の状態に依存しない、master@origin は clone 直後から存在、ADR-011 との分離)
    • 過去の不具合経緯 (新 PC の auto-track-bookmarks 欠落 → stale tree → stray edit)
    • ADR-004 (Stop hook 多層防御) との cross-reference

ADR-004 amendment

docs/adr/adr-004-stop-hook-quality-gate.md:

  • 新 section 「§ takt subsession skip (2026-06-26 追加、PR-W1 follow-up)」 を追加:
    • 趣旨 (= 本対話セッションの品質担保が ADR-004 の責務範囲、edit: false subsession は範囲外)
    • 検知条件 (.takt/runs/*/meta.json の status: "running")
    • 同 marker の他用途 (ADR-030 .failed marker + hooks-session-start reaper module) との相補的利用
    • 実装の所在と test カバレッジ
    • 由来事例 (バグ A 連鎖の半分としての発生経緯)

ADR-011 は touch しない

ADR-011 (push 戦略) は push の関心領域そのまま で維持。merge-pipeline は master@origin 直接参照に変わったため、auto-track-bookmarks 設定 (= push の Effect 1) と merge-pipeline は 完全に分離。両 ADR の責務領域が綺麗に分かれる。

検証

  • cargo build --workspace: clean
  • cargo test --workspace: 1308 tests passed (新規 12 件 = 3 sync_local + 9 takt_subsession)
  • cargo clippy --workspace -- -D warnings: clean
  • markdownlint (両 ADR): 0 errors
  • takt pre-push-review approved (1 iteration, 748s, Result: Success)

なぜ this PC で問題が顕在化しなかったか

.jj/repo/config.toml[remotes.origin] auto-track-bookmarks = "*" 設定 (ADR-011 推奨) が 偶然 次の effect を提供していた:

  • Effect 1 (push の関心): 新規 remote bookmark の auto-track
  • Effect 2 (merge の関心): tracked bookmark の fast-forward = jj git fetch 時に local master が master@origin に追従

merge-pipeline は Effect 2 に 暗黙的に依存 していたが、ADR-011 / ADR-013 のどちらにも記述されていなかった (= coincidental coupling の典型例)。

本 PR で merge-pipeline は master@origin を直接参照する設計に変わり、Effect 2 への依存が消滅。新 PC で auto-track-bookmarks 設定が無くても safe に動く。

PR 計画への影響

PR 状態
PR-W1 (hooks-post-tool-comment-lint-rust 分割) ✅ merged (#220)
本 PR (バグ A + バグ B fix + ADR 更新) ← 本 PR
PR-W2 (cli-pr-monitor 分割: poll/mod.rs + fix_commit.rs) next
PR-W3 (cli-merge-pipeline 分割: feedback.rs + main.rs) next
PR-W4 (cli-push-runner 分割: lint_screen.rs + config.rs) next
PR-W5 (Stop hook gate 追加、Phase 2 完了) future

本 PR は PR-W1 と PR-W2 の間に挟む follow-up として位置付け。merge-pipeline 自体は PR-W3 で分割対象になるが、本修正は分割前に root cause 解消を優先 (= 分割と修正は独立した変更、bundle すると review boundary が曖昧化する)。

Summary by CodeRabbit

  • New Features
    • takt の実行中サブセッションでは、Stop フックの品質ゲートを条件付きで自動スキップするようになりました。
    • マージ後のローカル同期手順が、より確実にリモート最新を基準にする形に更新されました。
  • Bug Fixes
    • 古いローカル状態や不完全なメタ情報による誤判定、品質ゲートの不必要なスキップを軽減します(異常時は安全側)。
  • Documentation / Tests
    • 手順と方針を ADR に反映し、関連するケースをテストで網羅しました。

…ook の takt subsession skip

新 PC での merge-pipeline 実行時に発覚した「post-merge-feedback subsession が
読み取り専用 (edit: false) のはずなのに lib.rs を stray 編集した」事故の
連鎖根本原因を 2 layer で修正。

## バグ A (本筋): merge-pipeline sync_local の stale local master 依存

src/cli-merge-pipeline/src/main.rs:685 の `jj new <branch>` (= jj new master)
は local master bookmark を解決するため、`.jj/repo/config.toml` に
`remotes.origin.auto-track-bookmarks = "*"` が無い環境では `jj git fetch` 後も
local master が古い tip に固定され、stale code (= squash 前の状態) に
working copy が乗ってしまう。

修正: `jj new <branch>@origin` (= jj new master@origin) に変更。
remote tracking ref を直接参照することで、auto-track-bookmarks 設定の
有無に関わらず必ず最新 tip に着地する。ADR-011 (push 戦略) への依存を
構造的に切り離す。

## バグ B (多層防御): hooks-stop-quality が takt subsession で無差別発火

src/hooks-stop-quality/src/main.rs:122 は stop_hook_active のみで判定し、
takt subsession (edit: false で起動された分析専用 session) でも品質ゲートを
発火させていた。stale tree + clippy 失敗で「直せ」指示が出ると、edit: false
のはずの subsession が stray edit を試みる。

修正: stop_hook_active チェックの直後で `.takt/runs/*/meta.json` を scan、
status: \"running\" の active takt run が 1 件以上存在すれば品質ゲートを
skip。reaper module の同 marker 利用パターン (hooks-session-start) を踏襲。

## ADR 更新

- ADR-013 (merge-pipeline): sync_local の `master@origin` 直接参照を前提条件
  として明文化 (バグ A 修正の根拠 cite)
- ADR-004 (Stop hook 品質ゲート): takt subsession skip 条件 = active meta.json
  を判定材料として明文化 (バグ B 修正の根拠 cite、ADR-031 weekly-review 等の
  read-only workflow と整合)

ADR-011 (push 戦略) は touch しない (= push と merge は別 concern、coincidental
coupling を解消する)。
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

takt の Stop フックに subsession 由来の skip 条件を追加し、sync_local の開始ベースを master@origin に変更した。関連 ADR、実装、テストも更新された。

Changes

Stop hook quality gate skip

Layer / File(s) Summary
Skip rule and flow
docs/adr/adr-004-stop-hook-quality-gate.md, src/hooks-stop-quality/src/main.rs
.takt/runs/*/meta.jsonstatus: "running" と鮮度条件で Stop フックの品質ゲートを skip する条件、判定関数、早期 return、block 出力の分割が追加された。
Skip rule tests
src/hooks-stop-quality/src/main.rs
takt_subsession_active と関連判定のテストが追加され、runs 欠落、meta.json 欠落、running、非 running、壊れた JSON、mtime 境界の各ケースが検証された。

Merge pipeline sync_local base update

Layer / File(s) Summary
sync_local origin base
docs/adr/adr-013-merge-pipeline.md, src/cli-merge-pipeline/src/main.rs
sync_local のベース作成が jj new master から jj new master@origin に変わり、完了メッセージと sync_local_new_command の切り出しが追加された。
sync_local command tests
src/cli-merge-pipeline/src/main.rs
sync_local_new_commandmaster/main@origin を含むコマンドを返すことを確認するテストが追加された。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • aloekun/claude-code-hook-test#154: .takt/runs/*/meta.jsonstatus と鮮度閾値を使う判定が同じため、Stop フック側の active run 判定に直接つながります。
🚥 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 主変更である sync_local の stale master 解消と Stop hook の takt subsession skip、関連 ADR 更新を具体的に示しています。
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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.

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/adr/adr-013-merge-pipeline.md`:
- Line 64: ADR内のコマンド表記が統一されていないため、本文中に残っている旧表記を修正してください。`adr-013-merge-pipeline`
の手順説明で `jj new master@origin` を正とするなら、同じ文書内の `new master` が残っている箇所もすべて
`master@origin` ベースに揃え、`sync_local` や関連手順の記述が一貫するように更新してください。該当箇所は `sync_local`
の説明とその周辺の手順セクションを確認して修正してください。

In `@src/hooks-stop-quality/src/main.rs`:
- Around line 71-96: takt_subsession_active currently skips the quality gate for
any run whose meta.json says status is "running", which can over-disable Stop
hook behavior for stale or unrelated runs. Tighten the check in
takt_subsession_active (and the caller path in should_skip_quality_gate) so it
only returns true for the intended read-only takt subsession, using an
additional freshness or run-type/ownership condition alongside
meta_status_is_running rather than trusting every running entry under
TAKT_RUNS_DIR. Ensure orphan or stale runs do not cause a global skip.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 621e5484-522a-4abd-aa0d-7d7aad1f8b66

📥 Commits

Reviewing files that changed from the base of the PR and between 2e7e3cc and 9417fe3.

📒 Files selected for processing (4)
  • docs/adr/adr-004-stop-hook-quality-gate.md
  • docs/adr/adr-013-merge-pipeline.md
  • src/cli-merge-pipeline/src/main.rs
  • src/hooks-stop-quality/src/main.rs

Comment thread docs/adr/adr-013-merge-pipeline.md
Comment thread src/hooks-stop-quality/src/main.rs
…+ ADR-013 旧表記統一

## 変更内容

### CR Major (src/hooks-stop-quality/src/main.rs)

`takt_subsession_active()` の skip 判定に **mtime ベースの freshness check** を追加。
status: "running" のみを根拠にすると、abrupt termination で残った orphan run が
.takt/runs/ に永続的に存在し、以降の全セッションで品質ゲートが skip されてしまう
致命的 regression を構造的に解消した。

- 新 helper `meta_is_active_run()`: status == "running" AND mtime fresh の AND 条件
- 新 helper `meta_is_fresh()`: mtime が ACTIVE_RUN_FRESH_THRESHOLD_SECS (= 1500s) 以内
- ACTIVE_RUN_FRESH_THRESHOLD_SECS は hooks-session-start の reaper の
  ORPHAN_THRESHOLD_SECS と同値 = 両者の「abrupt termination 判定」共通契約
- fail-closed: mtime 取得失敗 / 未来時刻 (clock skew) は active 扱いしない
- test 7 件追加 (boundary 値 / orphan skip / fresh vs stale 混在 / 各種 fail-closed 経路)

### CR Minor (docs/adr/adr-013-merge-pipeline.md)

ADR 内に残っていた旧表記 `jj new master` を 2 箇所 `jj new master@origin` に更新:
- Line 14 「マージ → fetch → new master を毎回手動で実行」
- Line 53 「jj git fetch && jj new master でローカル同期」

### ADR-004 拡張

§ takt subsession skip の判定条件に「mtime が ACTIVE_RUN_FRESH_THRESHOLD_SECS 以内」
を追記、新 sub-section「freshness check の必要性 (CR PR #222 Major 指摘対応)」で
orphan の永続 skip 問題と reaper との threshold 整合を明文化。

## 検証

- cargo test -p hooks-stop-quality: 25 passed (= 元 18 + 新 7)
- cargo test --workspace: 1315 passed
- cargo clippy --workspace -- -D warnings: clean
- markdownlint clean (両 ADR)

## CR thread

両 thread に resolved reply 投稿済。
@aloekun

aloekun commented Jun 27, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 27, 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.

🧹 Nitpick comments (1)
src/hooks-stop-quality/src/main.rs (1)

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

1500s の契約値は共通化しておきたいです。

ここは src/hooks-session-start/src/reaper.rs の orphan 判定閾値と同じ値に依存しているので、片方だけ更新されると Stop hook の skip 判定と reaper の回収判定が静かにずれます。共通定数に寄せるか、少なくとも両者の一致を検証するテストを足しておくと将来のドリフトを防げます。Based on learnings, ACTIVE_RUN_FRESH_THRESHOLD_SECS must stay aligned with the orphan-run threshold used by the hooks-session-start reaper module (currently 1500 seconds).

🤖 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/hooks-stop-quality/src/main.rs` at line 72, The 1500-second freshness
threshold used by ACTIVE_RUN_FRESH_THRESHOLD_SECS in main.rs is duplicated with
the orphan-run cutoff in the hooks-session-start reaper logic, so keep them from
drifting. Refactor the threshold into a shared constant or shared source of
truth that both the Stop hook path and the reaper module use, and update the
relevant symbols (ACTIVE_RUN_FRESH_THRESHOLD_SECS and the reaper’s
orphan-detection threshold) to reference it. If sharing the constant is not
practical, add a test that asserts both values stay equal so future changes fail
fast.

Source: Learnings

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

Nitpick comments:
In `@src/hooks-stop-quality/src/main.rs`:
- Line 72: The 1500-second freshness threshold used by
ACTIVE_RUN_FRESH_THRESHOLD_SECS in main.rs is duplicated with the orphan-run
cutoff in the hooks-session-start reaper logic, so keep them from drifting.
Refactor the threshold into a shared constant or shared source of truth that
both the Stop hook path and the reaper module use, and update the relevant
symbols (ACTIVE_RUN_FRESH_THRESHOLD_SECS and the reaper’s orphan-detection
threshold) to reference it. If sharing the constant is not practical, add a test
that asserts both values stay equal so future changes fail fast.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c3bdf3ac-8cc5-4e29-b836-f5d910184169

📥 Commits

Reviewing files that changed from the base of the PR and between 9417fe3 and 4ab5edb.

📒 Files selected for processing (3)
  • docs/adr/adr-004-stop-hook-quality-gate.md
  • docs/adr/adr-013-merge-pipeline.md
  • src/hooks-stop-quality/src/main.rs
✅ Files skipped from review due to trivial changes (2)
  • docs/adr/adr-013-merge-pipeline.md
  • docs/adr/adr-004-stop-hook-quality-gate.md

@aloekun
aloekun merged commit b0b9197 into master Jun 27, 2026
1 check passed
@aloekun
aloekun deleted the fix-merge-pipeline-sync-and-stop-hook-subsession branch June 27, 2026 08:03
aloekun added a commit that referenced this pull request Jun 27, 2026
… 223/224) (#223)

* docs(todo): PR #222 post-merge-feedback T1-1 + T3-1 採用 entries 追加 (順位 223/224)

PR #222 post-merge-feedback (.claude/feedback-reports/222.md) で採用承認された 2 項目を docs/todo10.md + docs/todo-summary.md に登録。Bundle 222-FB として 1 PR 推奨 (T1 mechanical + T3 docs の 2 層防御を 1 PR で land)。

- 順位 223 (🔧 Tier 2、Effort M): ACTIVE_RUN_FRESH_THRESHOLD_SECS と ORPHAN_THRESHOLD_SECS の compile-time 同期。hooks-stop-quality と hooks-session-start で 1500s magic number を別 crate 独立に持ち、現状はコメント契約のみで mechanical enforcement が欠落。cli-merge-pipeline/src/feedback.rs:60 の derived const + 上流定数 precedent を cross-crate に拡張、Option C (const re-export + const _: () = assert!(...) compile-time check) を MVP 推奨。analyzer Tier 1 = mechanical enforcement → feedback_tier_classification per project Tier 2 に再分類。

- 順位 224 (💎 Tier 3、Effort XS): ADR-043 (Security/Quality Gate Fail-Closed) に hooks-stop-quality の error handling を具体例として追記。meta_is_fresh / meta_is_active_run / takt_subsession_active の 4 error path (mtime 取得失敗 / clock skew / malformed JSON / file read error) すべてが false 返却 → gate effective に倒れる fail-closed 構造を concrete instantiation として codify。

新 PC 環境でも同 task list を確認できるよう docs-only PR として分離 (実装は別 PR で着手)。

* fix(review): apply CodeRabbit fixes for #223

Resolved findings:
- [Major] docs/todo10.md:925 「MVP 推奨」の記述と実際のアプローチに齟齬がある。
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