feat: stamp the version into the release payload and smoke-test it#130
Open
REPPL wants to merge 1 commit into
Open
feat: stamp the version into the release payload and smoke-test it#130REPPL wants to merge 1 commit into
REPPL wants to merge 1 commit into
Conversation
Phase 4 of the derived-versioning programme (itd-67 / spc-11), and the last. THE VERSION LANDS ON THE PAYLOAD, NEVER ON THE TREE `RenderPayload` stamps the derived version into the payload's copies of plugin.json and marketplace.json — primary, marketplace secondary, and the changelog entry — in lockstep. The working tree's manifests stay version-absent (ADR-19) and a test asserts a render leaves them byte-identical. The new .abcd/config/version-location.json is a decision artefact: it says WHERE a version goes (an RFC-6901 path), never what it is. THE LOCKSTEP GATE WAS INERT AND IS NOW LIVE version-location.json was absent, so CheckLockstep returned unreadable and `launch --dry-run` could never pass. Adding it exposed a second fault: DryRun and Ship asserted the PUBLIC polarity against the working tree, which under ADR-19 is version-absent forever, so the gate could never be satisfied and its diagnostic told the operator to add the very key the policy forbids. Both now assert TreeDev over the source tree; TreePublic stays where it belongs, over the rendered payload. The dry-run reports lockstep dev/ok and no refusals for the first time. THE LIGHT SMOKE Manifests parse, `source` resolves, and every declared command/agent/hook path exists in the resolved payload bundle — not in the working tree, because a file present in the tree but excluded from the payload is exactly the bug this catches. Wired into DryRun and Ship, so it gates rather than merely tests. Resolution is split from assertion behind a PayloadTree interface, with both implementations pinned to the same resolved list, so itd-66's deferred deep tier reuses the resolution unchanged. NOTHING PARTIAL, EVEN ACROSS STEPS The version-free half of the render — destination checks, bundle resolution and the smoke — is hoisted into PrecheckPayload and runs BEFORE the changelog write, so a payload refusal can no longer leave a written release record behind a staged directory. A rollback backstop restores the record if a later error slips through, and the message says which happened. Assisted-by: Claude:claude-opus-4-8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 4 — the final phase of the derived-versioning + auto-changelog + plugin-distribution
programme (plan:
.abcd/development/plans/2026-07-21-derived-version-and-changelog.md;spec: spc-11).
Fifth of five stacked PRs. Based on
feat/itd-67-p3-launch-ship-changelog(#129).The version lands on the payload, never on the tree
RenderPayloadstamps the derived version into the payload's copies ofplugin.jsonandmarketplace.json— primary, marketplace secondary, and the changelog entry — in lockstep.ADR-19 holds:
git diff … -- .claude-plugin/is empty, both committed manifests carryzero
versionkeys, and a test asserts a render leaves the working tree's manifestsbyte-identical. The new
.abcd/config/version-location.jsonis a decision artefact: itdeclares
manifest_pathand an RFC-6901json_pointer— where a version goes, never whatit is. The
seedfigure ADR-19's prose mentions was deliberately omitted, since outcome 4says the base is the newest git tag and a seed would be both stale and the one value in the
file readable as a version.
The lockstep gate was inert, and is now live
version-location.jsonwas absent, soCheckLockstepreturned unreadable andlaunch --dry-runcould never pass. Adding it exposed a second fault, which the reviewcaught:
DryRunandShipasserted the public polarity against the working tree —which under ADR-19 is version-absent forever. The gate could never be satisfied, and its
diagnostic told the operator to add the very key the policy forbids.
Both now assert
TreeDevover the source tree;TreePublicstays where it belongs, over therendered payload.
versionnow reads0.3.0, sourced from the CHANGELOG's dated heading rather than from amanifest read.
resolveVersionis deleted — post-ADR-19 there is no manifest version to read.The light smoke
Manifests parse,
sourceresolves, and every declared command/agent/hook path exists in theresolved payload bundle — not in the working tree, because a file present in the tree but
excluded from the payload is exactly the bug this catches. Wired into
DryRunandShip, soit gates a release rather than merely testing one.
The phase-4 STOP was not hit, and it is addressed structurally rather than asserted: a
PayloadTreeinterface splits resolution from assertion, andTestPayloadTreeImplementationsResolveIdenticallypins both implementations to the sameresolved list. itd-66's deferred deep tier reuses the resolution unchanged.
It also surfaces iss-110 honestly rather than hiding it: the resolved surface lists
agents/CHANGELOG.mdandagents/README.mdaskind: agent, because filtering them herewould conceal the defect that issue tracks.
What the review caught — three
FIX_FIRST, all fixedthe smoke, the gate meant to stop a broken release — ran after
release.Ingesthadwritten the dated heading. Reproduced twice: exit 2 with a message that never mentions the
write,
CHANGELOG.mdcarrying## [0.4.1], files staged in the destination, and the retrythen permanently refused as
release-in-flight. The version-free half of the render is nowhoisted into
PrecheckPayloadand runs before the write, with a rollback backstop thatrestores the record and says so if a later error slips through.
above).
source: "./"means theharness reads the committed manifest, which by ADR-19 carries no version, and
release.ymlpublishes only binaries and checksums. The claim is replaced with what is true today.
Verification
gofmt -l .silent ·go vet ./...·go build ./...·go test ./...·go test -race·go run ./cmd/record-lintexit 0 ·go run ./cmd/abcd docs lint0 blockers ·go run ./cmd/abcd launch --dry-runclean.Tested under CI's actual condition: full suite green in a clone with its tags deleted.
CHANGELOG.md,go.mod,go.sum,.claude-plugin/**and every.github/workflows/*.ymlare untouched.
Noted, not fixed
launch.Ship()/ShipRequesthas no caller outside its own tests — the CLI'sabcd launch shipgoes throughrelease.Emit/release.Ingest. Confirmed identical on #129,so this is pre-existing itd-65 scaffolding, left alone per "leave pre-existing dead code
alone — mention it".