Plan the shared validation foundation
This commit is contained in:
393
docs/roadmap/implementation.md
Normal file
393
docs/roadmap/implementation.md
Normal file
@@ -0,0 +1,393 @@
|
||||
# Validation model implementation plan
|
||||
|
||||
Status: Proposed implementation sequence; the API rollout question below needs
|
||||
resolution before the stages that expose optional selections.
|
||||
|
||||
This plan implements the enabling work in [model.md](model.md), guided by
|
||||
[ADR 0003](../adr/0003-declarative-validation.md). It is written for a Luna
|
||||
coding agent executing one stage per prompt, in numerical order. The
|
||||
[v0.2.0 roadmap](v0.2.0.md) owns the scope and defaults of the individual
|
||||
capabilities. Their checker implementations, module-selected CI tooling, and
|
||||
the publication endpoint expectation are subsequent feature work.
|
||||
|
||||
The foundation provides shared execution, diagnostics, committed-file access,
|
||||
and a concrete design for equivalent Go and profile declarations. Completion
|
||||
of this plan establishes that foundation; completion of the individual
|
||||
validation features makes their selections operational.
|
||||
|
||||
## Instructions for each stage
|
||||
|
||||
- Start with [development.md](../development.md), the
|
||||
[agent policy](../policy/agents.md), and the policies applicable to the stage.
|
||||
Inspect the worktree and reuse relevant validation already available.
|
||||
- Use the codebase graph to locate current symbols. The paths below identify
|
||||
the inspected baseline and may change during implementation.
|
||||
- Implement the stage's focused outcome with its behavioral coverage and
|
||||
applicable documentation. Commit completed units according to the agent
|
||||
policy and report the resulting commit and remaining work.
|
||||
- Continue through the sequence using local fixtures and automated checks.
|
||||
Consumer migrations, hosted actions, intermediate releases, and human review
|
||||
are outside these stages. Architecture review and next-step decisions follow
|
||||
the completed implementation.
|
||||
- Keep internal collaboration proportionate: ordinary structs, functions, and
|
||||
explicit conversions. Preserve the root and `buildinfo` facade boundaries.
|
||||
Current API contracts and selected user decisions determine compatibility.
|
||||
|
||||
## Design to carry through the stages
|
||||
|
||||
### Validation declarations
|
||||
|
||||
The intended complete facade shape is:
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
`ReleaseRequest.Validation` holds a `ValidationSpec` value. `Target` is the
|
||||
existing facade-owned operating-system/architecture pair. The corresponding
|
||||
profile keys are `validation.go.race`, `validation.go.full_vet`,
|
||||
`validation.go.compile_targets`, `validation.go_format`,
|
||||
`validation.markdown.enforce_checkout_boundary`,
|
||||
`validation.module_distribution.workspace_files`,
|
||||
`validation.module_distribution.local_replacements`,
|
||||
`validation.file_size.limit_bytes`, and `validation.file_size.exclusions`.
|
||||
Profile compilation targets use the existing `GOOS/GOARCH` string convention.
|
||||
|
||||
Value groups preserve zero-value defaults. A non-nil Markdown or file-size
|
||||
group enables its check; an empty JSON object has the same meaning. Each
|
||||
distribution boolean independently selects inspection for the corresponding
|
||||
distribution rule. Omitted optional groups select current default validation.
|
||||
A nil byte limit selects 1,000,000 bytes when file-size checking is enabled;
|
||||
an explicit zero selects zero bytes. Negative byte limits are invalid because
|
||||
byte counts are nonnegative. Exclusions identify literal repository-relative
|
||||
file or directory paths; a directory exclusion includes its committed
|
||||
descendants. Preserve Git path spelling and use slash-separated paths.
|
||||
|
||||
These declarations describe the complete intended model. Stage 5 implements
|
||||
the rollout selected under Open Questions. Public settings acquire operational
|
||||
behavior with their capability implementations, and current documentation
|
||||
describes the implemented boundary precisely.
|
||||
|
||||
### Execution and ownership
|
||||
|
||||
- `internal/candidate` retains capture and rechecks. Its existing `Validate`
|
||||
entry point delegates shared validation to `internal/validation` and then
|
||||
invokes `AdditionalChecks` once with the captured candidate.
|
||||
- `internal/validation` owns internal settings, the shared executor, command
|
||||
coordination, and diagnostic records. It receives the captured repository
|
||||
directory and commit explicitly. It depends on internal Go and Git helpers;
|
||||
internal production packages retain internal contracts.
|
||||
- Root facade helpers perform request and diagnostic conversions. `Check` and
|
||||
`Publish` share those helpers. The CLI maps its own JSON settings to the root
|
||||
facade in `cmd/releasekit`, preserving its helper-package dependency boundary.
|
||||
- Shared validation runs one base Go suite, selected full vet, selected package
|
||||
compilation, and selected file checks, followed by product-specific checks.
|
||||
Artifact builds retain their existing after-validation position. The
|
||||
foundation executes the currently implemented portion of this procedure.
|
||||
- Native commands retain the current offline environment. Future race
|
||||
selection changes that one suite; full vet adds `-vet=off` to testing and a
|
||||
separate vet invocation. Package compilation uses static target mode with
|
||||
`CGO_ENABLED=0`, independently of artifact declarations.
|
||||
- Preserve fail-stop behavior, context propagation, candidate rechecks, local
|
||||
`Check` behavior, and publication phase/state meanings. Cross-check ordering
|
||||
is internal procedure except for the relationships selected in the roadmap.
|
||||
|
||||
### Diagnostic model
|
||||
|
||||
Add facade-owned `ValidationCheck` (a string type), `ValidationFinding`, and
|
||||
`ValidationError`. The error carries `Check`, `Findings`, and `Cause`, implements
|
||||
`Error() string` and `Unwrap() error`, and is returned as a pointer. Findings
|
||||
carry `Path`, `Line`, `Column`, and `Message`, with `Target` and `Directive` for
|
||||
applicable file checks. A nullable `FileSizeMeasurement` containing
|
||||
`Bytes` and `LimitBytes` supplies structured size details. Each named type
|
||||
belongs to the root facade, with corresponding internal records and conversions.
|
||||
|
||||
Use check identities `go-test`, `go-vet`, `package-compile`, `go-format`,
|
||||
`markdown-links`, `workspace-files`, `local-replacements`, and `file-size` as
|
||||
their checks land. Findings use repository-relative paths and one-based source
|
||||
locations where available; zero locations mean unavailable. Human-readable
|
||||
messages supply detail without a promise of exact wording.
|
||||
|
||||
The shared error represents a failed validation check and preserves its
|
||||
underlying cause. Candidate guards, artifact failures, and publication outcomes
|
||||
retain their existing classification. Existing additional-check errors retain
|
||||
their cause and current wrapping; custom callbacks can return facade errors
|
||||
without losing their identity. Successful out-of-scope Markdown observations
|
||||
will use the existing output writer when that feature lands, leaving successful
|
||||
validation successful.
|
||||
|
||||
### Committed-file access
|
||||
|
||||
Provide an internal candidate-tree reader using the captured commit. Enumerate
|
||||
the full tree with NUL-delimited Git output and record path, object ID, entry
|
||||
kind/mode, and blob size where available. Read blob bytes by object ID, preserving
|
||||
binary bytes and filenames containing spaces, tabs, or newlines. Cache reusable
|
||||
metadata within the validation operation; read contents when needed.
|
||||
|
||||
Regular tracked files include executable files, vendor contents, and nested
|
||||
modules. The inventory applies no feature-specific exclusion. Symlinks expose
|
||||
their committed link text as blob contents. Gitlinks expose the recorded
|
||||
submodule commit and carry no parent-tree file size; the reader enumerates the
|
||||
parent candidate's committed tree. Traversing a submodule checkout would inspect
|
||||
different source and require a separately defined capability.
|
||||
|
||||
Formatting and Markdown source parsing operate on regular source blobs. File
|
||||
size can measure regular and symlink blobs. Supply an internal path-resolution
|
||||
helper for future Markdown destinations that resolves symlink text within the
|
||||
candidate tree, classifies destinations outside the checkout, and reports
|
||||
missing entries or cycles. A submodule's contents are outside the parent
|
||||
candidate's inspected tree. The Markdown feature owns whether a resulting
|
||||
observation is a finding or an out-of-scope report under its selected rule.
|
||||
The helpers read neither symlink targets nor submodule contents from the live
|
||||
filesystem. Their tests use synthetic trees and local Git repositories.
|
||||
|
||||
## Stage 1: Extract the shared validation executor
|
||||
|
||||
**Outcome:** One internal owner runs current default validation, with current
|
||||
candidate and publication behavior preserved.
|
||||
|
||||
1. Inspect `internal/candidate/check.go`, `internal/publish/publish.go`,
|
||||
`internal/gotool`, and their existing tests. Record the capture, validation,
|
||||
additional-check, build, and recheck boundaries in implementation notes.
|
||||
2. Move default Go validation into `internal/validation`. Accept context,
|
||||
captured repository/commit, output, and internal settings explicitly. Start
|
||||
with ordinary zero-value settings and the existing default invocation.
|
||||
3. Keep `candidate.Validate` as the shared bridge used by local checks and
|
||||
publication. Retain additional checks and after-validation builds in their
|
||||
existing responsibilities. Reuse `gotool.New` and its environment handling.
|
||||
4. Consolidate root callback/request conversion where `check.go` and
|
||||
`publish.go` currently duplicate it, using small private helpers.
|
||||
|
||||
**Verification:** Run affected candidate, publication, and facade tests. Add
|
||||
coverage only for gaps in default suite execution, callback ordering, failure
|
||||
stopping before builds/tag creation, and cancellation. Existing publication
|
||||
recovery tests retain ownership of remote-state behavior. Update the internal
|
||||
inventory to reflect the implemented executor.
|
||||
|
||||
## Stage 2: Introduce internal validation diagnostics
|
||||
|
||||
**Outcome:** The executor returns a small structured failure with an underlying
|
||||
cause, independently of public error conversion.
|
||||
|
||||
1. Add internal check identity, finding, measurement, and failure records to
|
||||
`internal/validation`, corresponding to the diagnostic design above.
|
||||
2. Attribute Go setup/test failures to the base test check. Preserve process
|
||||
errors and canceled contexts through `Unwrap` and ordinary wrapping.
|
||||
3. Keep candidate commit context in the candidate wrapper. Keep additional
|
||||
callback errors and artifact failures in their current flow.
|
||||
4. Define one internal formatting routine for the failure's useful summary;
|
||||
avoid duplicating diagnostic prose at each call site.
|
||||
|
||||
**Verification:** Protect `errors.Is` for cancellation and sentinel causes,
|
||||
`errors.As` for subprocess errors, and distinguish shared failures from callback
|
||||
and artifact failures. Test semantic fields and cause preservation rather than
|
||||
complete error text.
|
||||
|
||||
## Stage 3: Add committed candidate-file enumeration
|
||||
|
||||
**Outcome:** File-oriented validators can share an offline reader bound to the
|
||||
captured commit.
|
||||
|
||||
1. Add Git tree/blob operations to the existing internal Git boundary and a
|
||||
small candidate-tree reader used by `internal/validation`. Select a separate
|
||||
internal package only if it clarifies that responsibility.
|
||||
2. Implement enumeration and lazy blob reads according to the committed-file
|
||||
design. Bind queries to the captured commit and preserve Git/process causes.
|
||||
3. Represent regular files, symlinks, and submodules explicitly. Keep feature
|
||||
filtering in the respective future checker.
|
||||
4. Make the reader available to the executor for selected file checks. Default
|
||||
Go-only validation does not need to load all repository blobs.
|
||||
|
||||
**Verification:** Use real temporary Git repositories to show that bytes and
|
||||
sizes come from the selected commit despite changed worktree files, that unusual
|
||||
filenames survive enumeration, and that vendor/nested-module files are present.
|
||||
Include symlink and gitlink entries without requiring a network or neighboring
|
||||
checkout. Confirm inspection leaves repository contents unchanged.
|
||||
|
||||
## Stage 4: Add candidate destination-resolution primitives
|
||||
|
||||
**Outcome:** Future Markdown validation can resolve committed local destinations
|
||||
and classify checkout boundaries without filesystem traversal.
|
||||
|
||||
1. Implement a small internal resolver for repository-relative destinations
|
||||
against the candidate inventory. Keep URL decoding and Markdown parsing in
|
||||
the later Markdown capability.
|
||||
2. Resolve committed symlink text relative to its containing path. Distinguish
|
||||
existing entries, missing entries, outside-checkout paths, submodule contents,
|
||||
and symlink cycles. Classification is data for the caller.
|
||||
3. Preserve ordinary directory destinations where the committed tree contains
|
||||
descendants. Return the resolved identity needed for future fragment lookup.
|
||||
4. Keep inspection errors separate from normal resolution observations.
|
||||
|
||||
**Verification:** Cover relative paths, parent traversal within and outside the
|
||||
root, existing directories, symlinks pointing inside/outside the root, cycles,
|
||||
and submodule traversal. Show identical classifications with neighboring files
|
||||
present and absent. These tests own path resolution; later Markdown tests own
|
||||
syntax, anchors, and selected failure policy.
|
||||
|
||||
## Stage 5: Add facade declarations and shared request conversion
|
||||
|
||||
**Outcome:** The public model has an agreed representation, shared conversion,
|
||||
and cause-preserving errors across `Check` and `Publish`.
|
||||
|
||||
1. Apply the API rollout decision under Open Questions. For the recommended
|
||||
incremental rollout, implement the public diagnostic types now and retain
|
||||
the complete specification above as the blueprint for capability stages.
|
||||
Introduce `ReleaseRequest.Validation`, `ValidationSpec`, and supported
|
||||
settings with the first capability implementation. This avoids exporting an
|
||||
empty placeholder or a selection with no implementation.
|
||||
2. Define internal counterparts for the intended settings where required by
|
||||
actual coordination code. Use explicit field/target conversions through
|
||||
the same root helper for both operations. Add remaining fields as used.
|
||||
3. Convert internal failures into facade-owned `*ValidationError`, using typed
|
||||
recognition and explicit diagnostic-field conversion. Retain the original
|
||||
wrapped error as the cause so candidate context, underlying identity, and
|
||||
`errors.Is`/`errors.As` behavior survive the conversion. Render the useful
|
||||
failure once rather than repeating the same context at each wrapper.
|
||||
4. Keep source publication result conversion unchanged. Extend GoDoc for the
|
||||
errors and any implemented settings, with links to current consumer guidance.
|
||||
5. Extend compiled public API checks for facade-owned named types and dependency
|
||||
direction. Preserve the small independent `buildinfo` dependency graph.
|
||||
|
||||
**Verification:** External-package tests cover typed error access and
|
||||
`errors.Is`/`errors.As` through both facades. Reuse existing integration fixtures
|
||||
for a failing suite and an additional-check cause; avoid a new generic test
|
||||
framework. Run `go run ./internal/apicheck`.
|
||||
|
||||
## Stage 6: Establish equivalent profile mapping and CLI presentation
|
||||
|
||||
**Outcome:** Profile conversion has one owner and the CLI reports structured
|
||||
validation failures consistently while retaining current exit classification.
|
||||
|
||||
1. Inspect `cmd/releasekit/internal/cli/config.go` and
|
||||
`cmd/releasekit/main.go`. Share profile-to-request conversion for check and
|
||||
publication through a small command-layer helper. Preserve additional-command
|
||||
argument/environment handling and build/delivery mapping.
|
||||
2. Under the incremental rollout, add each `validation` setting with its
|
||||
capability. Use CLI-owned JSON structs and explicit conversion at the command
|
||||
boundary. Reuse ordinary JSON decoding and existing unknown-field rejection.
|
||||
The schema blueprint above guides later additions; profile settings acquire
|
||||
behavior at the same time as their Go counterparts.
|
||||
3. Add shared CLI presentation for facade validation errors. Show check identity,
|
||||
useful findings and cause context once. Retain publication phase/state and
|
||||
uncertain-outcome reporting. Present validation failures through the existing
|
||||
status 1 path and malformed profile settings through the existing status 2
|
||||
path; preserve other statuses.
|
||||
4. Update current CLI documentation for the diagnostic behavior that has landed.
|
||||
Update configuration documentation only for implemented settings.
|
||||
|
||||
**Verification:** Exercise default/omitted settings and existing additional
|
||||
commands. Check useful diagnostics and exit classifications for both operations,
|
||||
including a publication validation failure before tag creation. Capability work
|
||||
will add setting-specific mapping/equivalence cases at this established boundary.
|
||||
|
||||
## Stage 7: Record capability integration recipes and consumer shapes
|
||||
|
||||
**Outcome:** Subsequent feature prompts can use the foundation without redesigning
|
||||
its ownership or inventing a public extension framework.
|
||||
|
||||
1. Add focused internal guidance for adding a typed setting, facade/profile
|
||||
conversion, ordinary checker function, identity/findings, and behavioral
|
||||
coverage through the shared executor. Update the inventory and link to that
|
||||
owner for mechanics.
|
||||
2. Record the Go coordination recipe: construct one test command with selected
|
||||
flags; select native full vet when requested; use static package compilation
|
||||
for declared targets after test/vet. Reuse target/environment handling while
|
||||
keeping artifact metadata and version smoke tests in the builder.
|
||||
3. Record how file checks consume the candidate reader and return findings.
|
||||
Keep individual rule semantics in the release roadmap until implemented.
|
||||
Markdown adopts PromptKit's current conventions as its starting specification;
|
||||
parser/anchor implementation belongs to the Markdown feature prompt.
|
||||
4. Add prospective Go/profile examples under this roadmap for the three consumer
|
||||
shapes: Audita with omitted optional validation and existing artifact builds;
|
||||
ConfigKit with race, full vet, and two Linux compilation targets; PromptKit
|
||||
with shared selections plus substantive example assertions in `checks`.
|
||||
Label prospective fields clearly. Maintained copyable consumer examples gain
|
||||
selections when those selections are implemented.
|
||||
5. Compare those declarations with local consumer scripts and profiles read-only.
|
||||
Record which duplicated mechanics each later capability removes. ConfigKit's
|
||||
separate toolchain-support commitments remain consumer compatibility decisions.
|
||||
|
||||
**Verification:** Check the proposed declaration pairs against the schema design
|
||||
and the ownership table in `model.md`. Confirm product checks still fit the
|
||||
existing callback/profile extension point. Runtime tests are self-contained and
|
||||
independent of the three sibling checkouts.
|
||||
|
||||
## Stage 8: Verify and document the completed foundation
|
||||
|
||||
**Outcome:** The enabling refactor is complete, its current boundary is clear,
|
||||
and later feature implementation can proceed through the shared model.
|
||||
|
||||
1. Review the assembled request flow, cause conversions, candidate inspection,
|
||||
CLI presentation, and facade imports against ADR 0003 and the architecture
|
||||
policy. Address concrete defects within the stages' scope.
|
||||
2. Run `go test ./...` and `go run ./internal/apicheck` from the repository root.
|
||||
Add focused integration coverage only for a remaining risk, such as default
|
||||
behavior diverging between API/profile or check/publication paths.
|
||||
3. Update current GoDoc, consumer guidance, CLI reference, and internal owners
|
||||
for implemented behavior. Keep future setting examples and pending capability
|
||||
contracts in roadmaps. Check local links and Markdown whitespace.
|
||||
4. Update `model.md` and this plan's status to identify the completed foundation
|
||||
and the individual capability work remaining in `v0.2.0.md`. A foundation
|
||||
completion is distinct from a completed v0.2.0 release.
|
||||
5. Commit the completed unit and report its verification, remaining capability
|
||||
work, and clean or preserved worktree state. The subsequent architecture
|
||||
review and testing/next-step decisions belong to the user after this sequence.
|
||||
|
||||
## Open Questions
|
||||
|
||||
### Should the complete optional specification be exported in the enabling sprint?
|
||||
|
||||
**Recommendation:** Adopt the complete schema as the design and introduce each
|
||||
public setting with its functioning capability. Execute the shared refactor,
|
||||
diagnostics, candidate readers, and conversion boundaries in this plan first.
|
||||
The first capability then introduces `ValidationSpec` and the request/profile
|
||||
field; subsequent capabilities add their supported selections. This keeps Go
|
||||
and profile behavior equivalent, avoids public placeholders and temporary
|
||||
unsupported-option machinery, and lets each feature own meaningful coverage.
|
||||
The model's full target is reached as the feature implementations land.
|
||||
|
||||
**Viable alternative:** Export the full schema during the enabling sprint and
|
||||
report an explicitly selected, unimplemented check as unavailable before
|
||||
validation proceeds. This establishes the complete configuration shape at once
|
||||
and supports declaration-level integration work. It needs a small temporary
|
||||
internal availability check and clear current documentation; otherwise an
|
||||
enabled selection could be silently omitted. Remove each availability guard
|
||||
when its checker lands. Zero/default declarations continue to work.
|
||||
|
||||
**Another viable alternative:** Include all optional validation checker bodies
|
||||
in this sprint, splitting Markdown parsing and destination/anchor validation
|
||||
into separate additional stages. This delivers a fully operational specification
|
||||
at once and avoids transitional API behavior, while broadening this enabling
|
||||
plan into most of the validation feature work in the release roadmap. It still
|
||||
leaves CI tooling and the expected publication endpoint as separate work.
|
||||
|
||||
Stages 1–4 are independent of this choice. Stages 5–8 describe the recommended
|
||||
rollout. The choice needs resolution before an implementation agent treats the
|
||||
public API rollout as settled.
|
||||
Reference in New Issue
Block a user