From 3d0093e1fd9eb207ad3454db0084a538d836620a Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Mon, 28 Sep 2026 01:13:09 +0000 Subject: [PATCH] Align release policy and operational documentation --- docs/development.md | 12 +- docs/policy/release.md | 289 ++++++++++++++++++--------------- docs/release.md | 35 +++- docs/roadmap/implementation.md | 8 +- docs/roadmap/policy.md | 13 +- docs/roadmap/release.md | 18 +- scripts/release.sh | 44 ++--- 7 files changed, 242 insertions(+), 177 deletions(-) diff --git a/docs/development.md b/docs/development.md index 9c5608f..cb98157 100644 --- a/docs/development.md +++ b/docs/development.md @@ -64,7 +64,8 @@ git show cb5b435:docs/roadmap/remediation.md ``` Release-candidate, publication, and verification work follows the operational -[release procedure](release.md). +[release procedure](release.md). Its single publication command validates the +committed candidate and publishes only the intended refs. The completed string-and-integer schema-version implementation and remediation records are preserved in Git history at `244104f`. The @@ -116,9 +117,9 @@ Use focused checks while iterating, with validation proportionate to the change under the testing policy. Documentation-only work normally needs the local-link check below and whitespace review; behavior-bearing examples and commands also need execution. Use `go test -race ./...` for ownership or concurrency changes. -The [release checker](release.md#4-publish-the-committed-candidate) performs one -minimum-toolchain offline race-suite run, one full vet pass, and Linux -cross-builds using readonly module mode. +The [publication command](release.md#4-publish-the-committed-candidate) invokes +the release checker once. It performs one minimum-toolchain offline race-suite +run, one full vet pass, and Linux cross-builds using readonly module mode. It already covers examples, links, and package boundaries; do not additionally run the everyday checker just to repeat those checks. CI never gates a release. @@ -139,7 +140,8 @@ code changes. Use the component overview to find the corresponding implementatio | Secrets, bootstrap, redaction, explanation | [References](../internal/engine/secret_reference_test.go), [bootstrap](../internal/engine/secrets_test.go), [rendering](../internal/engine/render_test.go), [explanation](../internal/engine/explain_test.go) | `go test ./internal/engine -run 'Secret\|Bootstrap\|Effective\|Explain\|Explanation\|History\|Sensitivity'` | | Consumer examples and migration fixtures | [Consumer tests](../examples/consumers/consumers_test.go), [API examples](../example_test.go) | `go test ./examples/consumers` and `go test . -run '^Example'` | | Documentation links and dependency isolation | [Link checks](../documentation_test.go), [import boundary](../import_boundary_test.go), [public boundary](../public_boundary_test.go) | `go test . -run 'TestLocalDocumentationLinks\|TestDependencyImportBoundary\|TestPublicFacadeBoundary'` | -| Check-command behavior | [Shell regression](../release_check_test.go) | `go test . -run '^TestCheckScripts'` | +| Development and release checker behavior | [Shell regression](../release_check_test.go) | `go test . -run 'TestCheckScripts\|TestDevelopmentCheck'` | +| Guarded release publication and recovery | [Publication regression](../release_publish_test.go) | `go test . -run '^TestReleasePublication'` | ## Fuzz validation diff --git a/docs/policy/release.md b/docs/policy/release.md index 52cefe8..9598fe0 100644 --- a/docs/policy/release.md +++ b/docs/policy/release.md @@ -6,9 +6,6 @@ **Upstream Source:** [Standard Repository Policies](https://gitea.maximumdirect.net/eric/policy/src/commit/d84a38ebab26f048cafbe59a2246700c35d29843/docs/policy/release.md) -ConfigKit's [release procedure](../release.md) uses one publication command -with the selected local checker and explicit main-then-tag pushes. - ## Purpose This policy defines a proportionate workflow for validating and publishing an @@ -19,28 +16,21 @@ The repository-specific commands belong in `docs/release.md`. Historical release summaries belong in `docs/releases/`. The procedure must agree with the adopted policy and link to it rather than introduce additional release requirements. -## ConfigKit applicability +## ConfigKit Applicability -ConfigKit adopts a source-only Go module release model. The source tag and -version-matched release note are the declared deliverables. Standalone binaries, -container images, and separate binary/package-registry publication are not -required. This does not waive source compilation, dependency maintenance, testing, or -local target-build validation. Post-publication consumer retrieval is optional, -as described under Release Model. +ConfigKit publishes a source-only Go module. Its declared deliverables are the +immutable annotated source tag and version-matched checked-in release note. +Standalone binaries, container images, hosted release objects, and separate +registry publication are not required. Additional channels require a deliberate +update to the policy and procedure. -The release procedure must define the module path, authoritative remote, -supported Go versions and operating-system/architecture targets, prerequisite -dependency acquisition, and the exact verification commands once established. -The default tests themselves remain deterministic and offline. Additional -publication channels require documented deliverables and verification before use. - -The Go API, documented YAML/resolution semantics, and structured diagnostic -contracts all participate in compatibility review. A signature-preserving change +The Go API, documented YAML and resolution semantics, and structured diagnostic +contracts participate in compatibility review. A signature-preserving change can still break consumers. Human diagnostic wording is stable only where a -contract expressly promises it. Minimum Go version and platform support changes -also require an explicit compatibility assessment. +contract expressly promises it. Minimum Go version and platform support +changes also require an explicit compatibility assessment. -### Platform support +### Platform Support Linux is the release-validated platform. macOS has best-effort support: keeping it working remains desirable, but neither native macOS validation nor Darwin @@ -49,50 +39,59 @@ does not by itself block a release. Voluntary checks and fixes are welcome; ther is no guarantee that each release has been validated on macOS. The [release procedure](../release.md#release-identity-and-support) owns target -architectures and required Linux checks. This support change applies to upcoming -releases; historical release notes and their validation records remain unchanged. -The v0.3.0 release note must explain the change in Compatibility, with version -selection following this policy's existing compatibility rules. +architectures and required Linux checks. Historical release notes and their +validation records remain unchanged. -## Release Model +## Release Model And Completion A source release identifies one exact committed source revision through an -immutable version tag and a version-matched, checked-in release note. ConfigKit's -primary release branch is `main`; its tags are annotated. The -[release procedure](../release.md) names the authoritative publication remote. +immutable version tag and a version-matched, checked-in release note. The +primary release branch is `main` unless the adopted policy specifies otherwise. +The procedure must name the authoritative publication remote. A successful push of the intended tag, with its source commit available on the -authoritative primary branch, completes the release. ConfigKit publishes the -committed candidate to `main` before pushing only the intended tag. +authoritative primary branch, completes the release. The source commit may be +pushed before the tag or published together with it through an atomic push. +The procedure must choose and document the supported approach. + +ConfigKit uses sequential explicit pushes: first the captured candidate to +authoritative `main`, then the annotated tag. Its +[release procedure](../release.md) names `origin` and checks that its fetch and +push destinations resolve to the authoritative URL. Release commands must finish at successful publication. They must not wait for or poll CI, require successful CI status, inspect downloadable artifacts, or check consumer installation or Go module availability as completion gates. -Optional remote tag read-back and fresh consumer retrieval do not delay release -completion. - A failed or ambiguous push requires resolving the authoritative remote state before declaring success or retrying; successful pushes do not require a separate remote read-back gate. -ConfigKit publishes source only. If additional deliverables are deliberately -adopted, they must be traceable to the tagged source, identify the same version, -and have documented publication responsibilities and verification. +Remote tag read-back and fresh consumer retrieval are optional and non-gating +after successful publication. A failed push receives one bounded check of the +authoritative ref; do not poll or blindly retry. -Release automation may validate or publish a release only when its exact -responsibility is documented. Automation must not silently change the source -candidate, move a published tag, replace a published deliverable, or repair a -release in place. +Repositories may publish source only. Additional binaries, images, packages, +checksums, or other declared deliverables may be published asynchronously from +the tagged source. The procedure must identify those deliverables and the +responsibility of their automation. Published deliverables must be traceable +to that source and identify the same version. + +ConfigKit currently declares no additional deliverables. Its source-tag push +is the completion point. ## Versioning And Compatibility Stable releases use Semantic Versioning in the form `vMAJOR.MINOR.PATCH`, with -no leading zeroes. The selected version must be unused locally and on the -authoritative remote. A version known to have been published elsewhere must -not be reused. +no leading zeroes. Prereleases require an explicit adopted policy defining +their format and use. -Unless an adopted policy explicitly supports and defines prereleases, -prerelease versions are not permitted. +ConfigKit does not support prereleases. + +The selected version must be unused locally and on the authoritative remote. +Do not require queries to every configured remote or publication service. +A version known to have already been published must not be reused, even if the +normal guards do not inspect that channel. Adding a distribution channel +requires a deliberate update to the policy and procedure where needed. For releases at or after `v1.0.0`: @@ -105,89 +104,123 @@ configuration, data, integration, or compatibility contract when the release note explains the impact and required consumer action. A patch release must not intentionally break those contracts within its minor line. -A repository with additional compatibility promises must define them in its -adopted policy or canonical contract documentation. A release note may explain -a compatibility change, but it must not redefine the current compatibility -contract. +Additional compatibility promises and deliberate exceptions belong in the +adopted policy or canonical contract documentation. Release notes explain +changes; they do not redefine current compatibility contracts. -## Release Documentation +## Release Documentation And Preparation -Every release made under this policy must include -`docs/releases/.md` in the tagged source tree. The filename must use -the complete version tag, and the document must begin with a title containing -the product name and that exact version. +Every new release must include `docs/releases/.md` in the tagged +source tree. Use the complete version tag as the filename and begin with a +title containing the product name and that exact version. + +ConfigKit requires the first line `# ConfigKit `. Include a short summary and any relevant compatibility impact or upgrade instructions. Choose sections suited to the release and keep the note -proportionate to the change. The annotated tag's message is the checked-in note. +proportionate to the change. -Release notes are immutable historical summaries. They should link to current -canonical documentation for exact behavior. Every affected canonical document -must be updated in the same candidate; a release note must not substitute for -current documentation or defer required documentation changes. +Keep exact current contracts in their canonical documents and update affected +documentation with the behavior change. Link to those documents from the note +where useful. If annotated tags are used, derive their summary from the +checked-in note. -Each new release note must end with the release navigation defined by the -[OSS policy](oss.md#discoverability). Preserve those links on a hosted download -page if one is used. The release procedure owns URL construction and channel -treatment. This is ordinary preparation, not a new network check or approval -gate; historical release notes are unchanged. +ConfigKit uses the complete checked-in note as the annotated tag message. -## Candidate Preparation And Validation +Include the release footer defined by the [OSS policy](oss.md#discoverability) +in each new release document. The release procedure defines the concrete URLs +and publication mechanics. Prepare these links with the release note; they do +not add a network check or approval gate. If a hosted download page is used in +a future channel, preserve the footer there. -The publication command validates the final committed candidate once with -ConfigKit's local release checker, using one maintained patch of the minimum -supported Go line on Linux. The checker runs one full offline race suite, one -full vet pass, and CGO-disabled Linux amd64/arm64 builds. The race suite -protects library behavior, ownership, -and concurrent use; it includes examples, documentation links, consumer fixtures, -and package boundaries without separate runs. Full vet checks more than the -subset normally run by tests, so test-time vet is disabled. The two cross-builds -cover supported configurations that native race compilation does not. A second -toolchain is optional and non-gating. +Handle version selection, release-note content, and retirement of completed +roadmaps during preparation. Preserve planned and deferred work and repair +affected links when retiring roadmaps. Reuse ordinary change review for +unintended files, credentials, and private data. Scope release-content review +to the tagged source. -All Go checks use readonly module mode and an offline dependency cache. -Formatting and whitespace remain ordinary development checks. Review module -tidiness when dependencies change, rather than auditing it on every release. +## Candidate Validation -Commit the release note and associated changes before release validation. The -publication command captures the exact candidate and rechecks it after the -validator. A changed candidate requires a fresh invocation and validation. -Do not publish uncommitted or unvalidated content. Local check failures still -block publication. Skipped test cases are reported, not separate human sign-off -requirements. +Provide one publication command that validates the final committed candidate, +checks publication state, and publishes the intended refs. A validation-only +mode or command may be provided for diagnosis, but must not be a required step +before publication repeats the same checks. -Compatibility review, release-note accuracy, removal of completed roadmaps, and -review for unintended files, credentials, private data, and unrelated changes -are ordinary preparation. The executing agent may perform them without routine -human approval or separate manual evidence records. Ask the maintainer only for -genuine unresolved decisions or missing authority. A clean existing checkout is -sufficient; a dedicated clone is optional. Dependency and toolchain update -research belongs to maintenance or relevant changes, not every release. +ConfigKit uses `./scripts/release.sh vMAJOR.MINOR.PATCH`. The separate +`scripts/release-check.sh` remains an optional diagnostic command; the +publication command invokes it once and does not use a persistent candidate +record. + +Run the complete deterministic, offline default test suite once for the final +candidate, following the [testing policy](testing.md). For repositories that +publish builds, verify the required build targets. Select those targets for +the application's actual support commitments; native testing on each platform +and access to particular hardware are not default release prerequisites. + +ConfigKit's one maintained Go 1.25 baseline runs the full suite with the race +detector and test-time vet disabled. This protects library behavior, ownership, +and concurrent use; the suite already includes examples, consumer fixtures, +documentation links, and package boundaries. One full vet pass adds static +checks beyond the test-time subset. CGO-disabled Linux amd64 and arm64 builds +cover supported configurations distinct from native race compilation. macOS +and newer-toolchain checks are optional. + +Additional gates must address a concrete risk that the existing checks do not +adequately cover. Do not automatically require linting, race testing, module +hygiene audits, documentation audits, credential scans, example validation, or +expanded platform matrices solely because they are possible. Retain such +checks when their distinct protection justifies their cost. A one-time migration +check must not silently become a permanent release requirement. + +The selected Go commands use readonly module mode with `GOPROXY=off`; acquire +the baseline toolchain and dependencies before validation. Formatting and +whitespace remain ordinary development checks in `scripts/check.sh`. +Review module tidiness when dependencies change, not as a release gate. + +Release tests must not require real credentials, mutable application services, +paid APIs, provider probes, or inference calls. The procedure may describe dependency +preparation separately from offline validation. + +Avoid duplicate validation across release phases. Successful checks for the +same final candidate need not be repeated solely to satisfy another phase; +any reuse must clearly apply to that exact candidate. Persistent validation +caches are not required. If the candidate changes, validate the new candidate. +Cheap publication-state checks may be repeated immediately before pushing +because local or remote state may have changed during validation. + +Automate mechanical guards. Human review gates are strongly disfavored: add one +only through an explicit project-specific policy decision explaining the +concrete risk and why ordinary change review or automation is insufficient. +Do not add routine confirmations for checks the command can perform itself. + +Compatibility review, release-note accuracy, completed-roadmap retirement, and +review for unintended files or credentials remain ordinary preparation. The +executing agent may perform them without routine human approval or separate +manual evidence records. Publication itself requires authorization. ## Publication Guards -Publication requires authorization before invoking the command. The command -must verify all of the following: +Automate the checks needed to publish the intended source under a fresh version: -- the stable version has the required format and is unused locally and on the +- the version has the adopted format and is unused locally and on the authoritative remote; -- the current branch is `main`, and the worktree and index are clean; -- the committed source and matching release note identify the exact validated - candidate; -- the authoritative remote main can advance to that candidate without - discarding history; -- the local automated gates succeeded for that commit; and -- the annotated tag identifies that candidate and only the intended refs are +- the worktree and index are clean on the primary release branch; +- the source being published is the exact candidate that passed validation, + including its matching release note; +- the remote primary branch can be advanced to that candidate without + discarding remote history, and is published before the tag; and +- the tag identifies the validated candidate and only the intended refs are published. -Push the exact candidate to authoritative main, then create and verify the -annotated tag before pushing only its ref. Do not use `git push --tags` or -implicit follow-tags publication. A failed or ambiguous push receives one -authoritative-state check before success, rejection, or unknown status is -reported. +ConfigKit also checks that `origin` has exactly the authoritative fetch and +push destination, including configured push URLs. The command checks the +candidate and unused version again after validation, and checks local state +before creating the tag. Ignored local files do not block publication. -The repository-specific procedure must make these guards executable or give -copyable commands for performing them. +Fetch the authoritative branch and tag state as needed for these checks. +Do not force branch or tag updates to bypass a failed guard. Publish explicit +refs rather than all local tags. Do not require CI success, publication-service +availability checks, or a separate manual confirmation for each guard. ## Immutability And Correction @@ -201,33 +234,33 @@ rerun the publication command. If a push fails or its outcome is ambiguous, inspect the authoritative remote before retrying. Remove an unpublished local tag only after confirming it was never published or used by a release channel. An unavailable remote leaves publication status unknown; do not overwrite a tag -to resolve uncertainty. A transient transport failure alone does not require -a new version. +to resolve uncertainty. -If publication or post-publication verification reveals a defect, leave the -published release intact. Correct the defect on the primary branch, choose a -new patch version or other semantically appropriate new version, write a new -matching release note, and perform the complete release procedure again. +Transient network, authentication, CI, or consumer-availability failures do not +by themselves require a new version. -A distribution channel's supported yank, deprecation, or advisory mechanism -may warn consumers away from a defective release, but it must not replace that -release's contents or reuse its version. +A defect requiring changes to published source or contents requires a corrected +commit, a new semantically appropriate version, a matching release note, and +the release procedure again. Leave the original release intact. A channel's +yank, deprecation, or advisory mechanism may warn consumers away from a defective +release without replacing its contents or reusing its version. ## Repository Release Procedure -Maintain `docs/release.md` as the executable maintainer runbook. It identifies -prerequisites, version and note preparation, the single publication command, -declared deliverables and build targets, successful-tag-push completion, and -partial or unknown-state recovery. Keep optional diagnostics and consumer -verification clearly optional. The procedure must agree with this policy. +Maintain `docs/release.md` as a short, executable maintainer runbook. It must +identify prerequisites, the primary branch and authoritative remote, version +and note preparation, one publication command with local checks and guards, +source-only deliverables, the successful-tag-push completion point, and +partial or unknown-state recovery. + +Use copyable commands where practical. Keep optional diagnostics and consumer +verification clearly optional. The procedure must not turn preparation work, +ordinary change review, or post-publication investigation into additional +release gates. ## Policy Exceptions -Any exception to this policy must be explicitly documented in this section, -including the repository constraint requiring it, its rationale, scope, and -compensating controls. Customizing release mechanics must not silently weaken -the policy's integrity, validation, documentation, verification, or immutability -requirements. +The following historical exceptions do not change the current release workflow. For v0.1.1 only, the maintainer approved renaming the reserved `_configkit` key to `configkit` as an error-correcting point release because there are no current diff --git a/docs/release.md b/docs/release.md index 756b01c..32cf771 100644 --- a/docs/release.md +++ b/docs/release.md @@ -124,12 +124,21 @@ success. Otherwise it reports branch-only, conflicting, or unknown state and retains any local tag. Inspect exact refs before any retry. Set `release_version` to the attempted -version and compare main with the candidate printed or recorded from the run: +version and compare main with the candidate from the command's report: ```sh release_version=v0.5.0 authoritative=https://gitea.maximumdirect.net/eric/ConfigKit.git -git ls-remote --refs --exit-code "$authoritative" refs/heads/main +if remote_main=$(git ls-remote --refs --exit-code "$authoritative" refs/heads/main); then + printf 'remote main: %s\n' "$remote_main" +else + result=$? + if [ "$result" -eq 2 ]; then + printf 'authoritative main confirmed absent\n' >&2 + else + printf 'remote main state unknown; inspect before retrying\n' >&2 + fi +fi ref="refs/tags/$release_version" if remote_tag=$(git ls-remote --refs --exit-code "$authoritative" "$ref"); then printf 'remote tag: %s\n' "$remote_tag" @@ -141,7 +150,11 @@ else printf 'remote tag state unknown; do not reuse the version\n' >&2 fi fi -git rev-parse --verify "refs/tags/$release_version" +if local_tag=$(git rev-parse --verify "refs/tags/$release_version" 2>/dev/null); then + printf 'local tag object: %s\n' "$local_tag" +else + printf 'no local tag object\n' +fi ``` A query failure is not proof that the tag is absent. Compare the remote tag @@ -168,5 +181,17 @@ version into a new module without a local `replace` directive. These checks are optional and non-gating. Do not poll CI or wait for module propagation. Private-host credentials must already be configured. For direct-origin verification, use a temporary consumer module and check the exact module path -and version reported by `go list -m`. Report optional verification separately -from publication success. +and version reported by `go list -m`: + +```sh +consumer_dir=$(mktemp -d) +( + cd "$consumer_dir" + go mod init example.invalid/configkit-release-check + GOPRIVATE=gitea.maximumdirect.net GOPROXY=direct go get "gitea.maximumdirect.net/eric/ConfigKit@$release_version" + go list -m -f '{{.Path}} {{.Version}}' gitea.maximumdirect.net/eric/ConfigKit +) +``` + +The output should show the exact module path and selected version. Report this +optional check separately from publication success. diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 78d04a4..2ac5843 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -1,6 +1,6 @@ # Release Workflow Implementation Plan -Status: ready for implementation; no stages completed by this planning pass. +Status: implemented on 2026-09-28. All stages are complete. 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 @@ -177,6 +177,12 @@ no longer require a manually recorded candidate before single-command publicatio ## Stage 3 — Complete documentation alignment and integrated validation +**Completed 2026-09-28.** Reconciled the adopted release policy with its pinned +upstream source and ConfigKit decisions; aligned the runbook, development guide, +and roadmap records with the implemented command. Bash syntax, focused release +tests, local documentation links, whitespace review, and the final real baseline +release checker passed on Linux. No publication or push was performed. + 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 diff --git a/docs/roadmap/policy.md b/docs/roadmap/policy.md index 4610ccb..c510080 100644 --- a/docs/roadmap/policy.md +++ b/docs/roadmap/policy.md @@ -1,7 +1,7 @@ # Standard Policy Documentation Alignment -Status: implemented on 2026-09-28. The [implementation record](implementation.md) -contains the completed stages and validation evidence. +Status: implemented on 2026-09-28. The completed implementation record and +validation evidence are preserved at `faf5ff8:docs/roadmap/implementation.md`. ConfigKit's seven [adopted policies](../policy/meta.md) now identify the pinned Standard Repository Policies revision @@ -11,10 +11,9 @@ applicable policies. [COPYRIGHT](../../COPYRIGHT) and the [attribution notices](../oss/attribution.md) identify the separately licensed adapted policy and orientation material. -The [release policy](../policy/release.md) retains ConfigKit's existing -operational requirements. The [maintainer runbook](../release.md) remains their -command owner. The separate [release-workflow roadmap](release.md) owns future -publication automation and gate review; this policy alignment did not implement -that workflow. +The [release policy](../policy/release.md) and +[maintainer runbook](../release.md) own current release requirements and +operations. The separate [release-workflow roadmap](release.md) records the +subsequent publication-automation work. These completed records remain until normal release preparation retires them. diff --git a/docs/roadmap/release.md b/docs/roadmap/release.md index 5cca5a2..2a43311 100644 --- a/docs/roadmap/release.md +++ b/docs/roadmap/release.md @@ -1,7 +1,7 @@ # Single-Command Release Workflow and Gate Review -Status: accepted target; execution order and acceptance checks are defined in the -[implementation plan](implementation.md). +Status: implemented on 2026-09-28. The +[implementation plan](implementation.md) records the completed work and checks. ## Intent and baseline @@ -15,14 +15,15 @@ The upstream reference is release policy v1.4 at `d84a38ebab26f048cafbe59a2246700c35d29843`: [standard release policy](https://gitea.maximumdirect.net/eric/policy/src/commit/d84a38ebab26f048cafbe59a2246700c35d29843/docs/policy/release.md). -Today, `scripts/release-check.sh` performs local validation; the runbook separately +At the planning baseline, `scripts/release-check.sh` performed local validation; the runbook separately records a candidate commit and executes shell blocks for guards, branch push, annotated tagging, and tag push. The checker runs module hygiene, formatting and whitespace checks, one race-enabled suite, vet, and Linux amd64/arm64 cross-builds using a maintained Go 1.25 patch. Publication completes on successful tag push. -These remain the current requirements until deliberately revised. This roadmap -does not itself waive existing gates. +The implementation plan records the subsequent gate selection and publication +command. Current requirements belong to the +[release policy](../policy/release.md) and [runbook](../release.md). ## Target release contract @@ -40,7 +41,7 @@ authorization, not the standing local-commit policy. The command validates the exact candidate once. An optional validation-only mode may support diagnosis but is not a prerequisite followed by duplicate validation. Do not require both everyday and release checkers. An implementation need not -introduce a persistent validation cache merely to replace today's manual record. +introduce a persistent validation cache merely to replace the former manual record. Mechanical guards ensure a clean worktree/index on the primary branch, an unused valid version locally and on the authoritative remote, a matching release note, @@ -54,12 +55,11 @@ consumer installation, or optional verification. Do not add a successful-push remote read-back gate. If future artifact channels are adopted, delivery status must remain separate; this task introduces none. -## Gate review target +## Selected gate rationale Keep the full deterministic offline suite as the core check. The selected gates below provide distinct protection without repeating validation. Their rationale -belongs in the adopted release policy, with commands in the runbook and tooling -once implemented. +belongs in the adopted release policy, with commands in the runbook and tooling. | Check | Selected end state and rationale | | --- | --- | diff --git a/scripts/release.sh b/scripts/release.sh index 45785e6..adcea8e 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -12,13 +12,13 @@ fail() { } check_candidate() { - local current status - current=$(git branch --show-current) || fail "cannot read the current branch" - [[ "$current" == main ]] || fail "release requires the main branch" - status=$(git status --porcelain=v1 --untracked-files=all) || fail "cannot inspect the worktree" - [[ -z "$status" ]] || fail "release requires a clean index and worktree" - current=$(git rev-parse --verify HEAD) || fail "cannot identify the candidate" - [[ "$current" == "$candidate" ]] || fail "candidate changed; commit and validate it again" + local context=$1 current status + current=$(git branch --show-current) || fail "$context: cannot read the current branch" + [[ "$current" == main ]] || fail "$context: release requires the main branch" + status=$(git status --porcelain=v1 --untracked-files=all) || fail "$context: cannot inspect the worktree" + [[ -z "$status" ]] || fail "$context: release requires a clean index and worktree" + current=$(git rev-parse --verify HEAD) || fail "$context: cannot identify the candidate" + [[ "$current" == "$candidate" ]] || fail "$context: candidate changed; commit and validate it again" } check_unused_version() { @@ -59,7 +59,7 @@ push_urls=$(git remote get-url --push --all origin 2>/dev/null) || fail "origin fail "origin must have exactly the authoritative fetch and push destination" candidate=$(git rev-parse --verify HEAD) || fail "cannot identify the candidate" -check_candidate +check_candidate "before validation" check_unused_version note_path="docs/releases/$version.md" @@ -85,7 +85,7 @@ git merge-base --is-ancestor "$fetched_main" "$candidate" || fail "candidate cannot advance authoritative main without discarding history" ./scripts/release-check.sh || fail "release validation failed; no refs were published" -check_candidate +check_candidate "after validation" check_unused_version if git push --no-follow-tags "$remote_url" "$candidate:refs/heads/main"; then @@ -93,37 +93,37 @@ if git push --no-follow-tags "$remote_url" "$candidate:refs/heads/main"; then else if observed_main=$(read_remote_ref refs/heads/main); then [[ "$observed_main" == "$candidate" ]] || - fail "main push did not publish; authoritative main differs from the candidate" + fail "main push did not publish candidate $candidate; authoritative main differs" else result=$? - ((result == 2)) && fail "main push failed; authoritative main is absent" - fail "main push outcome is unknown; inspect authoritative main before retrying" + ((result == 2)) && fail "main push failed for $candidate; authoritative main is absent" + fail "main push outcome is unknown for $candidate; inspect authoritative main before retrying" fi fi -check_candidate +check_candidate "main at $candidate; tag not yet published" git tag -a -F "$note_file" --cleanup=verbatim "$version" "$candidate" || - fail "could not create the local annotated tag" + fail "main is at $candidate; could not create local tag $version" tag_target=$(git rev-parse --verify "refs/tags/$version^{}") || - fail "cannot verify the local tag target" -[[ "$tag_target" == "$candidate" ]] || fail "local tag does not identify the candidate" + fail "main is at $candidate; cannot verify local tag $version" +[[ "$tag_target" == "$candidate" ]] || fail "main is at $candidate; local tag $version targets another commit" tag_type=$(git cat-file -t "refs/tags/$version") || - fail "cannot inspect the local tag" -[[ "$tag_type" == tag ]] || fail "release tag must be annotated" + fail "main is at $candidate; cannot inspect local tag $version" +[[ "$tag_type" == tag ]] || fail "main is at $candidate; local tag $version is not annotated" tag_oid=$(git rev-parse --verify "refs/tags/$version") || - fail "cannot identify the annotated tag" + fail "main is at $candidate; cannot identify local tag $version" if git push --no-follow-tags "$remote_url" "refs/tags/$version:refs/tags/$version"; then : else if observed_tag=$(read_remote_ref "refs/tags/$version"); then [[ "$observed_tag" == "$tag_oid" ]] || - fail "authoritative tag conflicts with the local tag; never replace it" + fail "authoritative tag $version conflicts with the local tag at $candidate; never replace it" else result=$? ((result == 2)) && - fail "tag push failed; main is published, but the local tag is unpublished" - fail "tag push outcome is unknown; retain the local tag and inspect authoritative state" + fail "tag push failed; main is at $candidate, but local tag $version is unpublished" + fail "tag $version push outcome is unknown for $candidate; retain the local tag and inspect authoritative state" fi fi