Retire completed roadmaps and retain pending adoption work

This commit is contained in:
2026-10-08 12:41:11 +00:00
parent e84d28d249
commit 10964bebd4
8 changed files with 36 additions and 2026 deletions

View File

@@ -1,67 +0,0 @@
# Feature request from ConfigKit: adaptable release validation
Status: Requested by a prospective downstream consumer; not yet adopted by
ReleaseKit.
ConfigKit would like to adopt ReleaseKit's common source-release process and
retire most of its repository-owned release scripts and publication tests. It is
a source-only Go library. ConfigKit can follow ReleaseKit's existing convention
of publishing the candidate to `main` before running the tag-publication
command; it does not need binary artifacts or a different source-publication
sequence.
## Select one base Go test invocation
ReleaseKit's [candidate check](../consumers/check.md) currently runs its
standard `go test` suite before any configured additional checks. ConfigKit's
selected release validation includes a race-enabled run of its full test
suite, with a separate full vet pass. Expressing the race run as an additional
check would run the suite twice and retain avoidable consumer-specific release
wiring.
Please provide a small public API and profile capability for a consumer to
select its **one base Go test invocation** for candidate validation. The
selection should replace the default invocation, rather than add a second test
run. Without a selection, ReleaseKit's current default remains useful. Both
`Check` and `Publish`, including their CLI forms, should use the selected
invocation consistently while ReleaseKit continues to own candidate capture,
validation order, result reporting, and publication. ConfigKit needs to select
a full-suite race run with test-time vet omitted because it selects a separate
full vet pass. ReleaseKit can choose the simplest public expression of that
choice; ConfigKit is not asking for a general-purpose release pipeline or
consumer-owned orchestration.
## Evaluate shared release checks
ConfigKit also uses a separate full `go vet` pass and compile checks for Linux
amd64 and arm64. Please assess whether these checks are useful enough across
ReleaseKit consumers to offer directly through its public API and profile,
either as defaults or as selected options where product support differs.
In particular, a source-only library needs to verify that its packages compile
for selected targets without declaring executable artifacts or adding delivery
work. ReleaseKit could make that distinction available to consumers. The goal
is to keep these checks, when selected, within the same candidate validation
and publication flow without repeating work already done by the base test.
Whether race, vet, or cross-target compilation should be common defaults is a
ReleaseKit product decision; ConfigKit asks for an assessment, not a universal
new release gate.
## Optional publication endpoint expectation
ConfigKit currently identifies one expected authoritative Git destination. An
optional expected-endpoint setting in ReleaseKit's public API and profile could
let a consumer verify that the selected remote resolves to its declared
destination before publication. Please consider whether this is broadly useful
and simpler than retaining a small consumer-owned preflight check. Existing
ReleaseKit remote selection and endpoint handling should remain the common
publication model.
## Adoption outcome
A useful outcome would let ConfigKit declare its product identity, selected
validation, and any expected publication destination, then rely on ReleaseKit's
standard candidate check and tag-publication command. ConfigKit would retain
its compatibility decisions and release notes while removing duplicated release
mechanics. Source publication would still complete at the confirmed tag push;
CI and hosted delivery would remain separate from that completion point.

View File

@@ -1,182 +0,0 @@
# Feature Request From PromptKit: Shared Release Validation
Status: Requested by a prospective downstream consumer; not yet adopted by
ReleaseKit.
Date: 2026-10-07.
This request derives from PromptKit's
[ReleaseKit adoption assessment](https://gitea.maximumdirect.net/eric/promptkit/src/commit/7310ef34a44dae7288db7b281e2a6b7f2c56c4e9/docs/roadmap/releasekit.md),
which reviewed ReleaseKit `v0.1.0`. It describes downstream needs and leaves
public API design, shared defaults, and implementation choices to ReleaseKit.
## Consumer Context And Goal
PromptKit is a source-only Go library with concurrent LLM execution and
backend-capacity management. It would like to adopt ReleaseKit's common
source-publication process and reduce its own release infrastructure to
product settings and a few substantive project checks. Its existing release
scripts contain 209 lines, most of which implement validation rather than
publication.
PromptKit accepts changes to its release process in exchange for a common
standard and lower maintenance. The opportunity is to share useful validation
capabilities, rather than preserve every existing shell command or local rule.
Its consumer-example assertions and compatibility decisions remain meaningful
project responsibilities.
The four primary requests below concern reusable validation. Two additional
candidates are included for evaluation if their wider value justifies shared
support. PromptKit's choices about race testing, full vet, and credential
scanning remain under review; these requests describe useful capabilities
without making their acceptance a prerequisite for adoption.
## Primary Requests
### 1. Shared Go Formatting Validation
Please make Go formatting validation available through ReleaseKit's public
validation API and release profile, so the shared candidate workflow can
report tracked Go source that needs formatting.
PromptKit currently owns a shell check that identifies its tracked Go files
and verifies their formatting. The operation is common to Go repositories;
maintaining that mechanism independently adds little project-specific value.
A useful shared capability would inspect source, report the affected files,
and leave formatting changes to ordinary development.
The benefit is consistent validation and diagnostics with less local script
maintenance. PromptKit can follow ReleaseKit's selected formatting convention
without requiring preservation of its current enumeration or command loop.
### 2. Shared Offline Markdown-Link Validation
Please make validation of local Markdown links available through the public
validation API and release profile. PromptKit needs checks for local file
destinations and heading fragments, with enough diagnostic context to locate
and correct a broken link.
Documentation is part of PromptKit's consumer interface. Broken navigation
between its guides, formats, policies, and examples makes otherwise correct
documentation harder to use. Its current local Markdown checker is the
largest generic block in its release-validation script.
Shared support would remove a locally maintained parser and standardize the
supported Markdown behavior across consumers. ReleaseKit can establish the
supported link and heading conventions; PromptKit does not require its exact
current parsing or anchor rules. Repository-specific choices about links to
material outside the checkout should remain distinguishable from generally
useful local-link validation.
The desired operation is offline and reports problems without changing
documentation. External website availability is a separate concern and adds
no value to this request.
### 3. Race Detection Within One Standard Test Run
Please provide a supported public API and profile choice for race-enabled
execution of the standard full Go suite during candidate validation.
PromptKit's concurrent execution and capacity management make race detection
useful: ordinary successful tests can miss unsafe shared-state access. Its
current release validation runs the full suite with the race detector.
ReleaseKit's current
[candidate workflow](../consumers/check.md) always runs the ordinary suite;
expressing a race run as an additional check would execute the complete suite
twice.
The desired capability selects race-enabled execution for that one suite
run, while ReleaseKit continues to own the candidate workflow. The selection
should work consistently through the public API and CLI/profile forms of
candidate checking and publication. A consumer can select the ordinary suite
when that is sufficient.
This overlaps with ConfigKit's
[base-test invocation request](feature-request-configkit.md#select-one-base-go-test-invocation).
PromptKit would benefit from the same shared capability; it does not need a
separate PromptKit-specific test mechanism. ReleaseKit can decide the smallest
appropriate public expression of the choice and whether race testing belongs
among its common defaults.
### 4. Shared Full Go Vet Validation
Please consider exposing full Go vet analysis as part of ReleaseKit's public
validation API and release profile, coordinated with the standard suite.
PromptKit currently runs full vet separately. Ordinary Go test execution
includes only a subset of vet analyses, so replacing the current procedure
with the standard test run does not provide equivalent static analysis. The
additional analyses can detect defects that are not exercised by tests.
PromptKit can temporarily retain a configured full-vet command. A shared
capability would let consumers express that validation choice directly and
let ReleaseKit coordinate the analysis with testing to avoid unnecessary
overlap. The desired outcome is full selected analysis and one complete
suite execution; PromptKit leaves the means of achieving that outcome to
ReleaseKit.
This also overlaps with ConfigKit's
[shared-check request](feature-request-configkit.md#evaluate-shared-release-checks).
Full vet may be useful as a common default or as an explicit selection.
PromptKit is asking for a shared capability, not prescribing additional gates
for every ReleaseKit consumer.
## Additional Candidates For Evaluation
### 5. Optional Go Module Distribution Checks
PromptKit's architecture keeps committed workspace files and local filesystem
module replacements out of its released source. These rules help a shared
library remain independent of neighboring checkouts. Disabling workspace
discovery during validation controls the test environment but does not check
the files being distributed.
If other consumers have the same need, optional public validation capabilities
could check for committed workspace files and local filesystem replacements.
Consumers should be able to select the distribution rules relevant to them;
a versioned module replacement is different from a local filesystem
dependency, and Go applications may deliberately distribute workspace files.
The potential benefit is reusable inspection and consistent diagnostics
without each library owning its own checks. PromptKit can also keep a small
local check if that is simpler than introducing shared support. This is a
lower-priority candidate, not a request for universal bans on workspaces,
replacement directives, or vendoring.
### 6. Optional Recognizable-Credential Checks
PromptKit's assessment recommends retiring its mandatory credential-pattern
scan, while retaining ordinary credential-aware change review. A viable
alternative is to keep a lightweight automated signal for recognizable
accidentally committed credential formats.
If ReleaseKit finds broader consumer value in that alternative, please
consider an optional public validation capability for such checks. A useful
offering would make its detection scope clear and account for the possibility
of matching synthetic examples or fixtures. It would complement ordinary
review rather than imply comprehensive detection.
Shared support could centralize maintenance of the selected detection behavior
and remove another local script fragment. PromptKit is not requesting a
general security-scanning subsystem, preservation of its current regex, or
a mandatory credential gate. This candidate warrants adoption only if its
additional protection justifies its maintenance and false-positive costs.
## Desired Adoption Outcome
The strongest opportunities are shared formatting and Markdown validation,
followed by useful race/vet choices within a single candidate-validation flow.
The module-distribution and credential candidates merit separate evaluation
because their policies and benefits vary between consumers.
For accepted capabilities, availability through both the public Go API and
the maintained CLI/profile would let PromptKit select them without creating
its own release executable. PromptKit can retain its consumer-example
assertions as additional checks and use ReleaseKit's supported conventions
for shared validation and diagnostics.
PromptKit would continue to prepare its release note and publish its candidate
to authoritative `main` before invoking source publication. Confirmed tag
publication completes the source release; CI and hosted delivery remain
separate. Those responsibilities already fit ReleaseKit's common workflow
and do not require new features for this request.

View File

@@ -1,656 +0,0 @@
# ReleaseKit v0.2.0 implementation plan
Status: Complete. Stages 1–15 are implemented and locally verified.
This plan replaces the completed foundation sequence. It implements the target
state in [v0.2.0.md](v0.2.0.md), using the shared design in [model.md](model.md),
[ADR 0003](../adr/0003-declarative-validation.md), and
[ADR 0004](../adr/0004-module-selected-tooling.md). A Luna coding agent should
execute one numbered stage per prompt, in order.
## Current baseline and completion scope
At baseline commit `3974a46`, the foundation and all six
[remediation findings](remediation.md) were complete. `Check` and `Publish`
shared candidate-request conversion and validation.
`internal/validation` runs the default offline suite and owns diagnostics and
candidate-tree access. Committed-file readers are offline; destination resolution
handles symlinks, parent traversal, cancellation, and absolute in-checkout paths.
The root error types and CLI presentation preserve findings and cause context.
`buildinfo` retains its independent runtime dependency graph.
The stages in this plan complete the remaining v0.2.0 validation selections,
module-selected CI bootstrap, and Audita migration. Audita's migration is
recorded in commit `17421bc`. The final local audit covers the full Go suite,
compiled API/dependency boundaries, maintained examples, documentation, and
dependency notices. Architecture review, hosted deployment, release tagging,
and decisions about further testing follow this implementation.
| Release-roadmap item | Implementation stages |
| --- | --- |
| 1. Shared validation API/profile model | 1–3, 5–11, 15 |
| 2. Module-selected CI tool and Audita migration | 12–14 |
| 3. One suite with race selection | 1 |
| 4. Separate full vet | 2 |
| 5. Package compilation | 3 |
| 6. Expected publication endpoint | 4 |
| 7. Go formatting | 5 |
| 8. Markdown links and optional boundary | 9–11 |
| 9. Independent distribution rules | 6–7 |
| 10. Individual file size | 8 |
## Instructions for every stage
- Start with [development.md](../development.md) and applicable policies. Locate
current symbols with the codebase graph and inspect the worktree. The named
paths below describe the current baseline.
- Introduce each public and profile setting with its working capability, as
agreed. Update its GoDoc, configuration reference, consumer guidance, and
relevant CLI/internal documentation in that same stage.
- Use ordinary internal structs/functions and explicit facade conversions.
Keep product assertions in `AdditionalChecks` and profile `checks`.
- Select tests for the stage's actual risks under the
[testing policy](../policy/testing.md). Use local Git repositories, Go
packages, controlled command doubles, and HTTP fixtures. Default tests remain
offline and independent of real credentials or sibling checkouts.
- Verification lists describe required evidence, including existing tests.
Give each behavior a test owner: parser/checker tests cover syntax and rules;
candidate-tree and Git tests cover file access and resolution; conversion
tests cover each API/profile selection; shared orchestration tests cover
ordering, failure stopping, cancellation, and callback execution. Use
representative integration cases to verify these boundaries work together.
Add coverage where the change introduces a risk or leaves an evidence gap.
- Prepare newly selected development dependencies before offline validation.
Apply the [OSS policy](../policy/oss.md) when adding dependencies or adapting
third-party material, including any required attribution and license texts.
- Verify the focused outcome, stage its relevant changes, review the diff, and
create a nonempty commit with a short plain English message. Preserve unrelated
work, report the stage's outcome and verification, and stop until the next
stage is requested. These stages require local work and automated checks;
consumer changes in Stage 14 are committed in that repository.
## Design carried through the stages
### Public and profile declarations
The completed specification is facade-owned:
```go
type ValidationSpec struct {
Go GoValidationSpec
GoFormat bool
Markdown *MarkdownValidationSpec
ModuleDistribution ModuleDistributionValidationSpec
FileSize *FileSizeValidationSpec
}
type GoValidationSpec struct {
Race bool
FullVet bool
CompileTargets []Target
}
type MarkdownValidationSpec struct {
EnforceCheckoutBoundary bool
}
type ModuleDistributionValidationSpec struct {
WorkspaceFiles bool
LocalReplacements bool
}
type FileSizeValidationSpec struct {
LimitBytes *int64
Exclusions []string
}
```
Add `Validation ValidationSpec` and `ExpectedPushURL string` to `ReleaseRequest`.
`Target` is the existing root-facade operating-system/architecture pair. Each
stage adds only the fields it makes operational. The CLI owns corresponding
JSON structs and converts them in `cmd/releasekit`; internal production packages
use their own contracts.
| Go selection | Profile selection | Omitted/zero behavior |
| --- | --- | --- |
| `Go.Race` | `validation.go.race` | Ordinary native test suite |
| `Go.FullVet` | `validation.go.full_vet` | Existing test-time vet |
| `Go.CompileTargets` | `validation.go.compile_targets` | No package-compilation pass |
| `GoFormat` | `validation.go_format` | No formatting check |
| `Markdown` | `validation.markdown` | Nil/omitted/null disables the check; `{}` enables it |
| `Markdown.EnforceCheckoutBoundary` | `validation.markdown.enforce_checkout_boundary` | Outside-checkout links reported as out of scope |
| `ModuleDistribution.WorkspaceFiles` | `validation.module_distribution.workspace_files` | Rule disabled |
| `ModuleDistribution.LocalReplacements` | `validation.module_distribution.local_replacements` | Rule disabled |
| `FileSize` | `validation.file_size` | Nil/omitted/null disables the check; `{}` enables it |
| `FileSize.LimitBytes` | `validation.file_size.limit_bytes` | Nil/omitted/null selects 1,000,000 bytes when enabled |
| `FileSize.Exclusions` | `validation.file_size.exclusions` | No exclusions |
| `ExpectedPushURL` | `expected_push_url` | Empty/omitted disables the publication expectation |
Profile compilation targets use `GOOS/GOARCH` strings. Require a complete pair
because a partial target would otherwise silently select different compilation.
Use the existing Go target/environment mechanisms; let the selected Go toolchain
report unsupported targets. An artifact naming constraint does not determine
package-compilation compatibility.
Byte limits are integer counts; zero is an explicit zero-byte limit, and negative
counts are invalid. Exclusions are literal repository-relative file or directory
paths; directory selections include committed descendants. Match path boundaries
so excluding `assets` leaves `assets-other` selected. Ordinary relative path
normalization is sufficient. An unmatched exclusion has no effect. Metacharacters
remain literal Git path characters.
### Execution and ownership
The shared procedure is one native test suite, selected full vet, selected
package-compilation targets, and selected file checks, followed by product
checks and existing artifact builds/rechecks. File-check ordering is an internal
procedure: formatting, workspace files, local replacements, file size, then
Markdown. Stop after a failed check. A checker may return multiple findings;
this plan does not require collecting failures from every selected check.
Resolve the Go environment once per validation operation and reuse it. Native
race/vet work preserves the existing effective cgo/C-tool configuration. Static
package compilation uses `CGO_ENABLED=0`. The existing offline module/toolchain
controls remain owned by `internal/gotool`.
Pass settings through the root `candidateRequest`, `internal/candidate.Request`,
`internal/publish.Request`, and `validation.Request`. Both operations and their
CLI forms use the same decisions. Keep expected URL comparison in publication
preflight. Existing capture/rechecks, endpoint guards, recovery, publication
completion, and delivery outcomes retain their meaning.
Reuse the existing internal failures and facade conversion. Add stable identities
`go-vet`, `package-compile`, `go-format`, `workspace-files`, `local-replacements`,
`file-size`, and `markdown-links` as implemented. Supply paths, locations,
targets, directives, and measurements as applicable. Preserve causes and
cancellation through `errors.Is`/`errors.As`. CLI failures use existing exit
classifications and complete contextual presentation.
File checks share one candidate inventory per validation operation and load blobs
as needed. Inventory includes tracked vendor files and nested modules. Regular
source blobs feed formatting and Markdown parsing. Workspace-file detection
includes named symlink blobs; module parsing follows committed symlinks to
candidate-contained regular declarations. Unavailable selected input produces
an inspection error. File size measures regular and symlink blobs. Gitlinks have
no parent-tree blob size, and their contents remain outside the inspected tree.
### Dependencies and Markdown convention
Use standard `go/format` for Go formatting. Its convention follows the Go
standard library used to build ReleaseKit. Use `golang.org/x/mod/modfile` at
`golang.org/x/mod v0.40.0` for complete module-declaration parsing. Use
`github.com/yuin/goldmark/v2 v2.1.6` internally for Markdown AST parsing and source
positions. Both selected module manifests support the existing Go 1.25 baseline;
implementation should record their exact source revisions and notices under the
OSS policy. References: [modfile](https://pkg.go.dev/golang.org/x/mod/modfile)
and [Goldmark](https://github.com/yuin/goldmark/tree/v2.1.6).
PromptKit's current script supplies the starting heading/link examples. Preserve
its basic anchor transformation: lowercase heading text, retain Unicode letters
and numbers, underscore, hyphen, and spaces, then replace spaces with hyphens.
Use visible heading text from the Markdown AST, including inline code text and
excluding HTML tags/formatting delimiters. Support ATX and Setext headings. For
repeated headings, append increasing numeric suffixes starting at `-1`, selecting
an unused identifier when a generated suffix collides with another heading.
Document these refinements to the starting script's behavior.
Recognize inline links, images, and full/collapsed/shortcut reference links through
the parser. Code blocks and inline code supply examples rather than link nodes.
Raw HTML destinations remain outside this Markdown-link capability. Undefined
references rendered as ordinary text by the parser are not a new Markdown lint
rule. Decode Markdown escapes/entities through the parser and percent escapes
once when resolving URI path/fragment components. Compare decoded fragments in
lowercase to preserve PromptKit's starting convention. Ignore URI query
components for file existence. Empty paths target the source document; an empty
fragment selects its top. Schemed and protocol-relative URIs receive no availability
request. Fragment checking applies to regular Markdown destinations; other
existing files/directories receive existence checking.
Use committed destination resolution and cached parsed documents/anchors.
Missing destinations, unresolved cycles, and missing supported anchors fail the
selected Markdown check. Actual outside-checkout destinations are reported as
out of scope by default and fail with boundary enforcement. Submodule-content
observations remain reported as out of scope under either setting: the boundary
option concerns checkout escape and does not establish a submodule prohibition.
Write nonfailure observations to the supplied output writer; a nil writer
selects ordinary quiet library use. Diagnostics identify the source use site,
location, and destination. Document source columns as one-based byte columns.
### CI tool bootstrap
Build using temporary copies of the released application's `go.mod` and `go.sum`,
with Go's alternate `-modfile` and normal module preparation in those copies.
Confirm that ReleaseKit is already selected before building its command. This
preserves the application's version declaration and require/replace semantics
while allowing missing tool-only indirect requirements/checksums to be prepared
without dirtying immutable tagged source. It supports consumers whose application
imports only lightweight `buildinfo`. Stages 12–14 implement this agreed approach.
## Stage 1: Introduce the specification with race selection
**Completion:** Complete. `ReleaseRequest.Validation.Go.Race` and profile
`validation.go.race` select `-race` for the single native Go suite. Both
candidate operations share the selection, and race failures retain the `go-test`
identity without falling back to an ordinary suite.
**Outcome:** The first public/profile selection changes the one existing suite.
1. Add `ReleaseRequest.Validation`, `ValidationSpec.Go`, and
`GoValidationSpec.Race`, plus equivalent CLI-owned JSON settings. Add explicit
conversions through both candidate and publication request paths.
2. Build the base invocation in `internal/validation`: preserve
`go test -mod=readonly -count=1 ./...`, adding `-race` when selected. Reuse
the native Go environment and report tool/prerequisite failures as `go-test`.
3. Preserve additional-check and build ordering. Update exact API/profile
references and add a minimal consumer example for the selection.
**Verification:** Default and selected invocations, exactly one suite, API/profile
selection equivalence, both operations, unavailable race prerequisites with no
silent fallback, preserved causes/cancellation, and failure before product
checks/builds/tag creation. Use a small real race-enabled fixture on supported
hosts and a controlled Go command for portable prerequisite-failure coverage.
## Stage 2: Coordinate a separate full vet pass
**Completion:** Complete. `ReleaseRequest.Validation.Go.FullVet` and profile
`validation.go.full_vet` disable test-time vet for the one native test suite and
run native `go vet -mod=readonly ./...` afterward. Vet failures retain the
`go-vet` identity, and the selection is shared by the API and profile paths.
**Outcome:** Full vet replaces test-time vet overlap through one declaration.
1. Add `Go.FullVet` and `validation.go.full_vet` through the same conversion path.
2. When enabled, add `-vet=off` to the one test invocation and run native
`go vet -mod=readonly ./...` afterward using the same tool/environment.
3. Attribute vet failures to `go-vet` and update canonical usage guidance.
**Verification:** Ordinary, race-only, vet-only, and combined selections; one
suite; vet after successful tests; suppression of test-time vet only when
selected; failure/cancellation stopping; default behavior and API/profile parity.
Protect the selected command relationship without freezing incidental flag order.
## Stage 3: Compile packages for selected targets
**Completion:** Complete. `Go.CompileTargets` and profile
`validation.go.compile_targets` select static package compilation after tests
and vet. Failures use `package-compile` and include the selected target;
artifact building remains separate.
**Outcome:** Source compatibility checks work independently of artifacts.
1. Add `Go.CompileTargets` and string-array profile targets. Convert to internal
Go targets and validate complete pairs consistently for API and profile use.
2. After test/vet, run `go build -mod=readonly -o <os.DevNull> ./...` in static
mode for each selected target. The Go build null-output mode suppresses
executable output, including a module with a single main package.
3. Report failures as `package-compile`, including the selected target. Keep
output names, linker version injection, smoke tests, receipts, and manifests
in artifact building. Document compilation versus target execution.
**Verification:** Linux amd64/arm64, a supported additional Go pair, source-only
library, single-main and multiple-main modules (including repeated executable
basenames), target failure, unchanged candidate files, `CGO_ENABLED=0`, preserved
ordering, and page-only delivery with compilation selected but no build targets.
Reuse the existing Go environment tests for controls already protected there.
## Stage 4: Compare the optional expected push URL
**Completion:** Complete. `ReleaseRequest.ExpectedPushURL` and profile
`expected_push_url` require an exact match with Git's resolved effective push
URL before validation. Mismatches return `ErrUnexpectedPushURL` with expected
and resolved values; `Check` ignores the publication-only expectation.
**Outcome:** Publication rejects a different effective destination before validation.
1. Add `ExpectedPushURL` and `expected_push_url`, with an empty value selecting
existing behavior. Carry it only into the publication request.
2. Immediately after resolving/capturing `endpoint.PushURL`, compare exact strings.
Report expected and resolved values through a publication preflight error.
Add `ErrUnexpectedPushURL` as a public sentinel using the existing shared-error
ownership pattern so callers can use `errors.Is`.
3. Keep later endpoint rechecks and reconciliation intact. Update GoDoc,
publication guidance, and the profile reference.
**Verification:** Omitted/matching/mismatching values, Git `insteadOf` resolution,
separate fetch/push URLs, exact spelling differences, mismatch before Go/callbacks
or tag creation, and a local-only `Check` that ignores the publication expectation.
Existing endpoint-change and uncertain-push tests keep their ownership.
## Stage 5: Check committed Go formatting
**Completion:** Complete. `ValidationSpec.GoFormat` and profile
`validation.go_format` run `go/format` on regular Go blobs from the captured
commit. Findings identify unformatted paths; parser and inspection failures
retain their causes, and the check leaves repository contents unchanged.
**Outcome:** A declaration replaces tracked-file formatting loops.
1. Add `GoFormat` and `validation.go_format` with working executor integration.
2. Load the shared captured inventory and apply `format.Source` to regular `.go`
blobs. Compare original and formatted bytes, preserving repository contents.
3. Return `go-format` findings for affected paths and preserve parser/inspection
causes. Update usage and formatting-convention documentation.
**Verification:** Formatted and unformatted files, parse errors, vendor/nested
source, unusual filenames, committed versus worktree bytes, disabled/default
behavior, API/profile parity, and failure before later product/build work.
## Stage 6: Select committed workspace-file inspection
**Completion:** Complete. `Validation.ModuleDistribution.WorkspaceFiles` and
`validation.module_distribution.workspace_files` report committed blob entries
named `go.work` or `go.work.sum`, including nested paths and symlinks. The rule
inspects the captured candidate independently of `GOWORK` and leaves the Go
workspace environment and vendor behavior unchanged.
**Outcome:** One distribution rule works independently of the Go environment.
1. Add `ModuleDistributionValidationSpec.WorkspaceFiles` and the matching JSON
group/field. The remaining rule is added in Stage 7.
2. Inspect committed blob entries named `go.work` or `go.work.sum`, including
nested and symlink entries. Report paths using `workspace-files`.
3. Keep testing's existing workspace setting and consumer vendor choices in their
current owners. Document the distinction between environment and distribution.
**Verification:** Either workspace filename, nested paths, symlink names,
committed files with `GOWORK=off`, uncommitted files excluded from inspection,
selection disabled, and shared API/profile execution. Use meaningful rule cases
rather than retesting Git enumeration mechanics.
## Stage 7: Select filesystem replacement inspection
**Completion:** Complete. `Validation.ModuleDistribution.LocalReplacements` and
`validation.module_distribution.local_replacements` parse committed `go.mod`
declarations, including nested/vendor files and committed symlink declarations.
Filesystem replacements report their declaration path, line, directive, and
target as `local-replacements`; versioned replacements are permitted. Parse and
inspection failures retain their causes, and this rule is independent of both
workspace-file selection and `GOWORK`.
**Outcome:** The second distribution rule parses actual module declarations.
1. Add `ModuleDistribution.LocalReplacements` and the equivalent profile field.
Prepare `golang.org/x/mod v0.40.0`, module sums, and required OSS notices.
2. Parse committed `go.mod` files with `modfile.Parse`, including nested/vendor
declarations. Resolve symlink declarations within the candidate; report an
inspection error when their selected contents cannot be obtained locally.
3. Identify filesystem replacements by the parsed replacement's absent version;
permit versioned replacements. Report path, directive/source location, and
target through `local-replacements`, preserving parse/inspection causes.
4. Document independent distribution selections and their inspection scope.
**Verification:** Single and block directives, comments, quoted paths, relative
and absolute filesystem targets, versioned replacements, nested declarations,
valid/broken symlinks, parse failure, and all independent boolean combinations.
The rule applies to candidate contents even when testing disables workspace use.
## Stage 8: Inspect individual committed file sizes
**Completion:** Complete. `Validation.FileSize` and `validation.file_size` enable
a per-blob decimal-byte limit with a 1,000,000-byte default, strict greater-than
comparison, and literal repository-relative file/directory exclusions. The check
uses captured blob sizes, counts symlink link text, omits gitlinks, and reports
one oversized file with its measurement. Nil disables the check, and historical
or aggregate repository size is outside its scope.
**Outcome:** Optional limits and intentional path exceptions are operational.
1. Add `FileSizeValidationSpec`, `ValidationSpec.FileSize`, and matching JSON
fields according to the declaration table. Validate nonnegative byte limits.
2. Use shared blob sizes for regular files and symlink link-text blobs. Apply
literal file/directory exclusions. On an oversized blob, return `file-size`
with its path and existing `FileSizeMeasurement` fields; reporting one offending
file per failure fits the current diagnostic model.
3. Document decimal bytes, strict greater-than comparison, exclusions, and
current-candidate scope. Preserve default disabled behavior.
**Verification:** Below/at/above limits, the exact 1,000,000-byte enabled default,
zero/custom/negative limits, nil versus empty objects, directory/path boundaries,
literal metacharacters, unmatched exclusions, small binaries, symlink sizes, and
omitted submodule sizes. Historical and aggregate size do not become release gates.
## Stage 9: Implement internal Markdown parsing and anchors
**Completion:** Complete. Goldmark now records parsed links, images, reference
uses, use-site locations, and collision-free heading anchors behind an internal
per-candidate document cache. Its behavior remains unselected until the later
validation-integration stage.
**Outcome:** A parser with source positions and documented anchor semantics is ready.
1. Add `github.com/yuin/goldmark/v2 v2.1.6`, prepared dependencies, and applicable
OSS notices. Keep parser types inside internal implementation.
2. Parse links/images, reference uses, headings, and source positions into small
internal records. Derive visible heading text and apply the convention above.
3. Cache parsed records/anchors per candidate document. Keep this stage internal;
Stage 11 adds the public selection once complete checking works.
4. Compare fixtures with PromptKit's starting examples. Record intended refinements
in the roadmap until the public behavior lands, avoiding a second regex parser.
**Parser refinements:** PromptKit's starting script scans inline links, images,
and ATX headings with regular expressions. The Goldmark parser also records
resolved full, collapsed, and shortcut reference uses at each source use site.
Heading anchors use decoded visible AST text, include inline code,
exclude raw HTML tags, and support Setext headings. Parser-recognized CommonMark
autolinks are recorded alongside inline and reference links. Repeated-heading
suffixes skip identifiers already used by another heading.
**Verification:** Inline/reference forms and images, escaped/parenthesized
and angle-bracket destinations, optional titles, code fences/inline code,
Unicode/entity headings, ATX/Setext headings, repeated headings and suffix
collisions, and source use-site locations including repeated reference uses.
Undefined references and HTML prose remain governed by parser interpretation.
## Stage 10: Implement internal destination and fragment checking
**Completion:** Complete. The internal checker resolves committed destinations,
checks supported fragments, preserves inspection errors, and reports distinct
outside-checkout and submodule observations. Stage 11 exposes this checker
through public and profile selections and the shared executor.
**Outcome:** Committed Markdown navigation is checked with boundary observations.
1. Resolve parsed local URI components through `CandidateTree.ResolveDestination`.
Apply escaping/query/fragment rules above and use actual resolved identities
for target-document anchor lookup.
2. Check committed existence and supported heading fragments, recording source
path/location/target. Produce `markdown-links` failures and distinct nonfailure
observations for outside-checkout and submodule contents.
3. Implement optional checkout-boundary enforcement internally. Preserve the
distinction between external URIs, checkout escapes, and uninspected submodules.
4. Reuse remediated symlink/cancellation/local-object behavior. Keep parsing,
resolution, and rule evaluation in ordinary internal functions.
**Resolution refinement:** URI query components do not participate in path
checking. Compare fragments after one percent-decoding pass and lowercase them
to preserve PromptKit's starting anchor matching.
**Verification:** Valid/missing files and anchors, same-document/empty fragments,
percent-escaped paths and fragments, query strings, directories and non-Markdown
files, outside links with both settings, and submodule observations. Check how
resolver results and inspection errors become Markdown findings, preserving
causes and cancellation. Use representative committed-tree integration cases
and verify unchanged candidate/worktree bytes. Existing candidate-tree and Git
tests own detailed absolute-path, symlink-parent, cycle, neighboring-file,
missing-object, and traversal-cancellation cases.
## Stage 11: Expose Markdown selection and complete validation integration
**Completion:** Complete. `ValidationSpec.Markdown` and
`validation.markdown` select the shared committed-link checker through both
public operations and CLI profile commands. Omitted/null settings disable the
check; an empty object enables it, and the boundary option maps explicitly.
Warnings use the shared output writer, while Markdown failures stop product
checks and builds. GoDoc, profile, consumer, CLI, and internal documentation
now describe the operational behavior.
**Outcome:** All agreed validation selections work through both public operations.
1. Add `MarkdownValidationSpec`, `ValidationSpec.Markdown`, and their JSON fields.
Enable on a non-nil group/empty object and map the boundary boolean explicitly.
2. Wire the checker and nonfailure observations into the shared executor/output
writer. Reuse existing facade/CLI diagnostic formatting and exit classification.
3. Document the newly operational Markdown selection in its canonical owners.
Review earlier stages' GoDoc, configuration, check/publish guides, CLI reference,
internal inventory, and examples for consistency across combined selections.
4. Use self-contained fixtures representing Audita defaults/artifacts, ConfigKit's
race/vet/compilation, and PromptKit's shared settings plus example assertions.
Document how consumers remove duplicate generic commands when adopting settings.
**Verification:** Equivalent Go/profile declarations, nil/empty/null Markdown
settings, warnings with successful results, boundary failures before product/build
work, all-zero compatibility, representative combined selections, one suite and
one additional-check callback. Run the compiled API/dependency check, including
`buildinfo`'s standard-library/runtime-only dependency boundary.
## Stage 12: Build the CI tool from verified released module configuration
**Completion:** Complete. The maintained tag workflow verifies and checks out
the event tag before reading module files, then confirms the ReleaseKit module
selection and builds its CLI with temporary alternate module metadata outside
the source tree. The delivery command retains the event arguments and exit
status, and the cleanup trap removes its module copies and executable.
**Outcome:** The standard template has one tool-version declaration.
1. Revise `examples/woodpecker/tag-delivery.yml` into a compact bootstrap/delivery
step. Use available Git to validate/fetch the explicit event tag, compare its
peeled commit to the full provider object ID using existing case-insensitive
object-ID semantics, check out that commit detached, and confirm HEAD and
the existing clean tagged-source requirement. This ensures module files
actually belong to the verified release before selecting the tool.
Do this before reading/resolving the application module or executing Go.
2. Apply the agreed CI tool bootstrap approach: create a disposable directory
outside the source checkout, copy its `go.mod` and existing `go.sum`, and use
an alternate `-modfile` for tool resolution/build.
Run from the verified source with `GOWORK=off` and `GOTOOLCHAIN=local`.
3. Confirm the ReleaseKit module is already selected by that module configuration
using `go list -m`; then build its `cmd/releasekit` package without an `@version`
suffix. Allow ordinary network-capable module preparation in the temporary
metadata copies (`-mod=mod`). Respect selected require/replace relationships.
4. Invoke the resulting native executable's existing `ci-deliver` with the event
repository, commit, and tag. Keep delivery credentials in the existing delivery
step and use an exit cleanup trap for temporary metadata and executable files.
5. Remove the independent `RELEASEKIT_VERSION` workflow setting. Preserve current
Go-image customization, destination/profile ownership, and delivery outcomes.
Keep the bootstrap Git block small; the built tool retains its runtime guards.
**Verification:** Execute maintained template commands with a local tag/module
fixture and instrument Go/transport operations. Prove wrong tag/commit stops
before module resolution/build, a dirty module cannot select a different tool,
the verified checkout selects the tool, module metadata and source remain
unchanged, and the executable/temporary files are cleaned up. Tool metadata
preparation is separate from offline candidate checks. Include a representative
case that verifies invocation of the selected executable with the event arguments
and propagation of its exit status. Existing delivery tests own hosted behavior.
## Stage 13: Verify upgrades, retries, and bootstrap variants
**Completion:** Complete. The tag workflow is exercised against a local module
proxy with two tool versions, an older tagged configuration after the default
branch upgrades, and a versioned replacement. Missing tool-only dependency
checksums are prepared in temporary module metadata. The ReleaseKit source
workflow verifies its tag before self-bootstrap and writes the executable
outside the checkout. Delivery, configuration, and CLI guidance now distinguish
tool bootstrap from application dependency preparation and describe
immutable-source retries and installed-tool use.
**Outcome:** Tool selection follows the triggering release across supported uses.
1. Extend Stage 12's bootstrap fixtures with a local module proxy for two tool
versions and tagged consumer module configurations. Execute the maintained
workflow body against both.
Keep live infrastructure outside the default test suite.
2. Verify a retry of an older tag still selects that tag's module configuration
when the default branch has upgraded. Include missing tool-only checksums and
an existing versioned replacement to protect source-based resolution.
3. Align `releasekit-source-delivery.yml` with source verification before self
bootstrap and temporary output. Preserve self-bootstrap from `./cmd/releasekit`
and separately installed-tool guidance using the existing command.
4. Update delivery/configuration/CLI guidance and maintained templates together.
Explain compatible Go environments, dependency preparation, immutable-source
retries, and the distinct tool/app artifact preparation steps.
**Verification:** Two dependency versions, older-source retries, self bootstrap,
and separately installed tool, using the shared bootstrap fixtures. Existing
delivery and publication tests retain ownership of source-only pages, binary
delivery, destination handling, and publication states. Add integration coverage
for any new interaction introduced by bootstrap changes. An application without
a declared ReleaseKit dependency uses the installed-tool integration.
## Stage 14: Migrate Audita's local CI integration
**Completion:** Complete. Audita commit `17421bc` makes both local and hosted
bootstraps resolve ReleaseKit v0.1.0 from the consumer's module configuration.
The existing Go image, `GITEA_RELEASE_TOKEN` secret, release profile, caller
arguments, and native tool status are preserved. The local wrapper uses
temporary alternate module metadata and cleans it up before executing the tool.
Audita's runbook now describes the module-selected workflow.
**Verification:** Audita's `go test ./scripts -count=1`,
`bash -n scripts/release.sh`, and real `bash scripts/release.sh version` passed;
the wrapper reported `releasekit v0.1.0` and left source module files unchanged.
ReleaseKit's `TestWoodpeckerTagDeliveryTemplate` and
`TestWoodpeckerTagDeliveryUsesTaggedModuleVersion` passed with local Git and
module-proxy fixtures. Both repositories passed their relevant diff checks.
**Outcome:** Audita's local wrapper and CI select the same module-declared tool.
1. Read `../audita` orientation/policies and inspect its worktree before editing.
Adapt `.woodpecker/release.yml` to the maintained module-based template,
preserving its Go image, Gitea secret name, and release profile.
2. Retain its existing released ReleaseKit module selection for this migration;
that version already supplies `ci-deliver`. Make any necessary adjustments to
`scripts/release.sh` for the selected bootstrap metadata approach, preserving
arguments and native executable exit behavior. Keep generated tool output
outside the candidate source where needed.
3. Verify the consumer's actual wrapper/workflow using local tagged fixtures and
prepared module caches. Confirm matching tool selection, preserved arguments
and exit status, and unchanged source files. Review the diff for preservation
of the release profile and binary targets. Existing consumer and ReleaseKit
tests retain ownership of source validation and hosted delivery behavior.
4. Commit the focused migration in Audita with a plain English message. Record
its commit and local verification in this plan's implementation status.
**Verification:** The actual adapted consumer declarations use one dependency
version; local and CI bootstraps agree and retain existing caller arguments/status
semantics. Keep integration fixtures self-contained in ReleaseKit's default suite.
This migration uses an existing release and requires no intermediate publication
or live Woodpecker/Gitea run to complete the stage.
## Stage 15: Audit the completed v0.2.0 implementation
**Completion:** Complete. All ten release-roadmap items map to implemented API,
profile, execution, diagnostics, canonical documentation, and focused behavioral
evidence. The audit confirms default zero-value behavior, independent
module-distribution selections, one coordinated Go suite, source-only
compilation/delivery, and expected-push preflight. Review found no unintended
defaults, restrictions, or stronger guarantees. Audita's migration is recorded
at `17421bc`.
**Verification:** `go test ./...` and `go run ./internal/apicheck` passed.
Local Markdown destinations and fragments resolved across the documentation;
dependency versions, source revisions, attribution entries, and license texts
were checked. Maintained Woodpecker examples passed their offline fixtures.
**Outcome:** The entire scoped feature set is implemented and locally verified.
1. Match every row in the roadmap/stage table to implemented settings, execution,
diagnostics, current documentation, and focused behavioral evidence. Verify
all-zero compatibility, independent distribution choices, one coordinated Go
suite, source-only compilation/delivery, and selected endpoint preflight.
2. Review new code and tests for unintended defaults, restrictions, caps, or
stronger guarantees. Preserve user choice and the existing facade boundaries.
3. Run `go test ./...` and `go run ./internal/apicheck`. Add further integration
coverage only for a concrete remaining risk. Check documentation links,
fragments, whitespace, dependency notices, and maintained examples.
4. Update statuses in this plan, the release roadmap, and the model roadmap to
reflect actual completion, including the Audita migration evidence. Update
canonical current-behavior owners as needed and remove stale prospective text.
5. Commit the completed unit, report each stage's outcome and remaining work,
and finish with a clean workspace while preserving any unrelated user changes.
The subsequent architecture review and release/testing decisions follow here.
## Open Questions
None.

View File

@@ -1,149 +0,0 @@
# Validation API and profile model
Status: Complete for the agreed v0.2.0 shared validation model. Every selection
and execution path described here is implemented and locally verified; broader
consumer adoption remains in the product roadmap.
This roadmap describes the shared model that enables the optional validation
capabilities in the [v0.2.0 roadmap](v0.2.0.md). It owns their common API,
configuration, execution, and diagnostic design. The release roadmap owns each
capability's purpose and scope. The [implementation plan](implementation.md)
owns the stages and the agreed API rollout.
## Rationale and v0.1.0 baseline
ReleaseKit v0.1.0 exposes `Check` and `Publish` through the root facade. Both
accept `ReleaseRequest` and use shared candidate-validation machinery. Validation
runs the standard offline Go suite, followed by `AdditionalChecks` when supplied.
Selected executable builds follow validation. The CLI maps profile `checks`
commands to the additional-check callback.
This provides a common procedure and a product-specific extension point. The
request and profile have structured selections for race testing, separate full
vet, package compilation, formatting, Markdown, module distribution, and file
size. Publication also supports an expected push endpoint.
The model lets consumers declare their choices once while ReleaseKit owns
execution and coordination. This follows [ADR 0002](../adr/0002-routine-releases.md):
shared capabilities should reduce application code and routine developer
decisions. The facade boundaries in [ADR 0001](../adr/0001-public-facade.md)
provide the existing architectural foundation.
Optional capabilities can serve a small subset of consumers when they remove
application-owned release mechanics and have clear scope, compact configuration,
and proportionate maintenance costs.
## Target end state
### One validation specification
The facade-owned `ValidationSpec` on `ReleaseRequest` has a corresponding
`validation` object in the release profile. Typed Go fields and ordinary JSON
settings express the same supported selections. Related choices, including
base-test and vet settings, are grouped so their relationship is clear.
The specification includes the agreed capabilities in the
[release roadmap](v0.2.0.md), including optional Markdown boundary enforcement,
independent module-distribution rules, and file-size settings. Its zero value
and an omitted profile object preserve current default validation. Consumers
enable optional checks according to their product needs.
Go callers and CLI users receive equivalent validation behavior. The CLI owns
profile parsing and presentation; the facade owns the public types and contracts;
internal implementation owns execution. The [implementation plan](implementation.md)
records the selected field names, representations, and completed rollout.
### Shared coordination
`Check` and `Publish` pass the same specification through one internal validation
path bound to the captured candidate. ReleaseKit constructs the selected Go
commands, manages its shared environment, determines execution order, and
propagates failures and cancellation.
Race selection changes the one base suite invocation. Selecting a separate
full-vet pass automatically omits test-time vet. Package-compilation targets
express compatibility checks independently of executable artifact selection.
Consumers obtain this coordination through their declarations.
Keep the executor straightforward and internal, with ordinary functions for
the selected capabilities. The public model expresses validation choices;
ReleaseKit maintains their execution procedure. Existing candidate rechecks,
offline validation, and source-publication outcomes retain their meaning.
### Candidate-content inspection
File-oriented checks share candidate-file enumeration and inspection where
useful. Their scope is the captured candidate, and inspection remains offline
and read-only. Inspect committed candidate contents and define symlink and
submodule handling explicitly in the shared inspection model. Module-distribution
rules inspect the files being released independently of the environment used
to run Go commands. Markdown boundary
validation operates independently of neighboring checkout contents.
Keep each check's settings together: Markdown validation owns its optional
boundary rule; module-distribution validation owns its independent rule
selections; file-size validation owns its limit and path exclusions. The
[release roadmap](v0.2.0.md) defines the scope and defaults of each capability.
### Clear responsibility boundaries
| Concern | Model owner |
| --- | --- |
| Base tests, full vet, and package compilation | Shared validation specification |
| Formatting, Markdown links, module distribution, and file size | Shared validation specification |
| Product-specific assertions and example checks | Existing `AdditionalChecks` callback and profile `checks` |
| Expected authoritative push destination | Publication settings and endpoint handling |
| Executable package, binary name, and artifact targets | Build declaration |
| Hosted destination and credentials | Delivery settings |
`AdditionalChecks` and profile `checks` remain available for substantive
consumer behavior. Shared capabilities use the validation specification.
Preserve the current behavior of existing declarations as the new options land.
The expected-endpoint feature remains a publication guard, and `Check` remains
local-only. Selecting package-compilation checks leaves a source-only consumer's
deliverable selection unchanged. Tool installation and dependency preparation
remain separate from offline candidate validation.
### Consistent diagnostics
Shared validation failures identify the check and expose useful details to Go
callers. File-oriented checks provide source locations and actionable findings
where applicable, including relevant paths, directives, and measured values.
The CLI presents those details consistently through the ordinary candidate-check
and publication commands.
Use a small facade-owned error model with stable check identity and support for
underlying error causes and cancellation. Preserve `errors.Is` and `errors.As`
behavior across internal conversions and facade wrapping. Exact error types
and diagnostic fields are proposed in the [implementation plan](implementation.md).
Validation failures and publication outcomes have distinct meanings. Existing
publication states continue to describe whether publication was attempted,
confirmed, conflicted, or remains uncertain. The model preserves the existing
failure-stopping behavior of the candidate workflow.
## Implementation
The [implementation plan](implementation.md) records the completed rollout and
verification. The facade and CLI profile expose equivalent selections for the
single test suite, separate full vet, package compilation, formatting, Markdown,
module distribution, file size, and the expected publication endpoint. Shared
execution, ordering, diagnostics, cancellation, and failure stopping are
implemented by the owners described above. The maintained Woodpecker template
and Audita's local wrapper use the module-selected ReleaseKit version; see the
[v0.2.0 roadmap](v0.2.0.md) and [delivery guide](../consumers/delivery.md).
## Adoption evidence
Self-contained integration fixtures evaluate all three known consumer shapes:
Audita's default validation and executable builds; ConfigKit's selected test,
vet, and library compilation checks; and PromptKit's shared validation plus
substantive example assertions. Audita's module-selected CI migration is
committed at `17421bc`; the other consumer migrations remain separate adoption
work.
The model gives each adopting consumer a small declaration and the same routine
release operation. Shared execution and diagnostics cover the agreed v0.2.0
capabilities while product-specific work remains in the existing extension
point.

View File

@@ -1,210 +1,43 @@
# ReleaseKit: shared release process
# ReleaseKit adoption and deployment roadmap
Status: The v0.2.0 implementation, policy adoption, and architecture
remediation are complete. Audita's module-selected local and CI integration is
committed at `17421bc`; broader consumer adoption and live deployment remain.
Status: Pending consumer adoption and live deployment. The v0.2.0 implementation
and its remediation are complete.
This roadmap records the shared release-process opportunity identified in
PromptKit, ConfigKit, Audita, Notarius, Narratio, and Scriptorium on 2026-09-29,
and the remaining adoption goals. The [internal package inventory](../internal/overview.md)
records implemented capability owners and behavioral evidence. The
[architecture policy](../policy/architecture.md) and
[ADR 0001](../adr/0001-public-facade.md) own the facade boundaries and rationale.
The initial foundation, policy adoption, and remediation plans are preserved
in Git history; the current v0.2.0 completion evidence is in the
[implementation plan](implementation.md).
The [v0.2.0 release note](../releases/v0.2.0.md) summarizes the implemented
capabilities. The [internal inventory](../internal/overview.md) records their
owners and local verification. Completed plans and review findings remain in
Git history. The [architecture policy](../policy/architecture.md) and accepted
ADRs own the selected architectural constraints.
## Opportunity
## Remaining work
All six repositories follow the same base release policy. Their release scripts
repeat stable-version parsing, candidate and release-note checks, local and
remote tag checks, validation of one captured commit, explicit Git publication,
and recovery guidance for uncertain pushes. They differ in publication approach,
tag type, validation commands, and artifact delivery. The mechanics of embedding
a release version and cross-building an application are further candidates for
standardization.
- Choose the broader consumer integration test plan and migration sequence for
PromptKit, ConfigKit, Notarius, Narratio, and Scriptorium. Evaluate API clarity,
product-specific checks, and the infrastructure removed from each repository.
- Adopt the common publication, validation, runtime-version, build, and delivery
conventions where they apply. Keep product identity, compatibility decisions,
and substantive application checks with each consumer.
- Provision delivery credentials and activate ReleaseKit and consumer Woodpecker
workflows as part of deployment. The maintained consumer template selects
tooling from the released application's module configuration.
- Verify live Gitea delivery and Woodpecker execution. Current verification uses
local Git and HTTP fixtures; live deployment remains unverified.
- Measure realized reductions in consumer release infrastructure after migration.
Audita's module-selected local and CI integration is committed at `17421bc`.
If the projects deliberately standardize publication approach and tag type,
ReleaseKit can own most of the repeated release-process code. The current six
repositories contain 942 lines of release shell scripts and 742 lines of
dedicated release tests, or 1,684 lines total. A directional estimate is that
roughly 1,000–1,250 lines could leave those repositories: about 450–500 lines
of publication scripts, 150–250 lines of checks and builders, and 400–500 lines
of repeated publication tests. ReleaseKit would gain its own implementation and
tests, so this is a reduction in local code, not a net line-count saving.
## Proposals and deferred work
## Selected publication standard
1. Prepare and commit a version-matched note at `docs/releases/<version>.md`.
2. Publish the candidate commit to authoritative `main` before invoking the
release command.
3. Run one command that checks the candidate and unused stable version, runs
that repository's offline candidate validation once, and rechecks mutable
state.
4. Create an annotated tag at the validated commit with a message derived from
the checked-in note. Push only that exact tag object to the authoritative
endpoint. A successful tag push completes the source release.
5. Treat hosted release pages and binary assets as asynchronous delivery with
their own status. Do not poll CI or require artifact availability to declare
the source release complete.
This would replace Audita's atomic branch-and-tag push, ConfigKit's and
Notarius's branch push during the release command, and Audita's, Notarius's,
and Narratio's lightweight tags. Those changes require updates to each
repository's adopted release policy and procedure; they are not merely code
cleanup.
## Architecture
[ADR 0001](../adr/0001-public-facade.md) owns the accepted facade decision.
The [architecture policy](../policy/architecture.md) owns the adopted
responsibility and dependency rules, including the runtime version boundary.
The root `releasekit` package provides the public facade for candidate
validation, source publication, selected-target builds, and tagged-source
delivery. The Gitea page and asset-delivery client is integrated through that
facade and the CLI. The separate public `buildinfo` package provides lightweight
runtime version access.
Each facade owns its public named types and documented contracts. Public
operations, inputs, outcomes, errors, and callbacks express deliberately
selected consumer needs. Handles retain private implementation state; explicit
forwarding and conversions connect the public contracts to internal
representations.
Git operations, Go toolchain execution, Gitea integration, and orchestration
bookkeeping belong under Go's `internal/` boundary. Dependency direction runs
from the facades into internal implementation, with internal collaborators using
contracts defined inside that boundary. Implementation packages can evolve
while the supported public contracts retain their compatibility obligations.
The `buildinfo` facade keeps runtime version access lightweight. Applications
read their embedded version through its supported accessor. ReleaseKit's builder
and runtime implementation own the linker symbol and injection mechanism
together.
The `buildinfo` facade follows three constraints:
- Its public API exposes only runtime information justified by consumer needs.
- Its implementation uses the standard library and a small internal dependency
graph independent of release orchestration.
- Dependency checks protect that boundary, and build-and-run integration tests
verify that the builder injects the selected version and the public accessor
reports it correctly.
The release executable calls the orchestration facade and handles command
parsing, output, and exit codes. Library errors and outcomes report meaningful
publication state, including partial progress and uncertainty. Go declarations
and GoDoc own exact public API contracts; additional public packages and
extension interfaces require a deliberate decision grounded in durable consumer
needs.
Compiled-export and dependency checks protect facade ownership.
External-package behavioral tests protect error identity, value ownership,
callback lifetime, and observable outcomes across forwarding and conversions.
## Shared tool boundary
Provide a versioned ReleaseKit executable backed by a Go library. Existing
shell entry points can become short invocations of that executable. A library
alone would still require a caller in every shell-driven repository.
ReleaseKit should own:
- stable-version parsing and committed-note identity checks;
- authoritative Git endpoint resolution, candidate capture, clean `main` and
remote-branch agreement checks, and unused local/remote tag checks;
- one validation invocation bound to the captured candidate, followed by cheap
rechecks before publication;
- annotated-tag creation and verification, explicit tag-object push, and one
bounded authoritative-ref check after a failed or ambiguous push;
- a standard version accessor in the public `buildinfo` facade, backed by
release-build injection of the selected tag;
- a cross-build API that accepts the application's main package and selected
targets, initially Linux amd64, Linux arm64, and macOS arm64 (Go's
`darwin/arm64`), including an empty set for source-only projects;
- consistent release build flags and asset naming, plus a host-runnable build
that checks the executable's reported version; and
- Git publication tests using temporary local remotes, including remote
advancement, rejected pushes, lost acknowledgments, and unavailable remotes.
Each repository should retain its product name, compatibility decisions,
candidate-specific checks, main package, selected build targets, delivery
declaration, and a small test of its ReleaseKit wiring. For example, PromptKit
exercises its consumer examples. Applications would adapt their existing
version output to use the `buildinfo` accessor; one agreed version command
or flag would let ReleaseKit smoke-test a host-runnable build the same way.
Cross-built binaries would use the same injection mechanism, but compilation
alone would not establish that they run correctly on another platform.
ReleaseKit should run the selected checks and builds without silently adding
binary gates to source-only projects.
The build API should own the mechanics rather than require each application to
provide a custom linker symbol or shell loop. The initial target list reflects
the current projects, not a permanent limit on future platforms. It should
support both candidate cross-build checks and artifact builds from the tagged
source, using the same version and target selection.
## Woodpecker and Gitea delivery
A common tag-triggered Woodpecker workflow can run ReleaseKit against the
tagged source. Repository-local YAML should declare the pinned tool image and
credentials; each repository's ReleaseKit configuration should declare its
delivery destination and any binary main package and target set. ReleaseKit can
own version injection, cross-builds, checksum generation, and delivery. All
projects should receive hosted Gitea
release pages. Source-only repositories declare zero binary assets and deliver
the committed release note without a binary build or checksum manifest.
The shared delivery command should create or find the release for the tag,
use the checked-in note, and add declared binary assets and their checksum
manifest where applicable. A retry should inspect existing assets, verify
matching content, add only missing items, and stop on a conflict.
An existing checksum manifest must remain consistent with any later addition.
The release and its attachments are separate remote operations, so partial
delivery must be expected and reported clearly.
Audita, Narratio, and Scriptorium currently use the Woodpecker release plugin
with `file-exists: skip` and `overwrite: false`. Those settings avoid deliberate
replacement by filename, but they do not prove that a rebuilt file has the
same bytes as a previously published file. Content comparison would be a
reason to implement a shared Gitea delivery client rather than rely solely on
the plugin's skip behavior.
Hosted pages are selected for source-only projects, including PromptKit,
ConfigKit, Notarius, and ReleaseKit. Adoption adds a page deliverable and delivery
credentials to their procedures while preserving successful tag publication as
source completion. The initial implementation should support both page-only and
binary delivery through the same facade and command.
## Failure states
ReleaseKit should report source publication and artifact delivery separately:
| Event | Result |
| --- | --- |
| Candidate validation or a pre-push guard fails | No release was published; correct and validate the candidate again. |
| Tag push reports success | Source release is complete; delivery may still be pending. |
| Tag push fails or its acknowledgment is lost | Query the authoritative ref once. Confirm success only if the exact intended tag object is present on the intended commit; otherwise report failed or unknown status and retain the local tag. |
| Woodpecker or Gitea delivery fails after tag publication | Keep the source release successful. Retry missing delivery from the immutable tag after checking existing content. |
| Published source or content needs correction | Publish a new version; never replace content under the published version. |
## Consumer adoption target
All six projects should use the same selected publication, runtime version,
build, and delivery conventions where those responsibilities apply. Repository
entry points should contain only tool invocation and product-specific settings
or checks. Shared mechanics and their repeated tests belong in ReleaseKit.
Application runtime code should obtain version information through `buildinfo`.
Source-only libraries need no runtime import or automatic binary deliverable.
Projects declaring binaries should use the common target selection and version
injection; hosted delivery should use content-aware retries for both page-only
and binary releases.
Existing published tags, notes, pages, and assets retain their historical contents.
Consumer policy and procedure changes should describe the deliberately adopted
standard and its compatibility impact. Adoption should demonstrate reduced local
infrastructure and one coherent release model.
- Optional recognizable-credential checks remain an unevaluated suggestion from
PromptKit's feature request. Evaluate their detection scope, synthetic-fixture
handling, false-positive costs, and maintenance benefit before selecting shared
support. The proposal establishes no mandatory credential gate.
- ReleaseKit executable binary distribution remains deferred until deliverables
are deliberately selected. Selecting binaries would require delivery
declarations, target checks, automation responsibilities, and corresponding
release policy and procedure updates.
- Evaluate a maintained ReleaseKit CI image or specialized validation/build
settings when a demonstrated consumer need justifies their maintenance cost
under [ADR 0002](../adr/0002-routine-releases.md).
## Representative migration review
@@ -226,40 +59,3 @@ The self-contained ReleaseKit fixtures exercise a meaningful source-only
compatibility check through local Git publication, and a `buildinfo` binary
consumer with selected targets through local HTTP delivery. These are separate
publication and delivery operations.
## Remaining work
- Use the [v0.1.0 baseline](../releases/v0.1.0.md) for initial consumer
integration under the [maintainer procedure](../release.md). Consumers that
adopt the maintained tag workflow select ReleaseKit through the tagged
source's `go.mod` rather than a separate workflow version value.
- Choose the broader consumer integration test plan and migration sequence.
Evaluate API clarity, product-specific checks, and the infrastructure removed
from each consuming repository.
- Provision delivery credentials and activate consumer Woodpecker workflows
as part of deployment. The tagged consumer's module configuration selects
the ReleaseKit version used by the maintained workflow.
- Verify live Gitea delivery and Woodpecker execution. Existing verification uses
local Git and HTTP fixtures; no live deployment has been verified.
- Keep ReleaseKit executable binary distribution deferred until deliverables
are deliberately selected. Selecting binaries would require delivery
declarations, target checks, automation responsibilities, and corresponding
release policy and procedure updates.
- Evaluate a maintained ReleaseKit CI image or specialized validation/build
settings when a demonstrated consumer need justifies their maintenance cost
under [ADR 0002](../adr/0002-routine-releases.md).
## Reviewed implementations
- [PromptKit release script](../../../promptkit/scripts/release.sh),
[checker](../../../promptkit/scripts/release-check.sh)
- [ConfigKit release script](../../../ConfigKit/scripts/release.sh),
[checker](../../../ConfigKit/scripts/release-check.sh)
- [Audita release script](../../../audita/scripts/release.sh),
[Woodpecker job](../../../audita/.woodpecker/release.yml)
- [Notarius release script](../../../notarius/scripts/release.sh),
[checker](../../../notarius/scripts/check-release-source.sh)
- [Narratio release script](../../../narratio/scripts/release.sh),
[Woodpecker job](../../../narratio/.woodpecker/release.yml)
- [Scriptorium release script](../../../scriptorium/scripts/release.sh),
[Woodpecker job](../../../scriptorium/.woodpecker/release.yml)

View File

@@ -1,430 +0,0 @@
# ReleaseKit implementation remediation
Status: All nine findings addressed.
The v0.2.0 review covers committed state `edd670e` and the implementation
commits after `89981ab`, against the [implementation plan](implementation.md),
[feature roadmap](v0.2.0.md), [model roadmap](model.md), accepted ADRs, and
applicable repository policies. Both ReleaseKit and Audita had clean worktrees
when inspected. The review changed only this document; the resolutions below
record subsequent remediation.
## Foundation review history
Reviewed committed implementation `f53354b`, covering the eight foundation
commits after `0c7129a`, against the [implementation plan](implementation.md),
[model roadmap](model.md), accepted ADRs, and applicable repository policies.
The worktree was clean when review began. This document records required fixes;
production code was unchanged during the review.
## Foundation architecture assessment
The root orchestration facade and independent `buildinfo` runtime facade retain
their intended dependency direction. Both candidate operations share request
conversion and `internal/candidate.Validate`. Candidate capture, product
callbacks, builds, and rechecks retain their owners; `internal/validation` owns
the default Go suite, structured failures, and committed-tree inspection.
Publication retains endpoint guards, tag operations, and recovery. The CLI
shares profile conversion and owns output and exit classification.
The public error types belong to the root facade, and conversion preserves the
underlying error chain. Candidate inventory reads committed entries and blobs,
with separate handling for symlinks and gitlinks. Its default validation path
does not load that inventory.
The absent `ValidationSpec`, request/profile selections, optional checkers, CI
tooling changes, and expected-endpoint option are intentionally deferred under
the agreed incremental rollout. Their absence is consistent with foundation
scope. The defects below affect the implemented foundation, including helpers
that subsequent checkers will use.
## 1. P2: Expanding symlink cycles do not terminate or honor cancellation
**Resolution:** Addressed. The resolver tracks active symlink expansions,
releases them before traversing the remaining destination, and checks context
cancellation during traversal. Regression coverage includes direct and mutual
expanding cycles, finite repeated links, and cancellation.
`go test ./internal/validation` passed.
**Owner:** [candidate destination resolver](../../internal/validation/tree.go),
`CandidateTree.ResolveDestination`, especially the restart loop and cycle key.
The cycle key combines the symlink path with the unresolved suffix. For a
committed symlink `loop -> loop/child`, resolving `loop` visits suffixes of
increasing length. The key never repeats. Cached link reads do not consult the
context, and the resolver loop has no cancellation check. Resolution can keep
consuming CPU and memory after cancellation.
**Evidence:** A temporary overlay test using committed-entry metadata and
cached symlink text confirmed that the operation neither returned a cycle
classification nor stopped within 150 ms, despite a 20 ms context deadline.
Existing tests cover a two-link cycle whose suffix stays constant.
**Required remediation:** Detect recursive symlink expansion using the active
expansion chain and honor context cancellation during traversal. Preserve
legitimate finite paths that encounter a symlink again after an earlier
expansion has completed. Keep cycle detection separate from any arbitrary
hop/path-length limit; such a limit was not requested.
**Verification:** Add focused cases for direct and mutual expanding cycles,
finite repeated symlink traversal, and cancellation. Keep the fixture local
and bounded even when testing the broken behavior.
## 2. P2: Path cleanup changes symlink traversal and checkout classification
**Resolution:** Addressed. Destination components and symlink targets are
traversed in order, with parent traversal applied to resolved directories.
Missing entries and regular-file traversal remain observable. Regression tests
cover both destination and symlink-target parent components and confirm that
neighboring files do not change classification.
`go test ./internal/validation` passed.
**Owner:** [candidate destination resolver](../../internal/validation/tree.go),
initial destination construction and construction after symlink expansion.
`path.Join`/`path.Clean` eliminate `..` components before intervening symlinks
are resolved. This changes the destination rather than resolving it. It can
also discard a symlink that crosses the checkout boundary.
**Evidence:** With `docs/link -> ../nested/inner`, resolving
`docs/link/../file.md` returns `docs/file.md` instead of `nested/file.md`.
With `link -> ../neighbor`, `link/../README.md` is incorrectly classified as
the captured root's `README.md`, hiding its outside-checkout destination.
Both failures were reproduced with overlay tests. PromptKit's starting
convention resolves paths before checking their relation to the checkout.
**Required remediation:** Walk path components and symlink expansions in order.
Apply parent traversal to the resolved directory, retaining missing-file and
non-directory observations where traversal encounters them. Derive checkout
classification from that traversal, using candidate entries throughout.
**Verification:** Cover `..` following internal and external symlinks and
following missing or regular-file components. Assert resolved identities and
boundary classifications independently of neighboring filesystem contents.
## 3. P2: Committed-file inspection can fetch from a promisor remote
**Resolution:** Addressed. Candidate tree, size, and blob reads share a local
Git command helper that disables lazy fetching and transport access per child
process. Missing blobs report local unavailability, and Git process causes
remain wrapped. Partial-clone tests cover missing blobs and missing trees,
instrument transport attempts, and verify the object database stays unchanged.
`go test ./internal/git ./internal/validation` passed.
**Owner:** [Git source readers](../../internal/git/source.go), `ReadTree`,
`populateBlobSizes`, and `ReadBlob`.
The new readers invoke ordinary Git object-reading commands without suppressing
automatic fetching of missing promisor objects. Inspecting a filtered checkout
can therefore initiate remote access during the promised offline operation.
Successful fetching can also populate the checkout's object database.
**Evidence:** A local `--filter=blob:none --no-checkout` clone contained commit
and tree objects but no blobs. Its promisor URL was replaced with a synthetic
SSH destination, and a local SSH stub recorded attempts and exited. Calling
`ReadCandidateTree` invoked that stub through `cat-file --batch-check` and
returned a promisor-fetch error. No external host was contacted. Ordinary
fully populated repository tests do not exercise this behavior.
**Required remediation:** Make these candidate object reads explicitly local
and suppress lazy fetching using a mechanism supported by the applicable Git
version. Report unavailable local objects with preserved causes. Keep source
preparation and network-capable publication/delivery operations in their
existing separate boundaries; a global ban on partial clones is unnecessary.
**Verification:** Inspect a local filtered-clone fixture with unavailable blobs
and an instrumented remote transport. Assert that metadata/content requests
fail locally when necessary and never invoke that transport. Retain successful
reads from fully populated candidates.
## 4. P2: Public and CLI error presentation loses diagnostic information
**Resolution:** Addressed. Public errors use the internal diagnostic formatter
with explicit conversions, including findings, targets, directives,
measurements, and causes. Matching converted diagnostics reuse their contextual
cause's rendering once. The CLI preserves outer wrappers, and callback errors
retain their public identity. Regression tests cover both candidate operations
and caller-created errors with and without causes.
`go test . ./cmd/releasekit ./internal/validation ./internal/apicheck` and
`go run ./internal/apicheck` passed.
**Owners:** [public diagnostics](../../validation_error.go),
`ValidationError.Error` and its formatter; [CLI presentation](../../cmd/releasekit/main.go),
`candidateFailureText`; [internal formatting](../../internal/validation/diagnostic.go).
Two branches lose information:
- A caller-created `ValidationError` with a cause returns only `Cause.Error()`.
Its check, findings, and measurement disappear from the textual diagnostic.
Converted built-in failures happen to work because their retained internal
cause already formats those details.
- The CLI extracts an inner `ValidationError` with `errors.As` and formats that
inner value. For a callback-supplied public error, this drops the candidate
wrapper added by `candidate.Validate`.
The public and internal formatters also duplicate the finding/measurement
formatting procedure, making it easier for their behavior to diverge.
**Evidence:** A public error with check `go-format`, a `main.go:3` finding, and
cause `inspection failed` renders only `inspection failed`. A candidate-wrapped
callback diagnostic loses its captured-commit context through
`candidateFailureText`. Both failures were reproduced with temporary overlay
tests. Existing tests exercise only converted Go-suite failures.
**Required remediation:** Share diagnostic formatting behind the facade with
explicit conversions. Preserve the full contextual chain when presenting an
error and include caller-supplied structured detail alongside its cause.
Ensure converted built-in failures render once. Retain `errors.Is`/`errors.As`
and existing exit/publication classifications.
**Verification:** Cover public errors with and without causes, callback errors
with candidate context, and converted built-in failures. Assert useful fields,
cause identity, context, and absence of duplicate diagnostics without fixing
complete message wording as a contract.
## 5. P2: Absolute paths inside the checkout are classified as outside
**Resolution:** Addressed. Absolute destinations and symlink targets within the
captured repository directory map to candidate paths before component
traversal. Absolute paths with similar external prefixes remain outside.
Regression tests also retain symlink-before-parent semantics and demonstrate
that removed committed files resolve while live-only files remain missing.
`go test ./internal/validation` passed.
**Owner:** [candidate destination resolver](../../internal/validation/tree.go),
the absolute-destination and absolute-symlink branches.
Both branches classify every path beginning with `/` as outside the checkout.
This substitutes path syntax for the requested checkout boundary. An absolute
path naming a file inside the captured repository is also labeled outside by this
classification. A later boundary-enforcing checker would consequently apply
an additional restriction that the user did not select.
**Evidence:** For repository directory `/checkout/project` with captured
`README.md`, the resolver classifies `/checkout/project/README.md` as
outside-checkout. An overlay test confirmed this. PromptKit's starting checker
uses resolved containment and accepts an existing absolute destination within
its checkout; the user selected that convention as the starting specification.
The implementation plan calls for checkout-boundary classification and
committed-file access, without establishing an absolute-path prohibition.
**Required remediation:** Map absolute destinations and symlink targets that
name paths within the captured repository directory back to candidate-relative
paths, then resolve against committed entries. Classify actual external paths
as outside without reading them. A different absolute-path policy requires an
explicit scope decision.
**Verification:** Cover absolute direct destinations and absolute symlinks
inside and outside the captured root, including similar root prefixes. Use
committed entries for existence; neighboring files remain irrelevant.
## 6. P3: Future Go command behavior appears in implemented internal documentation
**Resolution:** Addressed. The internal guide describes current environment
reuse and links to the roadmap for proposed command coordination and Markdown
behavior. Its implemented executor, candidate-file mechanics, and checker
integration guidance remain in place. Local links and fragments were checked.
**Owner:** [shared validation guide](../internal/validation.md),
`Prospective Go command recipe`.
The section describes race, separate vet, and package-compilation behavior that
has not been implemented. Although it is labeled prospective, the
[documentation policy](../policy/documentation.md) assigns future behavior to
`docs/roadmap/` and limits other documents to their implemented boundary.
**Required remediation:** Replace the future command recipe with a link to the
roadmap's coordination design. Keep the internal guide's implemented executor,
environment reuse, candidate-file mechanics, and checker integration procedure.
Document exact command behavior in its canonical owners when the capabilities
land.
**Verification:** Review the guide against the current executor and check its
links. Preserve the distinction between foundation completion and completed
optional features.
## Foundation review evidence and limits
- `go test ./...` passed; unchanged packages used cached results.
- `go run ./internal/apicheck` passed.
- Recent production-code, test, and documentation diffs were inspected. The
existing tests protect default execution, callback failure handling, facade
error identity, publication state, and representative committed-tree cases.
- Temporary Go overlays outside the worktree reproduced the additional cases
above against current code. Their expected failures are review evidence,
not changes to the committed test suite.
- No new universal release gate, public validator-registration framework,
consumer-check requirement, or change to publication completion was found.
Finding 5 identifies an additional boundary restriction; findings 1–4
identify failures to satisfy the intended behavior. Finding 6 concerns
documentation ownership.
This review evaluates the completed foundation. It does not establish that the
remaining v0.2.0 features have been implemented or that their eventual checker
semantics are verified.
## v0.2.0 architecture assessment
The intended package and ownership boundaries are implemented. The root
`releasekit` facade owns requests, validation selections, diagnostics, and
publication/delivery outcomes. The separate `buildinfo` facade depends on the
standard library and `internal/runtimeinfo`; its graph remains independent of
orchestration and the new parsing dependencies.
`Check` and `Publish` use the same request conversion and
`internal/candidate.Validate`. `internal/validation` coordinates one native
test suite, selected full vet and static package compilation, and selected
committed-file checks. Formatting, distribution, file-size, and Markdown checks
share candidate inventory and lazy blob access. Goldmark and `x/mod/modfile`
remain internal dependencies. Product callbacks, artifact building and version
injection, publication guards/recovery, and hosted delivery retain their owners.
The expected push URL is compared exactly in publication preflight; local
checking ignores it. The CLI owns profile conversion, presentation, and exits.
The consumer Woodpecker template verifies tagged source before selecting the
tool through temporary module metadata. Audita's local wrapper and workflow
adopt that approach in commit `17421bc`; the inspected declarations preserve
its existing dependency version, image, secret name, and release profile.
All ten feature areas are present. The review identified three behavior gaps
within Markdown validation and self-bootstrap. Finding 8 imposed unrequested
existence/boundary checks on image alternative text. Finding 9 effectively
required a prepopulated tool module cache. The review found no
additional public package, validator-registration framework, universal optional
check, arbitrary inspection limit, or change to source-publication completion.
## 7. P2: Heading anchors omit autolink display text
**Resolution:** Addressed. Heading extraction includes autolink display labels.
Parser coverage includes URL/email headings and surrounding text; committed
fragment checks cover both forms. The new cases failed before the fix, and
`go test ./internal/validation` passed afterward.
**Owner:** [Markdown heading extraction](../../internal/validation/markdown.go),
`markdownHeadingText`, lines 115–139.
The agreed convention derives anchors from visible heading text. The extractor
handles text and code spans and recurses through formatting nodes, but Goldmark's
`ast.AutoLink` stores its visible text in `Label` and has no text children.
URL and email autolinks therefore disappear from the heading before slugging.
**Evidence:** A temporary overlay probe against the current checker used:
```markdown
# <https://example.invalid>
[section](#httpsexampleinvalid)
```
The computed anchor was the empty string, and the section link failed with
`heading anchor does not exist`. The same failure occurred for a heading
`# <maintainer@example.invalid>` and fragment `#maintainerexampleinvalid`.
Existing anchor tests cover text, code, formatting, entities, Unicode, and
duplicates, while existing autolink coverage only checks URL extraction.
**Required remediation:** Include an autolink's display label in heading text
before applying the existing normalization and duplicate-anchor algorithm.
Preserve the current HTML, code-span, and ordinary link-label handling.
**Verification:** Add parser coverage for URL and email autolinks inside headings,
including surrounding text, and a representative committed-fragment check.
Keep external destination availability outside this offline capability.
## 8. P2: Image alternative text creates additional release requirements
**Resolution:** Addressed. Destination extraction records an image source and
skips its alternative-text children. Regression coverage exercises nested
images/links, missing anchors, and outside-checkout label syntax under both
boundary settings. Real linked images still report missing sources and link
targets. The regression failed before the fix, and
`go test ./internal/validation` passed afterward.
**Owner:** [Markdown link extraction](../../internal/validation/markdown.go),
`parseMarkdown`, especially the image branch and AST traversal at lines 65–110.
After recording an image destination, the walker continues through the image's
label children and records nested link/image syntax as independent destinations.
Those children contribute plain alternative text to the rendered image. The
checker consequently requires files and anchors that the document does not
navigate to or load, and boundary enforcement can also reject those labels.
**Evidence:** With only `image.png` committed, this valid Markdown is rejected
for missing `missing.png`:
```markdown
![![inner](missing.png)](image.png)
```
Goldmark's own renderer produces `<img src="image.png" alt="inner">`.
Similarly, `![[label](missing.md)](image.png)` renders an image with alternative
text `label`, while ReleaseKit records and validates `missing.md` as a link.
Temporary overlay probes confirmed both extraction and rendering; the first
case also reproduced the checker failure against committed Git contents.
**Required remediation:** Record the outer image destination and treat its label
as alternative text during destination extraction. Preserve real linked images:
`[![logo](badge.png)](guide.md)` has an image source and an enclosing link target
that both require validation. This change removes the unintended requirement
without narrowing supported Markdown syntax.
**Verification:** Cover nested image/link syntax in alternative text, including
an outside-checkout label destination under boundary enforcement. Verify that
real linked images still report missing image sources and missing link targets.
## 9. P1: ReleaseKit's source-delivery bootstrap fails with an empty module cache
**Resolution:** Addressed. Self-bootstrap uses normal Go dependency access for
its readonly tool build after tag verification. The maintained workflow test
starts with an empty module cache and a local HTTP module proxy, builds a tool
with an external dependency, and verifies execution, clean tagged source, and
temporary-output cleanup. A wrong commit stops before Go or proxy access. The
regression failed before the fix and passed afterward. The delivery guide now
describes tool dependency access and its separation from offline validation.
The uncached offline suite, `go run ./internal/apicheck`, and local documentation
link/fragment checks passed after all three fixes.
**Owners:** [source-delivery workflow](../../examples/woodpecker/releasekit-source-delivery.yml),
lines 27–31; [workflow verification](../../cmd/releasekit/woodpecker_test.go),
`TestWoodpeckerSourceDeliveryTemplateSelfBootstraps`; and the
[delivery guide](../consumers/delivery.md#woodpecker-tag-workflows).
The source template sets `GOPROXY=off` and `GOSUMDB=off` before building the tool
and provides no tool dependency preparation. ReleaseKit now imports Goldmark and
`x/mod` through validation, so the standard Go image needs those modules to build
`./cmd/releasekit`. On a fresh runner the workflow stops before `ci-deliver` and
the hosted note page is never delivered. The previous dependency-free tool did
not expose this problem. The guide describes cache mounts as optional, while
the current implementation effectively requires a prepopulated module cache.
**Evidence:** Running the template's build command against `edd670e`, with a
temporary empty `GOMODCACHE` and its workspace/toolchain/proxy settings, returned
exit 1. Both dependencies reported `module lookup disabled by GOPROXY=off`.
The existing self-bootstrap test builds a synthetic command that imports only
`fmt` and `os`, so it cannot detect missing tool dependencies.
**Required remediation:** Allow ordinary tool dependency preparation after tag
verification. A network-capable readonly tool build or an explicit preparation
step can populate the module cache while preserving tagged source. Honor normal
Go proxy/private-module/cache settings. Keep offline candidate validation and
application artifact compilation in their existing owners. Update the template
and guidance together; a prewarmed cache remains an optional deployment choice.
**Verification:** Exercise the maintained self-bootstrap commands with an empty
module cache and a local module proxy serving a real external tool dependency.
Verify preparation follows tag/commit verification, the executable runs, tagged
module files remain unchanged, and temporary output is cleaned up. Existing
delivery tests retain ownership of hosted page behavior.
## v0.2.0 review evidence and limits
- Reviewed recent production, test, template, and documentation changes and
their shared request, execution, and dependency boundaries.
- `GOPROXY=off GOSUMDB=off GOTOOLCHAIN=local go test -count=1 ./...` passed.
- `go run ./internal/apicheck` passed. Direct dependency inspection of
`buildinfo` confirmed its standard-library/runtime-only graph.
- Inspected Audita commit `17421bc` and its actual local and CI bootstrap files.
- Temporary overlay tests outside the worktree reproduced findings 7 and 8.
Goldmark's renderer confirmed the alternative-text semantics. A separate
temporary empty module cache reproduced finding 9. These probes are review
evidence; production code and the committed test suite remain unchanged.
- Existing tests passed but omitted these cases at review time. The resolutions
above record completed fixes and regression coverage.

View File

@@ -1,299 +0,0 @@
# ReleaseKit v0.2.0 roadmap
Status: Implemented and locally verified. Stages 1–15 are complete; see the
[implementation plan](implementation.md) for the completion record. Audita's
module-selected local and CI integration is committed at `17421bc`. Broader
consumer adoption, architecture review, hosted deployment, and release tagging
remain separate work.
This roadmap catalogs improvements to ReleaseKit v0.1.0. Each item records its
rationale, target state, and relevant scope decisions. The
[implementation plan](implementation.md) owns the staged work and verification
evidence.
The priorities in [ADR 0002](../adr/0002-routine-releases.md) guide this work:
minimize application-owned release code, duplicated mechanics, and routine
developer decisions through a standard, minimal API.
## 1. Establish the shared validation API and profile model
### Rationale and baseline at plan start
The shared validation foundation and its remediation are complete. ReleaseKit
has common candidate execution, facade-owned diagnostics, committed-file
inspection, and a product-specific additional-check extension point. The
optional v0.2.0 capabilities now have functioning API/profile selections and
checker implementations.
The [validation-model roadmap](model.md) records this enabling work and its
rationale. A common model lets consumers declare choices once and receive
ReleaseKit's shared execution and coordination.
### Target end state
A facade-owned validation specification and corresponding profile object
express the agreed optional checks with consistent defaults and behavior.
ReleaseKit owns coordination and diagnostics through the existing `Check` and
`Publish` operations. Product-specific checks, publication expectations,
artifact builds, and delivery retain their distinct responsibilities.
The implemented API and profile provide equivalent declarations for the
validation capabilities below through the existing shared foundation.
## 2. Use the application's module-selected ReleaseKit version in CI
### Rationale and baseline at plan start
At the v0.1.0 planning baseline, the maintained CI template selected ReleaseKit
through a separate `RELEASEKIT_VERSION` value and invoked its command with an
explicit `@version`.
Audita demonstrates the resulting duplication: its local release wrapper builds
ReleaseKit using the application's module dependencies, while CI independently
selects ReleaseKit v0.1.0. A dependency upgrade can leave CI using an older tool.
Maintainers must remember to coordinate both declarations.
Using the application's module-selected version for both paths gives upgrades
one dependency declaration and keeps local publication and CI delivery aligned.
Stages 12–14 changed the maintained template and Audita's workflow to this
approach; the current [adoption guidance](../consumers/delivery.md#woodpecker-tag-workflows)
describes it.
### Target end state
For applications that declare ReleaseKit as a module dependency, the standard
CI integration uses the ReleaseKit version selected by the released source's
module configuration. The workflow verifies the triggering tag and commit and
checks out that source before resolving and building the release tool.
Local publication and CI delivery use the same module-based selection approach.
Updating the application's ReleaseKit dependency updates both paths. The
consumer workflow supplies the CI event, environment, and credentials; the
application profile continues to supply product and delivery settings.
Tool bootstrap and dependency preparation remain distinct from offline
validation and builds. Source publication and hosted delivery retain separate
outcomes. ReleaseKit's own source bootstrap and consumers using a separately
installed tool remain supported use cases.
Audita adopted the module-based integration as the first consumer migration in
commit `17421bc`.
The maintained template and Audita's first migration now use one ReleaseKit
module declaration for local and hosted integration.
## 3. Select one base test invocation, including race testing
### Rationale and baseline at plan start
ReleaseKit always runs its standard offline Go suite before additional product
checks. ConfigKit selects race-enabled testing for its release validation.
PromptKit requests the same capability for its concurrent execution and
backend-capacity management.
Expressing that selection as an additional check would execute the suite twice
and leave the application coordinating shared validation mechanics.
A small declarative selection lets consumers retain their chosen validation
while ReleaseKit owns the procedure and runs the suite once.
### Target end state
The public Go API and profile support selecting race testing for the single
base Go test invocation. With no selection, the current default applies.
`Check` and `Publish`, including their CLI forms, use the same selection.
ReleaseKit constructs the invocation and owns candidate capture, full-suite
scope, offline execution, ordering, and failure reporting. Additional product
checks remain available after shared validation. Selecting race testing reports
unavailable prerequisites clearly and preserves the selected validation.
The API and profile now let ConfigKit and PromptKit select a race-enabled suite
once and remove the corresponding consumer-owned test invocation when they
adopt the selection.
## 4. Offer a separate full vet pass
### Rationale and baseline at plan start
ConfigKit and PromptKit currently run a full `go vet` pass in addition to their
test suites. ReleaseKit relies on test-time vet unless a consumer supplies an
additional command. That leaves consumers coordinating vet coverage and
repeated work.
### Target end state
A public API and profile option selects a separate full vet pass within shared
candidate validation. ReleaseKit automatically omits test-time vet when that
pass is selected. The base suite runs once, followed by the selected vet pass,
with failures reported through the ordinary check and publication flow.
The option preserves current defaults for consumers that leave it unset.
ReleaseKit owns the offline command and its coordination with base testing.
The API and profile now support replacing ConfigKit's and PromptKit's separate
vet commands and manual test-time vet settings with one declarative choice.
## 5. Verify package compilation for selected targets
### Rationale and baseline at plan start
ReleaseKit's existing build declaration selects executable artifacts and their
delivery. ConfigKit is a source-only library that checks package compilation
for Linux amd64 and arm64. Its current release script owns the cross-target
build loop even though the underlying mechanics are shared across consumers.
### Target end state
The public API and profile can declare optional package-compilation targets
independently of executable artifacts. ReleaseKit compiles the module's packages
for those targets within candidate validation, after the selected test and vet
work. `Check` and `Publish` share this behavior and report compilation failures
through their existing outcomes.
Package compilation uses the existing pure-Go build baseline (`CGO_ENABLED=0`).
A source-only library retains page-only delivery. Package-compilation checks
require no binary name, version smoke command, or artifact manifest. Successful
compilation establishes build compatibility; execution on a target remains a
separate concern. Consumers select targets for their support commitments.
The API and profile now let ConfigKit declare its two Linux targets and remove
its release-specific compilation loop when it adopts the selection.
## 6. Check an optional expected publication endpoint
### Rationale and baseline at plan start
ReleaseKit resolves and captures the selected remote's push endpoint and
rechecks it during publication. ConfigKit additionally declares an expected
authoritative destination in its release script. Retaining that expectation
outside ReleaseKit would duplicate destination-resolution work.
### Target end state
An optional public API and profile setting declares the expected publication
destination. `Publish` compares it exactly with the effective push URL resolved
by Git before candidate validation and publication. A mismatch produces a clear
failure.
The existing captured-endpoint, recheck, and recovery machinery continues to
govern the operation.
The expectation applies to the publication destination. Fetch configuration
retains its existing semantics, and `Check` retains its local-only behavior.
Consumers that omit the expectation keep the current remote-selection model.
Consumers can now declare the intended push destination without maintaining
their own publication preflight logic.
## 7. Offer Go formatting validation
### Rationale and baseline at plan start
PromptKit maintains a shell loop that enumerates tracked Go files and checks
their formatting. ReleaseKit currently leaves this generic mechanism to
consumer-owned additional checks. Shared support would remove that script
fragment and provide consistent diagnostics across applications.
### Target end state
An optional public API and profile selection enables formatting validation for
tracked Go source using ordinary `gofmt` conventions. `Check` and `Publish`,
including their CLI forms, apply the same check within candidate validation.
The check runs offline, reports affected paths, and leaves files unchanged.
Consumers select the capability once and rely on ReleaseKit for enumeration,
format checking, and failure reporting. Existing validation defaults remain
unchanged when the option is unset.
PromptKit can now remove its local formatting loop and select the shared check
through its release profile when it adopts the capability.
## 8. Offer offline Markdown-link validation
### Rationale and baseline at plan start
PromptKit's largest generic validation block is a local Markdown-link checker.
It owns parsing, destination resolution, heading anchors, and diagnostics.
ReleaseKit currently leaves these mechanics to consumer-owned additional checks.
Sharing the check would remove a locally maintained parser and give consumers
one documented convention for validating navigation within their documentation.
Consumers that require documentation to remain within the checkout also need
a shared option to enforce that boundary.
### Target end state
An optional public API and profile selection checks tracked Markdown documents
for local destinations and supported heading fragments within the checkout.
`Check` and `Publish`, including their CLI forms, use the same offline,
read-only validation and report the source file, location, and target of each
problem.
ReleaseKit owns a documented link and heading-anchor convention that consumers
can follow, using PromptKit's current conventions as the starting specification.
The supported behavior accounts for common inline and reference links, code
examples, escaped destinations, and duplicate headings. External
website availability is outside the check's scope.
By default, links outside the checkout are classified and reported as outside
the shared check's scope. An optional setting makes local destinations outside
the checkout a validation failure, using the same parser and diagnostics.
Boundary validation remains independent of neighboring checkout contents.
Existing validation defaults remain unchanged when the option is unset.
PromptKit can now replace its local Markdown checker with a profile selection
and rely on shared parsing rules and diagnostics when it adopts the capability.
## 9. Offer module-distribution checks
### Rationale and baseline at plan start
PromptKit checks that released source is independent of neighboring checkouts
by excluding committed workspace files and local filesystem module replacements.
ReleaseKit's offline Go environment controls validation execution; inspecting
the distributed candidate requires separate checks that consumers currently own.
Shared inspection would remove these local checks and provide consistent
diagnostics for consumers with the same distribution requirements.
### Target end state
The public API and profile offer independently selectable rules for committed
workspace files (`go.work` and `go.work.sum`) and local filesystem replacements
in committed module declarations. Each selected rule reports offending paths
and, where applicable, module directives through shared candidate validation.
The replacement rule permits versioned module replacements. Workspace use and
vendoring retain their existing behavior unless affected by a selected rule.
The checks inspect candidate contents independently of the Go test environment.
`Check` and `Publish`, including their CLI forms, apply the same offline,
read-only checks. Omitted selections preserve current defaults.
PromptKit can now declare its distribution requirements and remove the
corresponding consumer-owned checks when it adopts the selections.
## 10. Offer an individual-file-size check
### Rationale and baseline at plan start
Unintentionally committed binaries and large assets can increase the size of
released source. ReleaseKit currently leaves file-size inspection to consumers.
A shared check would catch oversized files while allowing each consumer to
declare its intended size limit and exceptions.
### Target end state
An optional public API and profile selection checks individual tracked files
in the current candidate. When enabled, the default limit is 1 MB (1,000,000
bytes); consumers can configure the limit and explicit repository-relative
path exclusions for intentional large files. A file exceeding the limit fails
validation with its path, observed size, and configured limit.
The check measures file size. Small binaries remain subject to ordinary review.
Its scope is the current candidate's files; historical objects and aggregate
repository size remain separate concerns. `Check` and `Publish`, including
their CLI forms, use the same offline, read-only inspection. The check is
disabled by default.
Consumers can now catch oversized candidate files through a small declaration
and remove corresponding local inspection code when they adopt the check.