From 2d5a8110c61e31daf8698766114ae305d0e0e469 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Sun, 11 Oct 2026 00:57:29 +0000 Subject: [PATCH] Prepare v0.4.1 release notes --- docs/releases/v0.4.1.md | 29 +++++ docs/roadmap/audit.md | 152 -------------------------- docs/roadmap/promptkit-bug-report.md | 98 ----------------- docs/roadmap/race.md | 156 --------------------------- 4 files changed, 29 insertions(+), 406 deletions(-) create mode 100644 docs/releases/v0.4.1.md delete mode 100644 docs/roadmap/audit.md delete mode 100644 docs/roadmap/promptkit-bug-report.md delete mode 100644 docs/roadmap/race.md diff --git a/docs/releases/v0.4.1.md b/docs/releases/v0.4.1.md new file mode 100644 index 0000000..da8d629 --- /dev/null +++ b/docs/releases/v0.4.1.md @@ -0,0 +1,29 @@ +# ReleaseKit v0.4.1 + +## Summary + +This patch synchronizes shared state in the delivery and Gitea HTTP test +fixtures. It makes the test suite reliable under concurrent request handling +and the Go race detector. Production code and public behavior are unchanged. + +## Changes + +- Protect delivery fixture state with mutexes and independent snapshots; use + atomic observations for independent Gitea fixture counters and flags. +- Keep fatal test assertions in the test goroutine and ensure release test + servers close with their owning tests. +- Preserve existing delivery, conflict, retry, and source-tag assertions while + exercising the synchronized fixtures. + +## Compatibility + +There are no API, configuration, or runtime behavior changes, and no consumer +migration is required. The Go minimum remains 1.26.0. The published v0.4.0 tag +and its contents are unchanged. + +Consumers may upgrade to v0.4.1 with their existing API, profile, and workflow +configuration. + +[COPYRIGHT](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.4.1/COPYRIGHT) | +[LICENSE](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.4.1/LICENSE) | +[ATTRIBUTION](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v0.4.1/docs/oss/attribution.md) diff --git a/docs/roadmap/audit.md b/docs/roadmap/audit.md deleted file mode 100644 index eb85dbe..0000000 --- a/docs/roadmap/audit.md +++ /dev/null @@ -1,152 +0,0 @@ -# Targeted concurrency audit - -Status: complete; all five finding groups corrected and verified. - -## Scope and baseline - -Reviewed ReleaseKit at `6176971`, including the -[PromptKit report](promptkit-bug-report.md) and -[fixture synchronization roadmap](race.md). The audit covered shared-state -ownership in HTTP fixtures, background goroutines, cancellation callbacks, -subprocess-driven tests, output capture, and production operation callbacks. - -Code discovery used the codebase graph, with source inspection and filesystem -searches where the graph omitted recent files. Reviewed current source and -fixture consumers, including assertion and diagnostic reads. The full offline -race-enabled baseline used Go `go1.26.8 linux/amd64`: - -```sh -GOTOOLCHAIN=local GOWORK=off GOPROXY=off GOSUMDB=off \ - go test -race -mod=readonly -count=1 ./... -``` - -The recorded baseline failed in -`TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout`. It reported four race -diagnostics involving three fields: `commit`, `remoteCommit`, and -`releaseCreates`. Other packages passed. An earlier redirected invocation -returned unsuccessfully without diagnostics and supplied no usable baseline; -the complete results above came from a direct invocation. - -The additional findings below were established by source inspection. Their -absence from this baseline's diagnostics reflects its exercised schedules. - -## Findings and corrections - -### 1. CLI delivery fixtures share mutable state without synchronization - -Status: corrected; original reported defect and related fixture accesses. - -The HTTP handlers in [main_test.go](../../cmd/releasekit/main_test.go) read -configuration and update page state, counters, note content, and assets while -tests configure and inspect that state. The -[mapped workflow fixture](../../cmd/releasekit/multi_module_woodpecker_test.go) -has the same ownership pattern. Subprocess completion supplies no shared-state -synchronization with the parent process's HTTP goroutines. - -**Correction:** Each fixture owns a mutex and a separate plain state value. -Handlers serialize state access; test configuration uses locked updates, and -assertions and diagnostics use locked snapshots. Snapshots copy asset maps and -byte slices so inspection retains independent values. All consumers use that -boundary, including source-only, binary, sparse-checkout, alias, failure, -ordinary Woodpecker, and mapped-module tests. Response helpers run under the -handler's lock. The CLI fixture derives response URLs from the request; mapped -fixture configuration is initialized before its server starts. - -### 2. The public delivery fixture has the same unsynchronized ownership - -Status: corrected; additional finding. - -The fixture in [delivery_test.go](../../delivery_test.go) shares request counts, -tags, notes, release-page maps, and asset maps between handlers and test code. -The module-page test also changes the selected tag and note between deliveries. -Its consumers include [repository resolution](../../repository_test.go) and -[partial-clone inspection](../../source_inspection_test.go) tests. - -**Correction:** Added a fixture-local mutex, locked updates, and independent -snapshots, including copied page and asset maps. Tag and note changes occur in -one locked update. The server URL is initialized before serving requests. -Applied the same startup ordering to the existing synchronized benchmark server -and its [artifact-read regression test](../../delivery_reads_linux_test.go). -Binary assertion groups reuse their captured snapshot to avoid repeated asset -copies. - -### 3. Gitea adapter fixtures expose unsynchronized counters and flags - -Status: corrected; additional finding. - -[Release fixtures](../../internal/gitea/release_test.go) share creation and -lookup counts, creation flags, and cancellation observations between handlers -and assertions. Lost-acknowledgment scenarios also carry state across separate -HTTP connections. The -[external attachment fixture](../../internal/gitea/assets_test.go) shares its -download counter with the test goroutine. - -**Correction:** Used standard-library atomic counters and flags for these -independent scalar observations. Handler updates and assertion/diagnostic reads -use their atomic methods. Existing exact counts, conflict handling, recovery, -and external-origin credential assertions remain in place. - -### 4. HTTP assertions invoke fatal methods from request goroutines - -Status: corrected; additional finding. - -Gitea release and asset handlers, including their assertion helpers, used -`testing.T.Fatal` and `Fatalf`. Those methods terminate the calling goroutine -and belong to the test goroutine. In a handler, they can abandon request -processing and produce misleading transport failures. - -**Correction:** Handler assertions report through `Error` or `Errorf` and -return from the affected handler or helper. Fatal assertions remain in the test -goroutine. The fixtures still mark every violated assertion as a test failure. - -### 5. A release fixture omits server lifetime cleanup - -Status: corrected; additional finding. - -`newReleaseServer` returned an active HTTP server without registering cleanup, -and several callers omitted closure. This left listeners and serving goroutines -alive after their owning test finished. - -**Correction:** The helper registers `server.Close` with `t.Cleanup`, giving -server shutdown one owner for all its consumers. - -## Production and other reviewed paths - -The audit found no confirmed production concurrency defect in these paths: - -| Boundary | Ownership and synchronization evidence | -| --- | --- | -| [Git blob reader](../../internal/git/blob_reader.go) | A mutex serializes reads, close, and terminal state. A cancellation callback closes process pipes, and the read operation joins a started callback before continuing. Process diagnostics are inspected after child wait. | -| [Process adapter](../../internal/process/process.go) | Process handles are initialized before publication; cancellation uses a context. `sync.Once` protects child wait and its result. Ordinary command completion joins output-copy workers before returning captures. | -| [Go output capture](../../internal/gotool/gotool.go) | One mutex protects combined output, forwarding, and writer errors. Capture ownership transfers after the process finishes writing. | -| [Gitea upload](../../internal/gitea/upload.go) | The multipart worker owns the writer and file reader. An error channel transfers completion; request failure closes the pipe and joins the worker. | -| [Runtime metadata](../../internal/runtimeinfo/version.go) | The injected value is set at link time; fallback initialization uses `sync.OnceValue`. Existing concurrent accessor coverage uses atomics, channels, and a wait group. | -| [Go settings owner](../../internal/gotool/owner.go), candidate validation, and publication callbacks | Mutable owners and validation caches belong to one operation. Validation, product callbacks, and subsequent builds execute sequentially. Internal mutable owners remain behind the facade. | -| Background test workers | Reviewed build/process/dependency cancellation, Git-reader lifecycle, publication watchers, and destination-resolution workers. Results transfer through channels; the publication watcher has an explicit completion join. | -| Other HTTP fixtures | Immutable asset payloads remain read-only. The upload fixture already locks files and observations and returns copied POST names. The module-proxy fixture uses an atomic request counter. | -| Process environment | Tests changing environment settings run sequentially within their package; subprocess overlays belong to individual invocations. | - -These conclusions apply to the reviewed ownership paths. Race-enabled execution -and inspection supply evidence for this audit, rather than a guarantee about -every possible caller or schedule. Production files and public API contracts -were unchanged by the corrections. - -## Verification and completion - -The affected source-delivery, ordinary Woodpecker, and mapped-module workflows -passed with the race detector after fixture synchronization. The Gitea release -and asset checks also passed during focused verification. Existing assertions -continue to cover tag mismatch rejection, source preservation, complete notes, -idempotent delivery, asset counts, conflicts, and recovery. - -**Completed:** The final full offline race-enabled suite passed across all -packages with Go 1.26.8, including the original failing workflow and every -fixture consumer. The compiled public API/dependency check passed. The corrected -handlers, configuration updates, snapshots, diagnostics, counters, and server -startup/cleanup were reviewed against their ownership boundaries. Local -documentation links and fragments, Go formatting, and diff whitespace passed. -The linked roadmap and bug report record the completed remediation. - -The audit and corrections are local development work. Future publication follows -the existing [release procedure](../release.md), with the published v0.4.0 -contents preserved. diff --git a/docs/roadmap/promptkit-bug-report.md b/docs/roadmap/promptkit-bug-report.md deleted file mode 100644 index 59a6b74..0000000 --- a/docs/roadmap/promptkit-bug-report.md +++ /dev/null @@ -1,98 +0,0 @@ -# PromptKit Bug Report: Race In Source-Delivery Test Fixture - -Status: remediated in current source; focused and full offline race-enabled -verification passed. See the [concurrency audit](audit.md) for corrections and -additional findings. The original report below records the reviewed release. - -## Reviewed Release And Environment - -- ReleaseKit v0.4.0, commit - `b96262eb0ce9c8f2eb3e948a5841db2b799bc91b`, matching the published tag. -- Go `go1.26.8 linux/amd64`, with `GOTOOLCHAIN=local` and `GOWORK=off`. -- Discovered while PromptKit evaluated upgrading its release-tool dependency - from v0.3.0 to v0.4.0. - -## Observed Failure - -The upstream race-enabled suite failed in -`TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout`. The race detector -reported shared fixture state accessed by the test goroutine and HTTP-handler -goroutines without synchronization. - -Observed command, run from the ReleaseKit repository root at the commit above: - -```sh -GOTOOLCHAIN=local GOWORK=off go test -race -mod=readonly ./... -``` - -The root package and the other reported packages passed; `cmd/releasekit` failed -with `race detected during execution of test`. `go vet -mod=readonly ./...` -passed under the same toolchain/environment settings. - -For focused investigation, the affected test can be selected with: - -```sh -GOTOOLCHAIN=local GOWORK=off go test -race -mod=readonly -count=1 \ - ./cmd/releasekit \ - -run '^TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout$' -``` - -The full-suite command above is the observed reproduction. The focused command -is a suggested diagnostic; repeatability under that selection has not been -established. Race detection may depend on scheduling. - -## Race Locations - -Locations below refer to the reviewed v0.4.0 commit: - -| Shared field | HTTP handler access | Test goroutine access | -| --- | --- | --- | -| `remoteCommit` | Read in `newDeliveryServer`, `cmd/releasekit/main_test.go:1178` | Written in `cmd/releasekit/releasekit_delivery_workflow_test.go:90` and `:97` | -| `releaseCreates` | Updated during release creation in `cmd/releasekit/main_test.go:1207` | Read in `cmd/releasekit/releasekit_delivery_workflow_test.go:94` | - -See the [workflow test](../../cmd/releasekit/releasekit_delivery_workflow_test.go) -and [HTTP fixture](../../cmd/releasekit/main_test.go). Representative diagnostic: - -```text -WARNING: DATA RACE -Read ... newDeliveryServer.func1 ... main_test.go:1178 -Previous write ... TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout - ... releasekit_delivery_workflow_test.go:90 - -WARNING: DATA RACE -Write ... newDeliveryServer.func1 ... main_test.go:1207 -Previous read ... TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout - ... releasekit_delivery_workflow_test.go:94 -``` - -## Assessment And Impact - -The workflow test mutates `deliveryServerState.remoteCommit` to simulate a -hosted-tag mismatch and later restores it. The HTTP fixture reads that field -while handling requests and updates counters that the test inspects. These -accesses have no explicit Go synchronization. Subprocess completion does not -provide the Go synchronization needed for accesses shared with HTTP goroutines. - -Source comparison shows the same unsynchronized access pattern in v0.3.0. -This report does not establish that the failure was introduced by v0.4.0, and -the v0.3.0 race suite was not rerun for this assessment. - -The demonstrated defect is in the upstream test fixture, not production -publication or delivery code. PromptKit's existing disposable publication -success/failure fixtures passed using the published v0.4.0 tool. PromptKit uses -source-tag publication and does not currently use hosted delivery. Accordingly, -this finding is not a demonstrated blocker to that adoption, but it prevents a -clean upstream race-suite result and can cause validation noise. - -## Requested Outcome - -Please synchronize the fixture's cross-goroutine state accesses, including test -reads and writes, and check the other users of the shared fixture for the same -pattern. Preserve the mismatched-tag rejection and idempotent source-only -delivery assertions. A fixture-local synchronization approach is sufficient; -the evidence does not call for production API changes or additional release -gates. - -Verify the affected workflow under the race detector and rerun the upstream -race-enabled suite after remediation. Upstream should select the specific -implementation and any warranted follow-up coverage. diff --git a/docs/roadmap/race.md b/docs/roadmap/race.md deleted file mode 100644 index 9527d11..0000000 --- a/docs/roadmap/race.md +++ /dev/null @@ -1,156 +0,0 @@ -# Roadmap: synchronize delivery test fixtures - -Status: complete; all audited corrections implemented and verified in `059a795`. - -The [concurrency audit](audit.md) records the wider review, additional findings, -corrections, and final verification evidence. - -## Objective and evidence - -Make the delivery HTTP fixtures safe for concurrent access by test goroutines -and request handlers while preserving their existing behavioral assertions. -The [PromptKit bug report](promptkit-bug-report.md) supplies the original -diagnostics and consumer context. - -Upstream investigation reproduced -`TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout` under the race detector -with Go 1.26.8. The diagnostics identified `commit`, `remoteCommit`, and -`releaseCreates` accesses shared between the workflow test and HTTP handlers. -The workflow test and relevant unsynchronized accesses predate v0.4.0. -Subprocess completion supplies no synchronization for this shared Go state. - -The ordinary Woodpecker and multi-module workflow tests passed their focused -race-enabled runs during investigation. Source inspection nevertheless found -the same unsynchronized state pattern in their fixtures. A passing run alone -does not establish safe access under other scheduling. - -## Scope - -The correction belongs in test infrastructure: - -- The `deliveryServerState`, `newDeliveryServer`, and `writeDeliveryRelease` - helpers in [main_test.go](../../cmd/releasekit/main_test.go), including all - tests using that fixture. -- The [source-delivery workflow test](../../cmd/releasekit/releasekit_delivery_workflow_test.go) - and [ordinary Woodpecker workflow tests](../../cmd/releasekit/woodpecker_test.go). -- The `mappedDeliveryFixture`, `newMappedDeliveryFixture`, and their consumers - in [multi_module_woodpecker_test.go](../../cmd/releasekit/multi_module_woodpecker_test.go). -- The public delivery fixture in [delivery_test.go](../../delivery_test.go), - including repository-resolution and partial-clone test consumers, plus server - startup in the delivery benchmark and artifact-read regression test. -- Shared observations, HTTP assertion helpers, and server cleanup in the Gitea - [release tests](../../internal/gitea/release_test.go) and - [asset tests](../../internal/gitea/assets_test.go). - -Production behavior, public API contracts, and release requirements retain their -current definitions. Use the standard library and small fixture-local helpers, -following the [agent](../policy/agents.md) and -[testing](../policy/testing.md) policies. - -## Implementation - -1. **Synchronize the shared delivery fixture.** Give mutable fixture state a - `sync.Mutex`. Protect handler reads and writes, test-side configuration - changes, assertion reads, counters, page state, note content, and asset-map - operations with the same mutex. Initialize immutable configuration before - serving requests where practical; synchronize later initialization such as - the source commit assigned after profile creation. Review captured handler - values and response helpers as part of this boundary. - -2. **Update every shared-fixture consumer.** Use simple locked setters and - snapshot accessors for test configuration and assertions. Snapshot plain - values separately from the mutex, copying maps and mutable byte slices when - they will be inspected outside the lock. Include failure diagnostics that - format fixture state. Keep lock ownership clear in handler helpers and - release locks before executing CLI workflows or closing the server. - -3. **Apply the same discipline to the mapped fixture.** Protect its commit, - request and creation counters, page fields, uploads, and asset map, together - with all corresponding test accesses. Keep each fixture's synchronization - local and proportionate to its existing responsibilities. - -4. **Synchronize the public delivery fixture.** Protect request counts, - selected tags and notes, release-page maps, and asset maps with a fixture-local - mutex. Use locked updates and independent snapshots in every consumer, - including repository-resolution and partial-clone tests. Update related tag - and note fields together. Initialize server URLs before serving requests in - the public fixture, benchmark, and artifact-read regression fixture. Reuse - captured snapshots within binary assertion groups. - -5. **Synchronize Gitea scalar observations.** Use atomic counters and flags for - independent creation counts, lookup counts, creation state, cancellation - observations, and external attachment download counts. Update handler - accesses, test assertions, and diagnostic reads together. Retain exact - request-count, reconciliation, and credential assertions. - -6. **Keep fatal assertions in the test goroutine.** Change HTTP handlers and - their assertion helpers to report failures through `Error` or `Errorf` and - return from the affected handler or helper. Preserve failure reporting for - unexpected requests, malformed payloads, and incorrect credentials. - -7. **Give release-server cleanup a single owner.** Register `server.Close` with - `t.Cleanup` inside `newReleaseServer` so all consumers shut down the listener - and serving goroutines when their test finishes. - -8. **Preserve behavioral coverage.** Retain rejection of mismatched hosted tags - before release creation, exact source-tag preservation, complete note bodies, - idempotent page and asset delivery, expected asset counts, mapped module - identity, retries using tagged configuration, and existing failure outcomes. - Use these workflows as the regression coverage for the reported race. Add - further coverage only for a concrete gap discovered during the correction. - -## Audit finding coverage - -All five finding groups are corrected in `059a795`. The audit owns their -diagnostics and review evidence; this roadmap records the corresponding work. - -| Audit finding | Roadmap work | Status | -| --- | --- | --- | -| [1. CLI and mapped fixture state](audit.md#1-cli-delivery-fixtures-share-mutable-state-without-synchronization) | Steps 1–3: synchronized handlers, configuration, snapshots, and consumers. | Complete. | -| [2. Public delivery fixture state](audit.md#2-the-public-delivery-fixture-has-the-same-unsynchronized-ownership) | Step 4: synchronized public fixture and safe server startup. | Complete. | -| [3. Gitea counters and flags](audit.md#3-gitea-adapter-fixtures-expose-unsynchronized-counters-and-flags) | Step 5: atomic observations and diagnostic reads. | Complete. | -| [4. Fatal HTTP assertions](audit.md#4-http-assertions-invoke-fatal-methods-from-request-goroutines) | Step 6: failure reporting and returns from handlers. | Complete. | -| [5. Server lifetime cleanup](audit.md#5-a-release-fixture-omits-server-lifetime-cleanup) | Step 7: cleanup owned by the release-server helper. | Complete. | - -## Verification and completion - -With dependencies prepared, run the affected workflows from the repository root -using the local toolchain and offline module settings: - -```sh -GOTOOLCHAIN=local GOWORK=off GOPROXY=off GOSUMDB=off \ - go test -race -mod=readonly -count=1 ./cmd/releasekit \ - -run '^(TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout|TestWoodpeckerTagDeliveryTemplate|TestMultiModuleWoodpeckerDelivery)$' -``` - -Then run the full race-enabled suite once to verify the other shared-fixture -users and catch remaining cross-goroutine access: - -```sh -GOTOOLCHAIN=local GOWORK=off GOPROXY=off GOSUMDB=off \ - go test -race -mod=readonly -count=1 ./... -``` - -Review the fixture accesses as well as the test results: successful execution -complements the synchronization review. Keep local Git repositories, HTTP -servers, and synthetic module caches isolated as described in -[development](../development.md#testing-synthetic-modules). - -Completion means every audited fixture and its consumers use synchronized state, -HTTP assertions and server cleanup follow their goroutine ownership, the existing -assertions remain effective, and the focused and full race-enabled suites pass. -Record completion and verification here and update the bug report's status with -the remediation outcome. Commit the completed correction under the repository's -ordinary change workflow. - -This verification addresses the reported defect. Future release validation -continues to follow the [release procedure](../release.md). The correction is -suitable for a subsequent patch release; preparation and publication are separate -work under that procedure, preserving the published v0.4.0 contents. - -**Completed:** CLI, mapped, and public delivery fixtures and their consumers use -mutex-protected updates and independent snapshots. Existing mismatch, note, -asset, retry, and source-preservation assertions remain in place. Focused workflow checks and the -full offline race-enabled suite passed with Go 1.26.8; the compiled API/dependency -check passed. The audit also corrected public delivery fixture ownership, -Gitea scalar observations, HTTP assertion handling, and server cleanup.