Files
commandkit/docs/roadmap/implementation.md

27 KiB
Raw Blame History

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 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 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.
  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
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.

  1. 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
    
  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 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:

    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: pending. Prerequisite: Stage 1.

Files: root releasekit.json, scripts/release, .gitignore, and stage evidence in this plan.

  1. 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, 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:

    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:

    .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.

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:

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.

  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:

    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.