From 16d1c072e3358795289a8180da1696890770894c Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Tue, 3 Nov 2026 00:00:42 +0000 Subject: [PATCH] Record remaining recovery findings and recommended fixes --- docs/roadmap/remediation.md | 200 ++++++++++++++++++++++++++++++++++-- 1 file changed, 194 insertions(+), 6 deletions(-) diff --git a/docs/roadmap/remediation.md b/docs/roadmap/remediation.md index 8712bd5..02efdc6 100644 --- a/docs/roadmap/remediation.md +++ b/docs/roadmap/remediation.md @@ -1,12 +1,14 @@ # Publication reconciliation remediation -Status: complete. R01–R13 are addressed and individually verified. The full -offline suite (`go test ./...`) and compiled facade/dependency checks -(`go run ./internal/apicheck`) passed with Go 1.26.8 after remediation. +Status: open. R01–R13 are addressed and individually verified. R14–R17 record +remaining findings from the subsequent stage-by-stage review of `363f7cd`. +Address the two handoff defects first, then the classification and format gaps. -The findings describe commits `4dff808` through `599ad81`, against planning -baseline `447b42a`. Audit evidence and defect descriptions below refer to that -reviewed baseline; each completion marker records the implemented correction. +R01–R13 describe commits `4dff808` through `599ad81`, against planning baseline +`447b42a`. Their audit evidence and defect descriptions refer to that reviewed +baseline; each completion marker records the implemented correction. The full +offline suite (`go test ./...`) and compiled facade/dependency checks +(`go run ./internal/apicheck`) passed with Go 1.26.8 after that remediation. The [feature roadmap](publication-reconciliation.md) defines the requested behavior; the [implementation plan](implementation.md) supplies the intended @@ -583,3 +585,189 @@ Handle zero-entry failures and cancellation consistently with R04. Add a narrow fault-injected regression for entries-plus-error, verifying readable attempts, retained causes, and API/CLI partial-result behavior. This concerns data preservation, separate from R04's cancellation/error classification. + +## Follow-up stage review evidence + +The subsequent review examined all eight completed stages in the implementation +plan, including the R01–R13 corrections, at `363f7cd`. Existing tests for +classification, Git endpoints, records, public recovery, publication evidence, +discovery, CLI parsing/presentation, cold Go-tool handoffs, and the assembled +lost-output workflow passed with Go 1.26.8. The compiled facade/dependency checks +and local documentation link/fragment checks also passed. + +Disposable local probes reproduced R14–R17 using temporary Git repositories and +Go source overlays. Repository production code and tests remained unchanged. +No additional defects were identified in Stages 3–6. Stage 8 needs regression +coverage for the cases below and plan wording aligned with the implemented +record and handoff decisions. Existing passing coverage does not establish +correctness for these additional combinations. + +## R14: Support recovery for vendored API-only consumers — P2 + +**Relevant code:** `internal/gotool/recovery.go:95–107`; the command assembly in +`cmd/releasekit/main.go:126–145`. Affects Stage 7 and its Stage 8 coverage. + +The API-only handoff prepares its private alternate module files with +`go list -mod=mod`, then prints a `go tool` command running from the consumer's +module directory. Go automatically selects vendor mode when that directory +contains a suitable `vendor` tree. An API-only consumer vendors its imported +library packages, so the CLI package can be absent from that tree. + +**Evidence:** A temporary consumer imported ReleaseKit through its Go API, +vendored its dependencies, and used the current CLI for a locally rejected +publication. A complete record and recovery command were produced. Executing +the command failed before reconciliation with `import lookup disabled by +-mod=vendor`. Supplying `GOFLAGS=-mod=mod` also failed to change this Go-tool +behavior. Existing vendored-tool coverage uses an explicitly registered and +vendored CLI, which does not exercise this API-only case. + +**Expected behavior:** A consumer's ordinary library vendoring remains compatible +with the supported API-only recovery handoff. The command observes the explicitly +selected record using the selected ReleaseKit dependency. + +**Remediation:** Prepare the API-only Go-tool entry point in a private bootstrap +module root whose tool resolution is independent of the consumer's vendor tree. +Preserve the selected dependency graph, checksums, versioned replacements, and +the meaning of relative local replacements when relocating module metadata. +Keep source files and consumer vendoring intact, retain private storage handling, +and keep bootstrap ownership within ReleaseKit. Preserve registered-tool and +native entry points. Update Stage 7's handoff instructions to describe this +module-selected approach and the supported native fallback. + +**Regression coverage:** Add an assembled API-only consumer that vendors the +library without a tool declaration. Execute the printed command from another +directory after publication and launcher cleanup; assert that reconciliation +runs against the saved identity and returns its expected state/status. Cover +relative local replacements and unchanged consumer source/vendor contents. +Retain existing cold-build, registered/vendored-tool, quoting, and status cases. + +## R15: Preserve the recovery handoff after publication cancellation — P2 + +**Relevant code:** `cmd/releasekit/main.go:116–134`, especially the caller context +passed to `git.CommonDirectory`. Affects Stage 7 and its Stage 8 coverage. + +Command preparation reuses the publication context after publication returns. +When cancellation caused the failure, that context is already canceled. The +metadata lookup fails immediately and the CLI prints `recovery command +unavailable`, even when it has saved the record and a usable native entry point +is available. The native fallback is reached only when module selection returns +no Go invocation; a preparation error bypasses it. + +**Evidence:** A probe synchronized cancellation with a blocked tag push. The +publication returned status 1 after immediate reconciliation found the tag +absent. Standard error reported a saved record and an unavailable recovery +command, with no copyable handoff. This was cancellation during execution, +rather than termination before the record could be saved. + +**Expected behavior:** An ordinary canceled publication with a saved record +retains a usable recovery entry point. Its preparation leaves the original +publication outcome, cause, and exit classification intact. + +**Remediation:** Arrange handoff preparation so the failed publication's canceled +context does not prevent command construction. Prepare necessary durable launch +metadata before cancellation-sensitive publication work where appropriate, and +retain a usable native-command fallback when module-based preparation fails. +Account for cold Go-tool executables that disappear when the original driver +exits. Keep publication and later observation cancellation behavior from R04; +any separate post-failure preparation that performs subprocess work needs a +bounded lifetime. Update Stage 7 to cover cancellation and preparation failures. + +**Regression coverage:** Cancel an actively blocked push after pre-push recording, +then execute the resulting command using a fresh invocation/context. Cover a +persistent native executable and supported cold Go-tool adoption paths after +launcher cleanup. Assert saved identity selection, unchanged publication status +and original cancellation cause, and preservation of immediate recovery's bounded +context. Coordinate the push boundary explicitly and retain the existing +no-recovery-step behavior for successful publication. + +## R16: Classify endpoint setup failures by their actual cause — P3 + +**Relevant code:** `internal/publish/reconcile.go:116–128` and +`internal/git/remote.go`'s endpoint setup. Affects Stages 1 and 6. + +An `ObserveEndpoint` error defaults to `endpoint_changed` when the evidence has +no original remote name. With a name, the fallback is `transport_configuration`. +The choice can therefore describe the presence of optional context rather than +the failure that occurred. + +**Evidence:** A local checkout with malformed Git configuration and evidence +whose `Remote` was empty returned `unknown` with `endpoint_changed`. Its retained +cause was Git's bad-configuration error; no changed destination had been +established. CLI guidance consequently points the operator toward URL rewrite +settings despite the configuration-read failure. + +**Expected behavior:** `endpoint_changed` identifies a positively detected +destination change. Configuration and other operational setup failures retain +an appropriate unresolved classification and their original causes, including +when the original remote name is absent. + +**Remediation:** Distinguish setup failures at the internal Git boundary where +their source is known, then map them to the supported recovery kinds. Use the +existing endpoint-change sentinel for positively detected rewrites. Preserve +configuration/tooling causes through the facade and use the same interpretation +for human guidance and structured diagnostics. Clarify this mapping in Stage 1 +and its Stage 6 presentation requirements using the existing recovery kinds. + +**Regression coverage:** Exercise malformed local configuration with an empty +original remote name, a genuine rewrite mismatch, and a separate operational +setup failure at the Git boundary. Verify typed kinds, retained causes, unknown +state, and corresponding CLI human/JSON diagnostics. Keep remote queries from +reaching a substituted endpoint when a rewrite mismatch is detected. + +## R17: Validate the documented version-2 byte-field representation — P3 + +**Relevant code:** `internal/publish/record.go:48–59`; +[private record format](../integrations/publication-records.md#private-attempt-record). +Affects Stage 2 and its Stage 8 format coverage. + +Version-2 byte fields are documented as standard base64 strings. The loader +unmarshals each known field directly into `[]byte`; Go's JSON decoder also accepts +numeric arrays for that destination type. Consequently, an invalid structural +type under the documented format is silently treated as usable evidence. + +**Evidence:** Both `"djEuMi4z"` and `[118,49,46,50,46,51]` as the value of +`evidence_bytes.tag` loaded as `v1.2.3` without a format error. The array case is +outside the declared representation. This is a format-validation gap; the probe +did not demonstrate altered bytes or an incorrect remote observation. + +**Expected behavior:** Known byte fields follow the documented base64-string +representation. Structural and encoding failures return `record_format` with +their available causes. Supported partial evidence and ignored additional fields +retain their existing behavior. + +**Remediation:** Decode known byte-field values as strings explicitly, then use +the standard base64 decoder to recover their bytes, preserving decoding causes. +Keep version-1 behavior and version-2 lossless identity from R10. Update Stage 2 +to describe both supported versions and the writer's version selection, linking +to the canonical format definition. + +**Regression coverage:** Add known byte-field cases for valid base64, malformed +base64, numeric arrays, and other invalid structural types. Assert typed format +errors and retained causes for invalid inputs. Reuse the existing all-field +round trips, non-UTF-8 endpoint observation, partial-evidence, and unknown-field +tests rather than duplicating their fixtures. + +## Implementation plan and verification follow-through + +When implementing R14–R17, update [implementation.md](implementation.md) alongside +the corresponding changes: + +- Stage 1: record the cause-based endpoint setup classification from R16. +- Stage 2: describe ordinary version-1 records and version-2 byte-preserving + records, including the structural validation from R17. +- Stage 6: align typed failures, human guidance, and structured diagnostics with + R16; retain the implemented JSON output-failure handling from R06. +- Stage 7: replace the original executable-only handoff instructions with R07's + module-selected/durable API-only approach, extended for vendoring and + cancellation by R14 and R15. Retain a usable native fallback and explicit + record selection. +- Stage 8: add the regression coverage assigned above, update affected canonical + documentation as behavior lands, and run the offline suite and compiled + facade/dependency checks after assembly. Review local links and prose changes. + +Preserve earlier completion evidence and distinguish these follow-up corrections +from the original completed implementation. Add completion markers as the new +issues are addressed. The corrections retain the existing public facade, +publication completion point, separate recording failures, and operator-owned +recovery choices; they introduce no consumer pilot, intermediate release, or +external review gate.