From f89bdf7edf3805fe9e298c1d2c9132c12aefd1c0 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Fri, 9 Oct 2026 16:19:05 +0000 Subject: [PATCH] Plan module-scoped release support --- docs/roadmap/module-releases.md | 241 ++++++++++++++++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 docs/roadmap/module-releases.md diff --git a/docs/roadmap/module-releases.md b/docs/roadmap/module-releases.md new file mode 100644 index 0000000..05ebb51 --- /dev/null +++ b/docs/roadmap/module-releases.md @@ -0,0 +1,241 @@ +# Roadmap: release individual Go modules + +Status: planned feature; open recommendations await resolution. + +## Purpose + +Implement [ADR 0005](../adr/0005-module-release-scope.md) so a consumer can check +and publish 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 describes the target and the work from the current baseline. It +does not select a ReleaseKit release version or prescribe a staged implementation +plan. Recommendations under Open Decisions are proposals, not accepted contracts. + +## Current baseline + +The baseline is ReleaseKit v0.3.0 plus accepted ADR 0005; module selection is +not implemented. + +- `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/.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. + +The existing candidate capture, declarative validation, exact-object publication, +and outcome reporting provide the foundation. Scope and identity need to be +carried through those paths together. + +## 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 +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. +Dependency preparation and sibling release ordering remain consumer activities. + +The feature covers individual releases. Batch publication, automatic release +selection, dependency-driven versioning, and arbitrary tag templates remain +outside this roadmap. Artifact and hosted-delivery integration is an open scope +decision below. This work adds no consumer-installation, proxy-availability, CI, +or hosted-delivery completion gate. + +## Work needed + +### 1. Establish a shared module and release identity + +**Rationale:** One resolved identity prevents checks and publication from using +different interpretations of the selected module or tag. + +**Target:** Distinct repository directory, module directory, semantic version, +and complete tag are available throughout candidate handling and public evidence. + +**Work:** Add an optional repository-relative module selection to the public +request and profile, with root defaults. Resolve it against the captured +repository and committed module declaration. Derive tags using Go's standard +module conventions, including major-version directories: a `/v2` module stored +at `integrations/ckconfig/v2` uses `integrations/ckconfig/v2.0.0` for `v2.0.0`. +Carry the resolved identity through facade conversions, internal requests, +callbacks, results, and CLI diagnostics. Keep semantic-version validation +separate from full-tag handling. Exact field names belong in API design. + +### 2. Apply one validation scope + +**Rationale:** Consumers should select the release unit once and receive +consistent validation across enabled checks. + +**Target:** Tests, vet, package compilation, formatting, distribution checks, +file-size checks, Markdown, and product-check context all correspond to the +selected module and its relevant release materials. + +**Work:** Give the existing shared validation path both repository context and +module scope. Run Go commands in the module directory with the existing offline +and workspace-disabled environment. Select committed file inputs once under the +agreed boundary convention and apply that selection to each content check. +Retain repository access for resolving Markdown destinations and existing +committed-file references. A link to another module may be checked for existence +or its target heading without scanning that module's unrelated documents. + +Update module-distribution inspection to examine the selected release's inputs. +For example, an unrelated sibling's filesystem replacement should not fail the +selected module's check. Determine relevant shared release materials through +existing conventions; shared files should not pull in an entire parent tree. +Keep the optional checks optional and retain their existing individual rules, +error identities, cancellation, and cause propagation apart from the intended +scope change. Resolve boundary identification and path conventions below before +finalizing these contracts. + +### 3. Bind notes and publication to the resolved identity + +**Rationale:** Different modules can use the same semantic version while +publishing distinct source identities and release notes. + +**Target:** The selected module's committed note identifies its product and +semantic version. Publication and recovery address its complete tag. + +**Work:** Resolve module-local note paths and retain existing title validation +and captured note bytes. Thread the complete tag through local guards, remote +guards, annotated-tag creation, exact-object push, and bounded reconciliation. +Include the module, version, and resolved tag in success and failure evidence +as available, alongside commit, endpoint, phase, state, and tag object. Preserve +the existing publication outcomes and repository checks. Root notes and tags +retain their ordinary forms. + +### 4. Make API and CLI adoption small and coherent + +**Rationale:** The shared release operation should replace consumer publication +logic with small per-module declarations. + +**Target:** A shared launcher can select a profile and pass a semantic version; +API consumers express the equivalent operation. Additional checks receive +module context and repository context for explicit composition work. + +**Work:** Map profile selection into the shared request, document relative-path +behavior, and provide module/tag context to configured commands. Retain existing +profile selection through `--config`; a separate CLI override for the module is +not needed to satisfy the initial request. Add a maintained multi-module example +showing root and integration profiles and a small shared launcher. Show dependency +preparation before offline validation and sequential sibling releases when a +dependency version must be published first. Keep product checks focused on +consumer assertions instead of repeating the standard suite. + +### 5. Verify and document the complete behavior + +**Rationale:** This changes release identity and validation boundaries, including +the behavior of root modules with nested modules. + +**Target:** Representative public API and CLI workflows establish that the +selected module is validated and only its intended tag is published. + +**Work:** Extend existing behavioral tests using local repositories and remotes. +Cover root defaults; nested-module failure while the root suite passes; equal +versions across modules; major-version directories; full-tag reuse; and failed +or unknown publication with the correct recovery identity. Exercise optional +checks with violations inside and outside the selected scope, parent selection +excluding independent nested modules, and cross-module Markdown references. +Verify that repository guards still observe changes outside the selected module +and that module selection does not repeat the standard suite. Reuse existing +failure tests where they already protect unchanged behavior. + +Update GoDoc, CLI/configuration references, check/publication guides, adoption +examples, and recovery guidance together. Explain root-module scope changes in +the release note and migration guidance. Document semantic version versus full +tag in note titles and source/licensing links. ReleaseKit's own maintainer policy +continues to describe its root-module release; consumer guidance explains how +multi-module consumers adapt their policies. Run the normal offline suite and +public API checks for implementation changes; validate documentation links and +examples. Synthetic dependency fixtures use isolated module caches. + +## Open Decisions + +### 1. How should nested module boundaries be recognized? + +**Recommendation:** 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. Specify treatment of +vendored content and fixture modules before implementation: vendored material +remains part of the selected module's file-check inputs, while nested fixture +modules can be exercised through the parent's tests or product checks. A +repository-level workspace file outside a selected nested module's scope would +not fail that module's workspace-file check merely because it exists. + +The tradeoff is that a nested fixture module can be excluded from generic file +checks even when it has no independent release. That distinction needs clear +documentation and representative tests. + +**Alternative:** Declare independently released module directories explicitly +and exclude only those boundaries. This distinguishes products from fixtures +precisely and keeps fixture files in their parent's generic validation. It adds +a declaration consumers must maintain and coordinate with their module layout. +It is viable if real consumers need that distinction enough to justify the +configuration. + +### 2. Which directory anchors relative paths and product commands? + +**Recommendation:** Anchor module selection and profile lookup at the repository; +anchor module-owned validation settings, such as 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. This makes a +per-module profile self-contained while retaining unambiguous findings. + +Existing root profiles retain their meaning. Nested-module launchers would use +the provided repository context when invoking shared scripts. Document these +rules together rather than adding a path-base switch to each check. + +**Alternative:** Keep configuration paths and product-command execution +repository-relative, while shared check inputs remain module-scoped. This +preserves the current conventions and makes shared script invocation direct. +It requires module prefixes in otherwise module-specific settings and makes +moving or copying profiles more cumbersome. Both choices can implement the +accepted validation scope. + +### 3. Should nested-module builds and hosted delivery ship with publication? + +**Recommendation:** Deliver the requested source-only `Check` and `Publish` +capability first. Keep existing root build/delivery workflows working and document +the supported boundary. For that initial increment, report an unsupported +nested-module artifact-build request before validation or publication rather +than building the root product under the nested module's release identity. +This proposed limitation requires an explicit scope decision; it is not imposed +by ADR 0005. Package compilation remains part of module validation and is covered +by this roadmap. + +This meets CommandKit's immediate need with a focused change, at the cost of +requiring follow-up work for consumers needing nested-module artifacts or hosted +pages. Review shared profile handling so unsupported delivery paths do not +silently interpret a nested module as the root product. + +**Alternative:** Extend artifact builds and hosted delivery in the same release. +This gives every release operation the same module model immediately. It adds +work on build paths, semantic-version injection, receipts, tag verification, +note/source URLs, dependency preparation, and CI profile/tool selection. The +maintained tag filter and root-only bootstrap also need adaptation. This is a +viable cohesive release if those consumers are ready; it expands the sprint +beyond the source-only publication request. Delivery remains asynchronous under +either choice.