Write the staged audit remediation plan

This commit is contained in:
2026-11-01 21:08:40 +00:00
parent 59bb0745e7
commit 730e6380c8
2 changed files with 516 additions and 241 deletions

View File

@@ -1,269 +1,108 @@
# Audit remediation roadmap
## Status and baseline
## Status and scope
Proposed on 2026-11-01 against `899e7f1`. Implementation has not started.
Agreed remediation target as of 2026-11-01. Implementation is pending against
baseline `59bb074`.
This roadmap consolidates all three findings in the
[functional correctness audit](correctness.md) and all five findings in the
[interface audit](interfaces.md). Those documents retain the reproduction
circumstances and evidence. This document owns the proposed corrections,
implementation sequence, and completion criteria.
This roadmap defines the desired behavior and scope for all three findings in
the [functional correctness audit](correctness.md) and all five findings in the
[interface audit](interfaces.md). Those documents retain their reproduction
circumstances and evidence. The [implementation plan](implementation.md) owns
staging, code changes, regression checks, and completion tracking.
The baseline has separate orchestration and runtime facades, internal candidate,
validation, build, preparation, and publication services, and shared delivery
policy behind the neutral forge boundary. The corrections fit those existing
owners. The ordinary and race-enabled offline suites and compiled boundary
checks passed during the audits; they do not cover all identified defects.
## Purpose and architectural scope
## Targeted end state
ReleaseKit owns routine release mechanics so consumers supply product facts and
product-specific checks with little release-related code. As recorded in
[ADR 0002](../adr/0002-routine-releases.md), internal complexity is justified
when it reduces caller-owned release work. In particular, callers can reuse a
repository location across preparation and subsequent orchestration without
normalization code or another configuration option.
- Incomplete authoritative tag observations retain an unknown publication
outcome after a failed push acknowledgment.
- Dependency preparation uses ordinary module discovery from its selected
directory despite an inherited or persistent `GOFLAGS=-modfile=...` setting.
- Candidate checking establishes that its derived release tag is a valid Git
ref before accepting it as an unused local tag.
- Cancellation during build source inspection remains identifiable through
the returned error chain.
- Asset operations classify a missing service-provided release identity as a
malformed observation. Existing page-only reads retain their current minimal
response requirements.
- Preparation resolves the containing checkout root automatically, matching
other orchestration operations. Standalone preparation retains the supplied
directory as its base.
- Delivery documentation accurately describes identity requirements for empty
prebuilt results and credential preflight before full artifact verification.
The existing public orchestration and runtime facades and internal package
boundaries remain appropriate. These corrections use the existing public types,
receipt schema, module/tag conventions, and publication/delivery model.
These corrections preserve the existing public types, receipt schema, release
identity conventions, and publication/delivery model. Work stays focused on the
reported cases: Git supplies ref-name rules; local builds retain their current
filesystem choices; preparation retains network access and caller operational
settings; and page-only reads retain their current field requirements. Existing
source-verification observations and preflight evidence limits continue to
apply.
## Desired behavior
## Finding coverage
### Incomplete publication observations
| Audit finding | Work unit |
| --- | --- |
| Correctness 3: incomplete reconciliation becomes conflict | 1 |
| Correctness 2: ambient module-file redirection | 2 |
| Correctness 1: invalid derived tag accepted by Check | 3 |
| Interfaces 1: source-inspection cancellation identity | 4 |
| Interfaces 2: unusable successful release handle | 5 |
| Interfaces 3: preparation repository anchor | 6 |
| Interfaces 4: empty-prebuilt GoDoc | 7 |
| Interfaces 5: hashing before every hosted request | 8 |
After a failed push acknowledgment, a query containing only a peeled tag record
provides insufficient evidence about the exact annotated tag object.
Publication remains unknown and retains the original push cause. A complete
response works in either record order. Existing exact-object matches, genuinely
absent tags, different objects, and exact-object-only responses retain their
current outcomes.
## Implementation sequence
### Dependency preparation and ambient module files
Each unit includes its relevant validation and canonical documentation updates.
Complete the units as focused changes using the repository's ordinary commit
policy. Record completion here and update the corresponding audit finding's
status when its correction is verified. The sequence requires local repository
work and automated checks; it adds no pilot, intermediate release, or external
review step.
Preparation uses ordinary Go module discovery from its selected directory even
when inherited or persistent `GOFLAGS` selects an alternate module file. It
retains other effective flags and existing proxy, private-module,
authentication, checksum, cache, workspace, and local-toolchain behavior.
Go retains its existing checksum-update behavior, including upward declaration
discovery for standalone package-directory preparation.
### 1. Preserve uncertainty for incomplete remote tag observations
### Valid candidate tags
**Status:** open. **Source:** correctness finding 3.
Candidate checking establishes that the complete derived release tag is a valid
Git ref before accepting it as unused. Git's own ref rules remain the authority.
The correction concerns release candidate validity; standalone local builds
retain their existing filesystem choices and version rules.
**Owners:** `internal/git` ref parsing and `internal/publish` recovery, with CLI
outcome verification.
### Identifiable build cancellation
Finalize tag observations after the entire query response has been parsed.
A peeled record without its exact tag-object record is an inconsistent or
unreadable observation. Use the existing ref-state/error vocabulary so
publication reconciliation retains `PublicationUnknown` and the original push
cause. Keep valid records usable in either order; a peeled record may precede
its exact tag record.
Cancellation or deadline expiration during build source inspection remains
identifiable through the error chain. Ordinary Git failures retain useful
diagnostics, and partial results retain already completed artifacts.
**Completion evidence:** a local Git recovery fixture performs the intended
push, loses its acknowledgment, and supplies only the peeled record. The public
result is unknown, the cause remains inspectable, and the CLI uses status 3.
Existing exact-match, absent-tag, and different-object recovery outcomes still
work, along with complete records in either order. Query finalization retains
both genuinely absent results and valid exact-object-only results.
### Gitea release identity at asset use
### 2. Override the redirecting module-file flag during preparation
A backend-produced handle with a missing or nonpositive service release ID is a
malformed observation when an asset operation needs that ID. A handle of the
wrong type remains an invalid request. Locally rejected uploads retain
not-attempted write evidence.
**Status:** open. **Source:** correctness finding 2.
Existing page-only reads retain their current minimal response requirements.
This correction adds no universal positive-ID or page-URL requirement to a
successful release lookup and preserves existing delivery progress semantics.
**Owners:** `internal/gotool` dependency preparation and its CLI integration.
### Consistent preparation paths
Give `go mod download` an explicit empty `-modfile` argument. Command-line flag
precedence overrides an inherited or persistent alternate module-file selection
and restores Go's ordinary module discovery. A local Go 1.26.10 probe verified
this behavior from both a module root and its package subdirectory, with each
flag source.
Within a Git checkout, preparation resolves its containing root automatically
and interprets `ModuleDir` relative to that root. Empty selection means the
root module, consistently with `Check`, `Build`, and `Deliver`. This includes
calls made from checkout subdirectories and filesystem aliases.
This is narrower than replacing all `GOFLAGS` or disabling the persistent Go
environment. Preserve the other effective flags and the current proxy,
private-module, authentication, checksum, cache, workspace, and local-toolchain
behavior. Directory selection follows the checkout-root resolution in unit 6;
within the resolved selection, preserve Go's existing checksum-update behavior
and ordinary module discovery. Standalone package-directory preparation retains
upward declaration discovery.
Outside a Git checkout, preparation retains the supplied directory as its base
and the existing relative module selection. Standalone use remains supported.
It retains its existing Go-only use when Git is unavailable and the directory
can be identified as outside a checkout.
Discovery distinguishes this case from cancellation, permission failures, and
other Git errors, which remain failures. Existing symlink and containment rules
and the CLI's root normalization continue to apply.
**Completion evidence:** exercise both inherited and persistent `-modfile`
settings against different module graphs. Preparation consumes the ordinary
selected graph, including standalone use where Go discovers its declaration
above the working directory. Verify the existing network/private-module/cache
settings remain available and that unrelated flags retain their behavior. Use
test-owned `GOMODCACHE` directories for every synthetic-module download. Keep the
existing binary CI preparation ordering and cancellation/error propagation covered.
### Empty prebuilt results
Document the preparation flag override in its GoDoc and the
[Go environment guide](../internal/gotool.md). Update command fixtures for the
intentional invocation change; use graph-selection behavior as the regression
assertion rather than relying only on argument matching.
Page-only callers ordinarily omit both `Build` and `Prebuilt`. A supplied
prebuilt result with no targets or artifacts still identifies the matching
repository, module, tag, commit, and version. Documentation states that
requirement clearly. A zero `BuildResult` gains no new compatibility behavior.
### 3. Validate derived tag names during candidate capture
### Artifact-verification order
**Status:** open. **Source:** correctness finding 1.
Documentation consistently describes cheap local/source checks, read-only
credential preflight, full local artifact verification, and then hosted writes.
Digest verification completes before hosted writes. Credential preflight can
precede that verification and confirms only the access evidence exposed by the
backend.
**Owners:** `internal/candidate` and the shared Git ref-validation boundary.
## Scope boundaries
Apply the existing Git ref-name check to the complete derived tag before local
tag absence is accepted during candidate capture. Keep name validity distinct
from the existence query: exit status 1 from `show-ref` alone cannot establish
both. Reuse the same rule at any candidate recheck boundary that needs it,
without duplicating Git's character rules in ReleaseKit.
**Completion evidence:** public `Check` fails for derived tags containing the
reported space and `.lock` directory components, before validation callbacks
run. Ordinary root, nested, slash-containing, and supported major-version tags
still work. Preserve the captured identity in failure results and existing
publication guards. Standalone `Build` retains its current local-build behavior.
Update candidate guidance only as needed to explain the local failure; this
unit introduces no new version syntax or blanket directory-name policy.
### 4. Preserve cancellation during build source-state inspection
**Status:** open. **Source:** interfaces finding 1.
**Owner:** `cleanSource` in `internal/build/build.go`.
When the Git status command fails and its operation context is canceled or has
expired, retain that context error in the returned chain, following the
neighboring build helpers or the existing process boundary. Ordinary Git errors
retain their current useful diagnostics. Keep the partial build result intact.
**Completion evidence:** interrupt source inspection before compilation and
after completed artifacts exist. `errors.Is` identifies cancellation, and a
focused deadline case identifies deadline expiration. The latter build result
retains its completed artifacts. Retain existing managed-delivery cancellation
and temporary-output cleanup coverage; add an integration case only if the
helper-level/public-build coverage leaves a distinct propagation risk.
### 5. Classify incomplete Gitea handles at asset use
**Status:** open. **Source:** interfaces finding 2.
**Owner:** Gitea backend handle interpretation in `internal/gitea/backend.go`.
Keep `FindRelease` usable for page-only reads with the existing minimal response
fields. When an asset operation needs the positive release ID and a
backend-produced handle lacks it, return the existing malformed-response
classification with a useful missing-identity diagnostic. Distinguish this from
a handle of the wrong type, which remains an invalid request. Apply the
distinction consistently to listing and uploading; an upload rejected locally
retains not-attempted write evidence.
**Completion evidence:** feed a lookup response with a missing, zero, or negative
ID through the backend. Page-only matching still succeeds under its existing
rules; binary asset use fails with the malformed classification before an
asset request. Wrong-type handles remain invalid requests, valid IDs continue
to work, and existing page/write-progress outcomes remain accurate. Retain
`TestBackendFindReleaseDoesNotRequireWriteResponseFields` or equivalent
behavioral coverage. Update the integration guide's error description as needed.
### 6. Resolve preparation's checkout root automatically
**Status:** open. **Source:** interfaces finding 3.
**Owners:** the preparation facade, shared Git/selection boundaries,
`DependencyPreparationRequest` GoDoc, and consumer preparation guidance.
Resolve the containing checkout root from `RepositoryDir` before applying
`ModuleDir`. Within a checkout, interpret `ModuleDir` relative to that root;
empty selection means the root module, consistently with `Check`, `Build`, and
`Deliver`. For standalone use outside a Git checkout, retain the supplied
directory as the base and apply the existing relative module selection there.
Keep this normalization inside ReleaseKit so callers can reuse a repository
location across operations without normalization code or a new setting. This
applies [ADR 0002's priority](../adr/0002-routine-releases.md) of minimizing
caller-owned release work. Retain the CLI's existing root normalization and
shared symlink/containment rules. Distinguish standalone use from discovery
failures; cancellation, permission failures, and other Git errors remain
errors rather than silently selecting another base.
**Completion evidence:** cover calls from the checkout root and its
subdirectories, default and explicit nested-module selections, symlink-resolved
paths, standalone directories, and discovery failures. Include combined coverage
with unit 2 showing that preparation and subsequent orchestration select the
same module from the same repository location and `ModuleDir`, even with an
ambient alternate module file. Preserve standalone package-directory discovery
and the existing CLI behavior. Update GoDoc and consumer guidance to describe
the selected interpretation and the change for checkout-subdirectory callers.
### 7. Clarify empty prebuilt results
**Status:** open. **Source:** interfaces finding 4.
**Owners:** `DeliveryRequest` GoDoc and
[delivery consumer guidance](../consumers/delivery.md#go-api).
Describe a page-only prebuilt result as having no targets or artifacts while
still identifying the same repository, module, tag, commit, and version.
Present nil `Build` and nil `Prebuilt` as the ordinary page-only request.
Keep the existing identity checks and matching-empty-result behavior.
**Completion evidence:** the exported GoDoc and guide agree with
`validateBuildIdentity`, and no wording implies that `&BuildResult{}` is accepted.
Review existing page-only and prebuilt identity coverage; this wording correction
does not require a new zero-result compatibility path or additional tests.
### 8. Correct the documented artifact-verification order
**Status:** open. **Source:** interfaces finding 5.
**Owner:** the prebuilt paragraph in
[delivery consumer guidance](../consumers/delivery.md#go-api).
Replace the claim that digest checks precede every hosted request with the
selected order: cheap local/source checks, read-only credential preflight,
full artifact verification, then hosted writes. Keep the concise statement that
local verification completes before hosted writes consistent across GoDoc and
current guidance.
**Completion evidence:** affected descriptions agree with the implementation,
[ADR 0006](../adr/0006-neutral-forge-delivery-and-preflight.md), and existing
`TestAutomaticPreflightFailurePrecedesPrebuiltArtifactHashing` coverage. This
unit changes wording and preserves the current operation order.
## Final verification and completion
Run focused regression checks with each behavioral correction. Once the units
are complete, run the ordinary offline suite and compiled boundary check:
```sh
GOTOOLCHAIN=local GOPROXY=off GOSUMDB=off go test ./...
GOTOOLCHAIN=local GOPROXY=off GOSUMDB=off go run ./internal/apicheck
```
Use race-enabled checks for affected subprocess/cancellation and adapter paths
where the change warrants them. The audit's existing full race-suite result is
baseline evidence; repeat broader checks when implementation changes or failures
justify it. Keep fixtures local, deterministic, and independent of real
credentials, with isolated synthetic module caches.
Review public errors/results, CLI statuses, and canonical documentation against
the completion evidence above. Validate affected links and prose whitespace.
Mark this roadmap complete when all eight work units and their corresponding
audit findings have been addressed and verified, including the selected path-anchor
interpretation. The initial pass that creates this roadmap changes only this
document.
The corrections retain the current source-verification observations, bounded
publication recovery, explicit forge selection, and additive hosted delivery
policy. They introduce no new public configuration knobs, compatibility shims,
release procedure, pilot release, or intermediate human-review requirement.
The targeted sprint reaches this complete end state through local repository
work and automated validation.