Plan read-only publication reconciliation

This commit is contained in:
2026-11-02 16:50:16 +00:00
parent 691926d839
commit e0bf5ca57a

View File

@@ -0,0 +1,225 @@
# Roadmap: read-only publication reconciliation
Status: planned capability; recommendations under Open Decisions await resolution.
## Purpose
Let maintainers revisit a failed or uncertain source-publication attempt through
one ReleaseKit operation. PromptKit currently copies the endpoint checks, exact
tag query, and result interpretation into its runbook. ReleaseKit should own
these mechanics and the handoff of original attempt evidence.
The target includes a public Go API and a CLI command with equivalent behavior.
This roadmap describes the end state and required work, without selecting a
release version or prescribing a staged implementation plan.
## Current baseline
The current checkout contains the v0.7.0 release notes and publication workflow.
The [publication guide](../consumers/publish.md) describes existing outcomes;
the [maintainer runbook](../release.md#resolve-failures) describes manual recovery.
- `PublicationResult` supplies the repository and module identity, semantic
version, full tag, candidate commit, captured endpoint, tag object when known,
phase, and publication state. Its endpoint can contain credentials.
- Internal `reconcilePush` makes a bounded authoritative observation after a
failed push. It compares the annotated tag object and available peeled commit
evidence, returning published, absent, conflicting, or unknown outcomes.
- `internal/git.Endpoint.QueryTag` verifies the captured endpoint's scoped URL
resolution before querying the full tag and peeled ref. Inconsistent exact-ref
records remain unresolved.
- The CLI masks HTTP(S) endpoint userinfo in diagnostics. The runbook still asks
maintainers to copy the displayed endpoint and compare remote object IDs.
- There is no public operation or CLI command for later reconciliation, and no
reusable CLI attempt record for preserving the exact transport endpoint.
These existing observations and classifications provide the implementation
foundation. The main additions are a reusable operation, original-evidence
handoff, and documented operator and automation interfaces.
## Targeted end state
After connectivity or authentication is restored, a maintainer can give
ReleaseKit the original publication evidence and receive a fresh observation
of that attempt's destination and full tag. Library consumers use typed evidence;
CLI consumers use a record produced by ReleaseKit without parsing diagnostics.
| Observation | Reconciliation outcome |
| --- | --- |
| Intended annotated tag object, with consistent available commit evidence | Published |
| Authoritative full tag absent | Not published |
| Different tag object, including one pointing at the same commit | Conflict |
| Observation failure, inconsistent records, or insufficient original evidence | Unknown |
The implementation preserves existing exact-object semantics, including current
handling of an unavailable peeled ref. A matching commit alone is insufficient.
Absence describes the current observation; it does not establish that the tag
never existed. A record identifies the intended attempt and is not proof that a
push happened or that the remote accepted it.
Reconciliation uses the original captured endpoint and full tag, including nested
module prefixes. It checks that URL rewriting still permits observation of that
endpoint. A changed remote configuration does not substitute a new destination.
Authentication remains locally supplied through the supported Git mechanisms.
The operation observes remote state without creating, pushing, moving, or
deleting tags, changing branches, validating a candidate, or delivering hosted
content. Current branch, worktree cleanliness, and current `HEAD` do not need to
match publication prerequisites. Repository context may still be used for Git
configuration and authentication. Corrective actions remain explicit.
Successful source publication retains its existing completion point. Later
reconciliation is an optional recovery operation, with no routine read-back,
polling, CI, consumer-installation, or delivery gate.
## Work needed
### 1. Extract a shared observation and classification path
**Rationale:** Immediate and later recovery should interpret the same evidence
consistently.
**Target:** A reusable internal operation observes the captured endpoint and
classifies its exact tag using the existing publication semantics.
**Work:** Separate remote observation and classification from the original push
error and publication orchestration. Reuse the scoped endpoint and exact-ref
query behavior. Keep immediate failed-push reconciliation bounded and preserve
its existing error context. Later reconciliation should honor caller cancellation
and return an unresolved outcome when observation cannot complete. It should not
invoke the current-remote equality guards that belong to an active publication.
### 2. Define public evidence and reconciliation results
**Rationale:** Consumers need supported types for recovery instead of rebuilding
internal Git behavior or parsing diagnostic strings.
**Target:** A root-facade operation, provisionally `ReconcilePublication`, accepts
original evidence and returns a useful observation with preserved error causes.
**Work:** Define the minimum evidence needed for each classification: captured
endpoint, full tag, intended tag object, and candidate commit as applicable.
Carry module/version information for operator context. Reuse existing publication
state meanings while distinguishing the original attempt from the new observation.
Define behavior for incomplete evidence and unsupported record formats without
inferring missing identity from today's remote, tag, or profile. A deleted or
moved local tag should not invalidate otherwise sufficient retained evidence.
Keep facade ownership and internal Git boundaries. Exact type names, field names,
and signatures belong in API design; no new public implementation package is
needed for this feature.
### 3. Provide a practical attempt-evidence handoff
**Rationale:** Redacted diagnostics are useful displays but cannot reliably carry
the original transport URL into a later process.
**Target:** The CLI can retain and read a small versioned record containing the
original evidence. Library consumers can retain equivalent typed data.
**Work:** Capture evidence when the endpoint and tag object are established,
before attempting the push. Preserve useful partial evidence if an earlier stage
fails, clearly identifying what is missing. Make record production and loading
available through supported interfaces. Keep original identity separate from
later observations and avoid replacing one attempt's evidence with another's.
Resolve persistence defaults and sensitive-data handling below before defining
the storage contract. Use ordinary file handling proportionate to recovery;
this feature does not require a transactional journal or power-loss guarantees.
### 4. Expose recovery through the CLI and documentation
**Rationale:** A single command should replace copied query and comparison steps.
**Target:** A command provisionally named `reconcile` reads an attempt record and
reports the outcome, selected full tag, intended identity, and actionable cause
context. Automation has a documented structured result and exit behavior.
**Work:** Map the command to the facade, retaining credential-safe displays and
errors. Document valid invocation, missing or unreadable records, unknown
observations, and cancellation. Replace the runbook's manual endpoint-copy
procedure with this workflow and a concise link from consumer guidance. Explain
how maintainers retain records and deliberately address absence or conflicts.
Keep the distinction between source publication and hosted delivery explicit.
Document recovery limits for older attempts that lack sufficient saved evidence.
### 5. Verify recovery behavior and integration
**Rationale:** Incorrect recovery can direct corrective action at the wrong
repository or mistake a different annotated tag for the intended release.
**Target:** Behavioral coverage establishes equivalent immediate/later
classification and useful recovery across process boundaries.
**Work:** Use temporary Git repositories and local remotes to cover exact matches,
absence, different objects at the same commit, root and nested tags, changed
current remotes, rewriting that redirects the captured endpoint, inconsistent
observations, unavailable remotes, and cancellation. Test record round trips,
partial evidence, private storage behavior, and redaction with synthetic
credentials. Exercise recording before a failed or interrupted push and a later
invocation after local branch or tag state changes. Verify the diagnostic
operation performs no publication or cleanup actions and runs no candidate tests.
Reuse existing tests for unchanged Git behavior rather than duplicating them.
Run the normal offline suite and facade checks when code lands. Update GoDoc,
CLI documentation, the publication guide, and recovery instructions together.
Any release-policy clarification should describe later optional recovery without
adding checks to successful publication. This roadmap itself changes no code.
## Open Decisions
### 1. How should the CLI persist attempt records?
**Recommendation:** Retain records automatically in private repository-local Git
metadata, outside the tracked worktree, with a distinct record for each attempt.
Report the record path and allow explicit selection for reconciliation. Record
evidence before the push once the necessary identity exists. Treat storage
failure as a clearly reported loss of the saved handoff, while preserving the
actual publication result and existing publication prerequisites.
This removes a routine setup decision and keeps records out of commits. The
tradeoffs are local metadata accumulation and possible loss of recovery evidence
when storage fails or the checkout is removed. Document retention and manual
removal; automatic cleanup is not required for the initial capability.
**Alternative:** Require an explicit output path to enable recording. This makes
persistence deliberate and works naturally with caller-managed storage. It adds
launcher configuration and offers no durable handoff when a maintainer forgets
the option. A requested write could fail before the push if the caller requires
that handoff, but such behavior would need an explicit interface contract.
### 2. How should a record represent credential-bearing endpoints?
**Recommendation:** Preserve the exact captured endpoint in the private record,
with owner-only access using platform-supported file permissions. Continue
redacting endpoint credentials in human and machine diagnostic output; expose
only the record location for the handoff. Prefer locally supplied Git credentials
in adoption guidance. Treat exports and copies of the record as potentially
sensitive, just like the existing API result's endpoint.
This supports existing publication configurations without reconstructing an
endpoint from its display. It also places potentially sensitive material on disk;
permissions and clear documentation reduce accidental exposure but do not promise
encrypted storage or protection from other processes running as the same user.
**Alternative:** Store a credential-free endpoint and obtain authentication only
through local mechanisms. This reduces secrets in records, but stripping userinfo
can affect transport identity and rewriting. Adopting it requires a deliberate
endpoint-identity contract and migration guidance for credential-bearing URLs.
It cannot simply use the existing redacted display as a transport URL.
### 3. What should automation receive?
**Recommendation:** Offer human-readable output by default and a structured mode
with redacted identity, reconciliation state, and cause information. Use success
for confirmed publication, a non-success classification for absence/conflict,
and distinct unknown and invocation-error classifications, following the existing
CLI conventions where practical. Define exact numeric codes during CLI design.
This makes shell recovery straightforward and keeps structured state authoritative
for detailed consumers. The tradeoff is that a successful observation of absence
still yields a non-success process status; document that distinction explicitly.
**Alternative:** Return success for every completed observation, including
absence and conflict, and require automation to read the structured state. This
cleanly separates query execution from publication outcome, but a simple exit-code
check could be mistaken for confirmation that publication succeeded.