2026-10-08 14:18:58 +00:00
# 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 ](releasekit.md ).
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 ](../policy/release.md ),
[architecture policy ](../policy/architecture.md ), 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 ](https://gitea.maximumdirect.net/eric/CommandKit/src/commit/3871d837489b9955998b362587a52b47282dc021/docs/roadmap/implementation.md ).
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 ](https://gitea.maximumdirect.net/eric/ReleaseKit/src/commit/e030794969fb7787e647a90bbc4eb81bd0610f07/docs/cli.md ),
[profile reference ](https://gitea.maximumdirect.net/eric/ReleaseKit/src/commit/e030794969fb7787e647a90bbc4eb81bd0610f07/docs/config.md ), and
[publication guide ](https://gitea.maximumdirect.net/eric/ReleaseKit/src/commit/e030794969fb7787e647a90bbc4eb81bd0610f07/docs/consumers/publish.md )
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 `tool` directive 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/releasekit` for 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 `scripts` Go 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
1. Follow [developer orientation ](../development.md ) and the
[agent policy ](../policy/agents.md ). Inspect current state and prior stage
evidence; preserve unrelated work. Prefer knowledge-graph tools for code
discovery. Read policies relevant to the assigned edits.
2. 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.
3. 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.
4. 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 `publish` tests it.
5. 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 |
2026-10-07 03:55:36 +00:00
| --- | --- | --- |
2026-10-08 14:18:58 +00:00
| 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
2026-10-08 16:37:46 +00:00
**Status:** complete. **Prerequisite: ** repository orientation and this plan.
2026-10-08 14:18:58 +00:00
**Files:** `go.mod` , `go.sum` ; affected notices only if existing incorporated
dependencies change; stage evidence in this plan.
1. From the repository root, add the executable tool dependency:
```sh
GOWORK=off GOTOOLCHAIN=local go get -tool gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit@v0 .2.0
GOWORK=off GOTOOLCHAIN=local go mod tidy
```
2. 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/mod` requirements 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.
3. Apply [OSS policy ](../policy/oss.md ) to the actual result. ReleaseKit,
Goldmark, and `x/mod` are 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.
4. Prepare caches through normal Go access before offline checks:
```sh
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 ./...`.
2026-10-08 16:37:46 +00:00
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.
2026-10-08 14:18:58 +00:00
**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
2026-10-08 16:39:14 +00:00
**Status:** complete. **Prerequisite: ** Stage 1.
2026-10-08 14:18:58 +00:00
**Files:** root `releasekit.json` , `scripts/release` , `.gitignore` , and stage
evidence in this plan.
1. Add the root profile with exactly this initial configuration:
```json
{
"product": "CommandKit",
"remote": "origin",
"expected_push_url": "https://gitea.maximumdirect.net/eric/CommandKit.git"
}
```
Omit `checks` , `validation` , `build` , and `delivery` . Their omission selects
the upstream defaults without installing extra gates or deliverables.
2. Write `scripts/release` with `#!/bin/sh` and `set -eu` . Require exactly one
argument; otherwise print `usage: ./scripts/release VERSION` to stderr and
exit 2 before invoking Go. Pass the version argument unchanged to ReleaseKit;
ReleaseKit owns semantic version validation.
3. 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 with `CDPATH=` for the directory
change so an ambient `CDPATH` does not corrupt captured path output. Support
ordinary invocation from another working directory and paths containing
spaces; no general symbolic-link resolution framework is required.
4. Create `.releasekit/bin` . Query Go's `GOHOSTOS` and `GOHOSTARCH` using 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:
```text
go build -mod=readonly -o .releasekit/bin/releasekit gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit
```
This 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.
5. Replace the shell process with the built executable using `exec` and this
argument vector, quoting both absolute paths and the version:
```text
.releasekit/bin/releasekit publish --repo ROOT --config ROOT/releasekit.json VERSION
```
The flags precede `VERSION` as required by ReleaseKit. Preserve its streams
and exit status directly. Do not intercept its publication states, run `check`
first, auto-push `main` , or use `go run` as the publication process.
6. 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.
2026-10-08 16:39:14 +00:00
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.
2026-10-08 14:18:58 +00:00
**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:** pending. **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.
1. 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 `go` executable placed first on the child process's `PATH`
supplies 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.
2. 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 `@version` suffix; 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. |
3. 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.
4. 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.
**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.
1. Add `TestReleaseIntegration` , skipped unless
`COMMANDKIT_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.
2. Construct a disposable worktree on `main` and 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.
3. Copy CommandKit's current `go.mod` , `go.sum` , launcher, and ignore file into
the fixture. Copy the real JSON profile, changing only `expected_push_url`
to the local bare repository's absolute path. Keep product `CommandKit` and
remote `origin` . Do not copy CommandKit's tests: the fixture is a tiny Go
package, avoiding recursive execution of these integration tests.
4. Write a fixture-only note `docs/releases/v0.0.1.md` headed
`# 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, configure `origin` to the local bare path,
and push fixture `HEAD:refs/heads/main` . These are local fixture operations,
not publication of CommandKit.
5. 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.1` tag pointing at
that commit, leaves remote `main` at 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.
6. On that fixture, prepare and commit a second note for `v0.0.2` and change
`expected_push_url` to a different local path. Push the new fixture `main` ,
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.
7. Use `t.TempDir` cleanup 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:
```sh
COMMANDKIT_RELEASE_INTEGRATION=1 GOWORK=off GOTOOLCHAIN=local GOPROXY=off GONOPROXY=none GOSUMDB=off go test -mod=readonly -count=1 ./scripts -run '^TestReleaseIntegration$'
2026-10-07 03:55:36 +00:00
```
2026-10-08 14:18:58 +00:00
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.
1. Author `docs/release.md` as the canonical, concise maintainer runbook. Name
`main` , `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.
2. Give copyable preparation commands with a task-specific version variable.
Explain stable SemVer selection and pre-1.0 compatibility policy. Prepare
`docs/releases/<version>.md` with its product/version heading, useful summary,
compatibility/upgrade information, and exact immutable-tag footer URLs:
```text
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.md
```
The 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.
3. 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 on `main` , push
with `git 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 required `check` followed by
`publish` ; earlier development checks do not replace publication validation.
4. 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.
5. 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 exact `refs/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.
6. 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.
7. 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.
8. 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` , and `remediation.md` . Preserve their completed
status and historical statements. Link current ReleaseKit work separately to
`releasekit.md` and 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.
1. 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.
2. Confirm production imports and exported declarations have not acquired
ReleaseKit APIs. The production package boundary remains root `commandkit`
and `internal/cobra` ; `scripts` is tests-only. Use package/dependency
inspection or existing boundary evidence, not a new general API checker.
3. 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.
4. 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.
5. 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.