# Release Workflow Implementation Plan Status: ready for implementation; no stages completed by this planning pass. Implement the [release roadmap](release.md) in the numbered order below. Each stage is a bounded work unit for a gpt-5.6-terra coding agent; finish its tests and current documentation before committing it. The completed policy-alignment record formerly at this path is preserved at `faf5ff8:docs/roadmap/implementation.md`; use `git show` to read it. ## Scope and settled decisions Start from completed policy alignment at `faf5ff8`. Follow the [development guide](../development.md) and applicable adopted policies. Preserve library behavior, dependencies, the everyday checker, historical release notes, licensing, and support commitments. This plan authorizes no real release or push. Use release policy 1.4 and the adoption guide at upstream commit `d84a38ebab26f048cafbe59a2246700c35d29843`, available from `../policy` or the existing pinned upstream links. Do not add an upstream-version review. Use shared wording verbatim unless a substantive ConfigKit decision warrants adaptation. Preserve exact source metadata; update the local date when revising policy. The interface is `./scripts/release.sh vMAJOR.MINOR.PATCH`, with the existing `CONFIGKIT_BASELINE_TOOLCHAIN` prerequisite. Resolve the repository root from the script location. Retain `scripts/release-check.sh` as its validator and an optional diagnostic command, never a required prior run. No skip-validation option, persistent cache, routine confirmation, or automatic commit is needed. Stop using `.git/configkit-release-candidate`; ignore any old record without deleting it. Use sequential explicit main-then-tag pushes, not atomic-push negotiation. Keep Bash 3.2 and existing Git/Go prerequisites; no new release framework is needed. ## Stage 1 — Simplify the release checker and select its gates **Completed 2026-09-28.** The checker now runs one readonly offline race suite with test-time vet disabled, one full vet pass, and CGO-disabled Linux amd64/arm64 builds. The disposable module audit and release formatting/whitespace scans were removed. Focused checker tests, Bash syntax checks, and the real Go 1.25.14 release checker passed on Linux. Change `scripts/release-check.sh`, checker tests in `release_check_test.go`, and the affected descriptions in `docs/policy/release.md`, `docs/release.md`, and `docs/development.md` together. The existing guarded publication blocks remain operational until Stage 2; do not describe the new command as available yet. Use this exact gate set with the selected maintained Go 1.25 baseline: | Gate | Command and rationale | | --- | --- | | Full offline race suite | One `go test -mod=readonly -vet=off -race -v ./...`; protects library behavior, ownership, and concurrent use. Includes examples, consumer fixtures, documentation links, and boundary tests. | | Static analysis | One `go vet -mod=readonly ./...`; full vet provides checks beyond the subset normally run by tests. Disabling test-time vet avoids duplicating that subset. | | Supported target compilation | `go build -mod=readonly ./...` for Linux amd64 and arm64 with `CGO_ENABLED=0`; these configurations are not covered by native race compilation. | Keep `GOPROXY=off`, baseline availability/cache diagnostics, fail-fast behavior, and verbose skip reporting. Dependency/toolchain acquisition is explicit preparation; there is no automatic upgrade or latest-version survey. Readonly module commands prevent incidental manifest edits, not impose a new tidy policy. Remove the disposable-copy module-tidiness audit, release formatting/whitespace scans, and their unused machinery. Formatting/whitespace remain ordinary development checks in `scripts/check.sh`; module tidiness is reviewed when dependencies change. Offline tests/builds establish that the candidate compiles and works with its declared dependencies. Remove policy wording that makes the removed checks mandatory release gates. Shell syntax checks belong to development validation, not a separate publication gate. Do not change the everyday checker. Split the shared checker regression expectations where their contracts differ. Retain unusual-filename and whitespace tests for the everyday checker. Controlled Go subprocesses should verify the release toolchain/offline settings, one suite, one vet pass, the two target builds, and stopping after failure. Do not recursively execute the real full suite in a checker-orchestration test. Acceptance: focused `TestCheckScripts` tests and Bash syntax checks pass. Run the real revised checker with prepared baseline dependencies on Linux. Do not also run the everyday suite just to repeat its checks. macOS and newer-toolchain validation remain optional. ## Stage 2 — Implement single-command publication and recovery Add executable `scripts/release.sh`, calling the existing validator once rather than duplicating it. Replace `TestReleaseRunbookGuards` with tests of the actual script: prose headings cease to be an executable interface. Update the release policy and runbook in this stage to describe implemented behavior and recovery. ### Candidate and publication flow 1. Accept exactly one stable version argument using the existing no-leading-zero Semantic Version format. Require `main` and a clean index/worktree including nonignored untracked files. Ignored local files alone do not block release. Verify the existing authoritative `origin` endpoint and ensure pushes actually target it, accounting for separately configured push URLs and multiple push destinations. Do not publish to an unnoticed additional destination or print credentials in diagnostics. 2. Capture the full candidate commit ID. Read `docs/releases/.md` from that commit and require the first line `# ConfigKit `. Meaningful summary and applicable compatibility/upgrade content are ordinary preparation, not automated prose judgments or four mandatory headings. Retain the existing footer and local note-review requirements without a new network check. 3. Require the version unused locally and on the authoritative remote. Query the exact remote tag, distinguishing absence from query failure. Read remote main and check that publication can advance it without discarding history; main already at the candidate is acceptable. No all-remotes or all-tags survey. 4. Run `scripts/release-check.sh` once. Failure stops before any push/tag creation. Recheck branch, cleanliness, candidate identity, and unused version state after validation. Changed candidates require a fresh invocation after correction and commit; do not automatically commit or repair the checkout. 5. Push the captured commit explicitly to `refs/heads/main`, without force or incidental follow-tags publication. Override broad configured push defaults through explicit destinations/refspecs. A successful push establishes branch publication without a separate read-back gate. Recheck local candidate and clean state before tagging; ordinary concurrent local edits must not leak into publication. 6. Create an annotated tag at the captured commit, using the captured checked-in note as its message (`git tag -a -F`). Verify its peeled target and capture its tag-object ID. Push only `refs/tags/:refs/tags/`, without force or implicit publication of unrelated tags. 7. On successful tag push, report the version/candidate and return success immediately. No CI polling, remote read-back, module retrieval, hosted release creation, or routine human sign-off gates completion. Use straightforward private helpers. These are ordinary release guards, not a guarantee against hostile local processes or concurrent administrators. Do not add locks, snapshots, signing requirements, or unrelated Git configuration rules. ### Failure outcomes After a failed or ambiguous push, perform one bounded authoritative-state check; do not poll or blindly retry: | State observed | Behavior | | --- | --- | | Main push failed, but remote main equals candidate | Continue through remaining guards and tagging. | | Main push failed, remote differs or cannot be queried | Stop before tagging; distinguish rejection from unknown outcome and explain recovery. | | Tag push failed, but remote tag-object ID equals the created annotated tag | Report confirmed publication success; main publication was already established. | | Tag push failed, remote tag confirmed absent | Report branch-only progress and the unpublished local tag; retain it. | | Remote tag conflicts | Stop; never replace published contents. | | Remote tag cannot be queried | Report unknown status; retain local state, do not claim success or safe version reuse. | Document exact-ref recovery commands that distinguish absence from query failure. Only after confirming a local tag was never published or used by another channel may a maintainer delete that unpublished tag and rerun with the same version. A fresh invocation revalidates; this is failure recovery, not a mandatory double successful-path run. A correctly published tag needs no rerun. Published source defects need a new version; transient transport failures do not. Never roll back main or automatically delete tags to recover. ### Regression coverage and acceptance Use disposable real Git working repositories and bare remotes. Reuse the existing fixture-only authoritative-URL substitution or Git URL rewriting, with local-file transports and isolated identity/signing configuration. No production test-only bypass flags. Stub the expensive validator with a loggable result; use real rejection hooks and narrow Git-process wrappers for lost responses/unavailable queries. Tests must never contact the actual remote or recurse into the suite. Cover success with main initially behind the candidate and already equal; single validation; note-derived annotated tag/commit identity; ignored files; and unrelated local tags remaining unpublished. Cover malformed version, wrong branch/destination, dirty/changed candidate, missing/mismatched note, local/remote tag collision, failed validation, non-fast-forward main, and the meaningful failure outcomes above. A note without empty boilerplate headings must pass. Assert resulting refs and exit status, not helper structure or exact diagnostics. Verify completion has no CI/consumer-tool dependency. Acceptance: Bash syntax checks, focused checker/publication tests, and the offline default suite pass. The script is tested only against fixtures. Current docs must no longer require a manually recorded candidate before single-command publication. ## Stage 3 — Complete documentation alignment and integrated validation Reconcile `docs/policy/release.md` with the pinned upstream release policy. Adopt shared paragraphs verbatim except for substantive local decisions. Remove the temporary deferral, all-remotes freshness rule, and stale prospective v0.3.0 note instruction. Preserve known-published-version nonreuse, ConfigKit compatibility, source-only deliverables, annotated tags, selected gate rationale, Linux support, best-effort macOS, no-CI completion, and historical exceptions. No compensating controls are required merely to use successful-tag-push completion. Keep base version 1.4 and its existing pinned source; update the local date. Existing attribution and license scope already cover these edits. Do not change other policies, copyright/license grants, or historical release notes unnecessarily. Review `docs/release.md` as the operational owner: preparation, one publication command, completion, partial/unknown-state recovery, and distinctly optional diagnostics. Retain exact version-specific licensing footer URLs and optional consumer retrieval; neither adds a network gate. Remove duplicate manual publish instructions. Update `docs/development.md` navigation, gate summary, and test map. Search active documentation/scripts for stale candidate-file, section-template, module-tidiness gate, CI-wait, and manual publication instructions. Fix affected links without rewriting historical records. Correct the completed policy roadmap's implementation-record reference to `faf5ff8:docs/roadmap/implementation.md`, since this path now owns the new plan. Mark release-roadmap implementation status accurately after acceptance. Preserve unfinished/deferred work; retirement belongs to actual release preparation. Run Bash syntax checks, focused orchestration tests, documentation-link tests, and `git diff --check`. Run the revised real release checker once on the final implementation with prepared baseline dependencies. Reuse that successful suite result instead of separately repeating examples, boundaries, vet, builds, or the everyday suite. Report missing prerequisites rather than claiming success. Never invoke publication against origin as an acceptance test. Acceptance: scripts, policy, and runbook agree; tests protect selected behavior without extra contracts; current docs describe only implemented behavior. Commit each completed unit with a plain-English message and report validation/workspace state without publishing. No product decisions remain open in this plan.