Files
ReleaseKit/docs/roadmap/implementation.md

648 lines
40 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Implementation plan: individual Go module releases
Status: ready for implementation.
## Objective and authority
Implement the complete target in the [module-release roadmap](module-releases.md),
including its resolved decisions, and [ADR 0005](../adr/0005-module-release-scope.md).
The [CommandKit request](commandkit-feature-request.md) supplies consumer context.
The implementation covers checking, publication, standalone and composite builds,
prebuilt receipts, hosted delivery, CLI profiles, launchers, and Woodpecker.
This document is an ordered implementation guide for a Luna coding agent.
Complete Stage 1 through Stage 15 in order. Each stage is a focused implementation
unit with its own behavioral verification. Intermediate stages represent partial
implementation; completion means that all stages are complete. Proceed through
the plan without pilot integrations, intermediate releases, external actions, or
human review gates. Architecture review and subsequent adoption testing follow
completion of this plan.
Read [development](../development.md) and the applicable policies before working.
Use the codebase graph for code discovery and inspect the current source before
editing. Follow the [agent policy](../policy/agents.md) for scoped commits after
completed nonempty units. Record completion under each stage with a short account
of the change and verification. Keep behavior documentation aligned as each
capability lands; the feature roadmap owns the desired scope and policy choices.
## Baseline and implementation boundaries
The starting point is the v0.3.0 implementation with accepted module-release
decisions. Current entry points are the root facade's `Check`, `Publish`, `Build`,
`Deliver`, `PrepareTaggedCheckout`, and `PrepareBuildDependencies`.
| Current owner | Change needed |
| --- | --- |
| `check.go`, `publish.go`, `build.go`, `delivery.go`, `preparation.go` | Facade-owned selection and evidence types, explicit conversions. |
| `internal/candidate`, `internal/publish` | Committed module identity, module-local notes, full-tag publication and recovery. |
| `internal/validation` | One selected input set, full repository reference lookup, selected-module Go execution. |
| `internal/git`, `internal/preparation` | Literal full-tag operations and shared tagged-source verification. |
| `internal/gotool`, `internal/build`, `internal/artifact` | Operation-local Go settings, normalized build settings, module-based execution and output. |
| `internal/delivery`, `internal/gitea` | Committed delivery identity, module-bound artifacts, full-tag hosted identity. |
| `cmd/releasekit`, `cmd/releasekit/internal/cli` | Profile mapping, command context, receipts, CI event handling. |
| `examples/woodpecker/`, consumer guides | Root defaults and module-selected bootstrap from tagged source. |
| `internal/apicheck` | Compiled facade ownership and dependency checks for the current API. |
Keep implementation behind the existing facades. `buildinfo` keeps its small
independent runtime dependency graph. Use the existing pure-Go build baseline,
standard version injection, target rules, error causes, publication outcomes,
and delivery recovery behavior. Network-capable preparation remains separate
from offline validation and compilation.
The feature is a clean API and receipt cutover. Update callers and fixtures to
the current interface directly. Migration is documentation for the next release.
Implement individual module releases without batch publication, automatic module
discovery, dependency ordering, arbitrary tag templates, or compatibility shims.
## Concrete interface and ownership choices
These choices settle routine design details needed to execute the stages. GoDoc
and canonical references become their owners when the corresponding code lands.
### Selection and evidence
- Add `ModuleDir string` to `ReleaseRequest`, `BuildRequest`, and
`DeliveryRequest`. Input values are repository-relative; omission or `.` selects
the repository-root module. Keep `Version` as the stable semantic version.
- Add `ModuleDir`, `ModulePath`, and `Tag` to `Candidate`, `PublicationResult`,
`BuildResult`, and `DeliveryResult`. Result `ModuleDir` is the resolved absolute
execution directory; `ModulePath` is the declared import path; `Tag` is the
complete derived Git tag. Retain existing repository, commit, and outcome
fields, adding `RepositoryDir` to results that currently lack it.
- Use one private identity value in `internal/releaseidentity`, containing the
absolute repository and module directories, canonical repository-relative
module subdirectory, declared module path, semantic version, complete tag, and
repository-relative note path. Source commit remains captured operation
evidence. Facades own their exported types and explicitly convert this value.
- Add `SharedFiles []string` to `ValidationSpec`. Profile syntax is
`validation.shared_files`; profile module selection is top-level `module_dir`.
Preserve nil versus non-nil empty slices through all conversions and JSON
round trips. Existing `--config` selects the profile; the initial CLI adds no
separate module override.
- Change `PrepareBuildDependencies` to accept a facade-owned
`DependencyPreparationRequest` containing `RepositoryDir` and `ModuleDir`.
Preparation selects the working-tree module directory and performs the
existing network-capable preparation there. It needs no release version or tag.
- Replace `TaggedCheckoutRequest.Version` with `Tag`. Add `VerifyOnly bool`:
the default prepares the checkout, while `true` performs read-only verification
of an already prepared checkout against `Tag` and `ExpectedCommit`.
- Add optional `DeliveryRequest.ExpectedTag` for the CI event's literal tag.
Delivery compares it with the resolved tag before builds or hosted requests.
Ordinary callers omit it. The field expresses exact event identity.
Identity derivation is shared; operation prerequisites remain with their owners.
Check and publication read declarations from the captured commit. Delivery reads
committed declarations from its source checkout and verifies the derived tag.
Standalone builds read working-tree declarations. Composite builds receive the
owner's already resolved identity. Populate evidence as it becomes available,
including on errors; an unresolved field remains unset.
### CLI events, receipts, and consumer mapping
- `check`, `publish`, `build`, and ordinary `deliver` continue to accept a
semantic `VERSION`. `ci-deliver` accepts a complete `TAG`; its last path
component supplies the semantic version and its full value supplies
`ExpectedTag`. The selected profile supplies the physical module directory.
- Add `ci-deliver --prepared` to select `VerifyOnly`. The maintained workflows
use it after bootstrap's verified checkout. Standalone `ci-deliver` retains
checkout preparation by default. Both paths require `--commit` and perform
fresh read-only tag, HEAD, cleanliness, and expected-commit checks.
- Add optional `--expected-module DIR` to `publish` and `ci-deliver` for mapped
launchers. Compare its normalized repository-relative selection with the
profile's selection before application work. This equality expectation binds
the bootstrap module to the profile; module selection still comes from the
profile. Root-only invocations omit it.
- Keep `.releasekit-build.json` as the private receipt filename. The new schema
requires `schema_version: 1`, absolute `module_dir`, declared `module_path`,
and complete `tag`, alongside the existing fields. The current unversioned
receipt is rejected with regeneration guidance. Implement one schema reader.
- For the maintained multi-module example, use a small committed
`release-modules.tsv` shared by the launcher and CI. Each row contains four
tab-separated fields: selector, event-tag shell pattern, repository-relative
physical module directory, repository-relative profile path. Use `.` for root.
A shared POSIX shell helper reads the table for either a selector or an event
tag. An event must match exactly one row; ambiguity is a selection error.
Tag patterns select a row; the library remains the authority for tag derivation.
The maintained example owns this integration declaration.
Root-only consumers continue without a mapping file or helper.
For example, root v0/v1 and root `v2` directory layouts can use separate rows
with patterns `v[01].*` and `v2.*`. A nested `/v2` module can map
`integrations/ckconfig/v2.*` to either physical layout. Keep patterns quoted and
evaluate them as shell patterns without `eval`. Document the table's literal
tab-separated syntax. No matching pattern changes the derived release tag.
## Stage 1: Implement shared identity derivation and literal tag operations
**Outcome:** One internal owner derives release identity for both source modes;
Git primitives accept full tag names independently of semantic-version rules.
**Steps:**
1. Introduce `internal/releaseidentity` with a selection resolver and shared pure
derivation. Resolve module paths once from repository-relative input, with
ordinary normalization and a final location inside the repository. Keep root
defaults. Require the selected module's own declaration; do not silently
fall back to an ancestor module or require a repository-root `go.mod` for a
nested selection.
2. Provide committed and working-tree declaration readers. Reuse existing Git
committed-file and reference resolution where needed. Preserve cause and
cancellation handling. Do not fetch module metadata or infer identity from
the remote host, a root module declaration, or a directory-name registry.
3. Extract the module directive with the existing `modfile.ModulePath` helper
and apply `module.SplitPathVersion`/`module.CheckPathMajor`. Full `go.mod`
inspection retains its selected validation owner. Retain stable-version
syntax and use the shared module-path major-version rules.
Derive the tag prefix from the physical repository-relative directory,
removing a final physical major-version component only when it equals the
declaration's slash major suffix. Distinguish a normal directory named `v2`
from a physical `/v2` layout using the declaration. Retain standard special
path-major handling supplied by `x/mod`.
4. Derive `<module>/docs/releases/<version>.md` from the physical directory,
including a physical major-version directory. Keep tag prefix and note path
as distinct values. The
[Go module reference](https://go.dev/ref/mod#vcs-version) and
[path-major helpers](https://pkg.go.dev/golang.org/x/mod/module#CheckPathMajor)
define the relevant Go rules.
5. Replace Git's version-shaped tag validation with literal tag-reference
validation using Git's reference rules. Rename parameters and internal
fields that actually represent tags. Preserve exact ref/object operations,
supported tag object types, and repository-state checks. Semantic-version
validation stays at release identity boundaries.
**Verification:** Owner tests cover root, nested, root physical `v2`, nested
physical `v2`, `/v2` declarations without physical major directories, a normal
directory named `v2`, incompatible version majors, normalized selection, missing
or malformed declarations, and selection outside the repository. Test committed
versus working-tree readers and full slash-containing Git refs. Update synthetic
fixtures that need genuine module declarations. Run affected package tests.
## Stage 2: Carry identity through facade types and candidate capture
**Outcome:** Candidate capture supplies module-local notes and recoverable identity
through the public facade.
**Steps:**
1. Add the selection and evidence fields specified above to facade-owned types
and their internal counterparts. Use focused conversion helpers where they
remove repeated evidence construction. Preserve `errors.Is`/`errors.As`
behavior and existing callback signatures using facade-owned `Candidate`.
2. In `internal/candidate.Capture`, capture repository state and commit, resolve
committed identity once, then read the selected module's committed note and
apply existing product/version title validation. Use the full tag for the
local unused-tag guard.
3. Return progressively populated candidate evidence when later capture,
validation, or recheck work fails. Update the public `Check` conversion so
it preserves returned evidence alongside the error instead of replacing it
with an empty candidate.
4. Pass identity through candidate validation and post-validation callbacks.
Keep clean `main`, repository-wide status, capture/recheck behavior, and note
bytes with their current owners. Prepare internal request wiring for later
validation and build stages without adding alternate legacy paths.
5. Update compiled API checks and fixtures for the chosen facade declarations.
Keep internal identity types out of exported signatures and fields.
**Verification:** Root notes retain their path and title. A nested candidate uses
its own note, including a physical major directory. Note/title/tag conflicts and
later validation failures retain available module, version, tag, and commit
evidence. An unrelated sibling worktree change still fails the repository guard.
Run candidate/facade tests and `go run ./internal/apicheck`.
## Stage 3: Publish and reconcile the complete selected tag
**Outcome:** Publication addresses one module's exact tag and retains its current
known-failure, confirmed, and unknown outcomes.
**Steps:**
1. Thread captured identity through `internal/publish`; use `Tag` for remote
uniqueness, annotated-tag creation, exact-object push, and reconciliation.
Continue using `Version` for product/version messages where it is semantic.
2. Preserve the authoritative endpoint, expected push URL comparison, remote
branch/commit guards, local rechecks, bounded reconciliation, and tag object
evidence. These checks verify state against the captured identity and do not
invoke identity derivation again.
3. Populate `PublicationResult` identity before later failures and preserve it
through facade conversions. Recovery output identifies the full tag and
authoritative endpoint rather than constructing a ref from the version.
**Verification:** Publish two modules with the same version at the same captured
commit to a temporary local remote; only their separate intended tags appear.
Reject reuse of the full tag. Add representative nested-tag rejected-push and
unknown-outcome evidence cases. Reuse existing endpoint and lost-acknowledgment
tests for unchanged recovery mechanics. Run publication and Git tests.
## Stage 4: Construct the selected committed file inputs
**Outcome:** One ordered path set defines automatic validation inputs, while the
full committed repository remains available for reference lookup.
**Steps:**
1. Add scope selection beside `CandidateTree` metadata handling. Select entries
beneath the module directory, excluding descendants belonging to nested
committed `go.mod` boundaries. Boundary detection uses locations and does
not parse unrelated declarations. Treat vendored contents as selected-module
material; vendored `go.mod` entries do not establish exclusion boundaries.
2. Preserve the complete repository index and committed resolver separately.
Add a selected-input view used by checks; avoid replacing the complete tree
with a filtered map that breaks cross-module links or committed symlinks.
3. Resolve `SharedFiles` once against committed entries. Apply the roadmap's
optional defaults, replacement list, explicit empty list, missing-explicit
errors, and explicit cross-module file selection. Literal paths identify
individual files; metacharacters have no selection semantics. Retain existing
symlink/reference behavior where applicable.
4. Merge selected module entries and shared files in stable repository-path
order, deduplicating by committed path. Preserve per-path context when
different paths share a blob. A referenced destination joins the input set
only through independent module or explicit-file selection.
5. Create inputs only when enabled file checks or explicit shared-file validation
require them. An explicit missing file fails even with all file checks off;
a default-only request with no file checks needs no file-input construction.
Keep lazy blob access, operation context, cancellation, and reader cleanup.
**Verification:** Scope tests cover root and nested modules, independent sibling
and nested fixture modules, vendor inclusion, optional defaults, replacement,
empty, missing explicit, duplicates, and selected files in another module.
Use committed files rather than worktree presence. Show that full lookup still
resolves an excluded module's destination. Metadata-only selection starts no
batch content reader. Run validation tree/scope tests.
## Stage 5: Apply selected inputs to every optional file check
**Outcome:** Formatting, workspace, local replacement, file-size, and Markdown
checks use the same selected inputs with their existing individual rules.
**Steps:**
1. Update checker iteration to consume the selected-input view from Stage 4.
Retain the full repository resolver for targeted source and destination reads.
An unrelated sibling declaration or root workspace file outside a nested
module's inputs does not independently fail that module's check.
2. Translate module-relative file-size exclusions once into canonical
repository-relative paths before checker execution. Preserve existing literal
file/directory exclusion matching, limits, findings, and check order.
3. Retain formatter selection, symlink treatment, committed replacement
inspection, Markdown syntax and fragment rules, boundary observations,
source locations, cause propagation, and the existing per-blob caches.
Diagnostics remain repository-relative; Markdown links remain relative to
each source document.
4. Keep content-reader cleanup attributed to the last check that actually read
content. Do not reintroduce full raw-content retention or per-check tree
scans merely to carry module scope.
5. Give explicit shared-file selection errors a public validation identity
`shared-files`, with the selected repository-relative path and cause. Add its
facade conversion and CLI presentation through the existing failure model.
Shared-file selection itself enables no content check.
**Verification:** Place real violations inside and outside selection and show
that only in-scope inputs fail each selected checker. Cover shared files under
enabled checks, module-relative exclusions, and a cross-module Markdown heading
read that leaves unrelated destination documents unchecked. Equal Markdown blobs
at different paths keep distinct relative-link findings. Confirm selection
errors with content checks disabled and preserve cancellation/cleanup regressions.
Run affected validation and facade tests.
## Stage 6: Execute Go validation in the module with reusable local settings
**Outcome:** One selected standard suite runs in the module, and later composite
builds can reuse its effective Go environment.
**Steps:**
1. Change validation Go command directories to resolved `ModuleDir`. Preserve
offline, readonly, workspace-disabled behavior and selected race, full vet,
target compilation, output, and command ordering.
2. Introduce a small operation-local lazy Go-tool owner alongside the existing
`gotool.Tool`. Candidate validation and subsequent build callbacks share it;
delivery and standalone builds own separate instances. It acquires settings
only when a Go command needs them.
3. Reuse the discovered tool while effective environment inputs are unchanged.
Retain a snapshot of inherited environment and the persistent Go settings
source used for discovery; after consumer callbacks, compare those inputs
and rediscover if they changed. Use simple value/file comparisons within
this owner. Keep preparation's network-capable settings separate. Introduce
neither a public session object nor a process-global tool cache.
4. Pass the private owner through internal candidate/build coordination. Preserve
repository-wide state rechecks and the single shared validation invocation.
**Verification:** A failing nested test suite prevents its release while root
tests pass. An unrelated sibling failing suite does not run. Selected vet and
compile targets use the same module and existing baseline. Observe one standard
suite and reused discovery in an unchanged validation/build operation; changing
environment inputs through an additional callback triggers fresh settings.
Run Go-tool, validation, and candidate coordination tests.
## Stage 7: Build the selected module and bind output evidence
**Outcome:** Standalone and composite builds use the correct declaration source,
version injection, path base, and identity.
**Steps:**
1. Make public standalone `Build` resolve working-tree identity once, then call
an internal build path that consumes resolved identity. Candidate and managed
delivery builds call that path with committed identity and their Go-tool
owner. They do not call standalone resolution as a nested operation.
2. Consolidate normalized build settings in `internal/artifact`, including main
package, binary name, and normalized target pairs. Reuse this representation
for execution, evidence, later receipt binding, and settings comparisons.
Preserve supported target and artifact-name rules.
3. Build main packages from `ModuleDir`; resolve relative ordinary output there,
with default `<module>/dist`. Keep absolute output usable. Candidate and
managed delivery output, including disposable native smoke output, stays
outside the entire repository through the existing temporary-directory owner.
4. Use semantic `Version` for linker injection, filenames, and exact native
version checks. Populate build identity before later errors and retain partial
artifacts, produced-byte digests, compiler evidence, and source observations.
An empty target set performs no compiler or Go-environment discovery.
5. Preserve standalone modified-source support and existing `SourceVerified`
observations. A build does not require a note, unused tag, clean `main`, or
publication. Delivery still requires verified committed tagged source.
**Verification:** Build and run a nested native consumer using `buildinfo` and
cross-compile one supported second target. Filenames and output contain the
semantic version and retain ordinary filename components. Cover dirty declaration
changes and a new uncommitted module directory for standalone builds, committed
identity for composites, empty targets, and partial failures. Confirm temporary output is
outside the repository even with a nested selection and source-local `TMPDIR`.
Run build and runtime-boundary tests.
## Stage 8: Deliver module-local notes and assets to full-tag pages
**Outcome:** Page-only, managed-build, and API prebuilt delivery share committed
module identity and existing recovery behavior.
**Steps:**
1. Capture checkout commit and resolve committed identity once in delivery;
inspect the derived literal tag against that checkout. Compare optional
`ExpectedTag`. Keep historical lightweight tags, detached checkouts, clean
source, and the existing post-build source recheck.
2. Read and validate the module-local committed note. Populate delivery evidence
as resolution progresses, including build and hosted failures. Use Stage 7's
resolved build path for managed builds.
3. Bind prebuilt results to repository, module directory, declared module path,
version, tag, commit, and existing artifact/settings evidence. Require
`SourceVerified` for binary delivery and preserve caller ownership of files.
4. Give Gitea requests a distinct full-tag field; stop applying stable-version
syntax to that field. Use the literal tag for hosted tag lookup, release
lookup/create, and conflicts. Titles remain from the version-bearing note.
Encode tags at HTTP/source-route owners and verify escaped slash behavior.
5. Preserve one local digest-and-size verification before hosted requests,
deterministic manifests, remote exact comparisons, additive retries, and
existing failure states. Page-only delivery performs no application build,
Go environment discovery, application download, receipt, or manifest work.
**Verification:** Local HTTP fixtures receive correctly encoded nested tags and
their semantic-version note titles. Two module tags at one commit create
separate pages. Cover nested managed builds and reject an API prebuilt result
for another module before hosted requests. Reuse established tampering, conflict,
partial/unknown, historical-tag, and cleanup tests. Run delivery/Gitea tests.
## Stage 9: Replace receipts with the module-bound schema
**Outcome:** CLI prebuilt delivery uses one current receipt format and one
delivery-owned content verification pass.
**Steps:**
1. Implement the schema specified above in CLI receipt read/write and public
build conversions. Keep the existing private filename and write/partial-build
lifecycle. Require all identity fields, even for a root build.
2. Validate the schema version, field structure, source identity relationships,
normalized settings, artifact declarations, and receipt/output location.
Check internal tag/module/version consistency through shared pure derivation
using recorded identity, without resolving a new operation's source.
3. Reject old/unversioned or unknown-schema receipts with guidance to regenerate
through the updated build command. Do not migrate or accept their old shape.
4. Remove artifact content hashing from receipt loading and adjacent prebuilt
declaration/comparison work. Loading validates declared paths and digests;
Stage 8 delivery verifies actual bytes and sizes before any hosted request.
Build-time hashing records produced bytes independently.
5. Reuse normalized settings in profile-to-receipt comparisons. Keep structural
validation distinct from source verification and actual asset verification.
**Verification:** Round-trip a root and nested receipt. Reject old schema,
malformed identity, and mismatched module or settings. A loaded receipt with
changed or missing artifact bytes fails during delivery before hosted requests.
Protect verification ownership through an observable read boundary rather than
tests tied to private helper choreography. Run receipt and CLI prebuilt tests.
## Stage 10: Map profiles and product commands to module operations
**Outcome:** Local check, publication, and build commands express the new API with
small profiles and module context.
**Steps:**
1. Add strict profile decoding and mapping for `module_dir` and
`validation.shared_files`. Omission/null versus explicit empty selections
survive loading, copying, serialization, and facade mapping. Do not use
`omitempty` serialization that collapses explicit empty shared-file lists.
2. Keep profile lookup relative to the repository, including `--config`, and
pass module-relative owned paths to the common normalization boundary. Avoid
resolving identity a second time in CLI mapping.
3. Run configured product commands with `Candidate.ModuleDir` as CWD. Keep
existing version/commit context and add `RELEASEKIT_REPOSITORY_DIR`,
`RELEASEKIT_MODULE_DIR`, `RELEASEKIT_MODULE_PATH`, and `RELEASEKIT_TAG`.
Supply captured context without modifying the parent process environment.
4. Update check/publication/build diagnostics and recovery text to identify module,
semantic version, and full tag as available. Preserve existing native exit
classifications and presentation of underlying validation failures.
Implement `publish --expected-module` as a profile-selection comparison using
the shared path normalization, without another declaration resolution.
5. Apply the dependency preparation request cutover and its facade conversions.
Preparation resolves selection without release-note or publication prerequisites.
**Verification:** CLI profiles select root/nested API requests; explicit empty
lists survive round trips. Product commands observe module CWD and both contexts.
Relative profile lookup remains repository-based and build output module-based.
Missing explicit files fail without enabling checks. Verify nested failure
diagnostics and retain a minimal root profile with no new required fields.
Run CLI profile, command, preparation, and compiled API checks.
## Stage 11: Implement full-tag CI delivery and prepared-checkout verification
**Outcome:** The CLI can prepare a complete event tag or reuse a verified
bootstrap checkout, then deliver the selected module.
**Steps:**
1. Update `ci-deliver` parsing to accept full `TAG`, required expected commit,
and optional `--prepared`. Validate literal ref syntax and stable terminal
version separately; ordinary commands still accept semantic versions.
2. Implement `TaggedCheckoutRequest.Tag` and `VerifyOnly` in preparation. Share
read-only verification between both modes; default preparation fetches the
exact tag and detaches its expected commit, then verifies. Verify-only checks
matching tag, HEAD, clean repository, and expected commit without fetch or
checkout. Preserve default remote selection and existing failure causes.
3. Load the profile from the prepared source using repository-based path rules.
Compare optional `--expected-module` with its normalized selection before
application dependency preparation; mismatch is a profile-selection error.
Pass its module selection, parsed semantic version, and event `ExpectedTag`
into delivery. Delivery owns committed identity resolution and exact mapping
verification. Keep ordinary `deliver` free of checkout/download preparation.
4. Prepare application dependencies in the selected module only for binary
profiles. Continue reading delivery credentials only for delivery; preparation
and configuration failures retain their existing CLI classifications.
5. Update prebuilt CLI delivery to compare the new receipt and normalized profile
settings, then delegate actual source/asset validation to the facade.
**Verification:** Standalone CI delivery prepares a nested event tag. Prepared
mode succeeds offline without repeat fetch or checkout and rejects wrong commit,
dirty source, missing/moved tag, and inconsistent profile module. Verify detached
historical delivery, module-targeted dependencies for binaries, and page-only
application-preparation omission. Run preparation and CLI delivery tests.
## Stage 12: Preserve the small root-module workflow
**Outcome:** Existing root-only consumers retain a small launcher, profile, and
module-selected CI bootstrap under the current interface.
**Steps:**
1. Update `examples/woodpecker/tag-delivery.yml` and the self-bootstrap source
example to use prepared CI delivery after exact event checkout verification.
Keep root module files and default root profile selection.
2. Retain temporary alternate module metadata outside the repository, tagged-source
tool selection, workspace/toolchain controls, compatible Go image selection,
and credential handling. Tool bootstrap's network access remains separate
from offline application compilation.
3. Keep ReleaseKit's own `scripts/release`, root profile, and maintainer procedure
consistent where affected. Root consumers need no module map, extra required
configuration, or additional routine decisions.
4. Update root workflow tests for the receipt/API cutover and prepared mode.
Reuse existing synthetic tool modules and isolated module cache helpers.
**Verification:** Execute the root launcher/API/profile representative flows and
both maintained root workflow harnesses. Bootstrap and retry select tooling from
the original tagged module after the default branch changes. Confirm subsequent
delivery performs read-only checks without repeating checkout preparation.
Use local remotes/proxies and temporary `GOMODCACHE`, never live CI or credentials.
## Stage 13: Add a shared multi-module declaration and local launcher example
**Outcome:** Consumers select modules through small declarations with one maintained
helper shared by local release commands and CI.
**Steps:**
1. Add `examples/multi-module/` with root and integration profiles, the specified
TSV mapping, and POSIX shell selection helper. Include a physical-major
directory selection to demonstrate the distinction between module and tag.
2. Have the helper select by local selector or full event tag and return the
physical directory/profile. Share parsing and selection rather than copying
cases into separate launcher and workflow code. Keep unknown selectors and
ambiguous events as configuration errors.
3. Add a small shared launcher that chooses the row, bootstraps the tool from the
selected module's local configuration using temporary alternate module files,
and invokes publication with repository context, `--config`,
`--expected-module`, and semantic version. Module selection comes from the
profile; the expectation verifies table/profile agreement. The library
validates derived release identity.
4. Supply dependency preparation guidance before offline checks and illustrative
sequential core/integration publication when a sibling version must exist.
The launcher does not infer dependency order or repeat the standard suite.
**Verification:** Run selection and launcher harnesses with local temporary
repositories and isolated dependency proxies. Cover root/nested selection,
physical-major mapping, unknown/ambiguous selection, quoted paths, and profile
agreement. Verify the selected module supplies the tool dependency and publication
receives its semantic version/profile. Shell patterns never become arbitrary
library tag templates. Update example guidance with the implemented format.
## Stage 14: Bootstrap and deliver mapped module tags in Woodpecker
**Outcome:** A maintained multi-module workflow uses the same declaration and
tagged module selection for local publication, delivery, and retries.
**Steps:**
1. Add a multi-module Woodpecker example under `examples/multi-module/`, with tag
filters for its root and nested selections. Keep the simpler root template.
2. Verify the literal event tag and peeled event commit, detach and check clean
source, then load the mapping/helper from that verified checkout. Select the
physical module and profile before tool bootstrap. Do not derive physical
module location merely by removing the final tag component.
3. Copy the selected module's `go.mod`/`go.sum` to temporary bootstrap metadata
outside the whole checkout; run bootstrap from that module. Build the
ReleaseKit dependency chosen there, then invoke `ci-deliver --prepared` with
repository, selected profile, expected physical module, expected commit, and
full event tag.
4. Exercise page-only and binary declarations through the same workflow. Keep
source verification, dependency preparation, and credential handling with
their established owners. A retry reads the triggering release's mapping,
profile, module files, and selected tool rather than the default branch.
**Verification:** Workflow harnesses cover nested source-only and binary delivery,
root fallback in the separate simple template, and both physical layouts of a
major-version module. Change default-branch mapping and tool versions after a
tag and verify retry still uses its original selection. Fail inconsistent mapping
through the library's exact tag check. Isolate every synthetic dependency consumer
with test-owned `GOMODCACHE`; use local HTTP fixtures and local Git remotes.
## Stage 15: Complete consumer documentation and verify the whole cutover
**Outcome:** The supported interface, examples, migration guidance, and behavioral
evidence describe the complete implemented feature.
**Steps:**
1. Update GoDoc, [CLI](../cli.md), [configuration](../config.md),
[consumer guides](../consumers/), [Gitea integration](../integrations/gitea.md),
internal owner documents, examples, and recovery guidance at their canonical
owners. Explain module selection, semantic version versus full tag, declared
versus physical module identity, note/source/license links, path bases,
boundaries, shared-file semantics, preparation, and receipt ownership.
2. Write `docs/roadmap/next-release-notes.md` as the next release's pending notes
and migration guidance. A release version is not selected by this feature;
keep the draft under roadmap ownership until release preparation assigns its
`docs/releases/<version>.md` destination. Include API/profile updates, the
preparation signature and tag field changes, receipt regeneration, CI mapping
and prepared mode, and root-module exclusion of nested fixture module files.
Keep immutable historical release notes intact.
3. Verify ReleaseKit's own root-module release procedure still satisfies its
adopted policy. Consumer guidance explains module-local note adaptation;
this feature does not change the source-push completion point or introduce
live-service adoption/release gates. Correct stale baseline facts in owner
documents touched by the feature, including the declared Go minimum.
4. Audit the acceptance coverage below. Add only missing representative assembled
flows or owner-level cases with a concrete remaining risk. Reuse existing
recovery, cancellation, HTTP conflict, and facade-boundary tests; avoid a
Cartesian product of every module layout, check, target, and failure.
5. Run the normal offline `go test ./...` suite and
`go run ./internal/apicheck` from the repository root. Validate affected local
documentation links/fragments and examples, and run `git diff --check`.
Record the final results and feature completion in the roadmap/plan, then
commit the completed unit. Completion is the implementation and local
evidence; tagging, deployment, live CI, and consumer rollout remain later work.
**Verification:** Match every acceptance row to concrete test or review evidence,
record the full-suite and compiled API check results, and confirm that the
implemented examples and canonical documentation agree with the current API.
### Acceptance coverage audit
| Requirement | Primary stage/evidence owner |
| --- | --- |
| One resolution per operation, committed versus working-tree declarations, major layouts, version/tag distinction | Stages 1, 2, 7; identity tests and composite coordination. |
| Root defaults, own-module declaration, no root declaration required for nested selection | Stages 1, 2, 12; resolver and representative root/nested workflows. |
| Module-local notes; equal versions at one commit; only intended tag; full-tag reuse and recovery | Stages 2, 3; candidate/publication local Git fixtures. |
| Repository-wide guards and progressive failure evidence | Stages 2, 3, 8; facade conversion and operation failure tests. |
| One selected Go suite, vet/compile/module CWD, offline environment reuse | Stage 6; validation and Go-tool tests. |
| Nested boundaries, fixtures, vendor, shared defaults/replacement/empty, committed presence, missing explicit files without checks | Stages 4, 5, 10; scope, checker, and profile round-trip tests. |
| Path deduplication, equal-blob contextual findings, cross-module lookup without expanded inputs, module-relative exclusions | Stages 4, 5; selected-view and reference tests. |
| Existing selective caches, reader cancellation and cleanup attribution | Stages 4, 5; retained regression coverage. |
| Semantic binary versions, module output paths, temporary output outside the whole repository, empty/partial builds | Stage 7; real native consumer and cross-compilation evidence. |
| New-only receipt schema and module/settings/source binding; one local delivery content pass | Stages 8, 9, 11; receipt/prebuilt/tamper fixtures. |
| Distinct hosted pages, slash tags and source links, page-only and binary delivery, unchanged partial/retry outcomes | Stage 8; HTTP fixtures and retained recovery tests. |
| Repository/profile versus module path bases; both product-command contexts; simple root configuration | Stages 10, 12; command/profile/root workflow tests. |
| Exact event checkout, prepared read-only reuse, physical-major mapping, tagged-source retries and tool selection | Stages 11–14; preparation and workflow harnesses. |
| Facade ownership, independent `buildinfo`, documentation and migration guidance, complete offline suite | Stages 2, 7, 10, 15; compiled API checks and final review. |
No required product decisions remain open. The interface choices above provide
the starting specification; local implementation details may evolve within the
feature roadmap and repository policies.