28 KiB
ReleaseKit adoption implementation plan
Status: ready for implementation; Stages 1–6 pending.
Date: 2026-10-08.
Scope and authority
Implement the target in the ReleaseKit feature roadmap. Read this entire plan before starting, then complete the numbered stages in order. Each stage is one Luna implementation assignment. This plan supplies concrete execution choices; the feature roadmap owns the target and user intent. The release policy, architecture policy, and accepted ADRs control their respective boundaries.
This file replaces the completed initial library implementation record. That record remains available at its reconciled source revision. Historical documents still linking here concern that record, not these new assignments. Stage 5 repairs those references. Do not restart the completed library implementation or its remediation work.
The implementation sprint delivers maintainer tooling, not CommandKit's first published release. Do not push to the real repository, create release tags in CommandKit, prepare a placeholder release note, or configure hosted delivery as part of these stages. Local disposable Git repositories are the publication test boundary. Publication of a real candidate remains separately authorized work under the runbook.
Baseline and selected implementation choices
At planning time, HEAD is 0690ff6, the worktree is clean, and the feature
roadmap is committed. CommandKit's initial library and documentation
reconciliation are complete. Root go.mod declares Go 1.25.5 and has no
ReleaseKit dependency. There is no root release profile, publication launcher,
release runbook, or release-note history. The feature roadmap records the
remote and source-only release scope.
Use ReleaseKit v0.2.0, reviewed at
e030794969fb7787e647a90bbc4eb81bd0610f07. Its
CLI reference,
profile reference, and
publication guide
own upstream behavior. ../ReleaseKit is optional review evidence, not a build
dependency or a source tree to modify.
The execution choices are:
- Track the executable with a root Go
tooldirective and a v0.2.0 module requirement. Build from that selection, without an independent script pin. - Author a small POSIX shell launcher in
scripts/release, and use ignored.releasekit/bin/releasekitfor its native executable. - Author the launcher and runbook for CommandKit, using upstream as behavioral guidance rather than copying substantive source or prose. This avoids an unnecessary copied-material attribution obligation. Tool use alone does not require new component entries under the existing OSS policy; changes to incorporated runtime dependencies still receive their applicable treatment.
- Use a minimal source-only JSON profile with the default suite and explicit product, remote, and expected push URL. Leave optional checks and delivery unselected.
- Keep fast launcher tests in a tests-only
scriptsGo package. Keep a real local publication exercise explicitly opt-in in that same package, so ordinary release validation does not repeatedly bootstrap and retest the publisher. - Push the real candidate's branch before its tag when following the future runbook. ReleaseKit owns validation and tag publication; the launcher does not implement its own guards or branch push.
These choices add no production Go package, runtime ReleaseKit import, public API, standard command family, or changes to application version semantics. Hosted notes, binary targets, CI delivery, and additional release gates remain deferred as specified in the feature roadmap.
Instructions for every stage
- Follow developer orientation and the agent policy. Inspect current state and prior stage evidence; preserve unrelated work. Prefer knowledge-graph tools for code discovery. Read policies relevant to the assigned edits.
- Implement only the assigned stage and its necessary supporting documentation. Do not treat later stages or existing historical plans as already completed. If evidence contradicts a selected choice, report the concrete issue rather than silently substitute another version, integration, or release policy.
- Prepare dependencies separately when needed. Checks below are implementation evidence, not additional permanent release gates. No real credentials, hosted-service requests, or live publication are needed for them.
- Format changed Go files. Use focused checks for the stage, and reuse evidence
for unchanged work. The default suite is run in Stages 1, 3, and the final
audit to cover the changed module, new tests, and completed integration;
prose-only stages need document review, not another suite. This is development
validation across changing commits, not a requirement to test a real release
candidate repeatedly before
publishtests it. - Update this plan's stage status and evidence with actual results. Commit each completed stage under the local commit policy and report its commit, checks, and remaining work. Review the staged diff and whitespace before committing.
Stage inventory
| Stage | Deliverable | Roadmap coverage |
|---|---|---|
| 1 | Released Go tool dependency and applicable licensing review | RK-01, RK-05 |
| 2 | Source-only profile and native publication launcher | RK-02, RK-03 |
| 3 | Fast deterministic launcher tests | RK-03, RK-06 |
| 4 | Opt-in publication exercise against disposable local Git repositories | RK-06 |
| 5 | Maintainer runbook, policy status, and navigation reconciliation | RK-04 |
| 6 | Final adoption audit and completion record | RK-01–06 |
Stage 1: declare and prepare the released tool dependency
Status: complete. Prerequisite: repository orientation and this plan.
Files: go.mod, go.sum; affected notices only if existing incorporated
dependencies change; stage evidence in this plan.
-
From the repository root, add the executable tool dependency:
GOWORK=off GOTOOLCHAIN=local go get -tool gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit@v0.2.0 GOWORK=off GOTOOLCHAIN=local go mod tidy -
Review the resulting graph. Retain the tool declaration and released v0.2.0 requirement, with its necessary indirect requirements and checksums. Preserve CommandKit's Go 1.25.5 directive; ReleaseKit and its reviewed Goldmark and
x/modrequirements fit that minimum. Review any existing dependency version changes rather than silently upgrade unrelated libraries. Do not add a local replacement, a blank runtime import, a separate tools module, or a duplicate version constant to retain the executable. -
Apply OSS policy to the actual result. ReleaseKit, Goldmark, and
x/modare selected for development tooling, with no source copy or tool binary distribution in this integration. Tool use alone does not require new attribution entries. If module resolution changes an existing incorporated dependency, update its actual notices and pinned source links with that change. Record the review result here, not as an editorial paragraph in the attribution document. -
Prepare caches through normal Go access before offline checks:
GOWORK=off GOTOOLCHAIN=local go mod download
Evidence: inspect go list -m gitea.maximumdirect.net/eric/ReleaseKit with
workspace discovery disabled; it selects v0.2.0. Run go mod tidy -diff with
the same workspace/toolchain settings; it reports no further changes. Build
gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit with -mod=readonly
into a disposable directory outside the worktree and invoke only version.
Confirm that the build leaves go.mod and go.sum unchanged. Run the current
default suite once with GOWORK=off GOTOOLCHAIN=local GOPROXY=off GONOPROXY=none GOSUMDB=off go test -mod=readonly -count=1 ./....
Observed 2026-10-08: the tool selection resolves to ReleaseKit v0.2.0, with
Goldmark v2.1.6 and golang.org/x/mod v0.40.0 in the tooling graph. The prior
graph selected x/mod v0.8.0 through x/text; go mod why showed no package
use in CommandKit, and the current graph uses x/mod through ReleaseKit only.
No incorporated runtime dependency changed, so the OSS policy requires no new
attribution entry or license text. No source was copied and the tool binary is
not distributed. go mod tidy -diff reported no changes; go mod verify
reported all modules verified. The readonly CLI build succeeded, its version
subcommand reported v0.2.0, and go.mod and go.sum were unchanged by the build.
The default offline go test -mod=readonly -count=1 ./... passed for the root
and internal Cobra packages; both example packages compiled.
Completion: the root module retains its released tool selection through ordinary maintenance, the executable builds, affected notices are correct, and existing library checks pass. Commit the stage before proceeding.
Stage 2: implement the source-only publication entry point
Status: complete. Prerequisite: Stage 1.
Files: root releasekit.json, scripts/release, .gitignore, and stage
evidence in this plan.
-
Add the root profile with exactly this initial configuration:
{ "product": "CommandKit", "remote": "origin", "expected_push_url": "https://gitea.maximumdirect.net/eric/CommandKit.git" }Omit
checks,validation,build, anddelivery. Their omission selects the upstream defaults without installing extra gates or deliverables. -
Write
scripts/releasewith#!/bin/shandset -eu. Require exactly one argument; otherwise printusage: ./scripts/release VERSIONto stderr and exit 2 before invoking Go. Pass the version argument unchanged to ReleaseKit; ReleaseKit owns semantic version validation. -
Resolve the repository root from the launcher's directory, then change to that root. Use quoted path expansions and a task-specific variable such as
commandkit_root. Resolve the directory withCDPATH=for the directory change so an ambientCDPATHdoes not corrupt captured path output. Support ordinary invocation from another working directory and paths containing spaces; no general symbolic-link resolution framework is required. -
Create
.releasekit/bin. Query Go'sGOHOSTOSandGOHOSTARCHusing the local toolchain with workspace discovery disabled, storing them separately without replacing the version argument. Build the tool using those native target values,GOWORK=off,GOTOOLCHAIN=local, and this argument vector:go build -mod=readonly -o .releasekit/bin/releasekit gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekitThis builds the executable for the host that must run it, even when the caller has selected an application cross-compilation target. Dependency preparation remains separate; no launcher
go get,go mod tidy, alternate module file, linker injection, or independently selected tool version is needed. Bootstrap can use normal module access; the candidate suite itself is offline under ReleaseKit's environment. -
Replace the shell process with the built executable using
execand this argument vector, quoting both absolute paths and the version:.releasekit/bin/releasekit publish --repo ROOT --config ROOT/releasekit.json VERSIONThe flags precede
VERSIONas required by ReleaseKit. Preserve its streams and exit status directly. Do not intercept its publication states, runcheckfirst, auto-pushmain, or usego runas the publication process. -
Mark the launcher executable in Git. Add
/.releasekit/to the root ignore file; this local bootstrap output is not a declared release artifact.
Evidence: review the profile against upstream, run sh -n scripts/release,
and verify the executable mode and ignore rule. Exercise zero and multiple
arguments without a candidate; both return 2 without tool bootstrap. Do not
invoke a valid version against CommandKit's real repository as a smoke check.
Observed 2026-10-08: reviewed the v0.2.0 profile and CLI references at the
selected upstream commit. The profile contains only the product, remote, and
effective push URL. sh -n scripts/release and python3 -m json.tool releasekit.json passed. Git reports the launcher executable, and
git check-ignore -v .releasekit/bin/releasekit matches the root ignore rule.
Zero and two arguments both printed the required usage line and returned status
2 with a nonexistent PATH, confirming that Go was not invoked; no bootstrap
directory was created. No valid version was sent to CommandKit's repository.
Completion: the profile and launcher match the selected target and delegate publication to the module-selected native CLI. Commit the stage.
Stage 3: cover launcher behavior with fast offline tests
Status: complete. Prerequisite: Stage 2.
Files: scripts/release_test.go, plus launcher corrections found by the
tests and stage evidence in this plan. Use a tests-only external Go package such
as release_test; add no production Go file or exported helper API.
-
Use standard-library Go tests with temporary directories. Copy the real launcher and profile into a temporary repository-shaped directory whose path contains spaces. Invoke the copied launcher from a different directory. A controlled
goexecutable placed first on the child process'sPATHsupplies the two host environment answers and writes a fake executable at the requested build output path. The fake executable logs received arguments, writes distinct stdout/stderr messages, and exits with a test-selected status. Keep all test controls inside fixture subprocess environments, not launcher options or product configuration. -
Cover these observable integration risks:
Case Required observation Zero or multiple arguments Usage on stderr and status 2; Go is not invoked. Valid invocation from another directory Build occurs at the copied root; publication receives publish, explicit absolute repository/profile paths, and the supplied version as separate arguments.Module-selected native bootstrap The tool package has no @versionsuffix; readonly mode and the expected output are used; the local toolchain, disabled workspace, and queried host target govern the build.Executable returns 0, 1, 2, or 3 Each status is preserved, with stdout/stderr delivered to their corresponding streams. These cases test delegation, not upstream recovery semantics. Bootstrap failure The launcher stops with the build failure and never invokes the fake publication executable. -
Test argument values and outcomes rather than exact shell source or private call counts. Do not add a signal-management framework or a new guarantee merely to test
exec; source review plus direct process replacement establish the selected signal handoff. The script remains a POSIX maintainer tool: skip shell execution tests with an explicit reason if a POSIX shell is unavailable, rather than add a Windows publisher. -
Use per-command working directories and environments. Do not modify process global working directories or the real repository's remote configuration.
Evidence: format the test file, run the focused scripts package tests, then the default offline suite with the Stage 1 environment. None of these tests compiles a real publisher or contacts a service.
Observed 2026-10-08: gofmt -d scripts/release_test.go reported no changes.
go test -mod=readonly -count=1 ./scripts passed. The offline default
go test -mod=readonly -count=1 ./... passed for the library, internal adapter,
examples, and scripts package. The tests use only temporary fixtures and a
controlled Go executable; they do not build the real publisher or contact a
service.
Completion: the small launcher has regression coverage for its own parsing, path, build, and delegation responsibilities. Commit the stage.
Stage 4: exercise real publication against disposable Git repositories
Status: pending. Prerequisite: Stage 3 and prepared dependency caches.
Files: scripts/release_integration_test.go, supporting fixture helpers in
that tests-only package if needed, and stage evidence in this plan.
- Add
TestReleaseIntegration, skipped unlessCOMMANDKIT_RELEASE_INTEGRATION=1. This explicit development opt-in is not a profile check or a required step before ordinary releases. Keep fast tests enabled normally. Explain the opt-in and cache prerequisite in the test; Stage 5 exposes its command in the developer guide. - Construct a disposable worktree on
mainand a separate local bare Git repository. Supply fixture-local identity and disable signing and hooks through isolated Git configuration; do not alter the maintainer's global settings. Clear inherited Git directory/worktree overrides for fixture commands so every operation addresses the fixture. - Copy CommandKit's current
go.mod,go.sum, launcher, and ignore file into the fixture. Copy the real JSON profile, changing onlyexpected_push_urlto the local bare repository's absolute path. Keep productCommandKitand remoteorigin. Do not copy CommandKit's tests: the fixture is a tiny Go package, avoiding recursive execution of these integration tests. - Write a fixture-only note
docs/releases/v0.0.1.mdheaded# CommandKit v0.0.1, and a small standard-library Go test that appends one marker to a path supplied through its environment outside the worktree. This marker demonstrates one candidate-suite run without adding a product check. Commit fixture contents, configureoriginto the local bare path, and push fixtureHEAD:refs/heads/main. These are local fixture operations, not publication of CommandKit. - Invoke the copied launcher from another directory with the actual Go tool
and module-selected ReleaseKit. Make dependency and toolchain downloads
unavailable during the exercise. Check that it returns success, reports the
fixture candidate/version, publishes an annotated
v0.0.1tag pointing at that commit, leaves remotemainat the candidate, writes one suite marker, and leaves fixture candidate status clean. Check that only the intended tag was added and no binary delivery outputs were produced. The ignored local bootstrap executable is expected. - On that fixture, prepare and commit a second note for
v0.0.2and changeexpected_push_urlto a different local path. Push the new fixturemain, then invoke the launcher. Expect known failure status 1 with an endpoint mismatch diagnostic, no new local or remote tag, and no additional suite marker. This protects profile wiring without rebuilding upstream's entire remote-conflict and failed-push matrix. Status 3 propagation is already covered by the Stage 3 executable double. - Use
t.TempDircleanup and an overall subprocess context deadline to avoid a stalled fixture. Avoid timing-sensitive assertions and output snapshots. Missing shell/Git support can cause an explicit platform skip; when opt-in is selected, missing dependency caches or a real execution failure must be reported as failure, not disguised as a successful skip.
Evidence: prepare caches using the Stage 1 preparation command, then run:
COMMANDKIT_RELEASE_INTEGRATION=1 GOWORK=off GOTOOLCHAIN=local GOPROXY=off GONOPROXY=none GOSUMDB=off go test -mod=readonly -count=1 ./scripts -run '^TestReleaseIntegration$'
Record the passed publication and preflight observations. This exercise uses
no real remote, credentials, provider, binary target, or hosted page. Retain the
opt-in test for future tooling changes, without placing it in releasekit.json.
Completion: the actual module-selected executable and copied integration publish a source tag locally and respect the configured endpoint guard. Commit the stage.
Stage 5: document operation and reconcile release navigation
Status: pending. Prerequisite: Stage 4.
Files: new docs/release.md; docs/policy/release.md, README,
docs/development.md; historical roadmap references listed below; and stage
evidence in this plan. Apply documentation and OSS policy without changing
accepted ADR decisions or policy provenance.
-
Author
docs/release.mdas the canonical, concise maintainer runbook. Namemain,origin, the expected effective push endpoint, a local compatible Go toolchain (at least the module's 1.25.5 requirement), Git with push access, and a POSIX shell. Describe source tag plus committed note as the selected deliverables, with no hosted page, binary matrix, or delivery credentials. -
Give copyable preparation commands with a task-specific version variable. Explain stable SemVer selection and pre-1.0 compatibility policy. Prepare
docs/releases/<version>.mdwith its product/version heading, useful summary, compatibility/upgrade information, and exact immutable-tag footer URLs:https://gitea.maximumdirect.net/eric/CommandKit/src/tag/VERSION/COPYRIGHT https://gitea.maximumdirect.net/eric/CommandKit/src/tag/VERSION/LICENSE https://gitea.maximumdirect.net/eric/CommandKit/src/tag/VERSION/docs/oss/attribution.mdThe visible footer labels are
COPYRIGHT | LICENSE | ATTRIBUTION. Preparation review checks intended paths in the candidate; it does not require future tag URLs to resolve or run a separate note checker. -
Document cache preparation with
GOWORK=off GOTOOLCHAIN=local go mod download. If preparation changes module metadata, review and commit it as part of the candidate before publication. Commit the final candidate onmain, push withgit push origin HEAD:refs/heads/main, then invoke./scripts/release "$commandkit_version". State that the command bootstraps the tool, runs the offline candidate suite once after preflight, and publishes only the annotated tag. Do not prescribe a requiredcheckfollowed bypublish; earlier development checks do not replace publication validation. -
Describe completion at successful intended-tag push or an exact-object reconciliation confirming acceptance. Link to upstream for its CLI/status contract. Provide a short operational summary of statuses 0 (confirmed), 1 (known failure/conflict), 2 (input/profile), and 3 (unresolved push), keeping this operational summary out of CommandKit's application CLI reference.
-
Include concrete recovery instructions using the endpoint, version, and tag object reported by the failed invocation. Show the upstream pattern that verifies
git ls-remote --get-url -- "$releasekit_endpoint"still equals the captured endpoint before querying its exactrefs/tags/...ref. A failed query or changed URL resolution leaves the result unresolved. Compare the exact annotated tag object, not just its peeled commit. Exact match confirms publication; a different object is conflict; proven absence permits deliberate cleanup of an unpublished local tag only after confirming it has never been published or used by another release channel. Explain that retained local tags block an ordinary retry. Never move or overwrite a published tag or treat an unreadable query as absence. Published source corrections require a new version; transport failures alone do not. -
Replace only stale integration-status prose in the release policy with the implemented ReleaseKit selection and a runbook link. Retain source-only scope, substantive rules, adopted base version, and upstream provenance; update the local last-updated date for the edit.
-
Add runbook navigation in README and the developer guide. Add a current release-tooling reading row and the optional Stage 4 exercise command/cache prerequisite to developer guidance. Replace the obsolete statement that release tooling still awaits introduction. Keep commands owned by the runbook and use links from non-owning documents.
-
Repair links to the replaced completed implementation record. For historical initial-library references, use the immutable snapshot linked in this plan's introduction. Inspect and update README,
docs/development.md,docs/roadmap/01-cli-plan.md,02-dispatch.md,03-backend.md,04-facade.md,initial-plan.md,initial.md, andremediation.md. Preserve their completed status and historical statements. Link current ReleaseKit work separately toreleasekit.mdand this plan. Do not copy the old plan into another maintained implementation specification or add temporary-plan links to ADRs.
Evidence: review the runbook against the implemented launcher/profile and the selected upstream source. Check local destinations/fragments, preserved licensing footer navigation, intended release URL paths, changed historical references, provenance, and Markdown whitespace. No publisher invocation on CommandKit or another full test suite is needed for these prose edits.
Completion: implemented tooling has one canonical runbook, policy status is accurate, and old library history is distinguishable from current ReleaseKit work. Commit the stage.
Stage 6: audit and record adoption completion
Status: pending. Prerequisite: Stages 1–5.
Files: this plan and releasekit.md for completion evidence; focused fixes
only if the audit discovers a real incomplete deliverable.
- Audit every RK-01–06 result against current state: module/tool selection, minimal profile, launcher and executable mode, ignored output, dependency metadata preservation, licensing treatment, implemented runbook/navigation, fast tests, and recorded local publication evidence. Read actual files and results rather than infer completion from checked boxes.
- Confirm production imports and exported declarations have not acquired
ReleaseKit APIs. The production package boundary remains root
commandkitandinternal/cobra;scriptsis tests-only. Use package/dependency inspection or existing boundary evidence, not a new general API checker. - Run the default offline suite once for the completed implementation using the Stage 1 environment. Stage 4 stays opt-in. Reuse its successful result if its launcher, profile, tool selection, and test inputs remain unchanged; otherwise rerun that local exercise for the changed integration. Review affected prose, links, module diffs, and whitespace.
- Mark stage and feature completion only when their evidence is present. Record exact commands/outcomes and any platform skips. Keep historical baseline descriptions historical; update the roadmap's current status and link its completion evidence here. No real release note or tag is required to close adoption, and no deferred capability is promoted by this audit.
- Commit the completed audit and status updates, report all stage commits and checks, and leave a clean workspace. The handoff is tooling ready for separately authorized preparation of the first real release.
Evidence: record the final default-suite result, the applicable Stage 4 result, the reviewed deliverable and boundary checks, and the final clean Git status. Correct any missing result before declaring adoption complete.
Completion: all six stages and all roadmap coverage IDs have authoritative implementation evidence. Source publication itself remains unperformed.