Skip to content

fix(cleanup): prove squash merges by PR record, ff the local default first, clear worktrees in groups - #600

Merged
SUaDtL merged 5 commits into
mainfrom
fix/586-squash-merge-cleanup
Aug 5, 2026
Merged

fix(cleanup): prove squash merges by PR record, ff the local default first, clear worktrees in groups#600
SUaDtL merged 5 commits into
mainfrom
fix/586-squash-merge-cleanup

Conversation

@SUaDtL

@SUaDtL SUaDtL commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

This repo squash-merges by default, so post-merge-cleanup Phase 1's SHA-ancestry
check (git merge-base --is-ancestor) failed on every normally-merged branch, while
its own prose asserted squash merges were safe to delete — a self-contradiction.
Phase 4 also assumed the local default ref was current, so a stale local main
could make Phase 3's blocked-checkout STOP misattribute the block to a correctly-kept
artifact instead of the real cause.

  • core/surface/skills/post-merge-cleanup/SKILL.md — Phase 1 rewritten from bare
    ancestry to content-containment: ancestry, or a squash-merge proof via the merged
    PR record (headRefOid == HEAD), or (without gh) a byte-identical tree diff.
    Phase 3 now distinguishes a genuinely conflicting kept artifact from a stale local
    default ref. Phase 4 fast-forwards the local default ref (git fetch origin <default>:<default>) before checking it out — git's own refusal of a
    non-fast-forward update on a ref not checked out preserves the --ff-only
    guarantee — with a documented, correctly-attributed STOP if that fetch itself
    refuses (another worktree holding the default). Phase 5 documents why git branch -d already accepts a squash-merged branch via its upstream, and sanctions git branch -D only when this run's Phase 1 proof was the PR-record squash proof and
    -d refused, with the proof restated and the branch named.
  • core/surface/commands/cleanup.md and core/surface/skills/INDEX.md
    hard gate and catalog line reworded to match: containment, not bare ancestry.
  • core/surface/commands/standup.md (closes standup/cleanup: stale worktrees can only be cleared one confirmation at a time #596) — stale worktrees can now be
    confirmed for removal as one explicitly enumerated group naming every member,
    falling back to per-item confirmation on decline; branch pruning documents the
    same squash-merge -D exception, scoped identically to post-merge-cleanup.
  • site/src/curated/commands/cleanup.md / standup.md — hand-written docs
    aligned with the new contracts (no drift gate covers these).
  • Regenerated all three vendored plugin surfaces (plugins/ca, plugins/ca-codex,
    plugins/ca-pi) via tools/build-surface.py; --check and sync-core --check
    both pass.
  • Version bumps: ca 2.11.1 → 2.11.3, ca-codex 0.4.0 → 0.4.2, ca-pi 0.2.0 →
    0.2.2 (regenerated repo-root package.json via build-host-packages.py).
    README version badge, the "currently ships ca-codex X.Y.Z" line, and both
    root/ca-pi CHANGELOGs updated to match — each pinned by its own CI gate
    (check_badge_consistency.py, readme-professional.test.ts,
    test_release_trace.py, build-host-packages.py --release-guard-base).

Deviation from #586's suggested contract

#586 suggested the squash-merge proof require git diff --quiet origin/<default> HEAD (byte-identical tree) and the PR record. This PR demotes the diff to
corroboration: requiring it empty re-breaks the gate the instant any later PR merges
to the default branch — the common state on an active repo, not an edge case. The
PR-record identity (MERGED + headRefOid == HEAD) is what proves every commit on
the branch rode that PR's squash into the default; the diff is reported as a fact
either way (empty or not) but is never a requirement.

Test plan

  • python tools/build-surface.py --check — OK (claude, codex, pi in sync)
  • python tools/sync-core.py --check — OK (56 core files × 3 plugins, byte-identical)
  • git diff --check — no CR/whitespace errors
  • python -m pytest .github/scripts/test_build_surface.py — 40 passed
  • python -m pytest .github/scripts/test_release_trace.py — 29 passed, 34 subtests
  • python .github/scripts/test_badge_consistency.py — 11 passed
  • python .github/scripts/check_badge_consistency.py — consistent
  • python .github/scripts/payload_version_gate.py --plugin plugins/ca --base origin/main — advanced 2.11.1 → 2.11.3
  • python .github/scripts/payload_version_gate.py --plugin plugins/ca-codex --base origin/main — advanced 0.4.0 → 0.4.2
  • python tools/build-host-packages.py --check --release-guard-base origin/main — advanced 0.2.0 → 0.2.2
  • cd site && npm ci && npm run build && npm test — 512/512 passed
  • cd site && npm run typecheck — clean
  • cd site && npm run link-audit — 20057 internal links resolve
  • env -u NO_COLOR python -m pytest plugins/ca/hooks/tests — 1298 passed, 130 subtests; 10 failures confirmed pre-existing/environmental (worktree-checkout git-enforce resolution, not caused by this change — reproduced identically on the unmodified tree via git stash)

Closes #586
Closes #596

https://claude.ai/code/session_01QjJeSbcwPHwMmd6CEZeagB

SUaDtL added 3 commits August 4, 2026 07:37
…first, clear worktrees in groups

CHANGELOG: post-merge cleanup now proves squash merges via the PR record instead of STOPping on them, fast-forwards a stale local default before checkout, and standup can clear stale worktrees as one named group
Claude-Session: https://claude.ai/code/session_01QjJeSbcwPHwMmd6CEZeagB
The ca-pi release guard requires the version bump to ride a new changelog
heading; the prior commit bumped the version without one.

Claude-Session: https://claude.ai/code/session_01QjJeSbcwPHwMmd6CEZeagB
The release-trace guard requires ca's manifest version to be reconciled
with CHANGELOG.md's top section; add the missing heading for this PR's
version bump.

Claude-Session: https://claude.ai/code/session_01QjJeSbcwPHwMmd6CEZeagB
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 8dac69e5-35c5-4c92-af32-213fc0543ad2

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

SUaDtL added 2 commits August 4, 2026 08:19
…ainment contract

test_the_skill_carries_the_issue_308_acceptance_gates pinned the OLD Phase-1
ancestry-only wording and the OLD unconditional "-d, never -D" wording, both
of which this branch deliberately replaced (#586). Repoint at the new
contract instead of deleting: containment is now proven by ancestry OR the
PR-record squash proof (MERGED + headRefOid == HEAD), and `-D` is forbidden
everywhere except the scoped Phase-5 exception. Also fixes an assertion that
was already wrap-fragile ("MUST NOT delete a remote branch" spans a line
break in the rewritten Hard rules block) by matching on \s+ instead of a
literal space, following the pattern the file already used elsewhere.

Verified the repointed assertions actually pin the new contract: swapped in
the pre-#586 SKILL.md from origin/main for one vendored copy and confirmed
the test fails at the first new assertion (`contained in the fetched
default`); separately reverted just the Phase-1 gate line and the Phase-5
-d/-D hard rule and confirmed the `-D` scoping assertion also fails on its
own. Restored the fix before committing.

CHANGELOG: repoints the #308 acceptance-gate test at the #586 content-containment contract instead of the retired ancestry-only wording, so the suite pins the current gate rather than a regression to it
Claude-Session: https://claude.ai/code/session_01QjJeSbcwPHwMmd6CEZeagB
…cleanup

# Conflicts:
#	CHANGELOG.md
#	README.md
#	package.json
#	plugins/ca-codex/.codex-plugin/plugin.json
#	plugins/ca-pi/CHANGELOG.md
#	plugins/ca-pi/package.json
#	plugins/ca/.claude-plugin/plugin.json
@SUaDtL
SUaDtL merged commit d956d15 into main Aug 5, 2026
52 checks passed
SUaDtL added a commit that referenced this pull request Aug 5, 2026
Union-merged the version surfaces over #601 and #600: changelog sections
kept on both sides version-ordered ([2.11.5] atop [2.11.3]/[2.11.2];
ca-pi [0.2.4] atop [0.2.2]/[0.2.1]), manifests and README badge/ships
line kept at this branch's higher versions, root package.json
regenerated via build-host-packages.

Claude-Session: https://claude.ai/code/session_01QjJeSbcwPHwMmd6CEZeagB
SUaDtL added a commit that referenced this pull request Aug 5, 2026
Resolves conflicts on the shared version surfaces against #601 (release-lane
fixes, ca 2.11.2) and #600 (post-merge-cleanup containment contract, ca
2.11.3 / ca-codex 0.4.2 / ca-pi 0.2.2), both merged after this branch was
cut. Resolution: this branch's version bumps (ca 2.11.4, ca-codex 0.4.3,
ca-pi 0.2.3) sit strictly above main's on every changelog, README badge, and
manifest; every other surface (release skill/_releaselib, post-merge-cleanup,
standup, cleanup) takes main's side untouched, since this lane only edits
ORCHESTRATOR.md §6.

Claude-Session: https://claude.ai/code/session_01QjJeSbcwPHwMmd6CEZeagB
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant