# 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 `/docs/releases/.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. **Completed:** Added shared committed and working-tree module identity derivation, module-local note paths, and literal Git tag-reference validation. Verified root, nested, physical-major, and ordinary `v2` layouts, committed versus working-tree declarations, and slash-containing local and remote tag operations. `go test ./...` passes. ## 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`. **Completed:** Added module selection and identity evidence to the facade and internal request/result types. Candidate capture now reads the selected committed declaration and module-local note, guards the complete local tag, and preserves available evidence through callbacks and later failures. Verified nested physical-major capture, sibling cleanliness, facade error evidence, `go test . ./internal/candidate`, 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. **Completed:** Publication now uses the captured full tag for remote uniqueness, local annotated-tag creation, exact-object push, and reconciliation. Recovery errors identify the full tag and authoritative endpoint, while publication results retain identity through preflight and later failures. Verified two modules publishing the same version at one commit, full-tag reuse rejection, nested rejected and unknown outcomes, and `go test . ./internal/git ./internal/publish`. ## 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. **Completed:** Added a sorted, deduplicated committed input view for the selected module and explicit shared files while retaining full-tree reference lookup. Nested module boundaries exclude their descendants, vendored modules remain included, and shared-file defaults, replacement, empty selection, and missing committed files follow the roadmap. Verified root and nested scopes, worktree-only file rejection, cross-module references, lazy content reads, `go test ./internal/validation ./internal/candidate .`, and `go run ./internal/apicheck`. ## 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. **Completed:** Formatting, workspace, local-replacement, file-size, and Markdown checks now iterate the selected committed inputs. Targeted reads still resolve against the full repository tree. File-size exclusions are translated from the selected module to repository-relative paths, and shared-file failures preserve their public check, path, and cause. Updated file-size guidance and verified in-scope and out-of-scope findings, shared inputs, cross-module anchors, reader cleanup, facade conversion, `go test ./internal/validation .`, and `go run ./internal/apicheck`. ## 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. **Completed:** Go validation commands now run from the captured module directory. Candidate checks and publication share a lazy operation-local Go settings owner with their later build callbacks; standalone and managed delivery builds acquire separate owners. The owner reuses discovery while the inherited environment and selected `GOENV` file stay unchanged, and refreshes after consumer callbacks when either changes. Nested-module fixtures now include packages for selected-module validation. Verified one suite and shared discovery through candidate build coordination, environment and persistent-file refresh, nested-suite failure and sibling isolation, selected vet/compile command directories and compile baseline, `go test ./...`, and `go run ./internal/apicheck`. ## 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 `/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. **Completed:** Standalone builds now resolve working-tree module identity once; candidate and managed delivery builds consume their committed identity and Go tool owner through the resolved build path. Normalized build settings are shared by execution and delivery evidence. Builds run from `ModuleDir`, use module-based output paths, and keep temporary native smoke output outside the repository. Verified nested native version output, a second cross target, dirty declarations, new uncommitted nested modules, empty targets, temporary-output fallback and cleanup, full `go test ./...`, `go run ./internal/apicheck`, and `git diff --check`. ## 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. **Completed:** Delivery now captures checkout identity once, derives the selected module's committed tag and note path, compares an optional expected full tag, and returns module evidence through local and hosted failures. Managed and prebuilt binary results bind to the same repository, module, version, tag, commit, settings, and source verification; the CLI carries that identity through its existing private receipt. Gitea uses the literal full tag and escapes slashes in hosted routes. Verified two module pages at one commit, nested notes and slash tags, mismatched expected tags and prebuilt identities, preserved delivery recovery cases, `go test ./...`, `go run ./internal/apicheck`, and `git diff --check`. ## 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. **Completed:** Replaced the unversioned receipt with schema version 1 and pure recorded-identity checks for module path, physical directory, version, and full tag. Receipt loading now validates structure and normalized settings without opening artifacts; delivery remains responsible for verifying artifact bytes and sizes before hosted requests. Old and unknown schemas require regeneration. Verified root and nested receipts, malformed identity and settings, changed and missing artifacts, `go test ./...`, `go run ./internal/apicheck`, and `git diff --check`. ## 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. **Completed:** Profiles now map `module_dir` and `validation.shared_files`, preserving nil versus explicit empty selections through JSON and facade copies. Candidate commands run from the selected module with repository, module, path, version, commit, and full-tag context. `publish --expected-module` compares normalized profile selection before callbacks, and delivery/build map the profile module through their facades. Dependency preparation now accepts a repository/module request and runs in that module. Verified nested CLI checks and build selection, explicit missing shared files, mismatch rejection before product commands, module-targeted preparation, `go test ./...`, `go run ./internal/apicheck`, and documentation whitespace 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/.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.