Prepare the v0.9.0 release notes and retire completed roadmaps

This commit is contained in:
2026-11-04 01:59:54 +00:00
parent bf4e2329ec
commit 2180aa868e
5 changed files with 41 additions and 653 deletions

41
docs/releases/v0.9.0.md Normal file
View File

@@ -0,0 +1,41 @@
# ReleaseKit v0.9.0
ReleaseKit's maintained launchers and generated Go recovery commands now
prepare and run the release tool for the Go host when inherited `GOOS` and
`GOARCH` select another target. Release profiles still select the targets for
application builds.
## Launcher, delivery, and recovery fixes
- Local launchers and Woodpecker templates select the host before preparing
tool dependencies or compiling the CLI. Module-based launchers invoke
ReleaseKit by its full Go package path, so another registered tool named
`releasekit` does not make the invocation ambiguous.
- Generated Go recovery commands select the host when run in a POSIX shell
and find the Go executable through `PATH`. Recovery for API-only consumers
preserves both wildcard and version-specific module replacements when
relocating relative paths, fixing a panic after a failed publication.
- The manual hosted-page retry stops if fetching or checking out the tag fails,
or if local edits remain. It checks the tagged checkout before downloading
dependencies or building the CLI.
## Compatibility and upgrade
The module path remains `go.maximumdirect.net/releasekit` and the Go consumer
minimum remains 1.26.0. Public Go APIs, release profiles, and publication
record formats are unchanged.
Update copied launchers and CI templates to get the host and tool selection
fixes. Updating the module dependency alone does not update those files.
Configured product checks inherit the launcher's host target; if a check
needs an application target, set it explicitly in the check's `env` map
or command.
See the [adoption guide](https://forge.maximumdirect.net/base-layer/releasekit/src/tag/v0.9.0/docs/consumers/adoption.md),
[delivery guide](https://forge.maximumdirect.net/base-layer/releasekit/src/tag/v0.9.0/docs/consumers/delivery.md),
and [maintainer runbook](https://forge.maximumdirect.net/base-layer/releasekit/src/tag/v0.9.0/docs/release.md)
for updated recipes.
[COPYRIGHT](https://forge.maximumdirect.net/base-layer/releasekit/src/tag/v0.9.0/COPYRIGHT) |
[LICENSE](https://forge.maximumdirect.net/base-layer/releasekit/src/tag/v0.9.0/LICENSE) |
[ATTRIBUTION](https://forge.maximumdirect.net/base-layer/releasekit/src/tag/v0.9.0/docs/oss/attribution.md)

View File

@@ -1,93 +0,0 @@
# CommandKit feature request: host-safe consumer launchers
Status: proposed; not an accepted contract or implementation assignment.
## Consumer need
CommandKit selects ReleaseKit v0.8.0 as a Go tool and uses a small launcher to
publish its core and two optional integration modules. Maintainers can inherit
`GOOS` and `GOARCH` from application cross-build work. The release tool must run
on the maintainer's host even when those variables name another target.
CommandKit currently handles this by querying `go env GOHOSTOS` and
`go env GOHOSTARCH`, then using those values when building its native ReleaseKit
executable. Its `TestReleaseLauncherDelegatesPublication` test supplies foreign
target variables and checks native bootstrap behavior together with argument
and exit-status forwarding. Consumer evidence is in CommandKit's
`scripts/release` and `scripts/release_test.go` at local commit
`a74fcb20041cbccde93f9b9a1e3b24cb8dd131e7`.
Please incorporate this behavior and regression coverage into ReleaseKit's
maintained consumer launcher example so downstream projects can reuse the
recipe and rely on upstream tests for its generic bootstrap mechanics.
## Observed failure
Reviewed ReleaseKit v0.8.0, commit
`23203f235cc414c4ffe0fd3941030eb0c9f9943f`. The
[multi-module launcher](../../examples/multi-module/release.sh) invokes
`go tool` without normalizing the inherited target environment.
On Linux/amd64 with Go 1.26.8, from a prepared consumer module registering
`go.maximumdirect.net/releasekit/cmd/releasekit` at v0.8.0:
```sh
GOWORK=off GOTOOLCHAIN=local GOFLAGS=-mod=readonly \
GOPROXY=off GONOPROXY=none GOSUMDB=off \
GOOS=windows GOARCH=arm64 go tool releasekit version
```
The command built a Windows executable and failed to launch it with an
`exec format error`. Repeating the command with `GOOS=linux GOARCH=amd64`
succeeded and printed `releasekit v0.8.0`. These observations establish the
tool-execution failure; the complete upstream launcher was inspected rather
than executed in this reproduction. No publication was attempted.
## Requested change
Update the maintained launcher to select the native host target when preparing
and executing ReleaseKit, regardless of inherited application target variables.
Resolve the host through the selected Go toolchain rather than hard-coding a
platform. Scope this change to running the maintainer tool; preserve the
application targets selected through existing release profiles.
Retain the launcher's existing module/version selection, profile routing,
source-file preservation, workspace/toolchain settings, and child exit-status
behavior. Document the host-target handling in the
[consumer adoption guidance](../consumers/adoption.md) where appropriate.
Prefer a small change to the supported example and its tests. This request does
not require a new validation option, public bootstrap API, or test-helper
package. CommandKit's choice to select one tool version from its root module
remains consumer-owned; this request does not change ReleaseKit's example policy
of selecting the tool from the chosen module.
## Acceptance evidence
Extend the existing
[multi-module launcher tests](../../cmd/releasekit/multi_module_example_test.go)
with a controlled foreign-target environment. Demonstrate that:
1. The selected tool executes on the host despite foreign `GOOS` and `GOARCH`.
2. The selected module's tool version and publication arguments still reach the
child, and child statuses 0, 1, 2, and 3 are preserved.
3. Source module files remain unchanged and existing bootstrap cleanup behavior
is preserved.
Reuse existing coverage for unaffected behavior rather than adding a parallel
suite. A host-run test can use a synthetic local tool and needs no real release
or external service. Synthetic module downloads must use test-owned caches as
described in [the developer guide](../development.md#testing-synthetic-modules).
## Ownership and downstream follow-up
CommandKit's review found no locally maintained candidate validator that needs
to become a new ReleaseKit feature. Existing formatting, Markdown, and module
distribution options already cover their respective reusable checks; selecting
them as CommandKit release gates is a separate consumer decision.
ReleaseKit already owns publication-engine tests for annotated tags, exact
remote objects, nested tag prefixes, and endpoint preflight. CommandKit can
narrow its local publication exercise to its launcher and profile wiring
without upstreaming a duplicate engine test suite. Its selectors, product names,
publication endpoint, and release order remain local responsibilities.

View File

@@ -1,78 +0,0 @@
# Roadmap: host-native consumer launchers
Status: implemented.
## Purpose
Make ReleaseKit's maintained launchers and generated recovery commands prepare
and execute the release tool for the maintainer's host when inherited `GOOS`
and `GOARCH` select a different application target. Centralize the recipe and
regression coverage so consumers can retain small launchers and product-specific
wiring tests.
This is a launcher correctness improvement. It requires no public bootstrap API,
validation option, or test-helper package. This roadmap selects no release version.
## Baseline before implementation
The [CommandKit feature request](commandkit-feature-request.md) describes a
prepared v0.8.0 tool invocation on Linux/amd64 with `GOOS=windows GOARCH=arm64`
that built a Windows executable and failed with an exec format error.
CommandKit's local launcher already selects the Go host target before
building the release executable. The report's reproduction exercised tool
execution without attempting publication.
The maintained [multi-module launcher](../../examples/multi-module/release.sh)
already selected a module and profile, copied module metadata into temporary
storage, prepared the selected ReleaseKit dependency, and invoked
`go tool releasekit`. It set `GOWORK=off` and `GOTOOLCHAIN=local` while passing
inherited `GOOS` and `GOARCH` to dependency inspection and tool execution.
The [adoption guide](../consumers/adoption.md) included a minimal `go tool`
launcher. The [single-module CI template](../../examples/woodpecker/tag-delivery.yml)
and [multi-module CI template](../../examples/multi-module/woodpecker.yml) also
invoked the selected tool without explicit host-target normalization.
The [maintainer launcher](../../scripts/release),
[source delivery template](../../examples/woodpecker/releasekit-source-delivery.yml),
and [manual delivery retry](../release.md) built and executed the tool with the
same inherited-target assumption. The CLI's [generated recovery command](../../cmd/releasekit/main.go)
inherited the target from the shell where it ran, and [recovery dependency
preparation](../../internal/gotool/recovery.go) also inherited the target.
Existing [launcher tests](../../cmd/releasekit/multi_module_example_test.go)
exercised synthetic module selection, publication arguments, child statuses
0 through 3, and preservation of source module metadata. Those fixtures provided
the starting point for foreign-target regression coverage.
## Targeted end state
Maintained launchers and module-based recovery use `GOHOSTOS` and `GOHOSTARCH`
from the selected Go toolchain as `GOOS` and `GOARCH` while preparing and running
ReleaseKit. The tool executes on that host even when the caller was working with
a foreign application target. Normalize before target-dependent dependency
inspection as well as tool execution. Generated recovery commands select the
host when run, using the Go toolchain that will execute the command.
Application targets continue to come from existing release profiles. The
launcher's host selection preserves module-selected tool versions, profile and
argument routing, source-file preservation, temporary-file cleanup, existing
workspace/toolchain settings, and child exit statuses. Consumers retain their
own selectors, products, endpoints, and release order.
The scope is inherited `GOOS` and `GOARCH` handling for the maintainer tool. It
does not establish a hermetic environment, broaden hardware or platform support,
or add release gates. Existing Go/toolchain failures remain ordinary bootstrap
failures. No new fallback toolchain or download policy is introduced.
The [implementation plan](implementation.md) defines the work to reach this
state.
## Completion evidence
Completion requires host selection before dependency inspection and successful
host execution under a foreign target, including for generated recovery commands.
Preserve selected tool identity and arguments, child-status forwarding, unchanged
source metadata, and existing cleanup behavior. The maintained local and CI
recipes and adoption guidance must use the same host-selection approach.
Application build targets must retain their existing profile-driven behavior.

View File

@@ -1,193 +0,0 @@
# Implementation plan: host-native ReleaseKit launchers
Follow the stages in order as one sprint. The
[feature roadmap](host-native-launchers.md) defines the intended behavior and
scope. The focused checks below describe regression coverage; intermediate
stages need no separate review, release, or passing build. Run the assembled
checks in Stage 6.
## Stage 1: Normalize the local multi-module launcher
**Completed.**
1. In [the multi-module launcher](../../examples/multi-module/release.sh), after
entering the selected module and exporting its existing `GOWORK=off` and
`GOTOOLCHAIN=local` settings, insert the following before the first Go
bootstrap command. Reuse this shell recipe in the subsequent stages:
```sh
releasekit_host_os=$(go env GOHOSTOS) || exit $?
releasekit_host_arch=$(go env GOHOSTARCH) || exit $?
export GOOS="$releasekit_host_os" GOARCH="$releasekit_host_arch"
```
Keep the checked assignments separate from `export` so a failed Go query
stops the launcher. Keep host selection inside the existing launcher scope;
preserve selected module metadata, dependency preparation, publication
arguments, traps, and the child status. Application targets still come from
release profiles.
2. Extend [the existing launcher tests](../../cmd/releasekit/multi_module_example_test.go).
In `TestMultiModuleLauncherUsesSelectedModuleAndProfile`, let the Go shim
answer the two host queries and assert the exported target at
`go list -deps` in the selected module. In
`TestMultiModuleLauncherRunsToolFromSelectedDependency`, supply a foreign
target to the launcher and retain real Go tool preparation and execution.
Keep the checks for the nested module's version, forwarded profile and
arguments, statuses 0 through 3, unchanged source, and bootstrap cleanup.
For these fixtures, choose `windows` unless the host OS is `windows`
(then `linux`), and `arm64` unless the host architecture is `arm64` (then
`amd64`). This makes both variables differ from the host pair. Keep
synthetic versions in a test-owned `GOMODCACHE`.
## Stage 2: Normalize consumer Woodpecker templates
**Completed.**
1. Apply the Stage 1 recipe in the
[single-module](../../examples/woodpecker/tag-delivery.yml) and
[multi-module](../../examples/multi-module/woodpecker.yml) tag delivery
commands, under the existing Go settings and in the selected module. Insert
it after tag verification and checkout, before the first Go bootstrap
command. Write the new shell variable references as
`$${releasekit_host_os}` and `$${releasekit_host_arch}` so Woodpecker
passes them through. Keep the verified commit, selected tool, profile
mapping, credentials, alternate module and sum files, cleanup, and
`ci-deliver --prepared` invocation.
2. Update the existing
[single-module](../../cmd/releasekit/woodpecker_test.go) and
[multi-module](../../cmd/releasekit/multi_module_woodpecker_test.go) workflow
harnesses. Teach the controlled Go shim to answer host queries before
enforcing its alternate module file checks. In both Go shims, log the
target at `go list -deps`. Feed a foreign target to
`TestWoodpeckerTagDeliveryTemplate`,
`TestWoodpeckerTagDeliveryUsesTaggedModuleVersion`,
and `TestMultiModuleWoodpeckerDelivery` and check that dependency inspection
uses the host pair. Retain real execution where the fixture already uses it,
and keep the existing checks that a rejected tag or commit reaches no Go
command.
## Stage 3: Normalize source-built launchers
**Completed.**
1. Apply the same recipe in the [maintainer launcher](../../scripts/release)
before building and executing ReleaseKit. Prefix the two queries with
`GOTOOLCHAIN=local` to match its existing build. Preserve usage checking,
output location, build settings, and the final `exec`.
2. Apply the recipe in the
[source delivery template](../../examples/woodpecker/releasekit-source-delivery.yml)
after tag verification and checkout, under its existing Go settings and
before the source build. Use the Woodpecker escaping from Stage 2. Keep
its temporary build output, dependency preparation through the existing
build, and prepared delivery arguments.
Exercise `TestWoodpeckerSourceDeliveryTemplateSelfBootstraps` with a foreign
target; log and assert the host pair at the real `go build`, retain actual
execution, and keep the checkout, cleanup, and dependency preparation
assertions.
3. Update the manual delivery retry in the [maintainer runbook](../release.md).
After fetching and checking out the tagged version, run host selection,
dependency download, build, and delivery inside a subshell so the caller's
target environment stays intact. Query with `GOTOOLCHAIN=local` as above;
make the sequence stop on failure inside the subshell. Preserve the
dependency download before the offline build, the build-only proxy and sum
settings, the output path, and the older-tag credential example.
## Stage 4: Normalize module-based recovery preparation
**Completed.** The CLI adapter and assembled handoff tests follow in the next stage.
1. In [the Go recovery implementation](../../internal/gotool/recovery.go),
keep the existing registered-tool, API-only, and no-dependency selections.
Have `RecoveryTool` return `(*RecoveryInvocation, error)` within the
internal package. Give the descriptor `Directory string` and
`Args []string` fields. A nil result still selects the native executable fallback.
Set the directory to the original module for a registered tool or the
durable bootstrap root for an API-only consumer. Put `tool -modfile=`
and the existing full command package path in `Args`; the caller supplies
the working directory. Preserve the selected version, local and versioned
replacements, private module and sum files, and their existing lifetime.
2. Immediately before the API-only branch runs `go list -deps`, query
`go env -json GOHOSTOS GOHOSTARCH` in its private root with its existing
`GOWORK=off` and `GOTOOLCHAIN=local` overrides. Reuse the internal
`discover` runner with the existing context. Decode both fields, treat
missing values as a preparation error, and add only `GOOS` and `GOARCH`
to the environment of the dependency command. Keep the target-independent
module-selection query and the registered-tool early return. Use the
existing preparation error and cleanup paths; leave candidate Go settings,
proxy policy, and fallback selection as they are.
3. Update the direct `RecoveryTool` callers in
[the handoff tests](../../cmd/releasekit/publication_handoff_test.go) to run
the descriptor's arguments in its directory. In
`TestRecoveryToolPreservesVersionedReplacement`, set a foreign target for
the preparation call, log the target of the real `go list -deps` through a
delegating shim, and verify the host pair there. Run the test's subsequent
direct tool invocation with the host pair and retain its version and
metadata assertions. Retain the source-module and vendored API-only cases.
Update the CLI caller in Stage 5.
## Stage 5: Normalize the printed recovery command when it runs
**Completed.**
1. In `selectedPublicationRecoveryCommand` in
[the CLI](../../cmd/releasekit/main.go), render the descriptor as one
copyable POSIX shell command. Use this structure, with `<dir>`,
`<package>`, and `<record>` standing for shell-quoted operands:
```sh
(export GOWORK=off GOTOOLCHAIN=local && releasekit_host_os=$(go -C <dir> env GOHOSTOS) && releasekit_host_arch=$(go -C <dir> env GOHOSTARCH) && GOOS="$releasekit_host_os" GOARCH="$releasekit_host_arch" go -C <dir> tool '-modfile=' <package> reconcile --record <record>)
```
Build the common `go -C` prefix from the descriptor directory and render
its tool arguments using the existing diagnostic redaction and shell
quoting. Keep the explicit absolute record operand. Both host queries run
in the invocation directory with the same Go settings as the final command.
A failed query stops the command, and the subshell returns the final command
status when launched. Leave the native executable command, handoff timeout
and cancellation behavior, and existing fallback and diagnostic behavior
intact.
2. In `TestColdGoToolRecoverySurvivesLauncherExit`, execute the printed
command in a separate shell with a foreign target, after the original
launcher and its temporary files are gone. Exercise both registered and
API-only handoffs and retain status checks for 0 through 3. For the
alternate-record cases, replace the quoted original record operand inside
the command instead of appending flags after the closing subshell.
Retain the vendored API-only, native fallback, record-selection, and
redaction checks. Exercise the printed command itself; running the Go
descriptor directly would miss a regression in the shell recipe.
## Stage 6: Align consumer guidance and verify the sprint
**Completed.**
1. In the [adoption guide](../consumers/adoption.md), make the minimal
launcher use the Stage 1 recipe followed by `exec go tool releasekit "$@"`.
Run it from the selected module, retaining that consumer's workspace and
toolchain settings. Move the registration smoke check to follow the launcher
example and invoke `version` through that launcher. In the
[multi-module README](../../examples/multi-module/README.md), apply the same
recipe to the direct `check` smoke commands: enter each selected module in
its own subshell, select the host there, then run the existing check with
its repository-root profile and version. Preserve the source-selected
fixture instructions and avoid turning the smoke check into publication.
2. Align the [consumer delivery guide](../consumers/delivery.md),
[CLI contract](../cli.md), and [internal Go tool guide](../internal/gotool.md)
with the maintained recipes and handoff behavior. Explain that consumers
update copied launchers and templates separately from upgrading the module
dependency. ReleaseKit tests generic bootstrap behavior; consumers can
focus their own wiring tests on selectors and profile mapping. Profile
build targets continue to select application binaries.
3. Run the focused launcher, workflow, and recovery fixtures from the preceding
stages with the existing local Git and module proxy fixtures. Run
`go test ./...` and `go run ./internal/apicheck` as documented in the
[development guide](../development.md#validation). Check shell syntax,
including Woodpecker command blocks after interpolation, local documentation
links, and `git diff --check`. Use the existing offline fixtures; no hosted
delivery, live publication, or external runner is needed.
4. Include the following entry in the next release note during release
preparation, when its version is selected: “Maintained launchers and
generated Go recovery commands now use the host target to prepare and run
ReleaseKit. Update copied launchers and CI templates to get the fix;
release profiles still select application build targets.”

View File

@@ -1,289 +0,0 @@
# Host-native launcher review findings
Reviewed the implementation from `bf07bed` through `0269648` against the
[feature roadmap](host-native-launchers.md) and
[implementation plan](implementation.md).
The maintained launchers select the Go host before preparing and running the
tool. API-only recovery prepares its private module on the host; printed Go
handoffs discover the host again when run. The CLI retains the native fallback
and saved-record selection. Application build targets still come from the
profile, and the public Go facades and dependency boundaries are unchanged.
## Make the adoption smoke command runnable
**Completed.** The smoke command runs the saved launcher through `sh`; verified with a non-executable file and a foreign target.
**Evidence:** The [adoption guide](../consumers/adoption.md#cli-tool-dependency)
tells readers to save the launcher as `releasekit` and run
`./releasekit version`. A newly saved shell file ordinarily lacks its execute
bit. In a disposable directory, the documented command failed with status 126
and “Permission denied.” Invoking the same snippet through `sh` ran a
module-registered fixture tool on Linux/amd64 with inherited
`GOOS=windows GOARCH=arm64`.
**Fix:** Use `sh ./releasekit version` in the smoke instructions, or give an
explicit `chmod +x ./releasekit` step before direct execution. Verify the
published instructions from a newly created file under a foreign target.
## Limit the recovery guarantee to GOOS and GOARCH
**Completed.** The CLI reference names the two target variables and identifies the shell syntax.
**Evidence:** The [CLI contract](../cli.md) says Go handoffs select the host
“independently of inherited cross-compilation settings.” The
[command formatter](../../cmd/releasekit/main.go) overrides `GOOS` and
`GOARCH` and retains other build settings. For example, building the actual CLI
with the native target, `CGO_ENABLED=1`, and an unavailable C compiler named
by `CC` still fails in `runtime/cgo`. This is consistent with the roadmap's
scope, but the CLI wording implies broader independence.
**Fix:** Say that generated Go handoffs set `GOOS` and `GOARCH` from the
selected Go toolchain at invocation time. Preserve the other inherited settings
and the existing workspace and toolchain overrides. The documented handoff
uses POSIX shell syntax; identify that syntax when telling operators where to
paste the command.
## Exercise a profile target that differs from the launcher host
**Completed.** The binary delivery fixture selects a non-host Linux target and checks the uploaded ELF machine as well as the filename.
**Evidence:** The binary-delivery case in
[`TestMultiModuleWoodpeckerDelivery`](../../cmd/releasekit/multi_module_woodpecker_test.go)
supplies a foreign target to the workflow and checks the uploaded
`core-v2.1.0-linux-amd64` asset. The profile itself selects `linux/amd64`.
On the Linux/amd64 review host, the test cannot distinguish using the
profile target from accidentally using the normalized launcher target. The
current build code still applies the selected profile target explicitly.
**Fix:** In that fixture, select a supported profile target distinct from the
host and assert the uploaded asset matches it. Where practical, inspect the
uploaded executable header as well as the name, so the test catches a
host-built payload with a profile-derived filename. Keep the existing local
forge fixture and native version check; the cross-built asset need not run on
the host.
## Reconcile the feature roadmap with the completed implementation
**Completed.** The feature roadmap records implementation and labels the pre-change baseline. The release-note entry remains in the implementation plan.
**Evidence:** The [feature roadmap](host-native-launchers.md) still declares
“implementation pending” and describes the pre-change launchers as the current
baseline. All six stages in the [implementation plan](implementation.md) are
marked complete, and the maintained launchers and recovery code implement the
host selection described there.
**Fix:** Mark the roadmap implemented and label the old baseline as historical,
or retire the completed planning documents after review. Preserve the planned
release-note entry for the next selected release version.
## Verification
`go test ./...`, `go run ./internal/apicheck`, and syntax checks for the
maintained shell scripts passed. The Go suite exercised real tool preparation,
Woodpecker command blocks with local fixtures, and later execution of printed
recovery commands. Disposable smoke probes also verified the maintainer
launcher forwards the child status and the manual retry preserves the calling
shell's target variables. The review made no changes to production code or
tests.
## Integration audit
The audit below examines how the new bootstrap paths interact with existing
invocation recipes, product checks, and publication recovery.
### Use the external Go executable in printed recovery commands
**Completed.** All three Go invocations use external lookup through `PATH`. The handoff regression reproduces the shell-function failure before the fix and verifies reconciliation afterward.
**Evidence:** Before host normalization, the
[command formatter](../../cmd/releasekit/main.go) emitted
`env GOWORK=off GOTOOLCHAIN=local go ...`, which resolves Go through `PATH`.
[Recovery preparation](../../internal/gotool/recovery.go) still executes an
external Go command. The new printed command uses bare `go` for both host
queries and the later tool invocation, so a function named `go` in the
operator's shell can intercept it. In a local rejected-push fixture, the
actual printed command reconciled the saved record in an ordinary shell.
With `go() { GOOS=windows GOARCH=arm64 command go "$@"; }` defined, the same
command failed with an exec format error. The function overrode the final
assignments even though the host queries reported the correct host. In this
case both paths exited with status 1, but only one produced a reconciliation
result.
**Fix:** Restore external command lookup for the host queries and tool
invocation, for example by using `env go -C ...` throughout the subshell.
Keep selection through the operator's current `PATH`, including executable
shims, and keep the exact record and exit status. Exercise a generated
handoff from a POSIX shell that defines a cross-build Go function; assert
the reconciliation result as well as the status.
### Explain how host normalization affects configured product checks
**Completed.** The adoption guide explains inherited host variables and points checks that need an application target to the existing environment overlay.
**Evidence:** The updated launchers export `GOOS` and `GOARCH` to the
ReleaseKit process. Existing [configured checks](../config.md) inherit that
process environment unless their `env` maps override it;
[`RunChecks`](../../cmd/releasekit/internal/cli/config.go) adds the configured
and ReleaseKit-owned values to the child. This differs from built-in
application compilation, which chooses its target from the profile. A
disposable consumer's check using shell defaults for an application target
passed with the previous direct `go tool` invocation and no target
variables, then failed through the updated launcher because it observed
`linux/amd64`. Adding explicit `GOOS=windows` and `GOARCH=arm64` to that
check's `env` restored its behavior.
**Fix:** In the launcher adoption guidance, explain that host variables
reach configured child checks and that checks which need an application
target should set it explicitly in their existing `env` map or command.
Link to the configuration contract. Keep the current environment overlay
semantics and the separate profile selection for built-in builds.
### Align direct source and standalone CI recipes with the launcher guidance
**Completed.** The source examples link to host selection, and the standalone install recipe scopes the same settings to `go install`. Both paths were verified under a foreign target.
**Evidence:** The [README quick start](../../README.md) and
[CLI reference](../cli.md) still recommend bare `go run ./cmd/releasekit`
for source invocation. The [consumer delivery guide](../consumers/delivery.md#woodpecker-tag-workflows)
also recommends bare `go install ...@PIN` for consumers who install the
delivery executable separately. These older recipes remain target-sensitive
alongside the new host-native launchers. On Linux/amd64 with inherited
`GOOS=windows GOARCH=arm64`, the README's actual `version` command failed
with an exec format error. A versioned install from a disposable local
module proxy failed under the same variables with `GOBIN` set; the native
install succeeded and its binary ran.
**Fix:** Link these recipes to the host-selection instructions and make the
native target requirement explicit at the source invocation and at the
standalone installation step. Use the existing recipe where a copyable
command is useful. Keep the selected version, install location, and
`go run` exit-status caveat. An already installed native binary does not
need a new bootstrap step.
## Algorithmic audit
The host normalization itself adds a fixed number of Go invocations per
launch. The CLI renders a fixed argument list, so quoting and formatting take
time and space linear in the lengths of the directory and record paths. Go
dependency inspection and compilation retain their existing roles. The
findings below concern a pre-existing algorithm on the audited recovery path
and a new module-selection defect in the smoke recipe.
### Relocate recovery replacements in one pass without merging selectors
**Completed.** Recovery rewrites relative destinations in one pass. The CLI regression verifies both selectors, the selected tool, the rejected-push status, and later reconciliation.
**Evidence:** The API-only path in
[`RecoveryTool`](../../internal/gotool/recovery.go) walks the parsed
`go.mod` replacements. It calls `modfile.AddReplace` for each relative
local replacement. In the pinned `golang.org/x/mod` implementation,
each call scans the entire replacement slice. With `R` total directives
and `L` relative local directives, relocation visits `O(R × L)` entries,
and takes quadratic time when all or most entries require relocation.
This loop predates host normalization and still runs before the new host
query.
The same loop can panic on a valid module that declares both an unversioned
and a version-specific replacement for the same dependency. When the
unversioned directive appears first and uses a relative path, calling
`AddReplace` with an empty old version treats both directives as matches
and clears the version-specific entry. The outer loop then visits that
cleared entry as if it were another relative replacement. In a disposable
API-only consumer with both forms for ReleaseKit, Go accepted the module
and validation passed. After a local hook rejected the tag push, ReleaseKit
saved the publication record, then panicked in `modfile.addReplace`. It
exited with status 2 instead of reporting the rejected push and a recovery
handoff.
**Fix:** Rewrite each relative right-hand path on its existing parsed
directive in one pass, preserving the exact left-hand path and version
selector. For example, update the typed replacement and its corresponding
syntax token before formatting. This takes `O(R)` directive visits plus
the bytes processed for the paths and avoids selector merging. Keep the
existing relocation policy, the separate synthetic replacement for the
original module, and the private module lifetime. Add a recovery fixture
with distinct local destinations for a wildcard and a version-specific
replacement. Check that the handoff preserves both directives, selects the
version-specific destination, and reports the original publication status.
### Anchor smoke-check module paths to the intended checkout
**Completed.** Both nested paths use `./`; verified the full smoke recipe with a conflicting `CDPATH`.
**Evidence:** The [multi-module smoke recipe](../../examples/multi-module/README.md)
replaced `go -C integrations/...` with subshells that run
`cd integrations/...` before host discovery. A shell with `CDPATH` set can
resolve these relative names in another checkout. In a disposable probe
with matching module paths under two directories, the published snippet
sent both nested checks to the checkout named by `CDPATH` and returned
success. If the other checkout has valid profiles and tools, the smoke
result can appear valid for the wrong candidate.
**Fix:** Use `cd ./integrations/ckconfig` and `cd ./integrations/core/v2`,
or clear `CDPATH` for those two directory changes. The number of lookups is
already fixed; the change corrects which directory is selected. Verify the
copyable recipe with a conflicting `CDPATH` entry.
## Follow-up review after remediation
Reviewed all six implementation stages at `9797f43`. The focused launcher,
delivery, and recovery tests and the public API and dependency checks passed.
The host discovery work remains bounded to a fixed number of calls per
invocation, and recovery replacement relocation now visits each directive
once. No additional scaling issue was identified.
### Stages 1, 2, and 6: Invoke the exact registered Go tool
**Completed.** The launchers, templates, and consumer recipes use the full
package path. The real Go launcher regression verifies tool selection and
exit statuses with a second registered tool of the same short name.
**Evidence:** The [multi-module launcher](../../examples/multi-module/release.sh)
and both consumer Woodpecker templates
([single-module](../../examples/woodpecker/tag-delivery.yml),
[multi-module](../../examples/multi-module/woodpecker.yml)) register the
full ReleaseKit package path, but invoke the short name `releasekit`. The
[adoption launcher](../consumers/adoption.md#select-the-go-host-target) and
[multi-module smoke recipe](../../examples/multi-module/README.md) also use
the short name. Go allows two different package paths with the same final
component in the tool declarations. With a second local tool ending in
`/cmd/releasekit`, the maintained launcher and both extracted CI command
blocks prepared the intended module, then exited with an ambiguous-tool
error. Changing only the invocation to the full package path ran the
intended host-native tool in all three probes. Recovery already invokes
the full path and is unaffected.
**Fix:** Invoke the same full package path used for registration in the
maintained launchers, templates, and copyable consumer recipes. Preserve
the selected module version, arguments, and exit status. Extend an
existing launcher fixture with a second registered tool of the same short
name and verify that only the intended tool runs.
### Stage 3: Build the manual retry tool only from a clean tagged checkout
**Completed.** The retry resolves and checks out the fetched tag and checks
cleanliness before invoking Go. Local probes verified that fetch failure,
checkout failure, and remaining edits stop before Go, and a clean retry
builds and runs the tagged tool for the host.
**Evidence:** The [manual retry block](../release.md) runs `git fetch` and
`git checkout --detach` before the subshell that enables `set -e`. A
failed checkout can therefore be followed by dependency preparation, a
build, and execution from the wrong source. A successful checkout can
also carry local edits forward. Both cases were reproduced with the
documented block in disposable Git repositories and a local bare remote:
a checkout refused because of conflicting edits still ran a fixture
built from the edited source, and checking out the already selected tag
succeeded while retaining and executing a local edit. The fixture exited
successfully in both cases. ReleaseKit's normal delivery guards check
the tagged checkout after the executable has already been built and
started; they do not protect this bootstrap.
**Fix:** Include the fetch and checkout in the fail-fast subshell. Resolve
the literal tag to its commit and require a clean checkout before
downloading dependencies or building the CLI, as the
[source delivery template](../../examples/woodpecker/releasekit-source-delivery.yml)
does. Preserve the scoped host target, dependency download before the
offline build, and the existing retry arguments. Verify that checkout
failure and carried-over edits both stop before the first Go command.