530 lines
32 KiB
Markdown
530 lines
32 KiB
Markdown
# Publication reconciliation implementation plan
|
|
|
|
Status: ready for implementation.
|
|
|
|
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. Build the printed command from the running executable's absolute path and the
|
|
absolute record path, quoting each argument for the POSIX-shell runbooks.
|
|
This works for a directly built CLI and a Go-tool-launched cached executable
|
|
without assuming `releasekit` is on PATH or that the consumer registered a
|
|
tool directive. Quote apostrophes safely; print the command without executing
|
|
a shell. Explain in recovery guidance that operators can use their selected
|
|
supported CLI if the original executable is no longer available.
|
|
|
|
**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.
|
|
|
|
## Stage 8: Finish assembled coverage and canonical documentation
|
|
|
|
**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.
|
|
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.
|