feat(rt-release): port draft-release.sh → rt release (285 LOC, idempotency-critical) #557
Labels
No labels
bump
major
bump
minor
bump
patch
kind/bug
kind/chore
kind/docs
kind/feature
priority/critical
priority/high
priority/low
priority/medium
size/L
size/M
size/S
size/XL
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit#557
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Port
draft-release.sh(285 LOC, idempotency-critical) tort releasesubcommand — completes Phase 6 composite equivalence-harness coverage + wires Cutter.Fire's replay machinery to its first production caller.Scope
scripts/draft-release.sh(285 LOC: tag+publish only; no manifest, no seal) tocmd/rt/release.goreleasesubcmd short frozen from Phase 0b ("seal changelog, tag, publish" — seal is prep's, merged into main via #556) — Phase-0b interface refinement sanctioned by milestoneAC
rt releasecompiles + publishes release/tag/assets via internal/forgejo.Client wired through Cutter.Firedraft-release.shbyte-oracle (tag+publish half)TestPrepareForPublish_writesManifestKeyedOnTarget,_dryRunSkipsManifest,_idempotentManifestSkip,TestRollback_publishRestoresManifestLeavesChangelog) +internal/manifestStore equivalence (#542) for byte-format -- restated 2026-07-30: themanifest-precheck.sh + workflow-jqharness named at filing was not the shape built; the coverage is layered instead, and is disclosed in the COVERAGE SCOPE block ofinternal/release/equivalence_test.gogo vet ./...+golangci-lint runcleanFraming-verify ratification notes (2026-07-27, bus 361e)
Engineer's framing-verify surfaced two axes needing ratify before build, both substrate-honestly analyzed. Ratified consciously per dispatch-framing-is-a-claim discipline.
Axis 1: publish wiring — Ratified A (wire through Cutter.Fire)
Rejected: B (thin orchestrator over forgejo.Client mirroring draft-release.sh shape, Fire stays unused)
Reasoning:
Implementation shape (A): post-merge Prepare-LESS txn builder (body from CHANGELOG read-back per bash oracle, no re-seal/delete since prep already sealed) + extend Fire for publish_mode (add Draft field) + draft-state idempotency check.
Axis 2: manifest scope (AC3) — Ratified (i) (file-atomic in Cutter txn; git-commit/push stays workflow)
Rejected: (ii) (draft-release.sh-faithful publish-only, manifest stays wholly workflow's step, AC3 restated-to-reality)
Substrate-honest strengthening acknowledged: draft-release.sh has NO manifest (285 LOC tag+publish only). Manifest write is a SEPARATE workflow step at
reusable-release.yml:391-436— sequential to draft-release.sh and, per the workflow comment's own admission, CAN desync. Original AC3 wording ("manifest atomic with tag creation") is a STRENGTHENING over the oracle, not a port. Naming this consciously per Engineer's dispatch-framing-is-a-claim flag.Why the strengthening is desirable (ratifying (i)):
Cutter interface refinement accounting (across Phase 6)
Third refinement to #554's merged code this arc:
All 3 are §3.3-sanctioned reality-intrudes shape. Named threshold for restructure-vs-accretion revisit: 5+. We're at 3/5. Still inside §3.3 tolerance; if #558 adds a 4th and #559 adds a 5th, surface meta-question about Cutter's phase-shape restructuring.
Composite refinement framing (updated)
The #554 "interface UNCHANGED" load-bearing claim from Phase-5→6 transition now reads:
Related
79e5947), #556 rt-prep (MERGED @3385537) for design-continuity of manifest substrate, #541 forgejo.Client, #542 manifest.StoreFiled 2026-07-27 by Bosun. Framing-verify ratified 2026-07-27 per Engineer surface (bus 361e). Axis 1 = A (wire through Cutter.Fire). Axis 2 = (i) (manifest file-atomic in Cutter txn, git-commit/push stays workflow). AC3 explicitly restated as strengthening-over-oracle per §6 design-opportunity direction.
AC substrate-check (2026-07-30, Bosun d214 -> Engineer)
All 9 ACs re-derived from the substrate at
main@3cca3b9; gate re-run at thatSHA:
go build+go vet ./...clean, fullgo test ./... -count=1green (19packages, zero FAIL),
golangci-lint run0 issues.Per-AC substrate reference: Fire wiring ->
internal/release/cutter.go+cmd/rt/release.go; idempotency ->TestFire_publishDraftMismatchConflict,TestFire_publishEmptyExistingTargetConflicts,TestFire_publishReplayNoopSetsReplayed;publish_mode / Draft ->
internal/forgejo/interface.go:215(
CreateReleaseRequest.Draft); manifest-atomic strengthening + rollback property ->TestPrepareForPublish_writesManifestKeyedOnTarget,_dryRunSkipsManifest,_idempotentManifestSkip,TestRollback_publishRestoresManifestLeavesChangelog;byte-oracle ->
internal/release/testdata/oracle/draft-release-oracle.sh+TestReleaseEquivalence.Two ACs are ticked WITH DISCLOSURE, and the disclosure is load-bearing. The
draft-release.shdifferential covers the ORCHESTRATION surface (the machine-readableoutput lines, the 0/1 exit, and git_artifacts non-mutation under
--dry-run) -- itdoes NOT compare the release BODY, because bash sends its create payload to stderr,
which the harness discards. Body byte-equivalence (AC2) is therefore established at
internal/changelog(SectionContent,NormalizeReleaseBody) and the payloadbyte-format at
internal/forgejo's own equivalence (#541), not end-to-end here. Thescope block in
internal/release/equivalence_test.gois the authoritative statement;these ticks mean what that block says and no more.
One AC was restated rather than ticked as-worded (AC8, see the inline note): the
named
manifest-precheck.sh + workflow-jqharness was not the shape built. Tickingthe original wording would have asserted a harness that does not exist.
CLOSED via PR#563 merged @
b215acf(2026-07-27 05:13 CEST, rebase-merge, HTTP 200, gate 4/4 byte-verified pre-fire).ACs re-derived from merged substrate + Surveyor's independent deep-verify (bus 1e3f), applying the "name the coverage surface" discipline from the #556 close anchor:
rt releasecompiles + publishes release/tag/assets via internal/forgejo.Client wired through Cutter.Fire — surface: full-gate green host + forgejo-ci-go:latest; Fire now has first production caller (milestone #78 direction "precondition-check-inside-Cutter.Fire" satisfied)draft-release.sh:249); mutation restoring guard reds load-bearing testdraft-release.shbyte-oracle (tag+publish half) — surface: both bash-spawning equivalence harnesses in the 17 pkgs green independently (Surveyor confirmed)go vet ./...+golangci-lint runclean — surface: CI gate confirmsBoth ratifications built (bus 361e → 2c08): Axis 1=A (Fire wiring, milestone-direction-sanctioned); Axis 2=(i) (manifest file-atomic in Cutter txn; git-commit+push stays workflow).
Surveyor's should-consider fold (review 4612): Fire's
existing.TargetCommitish != "" &&diverged from bashdraft-release.sh:249unconditional compare. Zero-reachability today but real faithfulness bug — Surveyor empirically resolved via live prod-releases GET showingtarget_commitishis always 40-hex SHA (so legit replay never false-conflicts). Engineer folded the fix atb215acf; Surveyor re-verified the delta. This is exactly what #557's equivalence-harness exists to surface — first prod caller of Fire made a latent divergence live + caught.Refinement accounting: 3/5 (Surveyor's framing adopted, bus 1437→4334):
Threshold revisit at 5+ capability accretions: 3/5 with two accretions of headroom.
Composite-milestone-gate design vindicated (twice more):
Phase 6 status: 3/6 sub-trackers merged (#554 Cutter foundation + #556 rt-prep + #557 rt-release). Remaining: #555 rt-decide (pure-decision, parallelizable), #558 rt-manifest-check (5 checks), #559 rt-prune-rc-tags (exercises Fire replay). #499 stays open (folds into Cutter, closes at Phase 6 gate).
Closed by Bosun 2026-07-27 05:14 CEST per Engineer's Refs-only flag in PR body.
bosun referenced this issue2026-07-27 05:22:49 +02:00