Remove completed module release roadmaps and review notes

This commit is contained in:
2026-10-10 15:44:03 +00:00
parent 5a0bc6f78b
commit 4340c20eff
5 changed files with 4 additions and 1881 deletions

View File

@@ -1,158 +0,0 @@
# CommandKit feature request: publish individual modules in a shared repository
Status: requested upstream capability; not an implemented ReleaseKit contract.
## Consumer context
CommandKit uses ReleaseKit v0.3.0 for source-only publication. CommandKit is
still an unreleased prototype and is preparing to separate its core from
optional integrations into three independently versioned Go modules:
| Repository directory | Module path | Illustrative version | Corresponding Git tag |
| --- | --- | --- | --- |
| Root | `gitea.maximumdirect.net/eric/CommandKit` | `v0.1.0` | `v0.1.0` |
| `integrations/ckconfig` | `gitea.maximumdirect.net/eric/CommandKit/integrations/ckconfig` | `v0.2.0` | `integrations/ckconfig/v0.2.0` |
| `integrations/ckprompt` | `gitea.maximumdirect.net/eric/CommandKit/integrations/ckprompt` | `v0.1.0` | `integrations/ckprompt/v0.1.0` |
These versions illustrate independent numbering; they are not selected initial
release versions. Integration versions will describe their own changes rather
than matching either CommandKit core or upstream library versions.
The dependency direction is `ckprompt` toward `ckconfig` and core, and
`ckconfig` toward core. Consumers should acquire only their selected integrations
and their dependencies. Each integration therefore needs its own module files,
validation, release notes, and publication identity. More integration modules may
follow without requiring a new publication mechanism for each one.
Go requires repository-subdirectory prefixes on nested module version tags.
The semantic module version remains `vMAJOR.MINOR.PATCH`; the complete Git tag
additionally identifies the module directory. See the
[Go module reference](https://go.dev/ref/mod#vcs-version).
## Issue with the current publication contract
ReleaseKit's documented [CLI](../cli.md), [profile](../config.md), and
[publication API](../consumers/publish.md) assume one release version/tag and
one standard Go suite. Release notes use repository-root
`docs/releases/<version>.md`, and publication accepts stable
`vMAJOR.MINOR.PATCH` rather than a nested module's prefixed tag.
Changing the product name or choosing another profile does not provide a
separate module directory, semantic version, and Git tag identity. Running the
root module's suite also does not validate packages belonging to nested modules.
CommandKit would otherwise need to implement its own integration publication
path, duplicating functionality already owned by ReleaseKit. We would prefer
ReleaseKit to support selecting one Go module within the shared repository.
## Requested feature
Support checking and publishing one explicitly selected module per invocation,
through the public library API and native CLI/profile interface. The request is
about observable behavior; ReleaseKit should select its own internal design.
### Repository and module selection
- Distinguish the Git repository from the selected module's directory within it.
- Keep repository state, candidate commit, branch, remote, and publication guards
scoped to the shared repository.
- Execute the standard Go validation suite in the selected module. Its tests,
and any selected module-specific vet or package compilation, should operate on
that module rather than implicitly validating only the repository-root module.
- Preserve ordinary root-module publication when no nested module is selected.
- Permit a shared repository launcher to choose small per-module profiles without
requiring separate Git repositories or duplicated publication logic.
### Version and tag identity
- Accept the selected module's semantic version separately from its repository
subdirectory, deriving the corresponding Go module tag from those inputs.
- Use the complete tag for uniqueness checks, tag creation, publication, and
failed or ambiguous publication recovery.
- Make both module version and complete tag identifiable in public results and
CLI output, alongside the candidate commit and existing publication evidence.
- Allow modules to publish different versions and to release individually.
Equal semantic versions in different modules identify different tags.
No arbitrary tag-template language is needed by CommandKit. Standard Go module
tag conventions meet its requirements.
### Release notes
Support a distinct committed note for each module release, even when two modules
use the same semantic version. CommandKit's preferred layout is module-local
`docs/releases/<version>.md`, for example:
```text
docs/releases/v0.1.0.md
integrations/ckconfig/docs/releases/v0.2.0.md
integrations/ckprompt/docs/releases/v0.1.0.md
```
Validate the selected note against its product and semantic version in the
captured candidate. The complete tag remains the immutable source identity for
note links, licensing links, and publication recovery. A documented explicit
note-path setting would also be acceptable if module-local discovery is not the
selected public design.
Document how module version, complete tag, note title, and note location relate;
align affected release-policy language with the selected conventions rather than
treating the current single-module note path as an obstacle.
### Validation and completion
Run the selected module's standard suite once for the publication candidate,
retaining ReleaseKit's offline validation and existing publication outcome model.
Additional product checks may cover CommandKit-specific composition or dependency
boundaries without repeating the selected standard suite.
CommandKit will prepare dependencies before offline validation. Its published
modules will require actual released dependency versions; local workspace
composition is useful during development but does not establish ordinary consumer
resolution. For releases requiring new sibling versions, CommandKit can publish
core, then `ckconfig`, then `ckprompt`, preparing each candidate as needed.
Successful source-tag publication should remain the completion point. This
request does not add CI polling, hosted release pages, artifact delivery, module
proxy availability, or consumer installation as publication completion gates.
## Illustrative consumer interface
Conceptually, a request would supply:
```text
Repository: /work/CommandKit
Module directory: integrations/ckconfig
Product: CommandKit ConfigKit integration
Module version: v0.2.0
```
ReleaseKit would select that module's suite and committed note, and publish
`integrations/ckconfig/v0.2.0` for the captured repository candidate. Exact Go
field names, CLI options, and profile syntax remain ReleaseKit design choices.
CommandKit could then expose a small convenience launcher such as
`./scripts/release ckconfig v0.2.0`; that is illustrative consumer syntax, not a
request for ReleaseKit to hard-code CommandKit's module names.
## Acceptance scenarios
1. Publish the root module using ordinary unprefixed tags and existing defaults.
2. Publish either nested integration with its Go-required prefixed tag and its
own committed note, without publishing unrelated module tags.
3. Identify validation failures from the selected nested module and prevent its
publication, even if the root suite would pass.
4. Publish two modules using the same semantic version without note or tag
collisions; preserve existing guards against reusing a published full tag.
5. Report the complete selected tag in known-failure or unresolved-publication
results so recovery addresses the exact destination and tag object.
## Follow-up possibilities
Publishing an explicit set of independently versioned modules in one operation
could later reduce repeated repository checks and support coordinated releases.
Batch or atomic multi-tag publication is not needed for this initial request.
Neither are automatic module discovery, dependency-driven version selection,
matching version numbers, or a generic release-orchestration framework.
CommandKit can adopt single-module selection first and revisit release sets when
concrete maintenance needs justify them.

View File

@@ -1,797 +0,0 @@
# Implementation plan: individual Go module releases
Status: complete; all 15 stages implemented and locally verified.
The subsequent [remediation review](remediation.md) records post-implementation
findings, corrections, and their verification. The baseline and stage accounts
below retain the implementation sequence.
## 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.
**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 `<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.
**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.
**Completed:** CI delivery now accepts literal full tags and derives the stable
semantic version from the final path component. Tagged checkout preparation
shares read-only tag, HEAD, expected-commit, and cleanliness verification with
the offline `--prepared` mode. CI profiles load from that checkout, optional
`--expected-module` is compared before application dependency preparation, and
the event tag is passed through delivery for exact identity checks. Binary
dependency preparation remains module-targeted and page-only profiles skip it.
Verified nested tag preparation, prepared CLI delivery without a remote, wrong
commit and dirty/missing/moved tag rejection, profile parsing, full `go test
./...`, `go run ./internal/apicheck`, and `git diff --check`.
## 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.
**Completed:** Both maintained root Woodpecker templates now verify and detach
the exact event tag before invoking `ci-deliver --prepared`, so delivery repeats
the source checks without fetching or changing checkout state. Updated workflow
tests assert the prepared flag while retaining local proxy coverage for the
tagged ReleaseKit selection, self-bootstrap, retry behavior, and source-only and
binary delivery. Verified the root profile and compiled API boundaries, the
maintained workflow harnesses, `go test ./...`, and `git diff --check`.
## 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.
**Completed:** Added the multi-module profiles, four-column tab-separated
mapping, shared POSIX selector, and launcher. The mapping covers root and
nested integration tags plus a physical `/v2` module layout. The launcher
copies selected module metadata outside the checkout, builds the ReleaseKit
version selected by that module, and passes the mapped profile and
`--expected-module` to publication. Guidance covers per-module dependency
preparation and sequential releases where a sibling dependency is required.
Verified selector and ambiguity behavior, unknown selections, paths with
spaces, the physical-major row, and an isolated local-proxy launcher run that
proved the selected module's tool version and publication arguments. Also
passed `sh -n`, `go test ./...`, `go run ./internal/apicheck`, and
`git diff --check`.
## 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.
**Completed:** Added the multi-module Woodpecker workflow with full root and
nested tag filters, exact peeled-tag commit verification, and selection from the
verified checkout. Bootstrap uses the mapped physical module's committed Go
metadata and profile, then invokes prepared CI delivery with the expected module,
commit, and full tag. Added the binary core v2 example and documented committed
module checksums for clean binary delivery. Workflow harnesses cover nested
page-only and binary delivery, physical and virtual `/v2` module layouts, a retry
after default-branch mapping and tool changes, and an inconsistent mapping
rejected by exact event-tag validation. Tests use local Git remotes, local HTTP
delivery fixtures, a local module proxy, and test-owned `GOMODCACHE`. Verified
`go test ./...`, `go run ./internal/apicheck`, shell syntax, and `git diff --check`.
## 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 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.
**Completed:** Updated facade GoDoc, consumer and CLI/configuration guidance,
Gitea and internal ownership documents, recovery guidance, and maintained
examples for module selection, full tags, path bases, shared files, preparation,
and receipt ownership. Added pending next-release notes with migration guidance
and corrected the current Go minimum facts. Release preparation promoted those
notes to [the v0.4.0 release note](../releases/v0.4.0.md).
Audited acceptance coverage against
the stage-owned tests and reviewed ReleaseKit's root release procedure against
the adopted release policy; no new release gates were added. Verified
`go test ./...`, `go run ./internal/apicheck`, local links and heading fragments
in 13 changed Markdown documents, multi-module shell/config/module syntax, and
`git diff --check`.
### 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.

View File

@@ -1,335 +0,0 @@
# Roadmap: release individual Go modules
Status: implemented; post-implementation findings and corrections are recorded
in the [remediation review](remediation.md). Release version remains unselected.
## Purpose
Implement [ADR 0005](../adr/0005-module-release-scope.md) so a consumer can check,
publish, build, and deliver one selected Go module in a shared repository through
ReleaseKit's API and CLI. CommandKit's core and optional integrations provide the initial
consumer case. Each invocation should require only module selection, product
settings, and a semantic version, with ReleaseKit owning the common mechanics.
This roadmap records the feature's target and resolved scope and ownership
decisions. The [implementation plan](implementation.md) records the completed
stages; [remediation](remediation.md) records review corrections. Current behavior
belongs in the [consumer guides](../consumers/), [CLI](../cli.md),
[configuration](../config.md), and facade GoDoc.
## Historical starting baseline
Work started from ReleaseKit v0.3.0 plus accepted ADR 0005. At that starting
point, module selection was unimplemented and the following baseline applied.
- `ReleaseRequest` selects a repository, product, and version. `Candidate` and
`PublicationResult` expose the version without a distinct module or tag name.
- Candidate capture reads `docs/releases/<version>.md` from the captured commit.
Local and remote uniqueness checks, tag creation, push, and reconciliation use
the version as the Git tag.
- The shared validation path runs tests, selected vet, and package compilation
from the repository directory. Optional content checks inspect the committed
repository tree, including nested-module contents.
- Profiles support shared validation and additional commands. CLI product
checks run from the repository directory with candidate/version context.
- Artifact builds and delivery also use repository/version assumptions. The
consumer Woodpecker template selects `refs/tags/v*` and bootstraps ReleaseKit
from repository-root module files.
Candidate capture, declarative validation, exact-object publication, and outcome
reporting provided the foundation for carrying scope and identity through all
operations.
## Targeted end state
An invocation selects one module and one semantic version. Omitted selection
means the repository-root module. For example:
| Selected directory | Semantic version | Complete tag | Committed release note |
| --- | --- | --- | --- |
| Root | `v0.1.0` | `v0.1.0` | `docs/releases/v0.1.0.md` |
| `integrations/ckconfig` | `v0.2.0` | `integrations/ckconfig/v0.2.0` | `integrations/ckconfig/docs/releases/v0.2.0.md` |
| `integrations/ckprompt` | `v0.1.0` | `integrations/ckprompt/v0.1.0` | `integrations/ckprompt/docs/releases/v0.1.0.md` |
All mandatory and selected optional release checks use the selected module's
scope. Independently released nested modules are excluded from their parent's
automatic validation inputs. Shared file selections follow the
[resolved shared-file rules](#shared-file-validation-inputs). Checks may read
referenced material elsewhere in the captured repository without adding that
material's surrounding files to the input set. The checkout remains the boundary for outside-checkout link checks.
Repository cleanliness, branch, candidate commit, remote endpoint, and publication
guards remain repository-wide. The complete selected tag governs uniqueness,
publication, and recovery. A successful source-tag push remains completion.
Consumers prepare dependencies before offline candidate validation and choose
sibling release ordering. Dependency preparation for delivery targets the
selected module through the existing preparation operation when needed.
The feature covers individual releases. Batch publication, automatic release
selection, dependency-driven versioning, and arbitrary tag templates remain
outside this roadmap. Artifact builds, hosted delivery, and CI use the same
selected module and release identity. Delivery remains asynchronous and supports
both page-only and binary profiles. This work adds no consumer-installation,
proxy-availability, CI, or hosted-delivery publication completion gate.
## Capability requirements
### Module and release identity
Repository directory, physical module directory, declared module path, semantic
version, and complete Git tag are distinct and available throughout operations
and public evidence. Shared derivation follows standard Go conventions, including
major-version layouts with and without a physical major directory. A `/v2`
module at either `integrations/ckconfig` or `integrations/ckconfig/v2` uses
`integrations/ckconfig/v2.0.0` for `v2.0.0`. Failure evidence retains resolved
identity alongside existing commit, endpoint, phase, state, and tag-object fields
as available. Facade conversions preserve underlying causes.
### Coherent validation scope
Tests, vet, and package compilation execute in the selected module with offline
and workspace-disabled settings. Formatting, distribution checks, file size,
and Markdown share ordered committed module and selected shared-file inputs.
Product checks receive the selected module's context. File checks retain complete
repository lookup for targeted reference reads. Existing optional check rules,
error identities, cancellation, and causes apply within that scope.
### Notes, publication, and recovery
Each module's committed note identifies its product and semantic version at
its module-local note path. Local and remote uniqueness, annotated tag creation,
exact-object push, and bounded reconciliation address the complete tag.
Repository-wide guards and publication outcomes remain in place. Different
modules can use equal semantic versions with distinct notes and tags.
### Builds, receipts, and hosted delivery
Standalone builds, candidate build checks, managed delivery builds, and prebuilt
delivery use the selected module. Module-owned execution and output paths use
that module's directory. Binaries report semantic versions; hosted pages and
source links use full tags and module-local notes. Builds and receipts carry
module and source evidence. Delivery validates that evidence and actual artifact
content before hosted requests, retaining page/asset conflicts, additive retries,
and partial outcomes. Page-only profiles require no application binary or
Go environment discovery.
### CLI and module-selected CI
Profiles express the API's module and shared-file selections through existing
profile lookup. Product commands receive both module and repository context.
A small shared launcher selects profiles and semantic versions. Mapped CI
selects the physical module and profile from the verified tagged checkout before
bootstrapping its selected tool. Local launchers and CI share one consumer
selection declaration. ReleaseKit verifies selection against derived identity.
Root-only consumers retain default bootstrap and profile behavior without a map.
Retries use the original tagged source's selection and tool configuration.
### Complete adoption guidance and behavioral evidence
Canonical API, CLI, configuration, consumer, integration, and recovery documents
and maintained examples describe the complete interface. The next release's
migration guidance covers API and profile updates, receipt regeneration, CI
selection, and root-module scope changes, including nested fixture modules.
Focused owner tests and representative assembled workflows establish the target
behavior with local repositories, HTTP fixtures, and isolated synthetic dependency
caches. The [implementation plan](implementation.md#acceptance-coverage-audit)
owns the concrete acceptance coverage and execution steps.
## Resolved decisions
### Resolve identity once
Each ReleaseKit operation resolves repository directory, selected module
directory and declaration, semantic version, and complete tag once through
shared resolution logic. Pass that identity through candidate handling,
validation, callbacks, publication, builds, receipts, delivery, and evidence.
Subsystems consume the resolved module and tag. Repository-state, tagged-source,
and artifact verification continue against that identity through their existing
guards.
### Operation prerequisites and identity
Shared resolution supplies module and tag identity to each operation.
Candidate checking and publication retain clean source on the primary branch,
committed release notes, and their existing unused-tag checks. Delivery
retains clean tagged-source verification and supports historical releases and
detached checkouts. Ordinary builds retain their support for modified source
and operate independently of release-note and publication prerequisites.
Composite operations pass resolved identity to their internal collaborators.
Standalone operations use the same resolver at entry, with declaration source
selected by the operation as described below.
### Declaration source by operation
Candidate checks, publication, and tagged delivery resolve identity from
committed module declarations. Standalone builds read the selected module's
working-tree `go.mod`, so their identity corresponds to the declaration Go
uses for compilation. This supports local development of changed module paths
and new module directories before commit.
The operation selects the declaration source and uses shared identity derivation
logic. Composite candidate and delivery builds receive their already resolved
committed identity. Existing source-verification observations remain in place;
delivery requires a build verified against its committed tagged source.
### One validation input set and full repository lookup
Construct file inputs when enabled checks or explicit shared-file validation
need them. Compute module boundaries and the ordered, deduplicated input set
once using committed metadata. File checks consume that set. Keep the full
repository lookup available for targeted reads and reference resolution, with scoped reader
ownership and cancellation preserved.
Deduplicate inputs by path. Reuse parsed results by blob identity while retaining
each source path's findings and relative destinations. This preserves the
existing selective caches and avoids retaining all inspected raw content.
### Version and tag boundaries
Keep semantic version and complete tag distinct in internal requests and evidence.
Version injection, artifact filenames, smoke checks, and note titles use the
semantic version. Git publication, recovery, and hosted release identity use
the complete tag. URL construction belongs to each protocol owner, which encodes
the literal tag for its route. Verify slash-containing tags in API and source
links.
### Failure evidence
Populate available module, version, tag, and commit evidence as resolution
progresses. Later failures retain that evidence alongside the operation's
existing phase, endpoint, tag-object, and outcome fields as available. Shared
facade conversions preserve these fields and the underlying error causes.
### Go environment ownership
Lazily discover effective Go settings for the selected module and reuse them
within the operation's validation and build phases when environment inputs are
unchanged. Ownership and reuse are operation-local. Dependency preparation keeps
its separate network-capable environment. Page-only delivery retains its path
without Go-tool discovery or application dependency preparation.
### Build settings, receipts, and asset verification
Normalize build settings once and reuse that representation for execution,
receipt binding, and comparisons. The new receipt format binds module identity
alongside existing source and artifact evidence. Loading validates structure,
identity, and file declarations. Delivery owns the content-verification pass
before hosted requests, for both API and CLI callers.
Build-time hashing records the produced bytes; delivery-time verification
establishes that the selected files still match them. Candidate and managed
delivery output stays outside the whole repository. Ordinary output paths use
the selected module's path base.
### CI bootstrap and checkout ownership
For module-mapped workflows, use one small committed consumer selection
declaration shared by the launcher and CI. Bootstrap verifies the triggering
tag and commit, selects the physical module and profile, and builds the tool
from that module's configuration. ReleaseKit verifies selection against the
derived identity.
The workflow prepares the checkout before tool bootstrap. Subsequent delivery
reuses that checkout with fresh read-only source checks. Standalone CI delivery
performs preparation when needed. Share verification behavior across these
entry paths, keeping root defaults and tagged-source selection on retries.
### Simple single-module experience
Ordinary root-module consumers use the new interface's defaults: omitted module
selection means the repository root, the complete tag equals the semantic
version, notes use `docs/releases/<version>.md`, and validation and builds run
from the root module. Shared-file defaults apply with input deduplication.
Root-tag CI uses the ordinary profile and repository-root tool bootstrap.
Routine use requires no additional configuration fields, module mapping
declarations, or recurring consumer decisions. A representative root-module
profile, launcher, and CI workflow remain equally small after migration.
Consumers supply their existing product facts through the new interface.
One-time API updates and receipt regeneration belong in the next release's
migration guidance under the clean-cutover decision below.
### Module boundaries
Use committed nested `go.mod` locations as module boundaries for ordinary source
directories. This follows Go's module structure and avoids a second registry of
independently versioned products. Vendored material remains part of the selected
module's file-check inputs. Nested fixture modules are excluded from automatic
parent file checks and can be exercised through the parent's tests or product
checks. Document this distinction and verify representative cases.
A repository-level workspace file outside the selected nested module's scope
does not fail that module's workspace-file check merely because it exists.
Shared release materials and referenced destinations receive the targeted
inspection described above.
### Shared file-validation inputs
Provide an optional shared-file list in the public request and profile. Its
selection applies only to shared file-validation inputs. Existing validation
selections control which checks are enabled. Enabled checks retain their rules,
and Go tests, vet, and package compilation retain the selected module's scope.
| Selection | Shared files added automatically |
| --- | --- |
| Omitted list | Repository-root `COPYRIGHT`, `LICENSE`, and `docs/oss/attribution.md`, when present. |
| Explicit list | The listed files replace the defaults. |
| Explicit empty list | No shared files. |
In Go, a nil slice selects the defaults; a non-nil empty slice selects none.
In JSON, omission or `null` selects the defaults; `[]` selects none. Preserve
this distinction through profile mapping and facade conversions, including
slice copying and profile serialization. Resolve the defaults once. Exact field
names belong in API design.
Paths are repository-relative literal file paths. This initial selection
supports individual files. Resolve presence and file identity from the captured
committed tree. Missing explicitly selected files produce a configuration or
validation error identifying the selected path, including when optional file
checks are disabled. Defaults are optional when absent.
Deduplicate the combined module and shared-file inputs by committed path, so a
file already in module scope or repeated in the list is checked once. Explicit
selection may name a file in another module; include that file alone. Reference
resolution continues to inspect destinations using existing rules and leaves
those destinations outside the input set unless independently selected.
### Relative paths and product commands
Anchor module selection and profile lookup at the repository. Anchor
module-owned validation and build paths, including file-size exclusions, and
configured product-command execution at the selected module. Keep diagnostic
file paths repository-relative and expose both directories to product commands.
Markdown destinations remain relative to their source documents.
Normalize selections and resolve path bases at a shared boundary. Translate
file selections and exclusions to canonical repository-relative paths before
checks consume them. Use module-based filesystem paths for execution and output,
and retain repository-relative diagnostic paths through conversions.
Root profiles use the repository-root module by default. Nested-module launchers
use the provided repository context for shared scripts. These conventions keep
per-module profiles self-contained without adding per-check path-base switches.
### Complete operation support
Implement module selection across checking, publication, artifact builds,
hosted delivery, and CI in this feature. The target is a coherent release API
for root and nested modules. CommandKit's current source-only use supplies an
adoption case while the feature supports the full existing release workflow.
Feature completion includes all of these operations. Omitted module selection
uses the root module, and source publication and asynchronous delivery retain
their separate outcomes.
### Clean API and receipt cutover
Implement one current API and module-bound receipt format as the complete
supported interface after cutover. Consumers update their calls, profiles, and
CI declarations to that interface and regenerate prior receipts through the
updated build operation. Compatibility shims and legacy receipt readers are
outside the implementation scope.
Migration means guidance supplied in the next release's documentation. That
guidance explains the required consumer changes and the root-module validation
scope change; implementation completion covers the new interface directly.

View File

@@ -1,587 +0,0 @@
# Remediation: module release implementation review
Status: complete; all fifteen findings addressed and verified.
The initial review examined `90559fe`, compared with planning baseline `dbfae9e`.
The follow-up repository audit examined `6b0b8ce`, including those corrections.
## Scope and evidence
The [feature roadmap](module-releases.md) and
[implementation plan](implementation.md) define the intended module-release
architecture. Most of that architecture is implemented: the two public facades,
shared module/tag derivation, scoped committed inputs with full reference lookup,
module-based Go execution, full-tag publication and hosted delivery, current-only
receipts, and tagged-source workflow selection.
The ordinary offline `go test ./...` suite and `go run ./internal/apicheck`
passed with Go 1.26.8. The findings below also use source inspection and temporary
fixtures outside the checkout. Publication reproduction used a private local
bare repository. No ReleaseKit source or real release refs were changed.
Each issue describes required work and verification. Preserve the selected
scope, existing error causes, publication/delivery outcomes, facade boundaries,
and clean API cutover. These corrections add no release, live-service, or human
review gates. Each completion record describes the correction and verification.
## Issue 1: Resolve the actual Git repository root before release identity (P1)
Status: complete. Category: architecture gap and incorrect publication.
**Completion:** Source operations and CLI profile selection resolve the containing
Git checkout root before module selection. Regression tests verify that a child
directory cannot bypass a failing root suite or publish its tag, explicit nested
selection binds its own source and tag, aliases and independent worktrees retain
their identity, and dependency preparation works without Git. The focused public
API tests and candidate, delivery, CLI, and profile suites pass offline.
**Evidence:** [candidate capture](../../internal/candidate/check.go) treats
`filepath.Abs(request.RepositoryDir)` as the repository root, and
[selection resolution](../../internal/releaseidentity/identity.go) uses that
directory as its base. Git committed-file paths are relative to the actual Git
root. Go commands instead execute in the supplied directory.
A temporary repository had a failing root suite, a passing module at
`apps/widget`, and a root release note. `Check` from the actual root failed.
`Check` with `RepositoryDir` set to `apps/widget` and omitted module selection
succeeded, reporting the root module's declared path and unprefixed `v0.1.0`
tag while executing the nested suite. `Publish` with that request successfully
pushed the root tag to the private fixture remote. The declared release unit
and validated release unit therefore differ.
**Required change:** Resolve the containing Git checkout's actual root at source
operation boundaries, then anchor repository-relative module and note paths to
that root. Carry that resolved repository identity through checks, publication,
builds, delivery, and CLI profile lookup. Normalize an input directory within the
checkout rather than introducing a blanket prohibition on such inputs. Keep
dependency preparation's own prerequisites separate; this correction should not
require a Git checkout merely to prepare a Go module's dependencies.
**Verification:** Repeat the failing-root/passing-nested fixture through both
`Check` and `Publish`, including default CLI invocation from a nested directory.
Omitted selection must validate the actual root module and prevent publication
when that suite fails. Explicit nested selection must bind its declaration,
note, suite, and prefixed tag consistently. Retain symlinked checkout and
independent worktree behavior.
## Issue 2: Preserve standalone Build error classification and partial evidence (P2)
Status: complete. Category: regression and incomplete failure evidence.
**Completion:** Standalone builds classify invalid versions before source access
and retain resolved selection and normalized build settings on identity failures.
Working-tree derivation preserves progressive identity evidence. Native CLI
validation reports invalid-version status 2 before repository access. Focused
API, native CLI, parser, identity, cancellation, and partial-artifact tests pass
offline.
**Evidence:** [public Build](../../build.go) now calls working-tree identity
resolution before the internal builder. Invalid stable versions are returned by
`Derive` as ordinary formatted errors, bypassing the existing
`ErrInvalidVersion` identity. An API request with `bad-version` produced
`errors.Is(err, releasekit.ErrInvalidVersion) == false`; the compiled CLI exited
1 rather than its documented invalid-version status 2.
The same entry point returns only `Version` when identity resolution fails.
Selecting an existing nested directory with an invalid `go.mod` returned empty
repository and module fields, although both directories had already been
resolved. This contradicts the roadmap's progressive failure-evidence decision
and loses supplied build facts that the previous build boundary retained.
**Required change:** Preserve the existing invalid-version error at the build
entry boundary, including its classification before unrelated filesystem work.
Return available selection and build evidence when later declaration or identity
work fails. Share the appropriate conversion/resolution facts without resolving
the declaration again or inventing stronger source-verification guarantees.
**Verification:** Add API and native CLI cases for invalid versions, plus a
declaration failure after successful directory selection. Assert error identity,
exit classification, and available result fields. Keep cancellation causes and
later partial-artifact behavior intact.
## Issue 3: Use the module path base for relative prebuilt assets (P2)
Status: complete. Category: incomplete module path adoption.
**Completion:** Build output and CLI prebuilt assets share the selected module's
directory resolver. CLI and pending migration guidance describe the common base.
Local hosted regression tests verify root and nested relative build/delivery
pairs, absolute output, module binding, and rejection of changed artifact content
before hosted requests. Existing receipt/content checks and nested build cases
pass offline.
**Evidence:** [CLI prebuilt delivery](../../cmd/releasekit/main.go) joins a
relative `--assets` directory to `invocation.Repository`. Builds now resolve
relative `--output` from the selected module. A nested profile's successful
`build --output dist v0.1.0` wrote
`apps/widget/dist/.releasekit-build.json`; the corresponding
`deliver --assets dist v0.1.0` attempted to open the repository-root receipt and
failed. The documented build/deliver pair therefore uses incompatible bases.
**Required change:** Resolve relative prebuilt asset directories from the same
selected module base as build output, using shared path normalization. Preserve
absolute directories and repository-relative profile lookup. Document the base
in the CLI owner and update affected examples and migration guidance.
**Verification:** Exercise a nested profile's relative build/output and delivery
pair using a local hosted fixture. Include the omitted root selection and an
absolute caller-owned asset directory. Verify module binding and content checks
still happen before hosted requests.
## Issue 4: Compare receipt repository identity consistently through aliases (P2)
Status: complete. Category: regression from mixed path normalization.
**Completion:** Receipt source and location comparisons use filesystem directory
identity, accepting aliases of the same checkout or output directory. Regression
tests build and deliver through a checkout symlink, retry through its physical
path, and reject different repositories and modules. Receipt structure, exact
release binding, and delivery-owned content checks remain intact. Receipt and
assembled prebuilt-delivery cases pass offline.
**Evidence:** Release identity canonicalizes repository symlinks, so generated
receipts record the physical repository directory.
[`BuildReceipt.MatchesBuild`](../../cmd/releasekit/internal/cli/receipt.go) only
applies `filepath.Abs`/`Clean` to the supplied repository. A fixture built normally
and delivered with `--repo` pointing to a symlink of that same checkout, with
an absolute asset directory, failed the preliminary repository/settings comparison.
The delivery facade's shared resolver would identify the same repository.
**Required change:** Use consistent resolved repository identity for receipt
comparison, or leave source identity comparisons with the delivery owner that
already resolves and verifies them. Preserve structural receipt checks and exact
module/version/tag/commit binding. Avoid adding another declaration resolution
or a requirement that callers use one textual spelling of a checkout path.
**Verification:** Build and deliver through the same symlinked checkout argument,
and through its equivalent physical path. Both must identify the same source;
a genuinely different repository or module must still fail before hosted work.
## Issue 5: Keep Go environment reuse lazy and proportionate (P2)
Status: complete. Category: additional validation gates and unrequested restrictions.
**Completion:** Go discovery happens on acquisition for actual Go work. Completed
callbacks trigger no additional discovery; settings-file snapshot failures disable
reuse while preserving the Go tool's accepted configurations. Regression tests
cover check/publication completion after Go leaves PATH, subsequent builds needing
rediscovery, the final callback, unreadable GOENV snapshots, normal environment
and persistent-file changes, and cancellation. Focused public cases and complete
candidate and Go-tool suites pass offline.
**Evidence:** [candidate coordination](../../internal/candidate/check.go) and
[publication](../../internal/publish/publish.go) eagerly call
[`Owner.Refresh`](../../internal/gotool/owner.go) after callbacks. A candidate
with successful standard validation and a successful additional callback that
left Git available but removed Go from `PATH` failed during rediscovery, although
no subsequent Go command or build was selected. `Acquire` already detects changed
inputs before actual Go work.
The owner also requires its settings-file snapshot to be readable. With `GOENV`
set to a directory, existing `gotool.New` and the Go discovery commands succeeded,
while `NewOwner(...).Acquire` failed on the added `os.ReadFile`. Go's acceptance
of its configuration has become narrower through a cache bookkeeping step.
**Required change:** Rediscover changed settings when a subsequent Go command
needs them. Preserve successful callback/operation completion when no more Go
work remains. Make snapshot limitations disable safe reuse or otherwise follow
the Go tool's effective behavior; they should not independently reject a
configuration that discovery accepts. Retain operation-local ownership, normal
settings-change detection, offline compilation, and separate preparation settings.
**Verification:** Cover a completed validation operation whose callback changes
environment inputs with no later build, and one whose subsequent build actually
needs rediscovery. Compare owner discovery with the underlying Go-tool behavior
when persistent settings cannot be snapshotted. Keep existing settings-change
and cancellation tests.
## Issue 6: Avoid requiring downloadable module paths for ordinary main modules (P2)
Status: complete. Category: unrequested restriction.
**Completion:** Identity derivation uses Go import-path lexical validation and
retains path-major compatibility. A valid non-domain main module now completes
candidate checking, native standalone compilation, and hosted page-only delivery
against a local fixture. Qualified and local major-version derivation, missing
and malformed declarations, and incompatible majors pass their focused offline
regression tests.
**Evidence:** [`Derive`](../../internal/releaseidentity/identity.go) adds
`module.CheckPath`, including its requirement for a dot in the first path element.
A standard main module declared as `module example` passed `go test ./...` but
was rejected by standalone `Build` before any Go work. The
[Go module reference](https://go.dev/ref/mod#module-paths) permits main modules
that are not fetched as dependencies to use valid package paths.
The selected requirement is stable-version and path-major tag derivation.
Deriving a root or physical-directory tag does not require the declaration to
be a downloadable network module. No user decision imposed that extra
restriction on executable applications or local builds.
**Required change:** Validate the declaration and path-major facts needed by
identity derivation using Go's appropriate lexical rules for the supported
operations. Preserve standard major-version consistency. Remove the accidental
downloadability prerequisite rather than adding a consumer bypass setting.
**Verification:** Exercise a valid non-domain main module through standalone
build and representative check/page delivery, alongside ordinary qualified
module paths. Continue rejecting missing/invalid declarations and incompatible
major identities for the selected release convention.
## Issue 7: Resolve committed identity from committed material (P2)
Status: complete. Category: declaration-source divergence and added prerequisites.
**Completion:** Shared Git reference traversal now serves validation and committed
module identity. Committed selection follows directory and declaration links
using targeted object reads; working-tree builds retain filesystem selection.
CLI mapping and prebuilt path selection use committed directory facts. Regression
tests cover captured links, changed working declarations, cycles, checkout
boundaries, unavailable blobs, cancellation, and ordinary/prepared CI page-only
delivery from a sparse checkout without Go. The complete offline suite and
compiled public API/dependency checks pass.
**Evidence:**
- `FromCommittedSelection` passes raw `git show` bytes to the declaration parser.
A committed `go.mod` symlink therefore supplies its link text. In a local
fixture, Go validation and working-tree identity succeeded while committed
identity reported no valid module directive. Existing committed validation
already has reference/symlink resolution.
- `ResolveSelection` also requires the selected module directory to exist in
the worktree through `EvalSymlinks`. A clean, correctly tagged sparse checkout
retained a nested module's declaration and note in Git but omitted its physical
directory. Page-only delivery failed on `lstat` before inspecting committed
identity. Its source was present and it needed no application compilation.
**Required change:** Separate filesystem execution selection from committed
source selection where their prerequisites differ. Resolve committed declarations
and directory/reference identity from the captured tree, retaining existing
checkout boundaries, cycle handling, missing-object errors, and cancellation.
Go execution may naturally require materialized source when it runs; page-only
metadata delivery should not gain that compilation prerequisite. Keep
working-tree builds tied to the declaration Go actually reads.
**Verification:** Cover an in-checkout committed declaration symlink and a
page-only nested release from a clean sparse checkout with available committed
objects. Verify committed/working-tree differences deliberately and retain
reference-cycle, outside-checkout, unavailable-content, and source-tag guards.
## Issue 8: Preserve literal shared filenames (P2)
Status: complete. Category: unrequested path prohibition.
**Completion:** Shared selection preserves literal backslashes in committed Git
paths. A Unix regression selects and reads such a file once despite duplicates;
traversal, absolute paths, and directories remain invalid selections. Existing
literal-metacharacter, missing/default/empty-list, scope, and lazy-input tests
pass offline.
**Evidence:** [`normalizeSharedFile`](../../internal/validation/tree.go) rejects
every backslash. On Linux, a committed file named `docs/notice\copy.txt` is a
literal repository-relative file, but explicit selection failed as `shared-files`.
The roadmap selected literal individual paths and ordinary normalization without
globs; it did not exclude legal filename characters for committed inputs.
**Required change:** Preserve literal committed path characters under the chosen
repository-path convention. Keep absolute/outside-repository and directory
selection checks needed for the agreed scope. Introduce neither glob expansion
nor a portability restriction on otherwise valid committed filenames.
**Verification:** On a filesystem where backslash is a literal filename
character, select the committed file successfully. Retain traversal, missing
explicit files, literal metacharacters, deduplication, and empty/default-list
coverage.
## Issue 9: Separate synthetic bootstrap versions from maintained examples (P2)
Status: complete. Category: broken adoption example and incomplete cutover guidance.
**Completion:** Maintained module skeletons no longer pin synthetic releases.
Example setup explicitly selects compatible released tooling and documents source
verification in disposable checkouts. Workflow fixtures generate their synthetic
declarations. Current-source tool registration and all three example profiles,
including native version injection, pass offline; mapped launchers/workflows and
tagged-tool retries retain isolated fixtures. Adoption and migration guidance
cover coordinated tool/workflow upgrades and historical retries. The pending
release notes record concrete example pins as normal version-selection work.
**Evidence:** All three maintained
[multi-module declarations](../../examples/multi-module/) require ReleaseKit
`v0.2.197`. This is a synthetic test version served by
[`writeWorkflowToolProxy`](../../cmd/releasekit/multi_module_woodpecker_test.go),
whose executable forwards to the separately compiled current test binary.
A read-only query of the authoritative repository returned `v0.3.0` and no
`v0.2.197` tag. The copyable example cannot obtain its declared release tool
through ordinary module resolution.
Published pre-feature tooling also lacks the new profile/flag interface. The
maintained workflows always pass `--prepared`, and mapped launchers additionally
pass `--expected-module`. Migration guidance does not explain that adopting the
new workflow requires a compatible tool declaration, or that an older tag's
tool declaration requires its matching historical workflow for a retry.
**Required change:** Confine synthetic versions to test-owned module files and
proxies. Make the maintained example's setup explicitly select a ReleaseKit
version containing this feature, and update its concrete release dependency
during normal release preparation when that version is selected. Keep setup
usable for local source verification without fabricating a published release.
Document tool/workflow cutover and historical retry guidance in the pending
release notes and adoption owners. Maintain the clean API cutover; add no flag,
receipt, or source-identity compatibility shim.
**Verification:** Tests generate their own synthetic dependency declarations
instead of relying on fake versions committed in the copyable example. Verify
the setup's selected tool accepts the actual new interface, using current source
and isolated fixtures offline. Preserve tagged-source tool-version selection and
test-owned caches. This adds no live module-service completion gate.
## Issue 10: Remove the repeated boundary scan from file selection (P2)
Status: complete. Category: code quality and avoidable computational cost.
**Completion:** File selection indexes boundary directories and looks up each
entry's ancestors. Existing ordering, vendor/shared selection, full reference
lookup, and reader/cache behavior pass the complete offline validation suite.
A component benchmark retains 20,000 inputs with 0, 1,000, and 4,000 boundaries.
On this host, the 4,000-boundary case decreased from about 2.73 seconds to 38
milliseconds per selection; the new cases ranged from 29 to 38 milliseconds.
These are illustrative measurements without timing gates or input limits.
**Evidence:** [`selectInputEntries`](../../internal/validation/tree.go) loops
over all nested-module boundaries for every candidate entry. This costs
`O(files × boundaries)` even when the retained input set is unchanged.
Temporary committed-tree fixtures with 20,000 retained files took approximately
0.5 seconds with no boundaries, 1.0 second with 1,000 boundaries, and 2.8 seconds
with 4,000 boundaries. These timings include metadata reads and are illustrative
local measurements, not performance contracts.
**Required change:** Use direct ancestor-boundary lookup or another straightforward
path-based selection that avoids scanning every unrelated boundary for each file.
Preserve one ordered input set, vendor handling, path deduplication, explicit
shared files, full reference lookup, and selective content-reader ownership.
**Verification:** Retain the existing scope behavior tests and add a representative
component benchmark varying boundary count with the retained file count fixed.
Verify the algorithmic improvement without adding timing thresholds, input caps,
or new validation policies.
## Issue 11: Reconcile documentation with implemented scope and status (P3)
Status: complete. Category: incomplete documentation adoption and misleading contracts.
**Completion:** Facade GoDoc and canonical API, CLI, configuration, Gitea, and
internal guidance describe selected inputs, full-tag uniqueness, module-local
notes, shared-file scope, committed references, and lazy Go reuse consistently.
The feature roadmap identifies its historical baseline and completed stages,
with this review recording the corrections. Consumer-owned license layouts and
note links are distinguished from optional validation defaults. ReleaseKit's
root release policy and procedure retain their existing requirements. Reviewed
root/nested examples, verified local links/fragments across 37 Markdown documents,
checked example JSON/shell syntax and whitespace, and passed the final complete
offline suite and compiled API/dependency checks.
**Evidence:**
- [Configuration](../config.md), [CLI](../cli.md),
[check guidance](../consumers/check.md), and
[publication guidance](../consumers/publish.md) still describe formatting as
including nested-module source. `Check` GoDoc similarly says local-replacement
inspection includes nested modules. Automatic input selection now excludes
independent nested modules, retaining vendor and explicitly selected files.
- Check guidance retains the old short-version wording for note location and
tag uniqueness. The new identities use physical module-local notes and full
tags, allowing equal semantic versions across modules.
- [The feature roadmap](module-releases.md) still says the feature is planned
and module selection is not implemented, while the implementation plan claims
completion. Its baseline needs to be identified as the starting baseline.
- [Gitea guidance](../integrations/gitea.md) presents root shared-license and
footer locations as an unconditional layout. Describe the default convention
and consumer-owned note links accurately; shared-file validation selection
supplies neither a license-location guarantee nor automatic footer rewriting.
**Required change:** Refresh each affected canonical owner using the implemented
selected-input and full-tag semantics. Mark feature status accurately, including
these open remediation items, while retaining useful decision context and the
historical starting baseline. Preserve ReleaseKit's own root release policy.
Update migration/examples alongside the behavior corrections above.
**Verification:** Review GoDoc and canonical guidance against the current code;
check root/nested examples, note and tag wording, roadmap status, local links,
fragments, and Markdown whitespace. Keep exact contract definitions at their
canonical owners.
## Follow-up audit
The follow-up reviewed module identity, candidate validation and publication,
builds, receipts, delivery, Go/process ownership, committed reference handling,
facade conversions, CLI/profile mapping, maintained workflows, documentation,
and the eleven previous corrections. The complete offline suite and compiled
API/dependency checks pass with Go 1.26.8. The additional findings below were
reproduced with temporary local repositories, a filtered sparse clone, a Git
transport stub, and a local HTTP server. The artifact-read evidence also used
`strace`. This audit changes the remediation document only.
## Issue 12: Resolve module paths consistently across declaration sources (P2)
Status: complete. Category: inconsistent release-unit selection.
**Completion:** Filesystem selection follows symlinks before processing parent
components, matching committed traversal. Public build, candidate check, and
hosted page delivery now agree on the selected module and tag for `link/..`;
dependency preparation executes in that same module. The new regression cases
failed before the correction and pass afterward. Focused API/preparation tests
and the complete identity/Git suites pass offline, including aliases, separate
declaration sources, sparse selection, cycles, boundaries, and cancellation.
**Evidence:** [Filesystem selection](../../internal/releaseidentity/identity.go)
cleans the joined module path before following symlinks. Committed selection
uses [component traversal](../../internal/git/path.go), following a symlink
before processing a subsequent `..`. The same selection can consequently
identify different physical modules in an unchanged, clean checkout.
A fixture contained root and `nested` modules, a tracked directory
`nested/deep`, and `link -> nested/deep`. With `ModuleDir: "link/.."` and
`Version: "v0.1.0"`, public `Build` succeeded with the root module and tag
`v0.1.0`; public `Check` succeeded with the nested module and tag
`nested/v0.1.0`. Working-tree and committed declaration readers independently
confirmed the difference. Dependency preparation uses the filesystem selector,
while candidate and delivery operations use committed selection.
**Required change:** Give filesystem and committed selection the same component
and symlink semantics for identical paths in an unchanged checkout. Preserve
the agreed declaration source for each operation, support for working-tree
changes and committed sparse selection, checkout boundaries, and progressive
identity evidence. Module paths containing `..` need consistent resolution;
this correction does not require prohibiting those paths.
**Verification:** Exercise a tracked directory symlink followed by `..` through
standalone build, candidate check, dependency preparation, and representative
delivery. An unchanged checkout must identify the same physical module and tag
across operations. Retain deliberate committed/working-tree declaration
differences, ordinary aliases, cycles, and outside-checkout failures.
## Issue 13: Reuse local asset verification through the complete delivery path (P2)
Status: complete. Category: incomplete verification ownership and redundant I/O.
**Completion:** Assembled delivery reuses its preflight verification through the
internal upload path; standalone internal uploads retain local verification.
A Linux filesystem-event regression observes one verification read and one
upload read across public delivery and validates uploaded contents. It failed
with three reads before the correction. Delivery/Gitea suites pass offline,
including tampering, caller ownership, matching attachments, and partial retries.
The complete-delivery benchmark now supplies current module identity. Its 32 MiB
fresh-upload case measured about 352 ms before and 254 ms afterward on this host;
these illustrative measurements introduce no timing gate.
**Evidence:** [Delivery](../../internal/delivery/delivery.go) calls
`VerifyLocalAssets` before hosted requests. After release and attachment
inspection, [UploadMissing](../../internal/gitea/upload.go) calls
`verifyLocalAsset` again for every missing attachment. Both calls stream and
hash each local binary. The upload then reads it a third time to send its bytes.
Stages 8 and 9 of the [implementation plan](implementation.md) select one
delivery-owned content-verification pass.
A public prebuilt-delivery fixture completed against a local HTTP server with
one missing binary and its manifest. A system-call trace recorded three complete
reads of the binary: the delivery preflight, the uploader's repeated verification,
and the multipart upload. Receipt loading is already free of artifact hashing;
the remaining duplicate sits across the delivery/Gitea boundary. The current
asset-preflight benchmark ends before that second verification.
**Required change:** Reuse the completed local verification in the assembled
delivery path so each binary receives one delivery-time digest-and-size pass
before hosted requests, followed by its necessary upload stream. Preserve
validation for independently used internal client operations, build-time
hashing, deterministic manifests, actual remote-content comparison, additive
retry behavior, caller-owned files, and existing failure outcomes.
**Verification:** Observe file reads across a complete public binary delivery
with missing attachments, covering the uploader as well as preflight. Establish
one verification read plus the upload read. Keep tampered/missing-file rejection
before hosted requests and matching-asset/partial-retry cases. Measure the whole
local path without timing thresholds or additional source guarantees.
## Issue 14: Keep committed release-note inspection within the local object boundary (P2)
Status: complete. Category: incomplete earlier offline-inspection correction.
**Completion:** Committed-file reads now use the shared local-only Git object
runner. Filtered-clone regressions cover direct reads plus public check and
delivery with an unavailable note, preserving the note path, Git cause, and
captured identity while making no hosted request, transport attempt, or object
database change. Both regressions exposed transport before the correction.
The Git/candidate suites and focused source, page-delivery, and explicit tagged
checkout preparation cases pass offline; ordinary note bytes and cancellation
handling retain their existing command boundary.
**Evidence:** [ReadCommitFile](../../internal/git/source.go), used by candidate
capture and delivery for release notes, executes `git show` through the ordinary
Git runner. Its child does not receive the lazy-fetch and transport controls
used by `ReadBlob`, committed-path lookup, tree inspection, and the batch reader.
An unavailable note in a partial clone can therefore initiate remote transport
during source inspection. The earlier correction in `0205001` and its partial
clone tests cover the other object readers, leaving this entry point uncovered.
A clean filtered sparse clone had its root declaration and Go source locally
available, with the committed note omitted and its blob unavailable locally.
Public `Check` resolved the root identity, then invoked a fixture SSH transport
while reading `docs/releases/v0.1.0.md`. The stub recorded the attempt and exited;
the resulting error reported failure to fetch the note from the promisor remote.
The fixture contacted no external service.
**Required change:** Read committed note bytes through the same local-only object
boundary as the other source-inspection readers. Return an inspection error
when required objects are unavailable locally, preserving path context,
cancellation, underlying Git causes, and exact note bytes. Explicit checkout
and dependency preparation retain their network-capable responsibilities.
**Verification:** Cover direct committed-file reading and representative
candidate/delivery note inspection in filtered clones with unavailable note
objects. A controlled transport stub must remain unused, and inspection must
leave the object database unchanged. Existing ordinary note reads and explicit
network-capable preparation must continue to work.
## Issue 15: Limit blob availability requirements to selected validation inputs (P2)
Status: complete. Category: module-scope leakage through eager metadata enrichment.
**Completion:** Tree enumeration now acquires path/type/mode/object metadata
independently of blobs. The size check requests sizes only for selected,
non-excluded blobs; other checks read contents and references when needed.
Filtered sparse-clone regressions cover formatting, sizes, workspace metadata,
explicit shared selection, exclusions, required shared contents, and required
Markdown references. They failed before the correction and now preserve each
check's scope and error identity without transport or object-database changes.
Existing size, symlink, gitlink, vendor, boundary, reader/cache, and cancellation
tests pass. The complete offline suite, compiled API/dependency check,
documentation links, and whitespace checks pass for all four follow-up fixes.
**Evidence:** [ReadCandidateTreeForScope](../../internal/validation/tree.go)
calls [ReadTree](../../internal/git/source.go) before constructing module and
shared-file inputs. `ReadTree` asks `populateBlobSizes` for every repository blob
and fails on any unavailable object. Excluded modules and unreferenced files
therefore become availability prerequisites for a selected module's file checks,
even when the selected check needs no blob sizes.
In a clean filtered sparse clone, the root module's source, declaration, and
note were available; independently scoped `nested` module blobs were omitted.
Public root `Check` succeeded with ordinary validation. Enabling `GoFormat`
with an explicit empty shared-file list made the same check fail during size
enumeration on the unavailable blob for `nested/deep/placeholder`. That file
belongs to the excluded nested module and is neither a formatting input nor a
referenced destination.
**Required change:** Separate full repository path/type/object metadata from
blob enrichment. Construct the agreed input set before acquiring required blob
sizes or contents, and inspect referenced destinations when a check actually
needs them. Keep full lookup available without requiring every unrelated blob
locally. Preserve selected-input ordering, boundaries, vendor/shared selection,
size semantics, local-only reads, cancellation, and reader ownership.
**Verification:** Use a filtered sparse fixture with a complete selected module
and unavailable blobs in an excluded sibling or nested module. Representative
formatting, file-size, and explicit shared-file validation must work when their
required inputs are available. A selected missing blob or required referenced
destination must still produce the appropriate inspection error. Verify that
excluded unreferenced blobs cause neither content reads nor transport.

View File

@@ -27,10 +27,10 @@ done
```
Review and commit the generated tool, dependency, and checksum declarations.
Release preparation records the chosen compatible version in these maintained
module files. The feature roadmap leaves that release version to release
preparation. The [adoption guide](../../docs/consumers/adoption.md) owns tool
registration and coordinated tool/workflow upgrades.
The [adoption guide](../../docs/consumers/adoption.md) owns tool registration and
coordinated tool/workflow upgrades. The
[v0.4.0 release note](../../docs/releases/v0.4.0.md) describes module-release
support and migration from earlier versions.
## Module mapping and publication