Files
releasekit/docs/roadmap/implementation.md

545 lines
33 KiB
Markdown

# Publication reconciliation implementation plan
Status: complete.
Implement the target and resolved decisions in the
[feature roadmap](publication-reconciliation.md). The original
[PromptKit request](promptkit-feature-request.md) supplies consumer context.
This document owns implementation sequence and the concrete interface choices
needed for that work. The feature roadmap already separates capability
requirements from staged implementation and remains the owner of intent.
## Sprint instructions and baseline
Complete Stage 1 through Stage 8 in order in one end-to-end sprint. Intermediate
stages may leave dependent code incomplete. Continue through the stages without
pilot deployments, intermediate releases, external actions, or human review
gates. Follow the repository's agent and testing policies, including its commit
discipline. Mark completed stages in this document as work lands; retain the
remaining instructions until the sprint finishes.
Stage verification sections assign coverage to implement; run those checks as
their dependencies become available and continue through temporary compilation
gaps to the assembled validation in Stage 8.
Start with [development guidance](../development.md) and its applicable policy
reading guide. Relevant implementation locations are:
| Location | Current responsibility |
| --- | --- |
| `publish.go`, `check.go` | Public publication result, publication facade, and shared release request. |
| `internal/publish/publish.go` | Publication orchestration and immediate `reconcilePush` recovery. |
| `internal/git/remote.go`, `internal/git/tag.go` | Scoped endpoints, transport configuration, URL checks, exact refs, and annotated tags. |
| `internal/git/display.go`, `internal/process` | Credential-safe Git diagnostics and cancellable subprocesses. |
| `cmd/releasekit/main.go` | Command dispatch, repository discovery, publication output, and exit codes. |
| `cmd/releasekit/internal/cli/config.go` | Argument parsing and profile loading. |
| `publish_test.go`, `cmd/releasekit/main_test.go`, `internal/git/remote_test.go` | Existing publication, recovery, CLI, and transport fixtures. |
| `internal/apicheck` | Compiled facade ownership and dependency checks. |
Keep source publication's successful-push completion point and existing candidate
checks. Later reconciliation and discovery are optional recovery operations.
They observe evidence and configuration, with corrective actions remaining
explicit. Hosted delivery retains its separate workflow.
Use the existing root facade and internal packages. Put shared recovery and
record mechanics in `internal/publish`, Git mechanics in `internal/git`, and
presentation in the CLI. Public types belong to the root facade; internal
packages depend on internal types. Preserve the small `buildinfo` boundary.
## Stage 1: Extract the shared observer and classification
**Completed:** 2026-11-02
**Outcome:** Immediate failed-push recovery and a later observation can share
classification without sharing publication prerequisites.
1. Add `internal/publish/reconcile.go` with internal evidence, observation, and
recovery-error types corresponding to the public shapes specified in Stage 3.
Observation returns its state and available remote object/peeled-commit IDs.
Keep the original evidence separate from the observation.
2. Add an internal Git constructor for observing a captured endpoint. Accept
repository context, the exact endpoint string, and an optional original remote
name. Create a fresh command-scoped remote using the recorded URL for both
read and push URLs. Read the named remote's current supported transport
settings using the existing `transportConfig` path. An empty original name
uses endpoint/global configuration; it does not select `origin` implicitly.
3. Preserve `QueryTag`'s endpoint-resolution verification and exact full/peeled
ref handling. Reading transport settings requires no current fetch/push URL
equality check. Missing settings, including after a remote is renamed or
removed, permit observation; actual configuration-read failures retain their
causes. Reuse the supported transport keys already implemented.
4. Extract classification so the immediate path can supply its already captured
endpoint and transport settings. Keep `reconcilePush` as the publication
adapter: retain its existing detached, bounded recovery context, phase
transitions, and original push-error chain. A successful exact match still
resolves that push error. Successful pushes continue to return directly.
5. The later path uses the caller's context for configuration reads and the
observation. Use the evidence's `RepositoryDir` as Git working-directory
context; an empty value uses the caller's current directory. This preserves
the meaning of relative local endpoints in automatically captured records.
An unavailable recorded directory produces an unresolved observation with
actionable context. Recovery does not discover identity from current HEAD,
local tags, module files, profiles, or current remote URLs.
Use this classification order for later recovery:
| Evidence or observation | State and error behavior |
| --- | --- |
| Missing endpoint or full tag | `unknown`, with `missing_evidence`; list the missing fields and perform no remote query. |
| Invalid supplied full object ID or invalid Git tag ref | `unknown`, with `invalid_evidence`; perform no remote query. |
| Cancellation or deadline prevents completion | `unknown`, with `canceled` and the context cause. |
| Endpoint rewriting changes the captured URL | `unknown`, with `endpoint_changed`; do not query the substituted destination. |
| Transport-configuration read fails | `unknown`, with `transport_configuration`. |
| Query fails or returns unreadable evidence | `unknown`, with `remote_observation`. |
| Exact-ref records are inconsistent | `unknown`, with `inconsistent_observation`. |
| Full tag is authoritatively absent | `not_published`; the observation completed successfully, even if tag-object or candidate-commit evidence is missing. |
| Full tag is present and intended tag-object ID is missing | `unknown`, with `missing_evidence`, including when a commit matches. |
| Observed tag-object ID differs from the intended ID | `conflict`; include both IDs. |
| Tag-object IDs match and no peeled ref is available | `published`, preserving existing behavior. |
| Tag-object IDs match, a peeled ref is available, and candidate commit is missing | `unknown`, with `missing_evidence`. |
| Tag-object IDs match and the peeled commit matches the candidate | `published`. |
| Tag-object IDs match but the peeled commit differs | `conflict`; include expected and observed commit IDs as well as tag-object IDs. |
Reuse existing full SHA-1/SHA-256 ID and Git-ref validation through internal
helpers. Compare hexadecimal IDs by value, accepting their existing supported
letter cases; retain supplied evidence for reporting. Module/version labels and
recording time are context, rather than additional observation prerequisites.
Distinguish a positively invalid ref from tooling, directory, configuration,
or cancellation failures while checking it; operational failures retain their
appropriate unresolved cause. Expose the existing diagnostic formatter through
an internal Git helper for shared error/presentation use, preserving wrapped
causes and the current credential-redaction behavior.
For the later API, a completed observation of `published`, `not_published`, or
`conflict` returns a nil error. An unresolved observation returns an error and
its `unknown` result. The immediate publication adapter continues to preserve
the original push error for known absence/conflict and unresolved recovery.
**Verification:** Extend existing classification tests for the partial-evidence
matrix and expected/observed IDs. Reuse existing Git parser tests for unchanged
duplicate-ref and peeled-only behavior. Add focused Git-boundary coverage for
current transport settings, repaired settings, removed/renamed remotes, fixed
URLs, and configuration-read failure. Preserve the existing lost-acknowledgment,
rejected-push, endpoint-change, and cancellation behavior.
## Stage 2: Implement private attempt records
**Completed:** 2026-11-02
**Outcome:** A supported internal codec can save and load a single attempt
without relying on diagnostic output.
1. Add record encoding and storage helpers under `internal/publish`. Use a JSON
envelope with integer `format_version: 1` and an `evidence` object. Its fields
are `repository_dir`, `module_dir`, `module_path`, `version`, `tag`,
`candidate_commit`, `tag_object`, `endpoint`, `remote`, and `recorded_at`.
Store the exact endpoint. Fields other than the envelope version/object can
be missing in a partial record. Retain the evidence available at capture;
loading does not fill omissions from current repository state.
2. Decode one complete JSON document. Missing/unsupported envelope versions,
malformed JSON, and structural type errors are `record_format` failures.
Accept additional fields within a supported version. Semantic evidence
completeness is evaluated by reconciliation, so a readable partial record
remains usable for discovery and any classification it can support.
3. Resolve the default directory with
`git rev-parse --path-format=absolute --git-common-dir`, then append
`releasekit/publication-attempts`. Use Git metadata discovery rather than
assuming `.git` is a directory. This shares saved attempts across linked
worktrees of the repository. Repository discovery for storage needs no
candidate branch, cleanliness, or HEAD check.
4. Treat the override as a directory. An absolute override is used directly;
a relative override is resolved against the source repository root. Record
files have distinct `attempt-<random>.json` names. Use standard-library random
naming and exclusive file creation to keep concurrent calls and retries from
overwriting each other. The filename supplies uniqueness; time is discovery
metadata, recorded once per attempt as UTC RFC3339Nano text.
5. Create ReleaseKit-owned directories privately and files with owner-only
permissions, using platform-supported permissions. Apply this file handling
equally to default and overridden storage. Keep unrelated existing directory
permissions intact. Use ordinary encoding, writes, closes, and failed-write
cleanup; this is a small evidence file with the durability limits in the
feature roadmap. Report write/close failures rather than presenting a failed
write as a saved record. Report an absolute record path on successful save.
6. Preserve publication's existing clean-worktree requirement when resolving an
override. Git metadata, external locations, and Git-ignored locations can
retain records. If writing the proposed record would introduce unignored
worktree contents, report `record_write` and leave that record unwritten.
Use the actual proposed file path and Git's ignore rules before creating
record contents; account for ordinary filesystem path resolution. This is a
recording failure, not a new publication prerequisite or a reason to change
ignore configuration automatically. Do not modify tracked files or exclusion
rules to accommodate storage.
7. File-open/read failures are `record_read`; storage-resolution and save
failures are `record_write`. Preserve underlying causes through wrapping.
Error displays identify paths without echoing record contents. Retention and
manual removal remain operator-owned.
**Verification:** Use real temporary storage for complete/partial round trips,
unsupported versions, malformed data, read/write failures, unique attempts,
default and override paths, and permissions where supported. Verify linked
worktree metadata resolution and clean-tree preservation for an external,
ignored, and unignored override. Use injected failures at the small file/Git
boundary when platform permissions cannot reliably induce a failure. Verify
that private records retain synthetic credentials while diagnostics redact them.
## Stage 3: Add the facade-owned recovery interfaces
**Completed:** 2026-11-02
**Outcome:** Go callers can pass publication evidence directly to recovery or
load it through ReleaseKit.
Define these root-facade interfaces, with ordinary conversions to internal types:
```go
type PublicationEvidence struct {
RepositoryDir string
ModuleDir string
ModulePath string
Version string
Tag string
CandidateCommit string
TagObject string
Endpoint string
Remote string
RecordedAt string
}
type PublicationReconciliationResult struct {
Evidence PublicationEvidence
State PublicationState
ObservedTagObject string
ObservedCommit string
}
type PublicationRecoveryError struct {
Kind string
RecordPath string
MissingFields []string
// Private cause, with Error and Unwrap methods.
}
func ReconcilePublication(context.Context, PublicationEvidence) (PublicationReconciliationResult, error)
func LoadPublicationEvidence(string) (PublicationEvidence, error)
```
1. Implement `ReconcilePublication` using Stage 1 and the existing public
publication states. It performs observation only; Stage 1 defines its result
and error semantics. `LoadPublicationEvidence` uses Stage 2 and reads the
explicitly supplied path, relative to the caller's working directory when
relative. It performs no Git discovery or remote access.
2. Implement the facade-owned error with pointer receivers for `Error` and
`Unwrap`. Convert internal recovery errors while retaining original causes
for `errors.Is`/`errors.As`. Document `Kind` values from Stages 1 and 2 plus
`storage_enumeration` from Stage 5. `MissingFields` uses the record field
names. Provide credential-safe error text through the existing Git diagnostic
redaction machinery; raw evidence and unwrapped causes remain sensitive.
3. Add `PublicationRecordDir string` to `ReleaseRequest`, documenting it as a
publication-only directory override that `Check` ignores. Add
`Evidence PublicationEvidence`, `RecordPath string`, and `RecordError error`
to `PublicationResult`. Preserve existing flat fields and their behavior;
populate them and the new evidence from the same captured values. Stage 4
wires recording into publication. `Publish` is the supported record producer.
4. Keep serialization in the internal codec. The facade's evidence is the
reusable typed input, not the CLI's diagnostic JSON representation. Document
its raw endpoint and the sensitive nature of retained records.
**Verification:** Add external-package API tests for direct evidence recovery,
record loading, partial evidence, preserved causes, and state/error semantics.
Include a caller using `result.Evidence` and a caller using the loader's result
without copying fields. Extend the existing facade checks only as needed to
protect ownership of these types and existing dependency directions; keep the
current external-type allowances.
## Stage 4: Record attempts inside shared publication
**Completed:** 2026-11-02
**Outcome:** API and CLI publication retain the same evidence before pushing,
with recording failures separate from the publication outcome.
1. Wire the new request/result fields through the facade and
`internal/publish.Request`/`Result`. Keep recording in `internal/publish`.
Use named result returns in the internal publication operation so finalization
can add record information to every returned result without replacing the
publication error.
2. Initialize attempt metadata when candidate capture has established the source
repository, including when capture returns partial evidence with an error.
Capture the actual effective remote name, using publication's existing
`origin` default. Discover storage locally and retain any storage failure
separately. An invocation that fails before its repository is known returns
its available typed evidence with no initialized record or recording error.
3. Populate evidence from the captured candidate, resolved endpoint, and created
tag-object ID as those become available. Preserve tag-object evidence returned
alongside a tag-inspection failure. Use the stable attempt timestamp from
initialization and the same evidence value in the result and saved record.
4. Save one evidence snapshot after the final existing pre-push guards and
before calling `PushTag`. On an earlier ordinary return, save the available
partial snapshot during finalization instead. Finalization runs once for
recording purposes; it does not retry or create another attempt after a
failed save. An interrupted push can therefore leave a complete saved identity
even when the process never returns a publication result.
5. Preserve the original publication return value and error when storage fails.
Supply the wrapped recording failure in `RecordError`, with `RecordPath`
populated only for a saved record. A successful push with a failed save still
returns publication success. A failed/unknown push with a saved record keeps
its existing state and original error chain.
6. Records contain original identity and discovery metadata. Later observations
and publication's final outcome remain separate results. Reconciliation and
listing read saved records without updating them. Each later publication
invocation gets its own attempt; existing records are retained.
**Verification:** Extend public publication fixtures to assert that the record
exists and contains the intended endpoint, full tag, commit, and tag object
before the push subprocess proceeds. Exercise success, rejected push, unresolved
push, pre-push failure, tag-inspection failure, and storage failure. Verify
unchanged publication outcomes/cause chains, one useful partial record on early
failure, distinct records for retries/modules, and no standard-suite duplication.
Verify that default storage and a failed unignored override keep the candidate
worktree clean. Cover a non-nil `RecordError` on successful publication through
both API results and Stage 7's CLI integration, preserving publication success.
## Stage 5: Provide local attempt discovery
**Completed:** 2026-11-02
**Outcome:** Callers can find saved attempts even after losing publication
output, with useful partial discovery when a record is damaged.
Add these facade-owned types and helper, backed by `internal/publish`:
```go
type PublicationAttemptsRequest struct {
RepositoryDir string
RecordDir string
}
type PublicationAttempt struct {
RecordPath string
Evidence PublicationEvidence
}
type PublicationAttemptsResult struct {
RecordDir string
Attempts []PublicationAttempt
Diagnostics []*PublicationRecoveryError
}
func ListPublicationAttempts(context.Context, PublicationAttemptsRequest) (PublicationAttemptsResult, error)
```
1. An empty repository selection means the current directory. Resolve the
default or relative override using Stage 2's repository/storage rules. An
absolute `RecordDir` can be enumerated directly, including outside a checkout.
Discovery neither creates the directory nor requires a release profile.
2. Enumerate the selected directory's JSON record files at that level, using
the shared loader. Return readable attempts with absolute paths, including
partial records. Return a per-record recovery diagnostic for each unreadable,
malformed, or unsupported record, and continue to other entries. Use a
deterministic path order for presentation without introducing automatic
chronological selection.
3. Return a top-level `storage_enumeration` error for directory-resolution or
enumeration failure, including a missing selected storage directory. Preserve
already obtained entries/diagnostics when available. An existing readable
empty directory returns an empty successful result. Cancellation uses the
`canceled` kind and preserves partial results and its cause. Per-record failures
alone populate `Diagnostics` with a nil top-level error.
4. Listing reads saved identity and recording time. It performs no remote
query, current-candidate capture, local-tag inspection, recovery selection,
or cleanup. Error text follows Stage 3's credential-safe behavior.
**Verification:** Cover multiple modules/retries, empty storage, mixed readable
and damaged records, unsupported versions, inaccessible/missing directories,
explicit external storage, and cancellation. Verify no remote access and no
record mutation. Test public result/error distinctions and preserved causes;
reuse codec cases rather than repeating all parser permutations here.
## Stage 6: Add CLI recovery, discovery, and structured output
**Completed:** 2026-11-02
**Outcome:** Operators and automation can discover and reconcile explicitly
selected attempts without profiles or diagnostic parsing.
Implement these command forms in the existing parser and CLI dispatch:
```text
releasekit reconcile --record FILE [--json]
releasekit publication-attempts [--repo DIR] [--record-dir DIR] [--json]
```
1. Add command/argument fields to `cli.Invocation` and extend usage/parser tests.
Neither command takes a version positional argument or loads a profile.
Reconciliation requires its explicit record path. The loader resolves a
relative record path from the invoking directory; the evidence supplies the
original repository context for observation. Listing uses Stage 5's directory
rules. Route these commands before `main.run`'s current unconditional
repository-root lookup for other non-version commands.
2. Place recovery presentation in a focused CLI source file rather than growing
candidate/build/delivery handlers. Invoke the root facade for load, observe,
and list mechanics. A record-read/format error preserves the requested path
and produces an invocation error. A readable partial record reaches the
observer and can produce `unknown` or a supported known observation.
3. Human output shows the selected path, safe identity, state, available observed
IDs, and actionable cause context. Unknown diagnostics identify missing
fields, invalid evidence, changed endpoint rewriting, transport-configuration
failure, cancellation, or failed/inconsistent observation using the typed
recovery error. Show expected and observed IDs for conflicts, including
commit evidence when that is what conflicts. Explain possible next actions
from known causes, without guessing that a generic Git failure is an
authentication or connectivity failure.
4. Human listing shows path, module identity, full tag, candidate commit, and
recording time when available. Show per-record diagnostics alongside usable
entries and make incomplete discovery clear. Distinguish an enumeration
failure from a successfully enumerated empty directory.
5. Implement explicit CLI JSON DTOs. After successful argument parsing,
`--json` writes one JSON document to stdout for success and failure paths;
normal usage/argument-parse errors retain the existing stderr behavior.
Use these fields, with `schema_version: 1` and the command name:
| Command | JSON fields beyond `schema_version` and `command` |
| --- | --- |
| `reconcile` | `record_path`, safe `evidence`, `state`, `observed_tag_object`, `observed_commit`, and `diagnostic` when unresolved or loading fails. Omit observation fields when loading failed. |
| `publication-attempts` | `record_dir`, `attempts` (each with `record_path` and safe `evidence`), `diagnostics`, and a top-level `diagnostic` for enumeration/cancellation failure. Arrays are present, including when empty. |
Safe evidence uses the Stage 2 snake-case field names with credential-safe
string displays. A diagnostic contains `kind`, `message`, `record_path` when
applicable, and `missing_fields` when applicable. Encode error text explicitly;
keep raw Go errors and private record JSON out of these DTOs. Apply existing
URL/diagnostic redaction before JSON encoding, including nested messages.
Raw endpoint preservation belongs only to private records and typed evidence.
Human and JSON output use the same state/diagnostic interpretation.
6. Use these process statuses and document their distinct meanings:
| Command/result | Status |
| --- | --- |
| Reconciliation confirms publication | 0 |
| Reconciliation observes absence or conflict | 1 |
| Reconciliation invocation, selected-record read, or format failure | 2 |
| Reconciliation remains unknown, including missing evidence or canceled observation | 3 |
| Discovery completes with readable entries and no diagnostics, including an empty existing store | 0 |
| Discovery is incomplete or fails, including cancellation | 1 |
| Discovery argument/usage error | 2 |
A nil Go observation error for absence/conflict still maps to CLI status 1.
Preserve the original context or filesystem cause in the API even when its
CLI status is an invocation classification.
**Verification:** Add parser/dispatch tests and command tests for human/JSON
state parity, exit classifications, mandatory selection, unusable records,
partial discovery, and credential redaction. Assert semantic fields rather than
complete diagnostic wording. Test paths with spaces/apostrophes and running
record-based recovery from another working directory. Verify these commands
work with a dirty worktree, changed branch/HEAD, removed or moved local tag,
and unavailable profile, with no candidate commands or hosted requests.
## Stage 7: Integrate publication's record override and recovery handoff
**Completed:** 2026-11-02
**Outcome:** Existing publication commands expose saved evidence and recording
failures without additional consumer release infrastructure.
1. Add `--record-dir DIR` to `publish`, mapping it to
`ReleaseRequest.PublicationRecordDir`. Keep the automatic default sufficient
for ordinary callers. The override is a CLI/API option; profile JSON remains
unchanged for this feature. The flags for other existing commands retain
their meanings.
2. After publication, report a saved absolute record path or a credential-safe
recording warning on stderr, separately from the existing publication
summary/error. These messages leave publication's exit classification and
original failure diagnostics intact, including success with recording failure.
3. When publication returns an error at tag creation, push, or immediate
reconciliation and a record was saved, print a copyable `reconcile --record`
command selecting that exact path. Partial records can explain missing
evidence. Earlier failures still report their saved path when available.
Successful publication reports the path without prescribing a recovery step.
4. Prefer the consumer module's registered Go tool for the printed command.
API-only consumers use durable private module/checksum files under Git
metadata, with a separate bootstrap module root so consumer vendoring does
not govern tool resolution. Retain the selected requirements, checksums,
versioned replacements, and original locations of local replacements.
Consumers without a selected ReleaseKit dependency use the native executable
path. Select the absolute record path explicitly and quote every argument
for the POSIX-shell runbooks, including apostrophes. The command survives a
cold Go-tool executable's removal and launcher-owned metadata cleanup.
Explain how to use another selected supported CLI if an entry point becomes
unavailable.
**R14 follow-up completed:** API-only recovery uses an independent private module
root. R15 tracks cancellation and preparation-failure handling separately.
**Verification:** Extend existing publication CLI tests for the default, override,
recording warning with successful status, and original failed/unknown status.
Use a built CLI in an assembled test to execute the printed recovery command
and verify it selects the original record among retries/modules. Test shell
argument quoting with synthetic paths. Keep the existing module-selected Go-tool
launchers and source bootstrap working without adding consumer-owned recording.
Include an API-only consumer that vendors the library without declaring the CLI
tool; execute its handoff and verify source/vendor contents remain unchanged.
Exercise relative local replacements, versioned replacements, and ReleaseKit's
own source-module recovery entry point.
## Stage 8: Finish assembled coverage and canonical documentation
**Completed:** 2026-11-02
**Outcome:** The full feature works across process boundaries, with supported
contracts and a short consumer recovery workflow.
1. Add focused assembled tests for publication with a failed/interrupted push,
lost output followed by discovery, and a separate later recovery invocation.
Use a controlled local Git boundary to know when pre-push recording finished;
coordinate cancellation/interruption without arbitrary sleeps. Remove or move
the local tag and change branch, HEAD, remote URLs, or transport settings
before later recovery. Cover root/nested tags and original-record selection.
Retain the assembled vendored API-only handoff coverage added for R14,
alongside cold registered-tool and temporary-launcher recovery.
2. Establish read-only behavior through resulting refs, worktree/branch state,
saved records, and controlled observations of candidate/hosted boundaries.
Reuse lower-level classification and codec tests. Tests use local remotes,
temporary storage, synthetic credentials, and isolated environments; live
services and real credentials are unnecessary.
3. Update GoDoc for evidence, results, errors, request fields, and all new helpers.
Clarify existing publication-state comments where they currently describe
only successful pushes or a single invocation, while preserving those
states' established immediate-publication behavior.
4. Update documentation at its canonical owners:
| Owner | Change |
| --- | --- |
| `docs/cli.md` | New commands, flags, explicit selection, relative-path semantics, human/structured modes, and statuses; update the command inventory. |
| `docs/consumers/publish.md` | Automatic recording, separate recording failures, reusable typed evidence, loader/discovery use, optional later observation, storage placement/lifecycle, overrides, permissions, retention, and sensitive-data handling. |
| New `docs/integrations/publication-records.md` | Versioned private-record and CLI JSON formats and unsupported-format handling. Link to CLI/GoDoc for operation contracts and the publication guide for physical placement/lifecycle. |
| `docs/release.md` | Replace manual endpoint-copy/comparison instructions with explicit-record recovery and local discovery; describe lost/older evidence and unavailable original executable. |
| `docs/internal/overview.md` | Implemented observer, recording, discovery, facade, and CLI responsibilities; link to canonical formats and contracts. |
The runbook must explain that current absence does not prove a version was
never published or permit reuse of a known published version. Keep cleanup
and retry choices explicit and preserve immutable published tags/contents.
Storage failure loses the saved handoff, rather than changing the actual
publication outcome. Explain that direct typed evidence can still support
recovery when recording fails. Document the limits of abrupt process
termination before recording, removed storage, and older attempts lacking
sufficient retained identity. Later recovery adds no successful-publication
read-back, polling, CI, installation, or hosted-delivery gate.
5. Review examples, local links, fragment destinations, privacy language, and
consistency with the feature roadmap. Keep release-version selection and
tagging in later release preparation. Update any existing draft release note
only when its version/scope has been deliberately selected for this feature.
6. Once the assembled implementation is ready, run `go test ./...` and
`go run ./internal/apicheck` with the prepared compatible local Go toolchain
and offline dependencies. Run focused tests while implementing their owners;
reuse successful checks for unchanged content. Add targeted race detection
if new shared mutable state or concurrent record tests expose a distinct risk.
Finish with `git diff --check`, review all implementation/test/doc changes,
and complete the repository's normal commit discipline.
The sprint is complete when automatic API/CLI recording, direct and loaded
evidence reconciliation, partial local discovery, current transport settings
with fixed original endpoints, safe human/structured output, and the assembled
recovery workflow satisfy this plan and the feature roadmap. The final report
should identify implemented interfaces, validation results, and any remaining
limitations. Subsequent architecture review and release/testing decisions take
place after this complete initial implementation.