feat(devtools): enforce merge-gate at the actual merge boundary - #3607
Conversation
|
Warning Review limit reached
Next review available in: 27 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: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
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 |
Problem: `devtools workspace merge-gate record/check` and the one-full- verify-per-merge-train rule are both real fixes for real 2026-08-01 incidents (late CodeRabbit findings after a squash-merge; a 43-test regression no CI check or review comment ever flagged), but both fire only if a coordinator remembers to invoke them at the right moment. The fanout-operations incident-ledger cross-check found the pattern: fixes that became a command stuck, fixes that stayed a memory-triggered rule recurred. Duplicate bead filings polylogue-ct3r2/polylogue-t6iga name this same gap; both are addressed by this one change. Solution: `devtools/merge_boundary.py` adds `devtools workspace merge <PR>`, a wrapper around the actual merge action (`gh pr merge --squash` via `gh`, since this repo's CI is CircleCI-only with no GHA hook to attach to). It auto-records a merge-gate receipt when none is fresh for the current head sha, refuses to merge on any `merge-gate check` BLOCK, strips a doubled `(#N) (#N)` squash-subject suffix (the 2026-07-12/13 title-hygiene incident), then merges. `--dry-run` runs every check without merging; `--with-verify` runs and records the merge-train's terminal full-suite verify immediately after merging into a ledger (`.cache/verify/merge-gate/merge-train-ledger.json`). `train-status` reports (exit 1) any PRs merged since the last recorded full-suite verify -- the structural stand-in for "a merge-train records the full-suite verify as its terminal ledger step" -- and `record-full-verify` records that step directly. Registered in devtools/command_catalog.py; CLAUDE.md's merge-checklist sentence now points at `workspace merge` instead of the two-step memory-triggered `merge-gate record`/`check` sequence. Verification: devtools test tests/unit/devtools/test_merge_boundary.py tests/unit/devtools/test_merge_gate.py -> 35 passed. devtools verify --quick -> all steps ok, exit_code 0 (ruff format/check, mypy --strict, render all --check, topology/layering/manifests/lab-policy gates). Ref polylogue-ct3r2, polylogue-t6iga (duplicate filings of the same recommendation -- implemented once here; coordinator should dedupe the beads). Co-Authored-By: Claude <noreply@anthropic.com>
78a81b1 to
5f966d9
Compare
6753s (byte-dup supersession, PR #3624), c831 (message_type restamp, PR #3629, live backfill verified 0 remaining), qhk8z (duplicate-chain baseline tie-break, PR #3616), ct3r2 + t6iga (merge-gate enforcement, PR #3607, duplicate beads for the same fix). Co-Authored-By: Claude <noreply@anthropic.com>
Summary
Adds
devtools workspace merge <PR>, a wrapper aroundgh pr merge --squashthat structurally enforces the existingmerge-gaterecord/check safety net and the merge-train terminal full-suite-verify rule, instead of leaving both as steps a coordinator has to remember to invoke.Problem
devtools workspace merge-gate record/check(merge_gate.py) and the CLAUDE.md rule "run one full-suitedevtools verify --allper merge-train session" are both real, already-shipped fixes for real 2026-08-01 incidents:Both fixes work, but only if a coordinator remembers to invoke them at the right moment mid-session. The fanout-operations report's incident-ledger cross-check (referenced by beads
polylogue-ct3r2andpolylogue-t6iga, which are duplicate filings of the same finding) found the exact pattern: fixes that became a command stuck; fixes that stayed a memory-triggered rule recurred at least once.There is no GitHub Actions hook available to attach enforcement to here -- this repo's CI is CircleCI-only, GHA is intentionally dark. The actual merge boundary in practice is a human/agent coordinator invoking
gh pr merge --squash, so that is the wrapping point.Solution
devtools/merge_boundary.py, registered asdevtools workspace mergeindevtools/command_catalog.py:merge <PR>: refuses unless the PR isOPEN; if no fresh merge-gate receipt exists for the current head sha, auto-records one (running--command, defaultdevtools verify) instead of just erroring; runsmerge-gate checkand refuses to merge on any BLOCK (stale/missing receipt, nonzero exit, unacked late review comment); applies title hygiene by stripping a doubled(#N) (#N)squash-subject suffix (the 2026-07-12/13 incident where a manualgh pr edit --titlestep was skipped); then runs the actualgh pr merge --squash.--dry-runruns every check without merging.--with-verifyimmediately runs and records the merge-train's terminal full-suite verify after merging.train-status: reports (exit 1) any PRs merged since the last recorded full-suite verify, reading a ledger at.cache/verify/merge-gate/merge-train-ledger.json-- the structural stand-in for "a merge-train records the full-suite verify as its terminal ledger step."record-full-verify --command "devtools verify --all": runs and records that terminal step directly, clearing the ledger's pending list.CLAUDE.md's merge-checklist bullet ("Before squash-merging any PR") now names
workspace mergeas the command to actually use, rather than describing the two-stepmerge-gate record/checksequence as something the coordinator must remember to run in order.The lower-level
merge-gate record/check/ackcommands are untouched and still usable directly for ad hoc receipt inspection;mergeis a thin, tested composition on top of them (cmd_record/cmd_checkare called directly, not re-implemented).Verification
devtools test tests/unit/devtools/test_merge_boundary.py tests/unit/devtools/test_merge_gate.py-> 35 passed (decision-logic tests mocksubprocess.run/ghboundaries per the existingtest_merge_gate.pypattern: receipt fresh/stale/absent, unacked newer review comment, title-hygiene collapse/append/idempotence, dry-run never callinggh pr merge, ledger append/pending-PR detection,record-full-verifyclearing pending state).python -m mypy devtools/merge_boundary.py tests/unit/devtools/test_merge_boundary.py-> no issues.devtools verify --quick-> exit_code 0 (ruff format/check, mypy --strict,render all --check, topology/layering/manifests/lab-policy gates all ok).devtools render topology-projection/devtools render devtools-referencewere run and their outputs (docs/plans/topology-target.yaml,docs/devtools.md) are included in this diff.Not run:
devtools verify --all(full non-integration suite) -- left for the coordinator's merge-train terminal step, consistent with what this PR itself builds.Ref polylogue-ct3r2, polylogue-t6iga -- both bead filings describe the same recommendation; this PR implements it once. The coordinator should dedupe the two beads (close one as duplicate-of-the-other, or close both against this PR) rather than tracking the same follow-up twice.
Co-Authored-By: Claude noreply@anthropic.com