diff --git a/docs/consumers/delivery.md b/docs/consumers/delivery.md index 0376da8..b1f4867 100644 --- a/docs/consumers/delivery.md +++ b/docs/consumers/delivery.md @@ -48,8 +48,8 @@ acted as the selection base. Outside a checkout, the supplied directory remains the base for `ModuleDir`. Preparation overrides an alternate module file selected by `GOFLAGS`, while retaining other effective Go settings. The operation may use the network and -updates the Go module cache while -preserving the caller's proxy, private-module, credential, and cache settings. +updates the Go module cache while preserving the caller's proxy, private-module, +credential, and cache settings. If checksums are missing, Go may update `go.sum`. For binary CI delivery, prepare and commit the selected module's `go.sum` before tagging: delivery verifies the tagged source remains clean after dependency preparation. See the @@ -223,9 +223,9 @@ credential preflight, verifies the local artifacts in full, and then makes hosted writes. The current files are checked against their recorded digests before those writes. Preflight confirms only the access evidence available from the service, as described above. The files remain caller-owned, so callers can -retain the original bytes for retries and are responsible for cleanup. With neither -artifact source, an empty managed target set, or a matching empty prebuilt -result, delivery is page-only. A prebuilt result with no targets or artifacts +retain the original bytes for retries and are responsible for cleanup. With +neither artifact source, an empty managed target set, or a matching empty +prebuilt result, delivery is page-only. A prebuilt result with no targets or artifacts must still identify the matching repository, module directory and path, tag, commit, and version; a zero `BuildResult` does not. Do not set both `Build` and `Prebuilt` in one request. diff --git a/docs/roadmap/correctness.md b/docs/roadmap/correctness.md index 189fa09..4b1dccb 100644 --- a/docs/roadmap/correctness.md +++ b/docs/roadmap/correctness.md @@ -3,7 +3,8 @@ ## Status and scope Audit completed against source commit `31f5327` on 2026-11-01. The three findings -below remain open. This audit changes documentation only. +below are resolved as recorded in their completion markers. The audit itself +changed documentation only. The review covered candidate capture and rechecks, module/tag derivation, committed-file validation and reference traversal, builds and runtime version @@ -13,8 +14,8 @@ examined ordinary paths, empty selections, error and cancellation paths, and boundary interactions against the current code, GoDoc, consumer guides, accepted decisions, and existing tests. -The earlier [interface audit](interfaces.md) remains applicable. In particular, -its build-source cancellation defect remains open; this document records +The earlier [interface audit](interfaces.md) also recorded a build-source +cancellation defect; this document records additional functional findings rather than duplicating that issue or its contract-clarity findings. No production code or repository tests were changed. @@ -40,13 +41,16 @@ behavioral corrections within those boundaries. ### 1. Candidate checking can approve a tag that Git cannot create -**Priority:** medium. **Status:** open. +**Priority:** medium. **Status:** complete. + +**Completion evidence:** `b91a0d1`: public invalid-derived-tag regressions and +Git/candidate package tests pass. **Relevant code:** `Derive` in [internal/releaseidentity/identity.go](../../internal/releaseidentity/identity.go), and `Capture`, `Recheck`, and `hasLocalTag` in [internal/candidate/check.go](../../internal/candidate/check.go). Compare the -existing `checkTagRef` boundary in +existing Git ref-validation boundary in [internal/git/remote.go](../../internal/git/remote.go). **Circumstances:** a selected nested module has a physical directory name that @@ -85,7 +89,10 @@ restrictions or changing standalone local-build behavior. ### 2. Ambient Go flags can redirect dependency preparation to another module file -**Priority:** medium. **Status:** open. +**Priority:** medium. **Status:** complete. + +**Completion evidence:** `05f1b47`, `abbab3c`: local-proxy inherited and +persistent flag regressions verify selected graphs and build identity. **Relevant code:** public `PrepareBuildDependencies` in [preparation.go](../../preparation.go), its preparation bridge, and @@ -140,7 +147,10 @@ caller settings. ### 3. Incomplete tag reconciliation is reported as a known publication conflict -**Priority:** medium. **Status:** open. +**Priority:** medium. **Status:** complete. + +**Completion evidence:** `0632aa5`: both Git query paths and public/CLI +lost-acknowledgment recovery regressions pass. **Relevant code:** `mergeRef` in [internal/git/remote.go](../../internal/git/remote.go), `Endpoint.QueryTag` in @@ -208,10 +218,8 @@ The audit also reviewed committed-file input boundaries, local-reference and symlink traversal, validation ordering, build result/receipt binding, version injection, and additive delivery with read-only preflight and write recovery. No additional functional finding was substantiated in those paths beyond the -issues already recorded in [the interface audit](interfaces.md). Passing tests -provide regression evidence for existing cases; they do not resolve the three -newly demonstrated cases above. +issues already recorded in [the interface audit](interfaces.md). The tests +available at audit time did not cover the three demonstrated cases above. -Prioritize accurate publication recovery and preparation of the selected module, -then close the candidate tag-validation gap. These are corrections to existing -behavior; they do not require new public interfaces or a broader release model. +The agreed remediation corrected these behaviors without new public interfaces +or a broader release model. diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 4c12613..6f5fec9 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -2,8 +2,8 @@ ## Baseline and execution -This plan targets `59bb074` on 2026-11-01. The worktree was clean when planning -began. The production code still contains the findings recorded in +This plan targeted `59bb074` on 2026-11-01. The worktree was clean when planning +began. At that baseline, production code contained the findings recorded in [correctness.md](correctness.md) and [interfaces.md](interfaces.md). [remediation.md](remediation.md) defines the agreed behavior and scope; this plan defines how to reach it. @@ -412,7 +412,13 @@ Validate affected local links/fragments and prose whitespace. ## Stage 10: Verify the complete sprint and close the findings -**Status:** pending. **Depends on:** all preceding stages. +**Status:** complete. The complete offline `go test ./...` and +`go test -race ./...` suites and compiled `go run ./internal/apicheck` check +pass with `GOTOOLCHAIN=local GOPROXY=off GOSUMDB=off`. Additional discovery +failure and synchronized cancellation checks pass with `-race`. Changed local +Markdown links and fragments and whitespace checks pass. Public types and +receipt schema remain unchanged; the finding records contain completion evidence. +**Depends on:** all preceding stages. 1. Resolve every deferred check, format changed Go files, and inspect the final diff for changes outside the agreed remediation scope. Verify all eight diff --git a/docs/roadmap/interfaces.md b/docs/roadmap/interfaces.md index 29eed4e..66ff4f8 100644 --- a/docs/roadmap/interfaces.md +++ b/docs/roadmap/interfaces.md @@ -3,7 +3,8 @@ ## Status and scope Audit completed against source commit `3f00ad2` on 2026-11-01. The findings below -remain open for consideration. This audit changes documentation only. +are resolved as recorded in their completion markers. The audit itself changed +documentation only. The review covered exported declarations and conversions in both public facades; candidate capture, validation, callbacks, and publication outcomes; @@ -46,7 +47,10 @@ an architectural redesign. ### 1. Build source inspection loses cancellation identity -**Priority:** medium. **Status:** open. +**Priority:** medium. **Status:** complete. + +**Completion evidence:** `28a4144`: synchronized cancellation, deadline, +partial-artifact, and race checks pass. **Relevant code:** `cleanSource`, `gitCommit`, and `checkVersion` in [internal/build/build.go](../../internal/build/build.go), reached through public @@ -81,7 +85,10 @@ error type or cancellation policy. ### 2. A release lookup can produce an unusable asset handle without an error -**Priority:** low. **Status:** open. +**Priority:** low. **Status:** complete. + +**Completion evidence:** `9fa4407`: backend and public page-only/prebuilt +regressions pass with malformed-handle classification. **Relevant interfaces:** `forge.Backend.FindRelease` and `ListAssets` in [internal/forge/forge.go](../../internal/forge/forge.go); Gitea `FindRelease`, @@ -117,7 +124,10 @@ page URLs or write-response fields on page-only reads. ### 3. Dependency preparation uses a different repository anchor -**Priority:** low; contract clarification needed. **Status:** open. +**Priority:** low; contract clarification needed. **Status:** complete. + +**Completion evidence:** `b47202a`, `5aba4c6`, `abbab3c`: checkout discovery, +standalone preparation, and build-composition regressions pass. **Relevant API:** `DependencyPreparationRequest` and `PrepareBuildDependencies` in [preparation.go](../../preparation.go), compared with `Build`, `Check`, and @@ -153,7 +163,10 @@ to remove standalone directory preparation. ### 4. Empty-prebuilt GoDoc omits the required source identity -**Priority:** low. **Status:** open. +**Priority:** low. **Status:** complete. + +**Completion evidence:** `d368bb3`: GoDoc and consumer guidance describe +matching empty results; page-only regressions pass. **Relevant API:** `DeliveryRequest` in [delivery.go](../../delivery.go), `validateBuildIdentity` in @@ -181,7 +194,10 @@ and consumer guide without adding a zero-result compatibility path. ### 5. Prebuilt delivery guidance promises hashing before every hosted request -**Priority:** low. **Status:** open. +**Priority:** low. **Status:** complete. + +**Completion evidence:** `d368bb3`: consumer guidance matches preflight-first +order; the existing preflight-before-hashing regression passes. **Relevant documentation:** the prebuilt paragraph in the [Go API delivery guidance](../consumers/delivery.md#go-api), compared with @@ -227,8 +243,8 @@ preflight continues to verify the available access evidence, and `SourceVerified` continues to describe source observations rather than an attestation. -Address the cancellation error chain and asset-handle failure classification -first, then align the preparation and delivery descriptions with the selected -behavior. The findings do not call for new public extension interfaces, +The remediation addresses the cancellation error chain and asset-handle failure +classification and aligns preparation and delivery descriptions with the selected +behavior. The findings did not call for new public extension interfaces, compatibility shims, stricter page-only response requirements, or a new release procedure. diff --git a/docs/roadmap/remediation.md b/docs/roadmap/remediation.md index 5c3bf2b..2bc4dfa 100644 --- a/docs/roadmap/remediation.md +++ b/docs/roadmap/remediation.md @@ -2,8 +2,8 @@ ## Status and scope -Agreed remediation target as of 2026-11-01. Implementation is pending against -baseline `59bb074`. +Remediation complete against baseline `59bb074`. The implementation plan +records stage and validation evidence. This roadmap defines the desired behavior and scope for all three findings in the [functional correctness audit](correctness.md) and all five findings in the diff --git a/internal/git/discovery_test.go b/internal/git/discovery_test.go index ec986c3..0499e20 100644 --- a/internal/git/discovery_test.go +++ b/internal/git/discovery_test.go @@ -8,6 +8,7 @@ import ( "path/filepath" "strings" "testing" + "time" ) func TestDiscoverRepositoryRoot(t *testing.T) { @@ -39,6 +40,9 @@ func TestDiscoverRepositoryRoot(t *testing.T) { if _, err := RepositoryRoot(context.Background(), standalone); err == nil { t.Fatal("strict lookup accepted standalone") } + if _, found, err := DiscoverRepositoryRoot(context.Background(), repo.bare); err == nil || found { + t.Fatalf("bare repository: found %t, error %v", found, err) + } broken := t.TempDir() if err := os.WriteFile(filepath.Join(broken, ".git"), []byte("gitdir: missing\n"), 0600); err != nil { t.Fatal(err) @@ -80,6 +84,13 @@ func TestDiscoverRepositoryRootFailures(t *testing.T) { if found || !errors.As(err, &exitErr) || exitErr.ExitCode() != 128 || !strings.Contains(err.Error(), "dubious ownership") { t.Fatalf("found %t, error %v", found, err) } + if err := os.WriteFile(filepath.Join(bin, "git"), []byte("#!/bin/sh\nprintf '%s\\n' 'fatal: permission denied' >&2\nexit 128\n"), 0o755); err != nil { + t.Fatal(err) + } + _, found, err = DiscoverRepositoryRoot(context.Background(), dir) + if found || !errors.As(err, &exitErr) || exitErr.ExitCode() != 128 || !strings.Contains(err.Error(), "permission denied") { + t.Fatalf("found %t, error %v", found, err) + } ctx, cancel := context.WithCancel(context.Background()) cancel() if _, _, err := DiscoverRepositoryRoot(ctx, dir); !errors.Is(err, context.Canceled) { @@ -87,6 +98,48 @@ func TestDiscoverRepositoryRootFailures(t *testing.T) { } } +func TestDiscoverRepositoryRootInterruptedGit(t *testing.T) { + dir := t.TempDir() + bin := t.TempDir() + ready := filepath.Join(t.TempDir(), "ready") + script := "#!/bin/sh\n[ \"$LC_ALL\" = C ] || exit 42\n: > \"$DISCOVERY_READY\"\nexec /bin/sleep 60\n" + if err := os.WriteFile(filepath.Join(bin, "git"), []byte(script), 0o755); err != nil { + t.Fatal(err) + } + t.Setenv("PATH", bin) + t.Setenv("DISCOVERY_READY", ready) + t.Setenv("LC_ALL", "different-locale") + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + done := make(chan error, 1) + go func() { _, _, err := DiscoverRepositoryRoot(ctx, dir); done <- err }() + timer := time.NewTimer(5 * time.Second) + defer timer.Stop() + ticker := time.NewTicker(10 * time.Millisecond) + defer ticker.Stop() + for { + if _, err := os.Stat(ready); err == nil { + break + } + select { + case err := <-done: + t.Fatalf("Git ended before readiness: %v", err) + case <-timer.C: + cancel() + err := <-done + t.Fatalf("Git did not become ready: %v", err) + case <-ticker.C: + } + } + cancel() + if err := <-done; !errors.Is(err, context.Canceled) { + t.Fatalf("cancellation = %v", err) + } + if got := os.Getenv("LC_ALL"); got != "different-locale" { + t.Fatalf("parent locale changed to %q", got) + } +} + func TestDiscoverRepositoryRootWithoutGit(t *testing.T) { repo := newRemoteFixture(t) standalone := t.TempDir() diff --git a/preparation.go b/preparation.go index 48fbc31..5a7fbce 100644 --- a/preparation.go +++ b/preparation.go @@ -53,8 +53,8 @@ type DependencyPreparationRequest struct { // toolchain selected. Alternate module files selected by GOFLAGS are overridden // for this download; other effective Go settings remain available. It may // access module networks and updates the Go module cache using the caller's Go -// proxy, private-module, credential, and cache -// settings. If required checksums are missing, the Go command may update +// proxy, private-module, credential, and cache settings. If required checksums +// are missing, the Go command may update // go.sum. Cancellation and Go command failures are returned to the caller. func PrepareBuildDependencies(ctx context.Context, request DependencyPreparationRequest) error { if strings.TrimSpace(request.RepositoryDir) == "" {