This plan turns the [performance roadmap](performance.md) into ordered work for
a Luna coding agent. The inspected baseline is commit
`ff90510c380b47a23d0ac1cd8c314b1c4366a15b`; this planning change updates only
the two roadmap documents. Recheck the worktree and relevant code before each
stage because subsequent stages build on earlier changes.
Execute Stage 1 through Stage 11 in order. Each stage is one focused
implementation prompt. Complete its changes, applicable validation, canonical
documentation updates, and local commit under the
[agent policy](../policy/agents.md), then proceed. The sequence ends with a
results report for review. Consumer pilots, hosted operations, and intermediate
releases are outside this implementation sequence.
Read [development.md](../development.md) and its applicable policy guide.
Apply the [architecture policy](../policy/architecture.md),
[testing policy](../policy/testing.md), and
[documentation policy](../policy/documentation.md). Use graph tools for code
discovery as instructed by `AGENTS.md`.
## Selected Design And Scope
| Area | Selected implementation |
| --- | --- |
| Child process | A small piped-process helper in `internal/process`, sharing existing command setup and error wrapping. |
| Git blobs | One lazy `git cat-file --batch` session per candidate tree, with one request/response exchange at a time. |
| Raw file bytes | Caller-owned bytes for regular-file reads; immutable symlink strings cached by object ID. |
| Repeated checks | Compact formatting outcomes, parsed Markdown documents, and replacement summaries cached by object ID within their check. |
| Artifact hashing | Data-only declarations followed by one existing local preflight verification; retain build hashing and missing-upload verification. |
| Go output | Separate status and captured-output methods; transfer completed buffers without copying. |
| Runtime version | A standard-library `sync.OnceValue` cache for the fallback, with an immediate injected-version return. |
These are internal implementation choices. Preserve public facade signatures,
findings, failure causes, publication guards, and delivery recovery. Existing
public contracts remain authoritative. New helpers expose only the internal
operations needed here. File-size selection and existing supported inputs keep
their current semantics.
The Git session and checker caches belong to one validation operation. Existing
sequential check execution remains sufficient for this design. The runtime
fallback is immutable process data and uses the standard library's concurrent
initialization mechanism.
## Stage 1: Measure Candidate Content And Validation
**Owners:** Benchmark files in `internal/validation` and root facade fixtures.
**Completion:** Complete. Measurement-only benchmarks cover tree metadata,
content reads and formatting for 1,000 unique or repeated-ID Go blobs in loose
and packed repositories, repeated links to identical 1 MiB Markdown documents,
retained content after processing, and a real offline facade `Check`. Fixtures
verify committed bytes, unique/repeated object IDs, link counts, and Git pack
state. The facade fixture selects Go formatting, local-replacement inspection,
and Markdown while using test-owned module and build caches.
### Baseline Measurement Record
The baseline is commit `ca571aca4f2fd43798174a6d0d7c5a588b5347c9`, before adding
measurement support. Results use Go `go1.26.8 linux/amd64`, Git `2.53.0`, and a
Linux x86_64 Intel Core i5-7260U at 2.20 GHz. The command was
`go test ./internal/validation . -run '^$' -bench '^BenchmarkPerformance' -benchmem -count=3`.
Each candidate-content operation used a fresh empty content cache with tree
metadata prepared outside its timer. The loose fixtures had loose objects and
no pack; packed fixtures were checked after `git gc --aggressive --prune=now`.
The unique Go fixture contains 1,000 distinct, formatted files totaling 49,000
bytes. The repeated-ID fixture contains 1,000 paths sharing one 15-byte blob.
The Markdown fixture contains two identical 1 MiB documents, a 47,780-byte
index with 1,000 repeated links, and 2,144,932 bytes processed per check. The
complete-check module contains 906 bytes of Go/module input. Its `GOMODCACHE`
and `GOCACHE` are test-owned temporary directories, and module network access
is disabled; the build cache is warmed before timing.
| Measurement | Baseline observations | Final comparison |
| --- | --- | --- |
| Tree metadata, 1,000 entries | Loose: 27.5–28.2 ms; packed: 25.0–28.1 ms. About 1.28 MB allocated per operation. | Loose: 33.0–35.1 ms; packed: 7.43–15.10 ms. About 1.28 MB allocated. |
| Unique-blob reads, 49,000 bytes | Loose: 1.86–1.96 s; packed: 2.57–3.08 s. About 34.57 MB allocated per operation. | Loose: 89.8–126.3 ms; packed: 41.5–61.0 ms. About 530 KB allocated. |
| Repeated-ID reads, 15,000 bytes across 1,000 paths | Loose: 2.12–3.00 ms; packed: 3.42–7.75 ms. About 50.7 KB allocated per operation. | Loose: 84.8–99.5 ms; packed: 22.3–22.9 ms. About 480–483 KB allocated; slower than baseline because raw file bytes are deliberately not retained between reads. |
| Formatting, 1,000 unique files | Loose: 1.87–2.94 s; packed: 2.12–4.55 s. About 36.9 MB allocated per operation. | Loose: 111.9–114.6 ms; packed: 56.2–59.1 ms. About 2.89 MB allocated. |
| Formatting, 1,000 paths sharing one object | Loose: 4.52–12.66 ms; packed: 6.11–8.62 ms. About 1.49 MB allocated per operation. | Loose: 2.58–2.81 ms; packed: 2.07–2.13 ms. About 121 KB allocated. |
| Markdown links and parsing | 19.4–21.5 ms for two identical 1 MiB documents and 1,000 links; about 7.67 MB allocated per operation. | 10.90–11.14 ms for the same fixture; about 2.29 MB allocated. |
| Facade check, standard Go suite only | 385–690 ms; 906 module bytes; 336–340 KB allocated in the benchmark process. | 374.0–386.9 ms; 906 module bytes; 336–337 KB allocated in the benchmark process. |
| Facade check with selected file checks | 528–634 ms; 906 module bytes; 632–646 KB allocated in the benchmark process. | 379.7–391.8 ms; 906 module bytes; 503–513 KB allocated in the benchmark process. |
| Retained content after formatting and Markdown parsing | 1,385,040–1,385,616 bytes above the post-GC metadata baseline, with the candidate and Markdown cache kept alive. | 180,384–198,000 bytes above the post-GC metadata baseline, with the same caches kept alive. |
Raw output is in temporary storage at `/tmp/releasekit-performance-baseline.txt`.
The shared machine produced substantial timing variation, especially for packed
content and the full check; allocations and retained-byte observations were
more stable. `B/op` for facade checks measures allocations in the benchmark
process and does not include allocations inside its Go subprocess. These are
workload-specific component measurements, not runtime guarantees.
1. Add benchmarks named with the `BenchmarkPerformance` prefix. Reuse local Git
fixture patterns; adapt shared helper parameters to `testing.TB` only where
a benchmark needs them.
2. Measure content access and formatting for 1,000 unique regular Go blobs.
Include loose and packed objects and a repeated-object-ID case. Measure
Markdown checking with a 1 MiB document, repeated links to it, and identical
contents under different directories.
3. Measure a complete facade `Check` using a temporary committed Go module with
a valid release note, Go formatting, local replacements, and Markdown selected.
Keep its Go suite real and offline. Prepare fixtures outside timed work.
Report allocations and bytes processed, separating content access, tree
metadata, and Go test/compile work.
4. Add one diagnostic retained-memory observation after file processing, with
the candidate and document cache kept alive, to expose raw-content retention.
Keep GC/profile collection outside timing loops.
5. Create a measurement record in this document for baseline and final results.
Include commit, Go/Git versions, fixture sizes, packed/loose state, and cache
state. Keep raw benchmark output or profiles in temporary storage.
**Validation and completion:** Run these benchmarks with `-run '^$'`,
`-bench '^BenchmarkPerformance'`, `-benchmem`, and `-count=3`. Verify the
fixtures inspect the intended bytes. Synthetic modules use test-owned module
caches under the developer guide. Timing ratios remain reported measurements;
behavioral tests protect content, failures, and ownership. This stage changes
measurement support only.
## Stage 2: Measure Delivery, Output, And Runtime Lookup
**Owners:** Benchmark files in `internal/delivery`, `internal/gotool`,
`internal/runtimeinfo`, and root delivery fixtures.
**Completion:** Complete. Measurements cover declarations and local
verification of two deterministic 16 MiB files, prebuilt facade delivery for
fresh uploads and matching retries, combined output capture and forwarding at
4 KiB and 8 MiB, and injected and real metadata version access. The local HTTP
fixture hashes and counts incoming multipart bytes incrementally and streams
matching files from immutable fixture data. It verifies uploaded identities and
that retries match all three declared assets without uploading again.
### Delivery, Output, And Runtime Baseline
The baseline is commit `29e1bab`, before production refactoring, using the same
Go, Git, operating system, processor, benchmark command, and three repetitions
recorded above. Artifact declaration and preflight fixtures contain two
deterministic 16 MiB files (33,554,432 binary bytes per operation); setup and
initial writes are outside the timers. Declaration currently hashes each file,
and declaration plus local verification therefore measures both hashing passes.
The facade benchmark uses a committed tagged source fixture and prebuilt
artifacts, so its time includes local source checks, hashing, HTTP requests, and
streamed body comparison or upload, but no Go compilation. Fresh hosted state is
reset outside the timed region for every operation. Retry state is prepared
before timing. The fixture server retains only digest and size metadata for
uploads and streams known matching bytes directly from shared fixture content.
Output measurements call the combined writer directly, with forwarding to
`io.Discard`, then capture its completed buffer. Each baseline and final
comparison writes its complete input in one call. The baseline includes the
against real build metadata; injected access measures the selection helper's
injected-version branch. The benchmark process reports its own allocations,
including local HTTP fixture work; streamed artifact bodies are not buffered by
the fixture server.
| Measurement | Baseline observations | Final comparison |
| --- | --- | --- |
| Artifact declarations, two 16 MiB files | 94.3–95.4 ms; 33,554,432 bytes processed; about 68.9 KB allocated. | 11.4–19.4 μs; 33,554,432 declared bytes; 2,296 B and 33 allocations. Declaration no longer reads or hashes file contents. |
| Declarations plus local verification | 186.7–188.4 ms; 33,554,432 binary bytes per each of two passes; about 136.2 KB allocated. | 94.3–95.3 ms; one 33,554,432-byte verification pass; about 69.7 KB and 58–59 allocations. |
| Facade delivery, fresh uploads | 429–447 ms; 32 MiB binary payload, two files plus manifest; 1.43–1.46 MB allocated. | 331.9–415.3 ms; same payload; 1.38–1.39 MB allocated. |
| Facade delivery, matching retry | 309.1–310.0 ms; 32 MiB of local verification plus streamed remote comparisons; 1.21–1.22 MB allocated. | 210.7–217.5 ms; same local verification and streamed comparisons; 1.15 MB allocated. |
| Combined output, one 4 KiB write and `io.Discard` | 1.82–1.87 μs; 8,256 B and 3 allocations per operation. | 0.94–1.06 μs; 4,176 B and 2 allocations. |
| Combined output, one 8 MiB write and `io.Discard` | 2.02–2.36 ms; 16.78 MB and 3 allocations per operation, including the completed capture copy. | 1.73–2.22 ms; about 8.39 MB and 2 allocations. |
| Injected version selection | 0.745–0.754 ns; 0 B and 0 allocations. | 0.754–0.758 ns; 0 B and 0 allocations. |
| Repeated real metadata fallback | 1.438–1.474 μs; 1,184 B and 7 allocations per operation. | 4.59–4.65 ns after cache initialization; 0 B and 0 allocations. Separate initialization: 1.54–2.14 μs; 1,264 B and 9 allocations. |
The delivery observations include local HTTP and filesystem costs specific to
these fixtures. The output component benchmark does not include child startup;
the real delivery benchmark does include HTTP request handling. These results
describe measured workloads and do not establish runtime guarantees.
### Output Chunking Follow-Up
The R3 follow-up extends `BenchmarkPerformanceCombinedOutput` with 32 KiB
writes. Three repetitions on Go 1.26.8, linux/amd64, and the Intel Core i5-7260U
used the completed writer at `7d92c47` with the extended benchmark:
```sh
GOPROXY=off GOSUMDB=off GOTOOLCHAIN=local go test ./internal/gotool -run '^$' -bench '^BenchmarkPerformanceCombinedOutput$' -benchmem -count=3 -benchtime=1s
```
| Workload, forwarding to `io.Discard` | Elapsed time | Total allocated bytes/op | Allocations/op | Returned output length |
| --- | --- | --- | --- | --- |
| One 8 MiB write | 1.49–1.56 ms | 8,388,697–8,388,702 | 2 | 8,388,608 bytes |
**Status:** All five target components implemented. The
[implementation record](implementation.md) owns completed stages and performance
results; the [remediation record](remediation.md) owns review findings and their
completion status.
## Objective And Ownership
Reduce the time, allocations, retained memory, and repeated I/O of routine
release operations. Improvements belong behind the existing `releasekit` and
`buildinfo` facades and follow the [architecture policy](../policy/architecture.md)
and [routine-release priorities](../adr/0002-routine-releases.md).
This document owns the desired target state and its rationale. The
[implementation plan](implementation.md) owns stages, tasks, validation, and
implementation status. The [internal inventory](../internal/overview.md) and
focused internal documents own implemented mechanics; Go declarations and
GoDoc own public contracts.
## Review Evidence
The computational efficiency review examined commit `db9e24f` on 2026-10-09.
Temporary offline probes produced these local measurements:
| Probe | Result |
| --- | --- |
| Read 1,000 unique committed Go blobs totaling 59,670 bytes through the existing path | 1.83–2.00 seconds across three runs. |
| Read and verify the same blobs through one Git batch command | 48–59 milliseconds across three runs. |
| Read a cached 1 MiB blob | One allocation of 1 MiB per read. |
| Resolve runtime version through the Go build metadata fallback in a test executable | Approximately 2 microseconds, 1,184 allocated bytes, and seven allocations per call. |
| Resolve an injected runtime version | Zero allocations. |
Blob timings cover content access, with tree metadata discovery measured
separately. They exclude Go tests, compilation, formatting, and Markdown parsing.
The fixture contained small, loose Git objects. These are component measurements
on one machine; overall release gains depend on consumer workloads. The probes
were temporary and left the repository unchanged.
Static review also identified repeated artifact hashing and unconditional Go
output copying. The [implementation record](implementation.md) contains their
baseline and completed-implementation measurements.
## Desired Target State
### Git Content Access
Selected file checks share an operation-scoped Git content reader. Requests
remain lazy, use committed object IDs, and consume responses incrementally.
Subprocess startup is amortized across the operation. Metadata-only checks
continue to use the captured tree without starting a content reader.
The reader preserves offline object access, cancellation, contextual inspection
errors, and explicit process cleanup. Each validation operation owns its reader
and reusable data independently.
### Validation Memory And Reuse
One-use regular-file bytes belong to their immediate consumer and are released
from retained references after inspection. Repeated work reuses immutable parsed
or compact results keyed by object ID within the relevant check. Reusable
symlink targets belong to the candidate tree. Link checks apply shared document
data in each source path's own context, preserving findings, source locations,
and relative destinations.
This replaces retention of every inspected raw blob with data that supports
actual repeated work. Explicit repeated raw reads may reread an object through
the batch reader. The same bytes can be interpreted separately by different
checks; reuse within one interpretation preserves object-ID deduplication.
### Artifact Verification
Delivery declarations reuse recorded digests and collect file metadata. One
preflight verification checks current local bytes before hosted requests. This
combines adjacent declaration-time and asset-verification hash passes for both
managed and prebuilt delivery.
Build-result hashing and upload preflight retain their separate responsibilities.
[output benchmark](../../internal/gotool/performance_test.go), and the
[implementation results](implementation.md).
The internal Go-output document says an 8 MiB forwarded capture currently
allocates about 8.39 MB per operation. The cited benchmark makes one 8 MiB
Write call. Actual command output can arrive in smaller chunks, which incur
additional bytes.Buffer growth allocations. The statement needs its workload
qualification to describe the measured behavior accurately.
**Evidence:** Temporary component probes using the current writer and io.Discard
measured approximately 8,388,691 B/op and two allocations for one 8 MiB write,
versus 16,744,535 B/op and ten allocations for the same total output in 32 KiB
writes. These measurements exclude subprocess overhead and failure diagnostic
strings. Both paths correctly avoid the former final capture copy.
**Required outcome:** Identify the single-write workload wherever that figure
is quoted. Include a representative chunked capture measurement in the benchmark
set and results. Distinguish allocated bytes across an operation from retained
output bytes and peak memory. Complete in-memory capture remains the selected
design; these findings do not require disk-backed diagnostics or a memory cap.
**Validation:** Compare equal output lengths across the two chunking patterns
and confirm captured bytes and forwarding remain correct. Report allocation
results as workload-specific measurements with no numerical runtime guarantee.
**Completion:** Complete. The benchmark now includes equal 8 MiB output in one
write and 32 KiB chunks, with untimed byte-for-byte capture and forwarding checks.
Three repetitions passed and measured about 8.39 MB/2 allocations and
16.74 MB/10 allocations respectively. Go-output documentation and implementation
results qualify the single-write figures and distinguish allocation totals,
retained output, and unmeasured peak memory.
## R4: Reconcile Feature And Implementation Status
**Priority:** Low.
**Owner:** [performance roadmap](performance.md).
The performance roadmap still says "Ready for implementation; implementation
pending." The implementation document marks Stages 1–11 complete, and the code
contains the five target components. Stage 11 explicitly called for updating the
feature roadmap status, and its completion record claims the work is complete.
**Required outcome:** Update the feature roadmap to distinguish the implemented
target components from the outstanding review findings recorded here. Keep
remediation status in this document and performance results in the implementation
record, consistent with their documentation ownership.
**Validation:** Check status and navigation across the three roadmap documents
against the actual implementation and remediation outcomes.
**Completion:** Complete. The feature roadmap identifies all five implemented
components and links to their implementation results and review remediation.
The implementation record links to completed remediation. Status, ownership,
and local navigation across all three documents were checked against the code
and completed R1–R3 work.
## Remediation Validation
- The full offline Go test suite passed after the production fixes.
- Compiled public API and dependency checks passed.
- Race tests passed for validation, Git, and process packages.
- The output benchmarks passed three repetitions, including exact capture and
forwarding checks for both chunking patterns.
- Changed documentation links, fragments, and whitespace checks passed.
## Review Validation
- The full offline Go test suite passed; some unchanged packages used cached
results.
- The compiled public API and dependency checks passed.
- Focused race tests passed for internal/git, internal/process,
internal/runtimeinfo, internal/gotool, and internal/validation.
- The targeted probes used temporary Go overlays and local child-process
fixtures. They reproduced R1 and R2 and measured the workload distinction in
R3. No production code or permanent tests were changed during this review.
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.