From 447b42a1cd63943704881c93018a8bd624c1a1f7 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Mon, 2 Nov 2026 18:24:44 +0000 Subject: [PATCH] Plan publication reconciliation implementation --- docs/roadmap/implementation.md | 515 ++++++++++++++++++++++ docs/roadmap/promptkit-feature-request.md | 80 ++++ 2 files changed, 595 insertions(+) create mode 100644 docs/roadmap/implementation.md create mode 100644 docs/roadmap/promptkit-feature-request.md diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md new file mode 100644 index 0000000..6ed650c --- /dev/null +++ b/docs/roadmap/implementation.md @@ -0,0 +1,515 @@ +# 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 + +**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 + +**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-.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 + +**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 + +**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 + +**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 + +**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 + +**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. diff --git a/docs/roadmap/promptkit-feature-request.md b/docs/roadmap/promptkit-feature-request.md new file mode 100644 index 0000000..30d1732 --- /dev/null +++ b/docs/roadmap/promptkit-feature-request.md @@ -0,0 +1,80 @@ +# Promptkit Feature Request: Read-Only Publication Reconciliation + +## Consumer Context + +Promptkit uses ReleaseKit v0.7.0 as a module-selected Go tool for candidate +validation, source-tag publication, and page-only Gitea delivery. Source +publication and hosted delivery have separate outcomes. A successful intended +tag push completes the source release without waiting for CI or module +availability. + +ReleaseKit already reconciles an unsuccessful push with a bounded authoritative +query. This request concerns later recovery when that observation remains +unknown and the maintainer can investigate again after connectivity or +authentication is restored. + +## Current Pain Point + +Promptkit's runbook reproduces a manual recovery procedure: retain the captured +publication endpoint, version, candidate commit, and tag object; check that Git +URL rewriting still resolves the endpoint to itself; query the exact tag; then +interpret whether the original tag was published, absent, conflicting, or still +unobservable. + +That procedure is generic release logic, rather than Promptkit-specific policy. +Keeping it in each consumer's instructions adds maintenance and opportunities +for mistakes. Querying the current remote instead of the original captured +endpoint can establish the wrong outcome after configuration changes. + +Credential-safe diagnostics introduce an additional handoff issue: a displayed +HTTP(S) endpoint can have its userinfo redacted. That display is not necessarily +a usable transport URL, and asking an operator to reconstruct the original +endpoint from it is inconvenient. Consumers should not need to parse diagnostic +text or independently implement ReleaseKit's Git observation rules. + +## Requested Capability + +Please consider a first-class, read-only CLI operation for reconciling a previous +source-publication attempt. A name such as `reconcile` or `publication-status` +would be suitable; the exact command and interface are for ReleaseKit to choose. +A corresponding public Go API could serve library consumers with the same need. + +The capability would use the original attempt's evidence, including the exact +captured endpoint, complete Git tag, intended tag-object identity, and candidate +commit as applicable. It should: + +- Classify confirmed publication, authoritative absence, a conflicting tag, and + an unresolved observation using ReleaseKit's existing publication semantics. +- Preserve the distinction between the annotated tag object and its peeled + source commit; a commit match alone does not establish the intended tag object. +- Account for endpoint rewriting and configuration changes rather than silently + substituting the current remote destination. An observation failure or + insufficient original evidence should remain unresolved. +- Offer a practical handoff of attempt evidence without requiring consumers to + parse human-readable output or retype a credential-bearing URL from a + redacted diagnostic. Authentication can remain locally supplied, and displayed + endpoints should retain the existing credential-safe treatment. +- Return documented, actionable outcomes for operators and automation. + +Promptkit has no preference for a particular evidence-storage format or internal +implementation. Explicit inputs or a reusable attempt record could meet the +need; the important property is that recovery refers to the original attempt. + +## Scope And Workflow + +This is a diagnostic operation after a failed or uncertain publication. It +would observe remote state without creating, pushing, moving, or deleting tags; +changing branches; publishing hosted pages; or rerunning candidate validation. +Any subsequent corrective action would remain explicit. + +It would not become a read-back gate after a successful push or a prerequisite +for ordinary publication. CI status, consumer retrieval, and hosted-page delivery +would remain separate concerns. There is no request for a combined publish-and- +deliver command, automatic tag cleanup, or a new release approval step. + +## Expected Benefit + +Promptkit could replace its copied Git query and comparison instructions with +one upstream-owned command and a short recovery link. Other ReleaseKit consumers +could use the same classifications and credential-safe evidence handoff, reducing +both duplicated instructions and divergent recovery behavior.