diff --git a/docs/roadmap/audit/06-error-handling.md b/docs/roadmap/audit/06-error-handling.md new file mode 100644 index 0000000..66f5422 --- /dev/null +++ b/docs/roadmap/audit/06-error-handling.md @@ -0,0 +1,54 @@ +# Error-handling audit + +Reviewed baseline: `3754242` on `main`. The review traced public errors and +partial results through candidate validation, Git publication, build and +preparation, subprocess execution, and hosted delivery. Existing findings about +subprocess cancellation, publication integrity, temporary cleanup, and remote +resource consumption are retained in the [correctness](01-correctness.md), +[concurrency](04-concurrency.md), [resource](05-resources.md), and +[security](07-security.md) audits rather than duplicated here. + +The existing `internal/delivery`, `internal/git`, and `internal/gitea` tests +passed with the Go test cache. An isolated temporary repository, a copy of the +Git and process packages, and a Git wrapper were used to exercise the finding +below; no production source or repository tests were modified. + +## A post-creation inspection failure hides a retained local tag + +**Relevant code:** [CreateAnnotatedTag](../../../internal/git/tag.go), +[publish.Publish](../../../internal/publish/publish.go), and the public +[PublicationResult](../../../publish.go). + +**Failure condition:** Git successfully creates the annotated local tag, but +a subsequent `cat-file` or `rev-parse` inspection fails, for example because +the process is interrupted or the local Git operation encounters an error. +`CreateAnnotatedTag` returns an empty object ID on every such error. Its caller +returns immediately without setting `PublicationResult.TagObject`. The local +tag is not removed, and the next publication attempt's candidate check rejects +that existing tag. No remote push has been attempted at this point. + +**Expected and actual behavior:** `PublicationResult.TagObject` is documented +as the local annotated tag object ID when created, and publication results are +intended to give callers evidence for recovery. The result instead reports +`not_published` and the tag-creation phase with an empty object field even +though a local tag may remain. The returned error identifies the failed +inspection but does not explain that tag creation already succeeded. An +operator may have to discover the tag independently before deciding how to +recover; the absence of an object ID is not evidence that no tag exists. + +**Evidence:** In a temporary repository, a wrapper delegated real Git commands +except for the post-creation `cat-file -t` inspection, which returned an error. +`CreateAnnotatedTag` returned an empty object ID and an error, while an +independent Git query confirmed the local tag existed. The public result's +empty field follows from the caller's immediate error return; the probe did +not run the complete public publication workflow. + +**Potential approaches:** Preserve any verified object ID obtained before a +later failure and return it with the error. When creation succeeded but the +object could not be inspected, report explicitly that the local tag may exist +and identify its ref for manual inspection. Keep the failed verification and +original cause visible, never treat the tag as publishable without the existing +checks, and avoid deleting or changing a ref whose current ownership is not +established. A focused regression can force an inspection failure after +successful creation and check both the local ref and returned recovery +information.