586 lines
32 KiB
Markdown
586 lines
32 KiB
Markdown
# Computational Efficiency Implementation Plan
|
||
|
||
Implement the remaining local work in the [performance roadmap](performance.md)
|
||
in order. Each sequential stage is scoped for one Luna coding-agent prompt.
|
||
Stage 1 is complete; Stages 2–14 remain pending. Update status and evidence
|
||
as each work unit lands.
|
||
The completed inspection plan is preserved at
|
||
`481e2d4:docs/roadmap/implementation.md` in Git history.
|
||
|
||
## Baseline And Scope
|
||
|
||
- Planning baseline: `481e2d494c5a4e8b7f3a2a6f1cceaeb3f7c71d05`.
|
||
- Roadmap item 1 is complete. ConfigKit received the performance request in its
|
||
`docs/roadmap/promptkit-performance-request.md`, commit `d53021e`. Do not
|
||
resubmit it, implement upstream changes, wait for a release, or upgrade the
|
||
dependency as part of this plan. A later ordinary dependency update will
|
||
pick up available upstream improvements.
|
||
- Roadmap items 2–5 are localized runtime improvements. Item 7 concerns
|
||
identity/metadata inspection; item 6 is conditional on ownership evidence
|
||
and measured benefit. The latter condition is part of the accepted scope,
|
||
not permission to leave the review unfinished.
|
||
- No public declarations, configuration semantics, dependencies, release
|
||
tooling, or version tags need changing. This is an internal optimization
|
||
effort, not a new consumer performance contract.
|
||
|
||
## Working Rules
|
||
|
||
Start with [development](../development.md) and select applicable policies.
|
||
Read the [agent](../policy/agents.md),
|
||
[architecture](../policy/architecture.md),
|
||
[testing](../policy/testing.md), and
|
||
[documentation](../policy/documentation.md) policies; reuse unchanged policies
|
||
already read. Consult the [component overview](../internal/overview.md) and the
|
||
focused documents named below.
|
||
|
||
Preserve the [configuration handoff ADR](../adr/0001-configuration-boundary.md).
|
||
Use captured final configuration; operator provenance stays out of ordinary
|
||
execution. Keep request/result ownership, error identities, exact selection,
|
||
prepared-handle lifecycle, cancellation, and generation/repair behavior intact.
|
||
Retain indexed lookup, captured parses, per-render text reuse, operation-local
|
||
schema reuse, frozen transport retries, and constant-time queue removal.
|
||
|
||
Use standard-library mechanisms. Add no global cache, writer pool, borrowed
|
||
public result, new execution/input limit, or performance release gate. These
|
||
are scope boundaries, not new permanent product prohibitions. Internal helper
|
||
names and allocation choices below are implementation decisions, not public
|
||
invariants. Tests must not freeze a particular capacity, clone count, internal
|
||
call sequence, or exact allocation/timing number.
|
||
|
||
For each stage, inspect the current tree, preserve unrelated work, implement
|
||
only that stage, format changed Go files, run its focused checks, review the
|
||
diff, and commit the completed unit with a plain-English message. Update owning
|
||
internal documentation when its mechanics change. Do not widen scope to
|
||
unrelated cleanup or retire other roadmaps. Publication is a separate task.
|
||
|
||
## Measurement And Evidence Method
|
||
|
||
Add maintained benchmarks before changing the behavior they measure. Record
|
||
the benchmark-only baseline commit so the same fixture can be rerun later.
|
||
Keep setup, source acquisition, template compilation, fixture creation, and
|
||
engine construction outside timing unless the benchmark explicitly measures
|
||
that operation. Use `ReportAllocs`, retain a result in a sink where necessary,
|
||
and check representative output outside timing. Run benchmark comparisons
|
||
without the race detector.
|
||
|
||
The default comparison command is:
|
||
|
||
```sh
|
||
GOTOOLCHAIN=local GOWORK=off GOPROXY=off \
|
||
go test -mod=readonly -run '^$' -bench 'BENCHMARK_PATTERN' \
|
||
-benchmem -benchtime=500ms -count=5 PACKAGE
|
||
```
|
||
|
||
Replace the pattern and package with each stage's values. Dependencies and the
|
||
selected Go toolchain must already be installed for this offline invocation.
|
||
Use the same environment and unchanged fixture for before/after comparisons.
|
||
Record revisions, Go/module versions, processor, parallelism, command, repeated
|
||
results, and a short interpretation in this plan's execution evidence. Compare
|
||
ranges and medians; no benchmark-analysis dependency is required. Keep raw
|
||
profiles and generated binaries outside the repository.
|
||
|
||
For changes affecting buffer/string/handle lifetimes, also compare retained
|
||
heap with a documented, fixed synthetic workload: hold a small batch of
|
||
results or handles, force GC, compare live heap against an equivalent setup-only
|
||
baseline, and use `runtime.KeepAlive` for the retained objects. An opt-in
|
||
benchmark is sufficient; do not add heap/timing assertions to ordinary tests.
|
||
Distinguish immutable shared strings from mutable deep copies. Report noisy
|
||
retained-heap evidence honestly rather than deriving it from `B/op`.
|
||
|
||
Probes from the original review are preliminary evidence. Maintained fixtures
|
||
and recorded comparisons own the implementation evidence. Retain improvements
|
||
when measurements support the intended reduction without a material correctness,
|
||
retention, or maintenance trade-off; the original percentages are not targets.
|
||
|
||
## Stage 1 — Establish Validation And Captured-Rendering Benchmarks
|
||
|
||
**Status: Complete (2026-10-09).** Maintained basic-validation, compiled
|
||
rendering, and retained-output benchmarks are committed. The benchmark-only
|
||
baseline is `0eb009ad5a3248e2a1e12cad9b3a05906d6b0850`; no production behavior
|
||
changed.
|
||
|
||
**Outcome:** Reproducible baselines for roadmap items 2 and 3; no production
|
||
change in this stage.
|
||
|
||
**Read:** `internal/validate/standard_validator.go`, its tests,
|
||
`internal/prompt/go_renderer.go`, `template.go`, renderer tests, and
|
||
[internal sources](../internal/sources.md).
|
||
|
||
**Work:**
|
||
|
||
1. Add `BenchmarkBasicValidation` with short text, 1 MiB nonempty text,
|
||
whitespace-only text, and Unicode whitespace. Construct bodies/contracts
|
||
outside timing and exercise the real basic validator.
|
||
2. Add `BenchmarkCompiledRendering` using `CompileTemplate` and a small
|
||
`TemplateLookup` fixture built before timing. Exercise
|
||
`NewCompiledRenderer`, not only `NewGoRenderer`'s reparsing fallback.
|
||
3. Cover a small message, an artifact-backed message around 256 KiB, and
|
||
repeated references across session/messages. Keep session output within the
|
||
existing session contract, e.g. render an input length rather than its body.
|
||
Retain the existing fallback benchmark where useful; label the paths clearly.
|
||
4. Add opt-in `BenchmarkRetainedRenderedOutput` using the evidence method
|
||
above. Capture baselines and record the benchmark commit for later comparison.
|
||
|
||
**Verify:** `go test ./internal/validate ./internal/prompt`; compare the two new
|
||
benchmark families with the standard command. Benchmarks have no provider
|
||
calls, allocation assertions, or source parsing hidden in the compiled path.
|
||
|
||
## Stage 2 — Check Basic Output Emptiness Without A String Copy
|
||
|
||
**Outcome:** Basic validation avoids a response-sized temporary string.
|
||
|
||
**Work:**
|
||
|
||
1. Replace `strings.TrimSpace(string(artifact.Body)) == ""` in the basic branch
|
||
with `len(bytes.TrimSpace(artifact.Body)) == 0`. Remove imports only if unused.
|
||
2. Preserve statuses, `IsValid`, repair input, error handling, and the existing
|
||
before/after cancellation checks. Do not change the other validation modes.
|
||
3. Reuse existing behavioral tests. Add only missing meaningful cases for empty
|
||
bytes, ASCII/Unicode whitespace, and nonempty bytes, preserving behavior for
|
||
arbitrary bytes rather than adding a UTF-8 requirement.
|
||
4. Compare `BenchmarkBasicValidation` against Stage 1 and record the result.
|
||
|
||
**Verify:** `go test ./internal/validate ./internal/usecase`; run the benchmark
|
||
family. Existing empty-output repair behavior remains unchanged.
|
||
|
||
## Stage 3 — Render Messages And Sessions With Local String Builders
|
||
|
||
**Outcome:** Template output becomes a string without a final buffer-to-string
|
||
copy.
|
||
|
||
**Work:**
|
||
|
||
1. Replace the local message `bytes.Buffer` in `Render` and the session buffer
|
||
in `executeSessionTemplate` with local `strings.Builder` values. Execute
|
||
templates into their addresses and obtain `String()` afterward.
|
||
2. Keep template cloning/binding, error checks, cache metadata, ordering, and
|
||
cancellation placement. Leave artifact-text conversion/cache unchanged.
|
||
Do not introduce eager growth based on all artifacts or shared writers.
|
||
3. Run existing exact-content, session, cancellation, and compiled-template
|
||
concurrency coverage. Add a behavioral case only if the changed path lacks
|
||
protection; do not test builder internals.
|
||
4. Compare Stage 1's compiled-rendering and retained-output measurements.
|
||
Check both allocated bytes and live output storage; explain any retention
|
||
trade-off before retaining the change. Update internal sources if needed.
|
||
|
||
**Verify:** `go test -race ./internal/prompt`; run compiled-rendering benchmarks
|
||
without `-race`. Different request inputs must remain independent.
|
||
|
||
## Stage 4 — Reserve A Useful Initial File-Read Capacity
|
||
|
||
**Outcome:** Ordinary file reads avoid early geometric buffer growth without
|
||
trusting metadata as the amount of content to return.
|
||
|
||
**Read:** `internal/artifact/reader.go`, `reader_test.go`, and internal sources.
|
||
|
||
**Work:**
|
||
|
||
1. Add `BenchmarkArtifactFileRead` using real temporary regular files of small,
|
||
1 MiB, and larger representative sizes. Create files outside timing, warm
|
||
content-type lookup, then measure the real reader. Record a baseline before
|
||
production edits.
|
||
2. Reserve initial capacity from the opened file's size. Use zero for a
|
||
nonpositive hint and `min(size, 1 MiB)` for a positive hint before conversion
|
||
to `int`. The 1 MiB reservation ceiling is an internal allocation heuristic,
|
||
not a file/content limit; data continues to grow normally beyond it. This
|
||
avoids an enormous eager allocation from stale or unusually large metadata
|
||
and keeps integer conversion safe on supported architectures.
|
||
3. Keep the read-until-EOF loop and existing regular-file, error, hash, metadata,
|
||
closure, and cancellation behavior. Do not size the result from `Stat`, use
|
||
`ReadFull` to enforce its size, impose an input limit, or add background I/O.
|
||
4. Extend the existing opened-file seam to test an overreported and an
|
||
underreported size, including a very large reported hint with small actual
|
||
content. Assert returned bytes/size/hash and failures, not backing capacity.
|
||
Add opt-in `BenchmarkRetainedArtifactFileRead` for representative live-buffer
|
||
retention, including overreported size. Compare both allocation and retention
|
||
and update internal sources.
|
||
|
||
**Verify:** `go test ./internal/artifact`; run `BenchmarkArtifactFileRead`.
|
||
Existing progress-cancellation and nonregular-file tests remain authoritative.
|
||
|
||
## Stage 5 — Establish Encoding Benchmarks And Error-Boundary Coverage
|
||
|
||
**Outcome:** A repeatable request-preparation baseline and focused protection
|
||
for the classifications that Stage 6 must preserve. No production change.
|
||
|
||
**Read:** `internal/llm/openai_compatible_client.go`, client/prepared-generation
|
||
tests, [integration contract](../integrations/openai-compatible-chat.md), and
|
||
[internal model client](../internal/llm.md).
|
||
|
||
**Work:**
|
||
|
||
1. Add `BenchmarkPrepareGeneration`, measuring actual `PrepareGeneration`
|
||
without HTTP dispatch: no extras, small extras, a 1 MiB string extra, and
|
||
nested JSON-compatible extras. Build inputs and the client outside timing;
|
||
use a direct synthetic credential or no credential so environment changes
|
||
do not affect results. Include a representative structured-output request.
|
||
2. Extend existing invalid-extra tests only as needed for a nested unsupported
|
||
value or cycle, checking `ErrInvalidRequest`, useful key context, and zero
|
||
transport calls. Do not freeze full diagnostic wording or all invalid-key
|
||
combinations.
|
||
3. Cover a serializable-extra request whose schema cannot be encoded: the
|
||
client currently classifies full-request encoding failure as
|
||
`ErrRequestFailed`. Also cover invalid extras plus an unencodable schema:
|
||
invalid extras retain `ErrInvalidRequest`. This protects classification
|
||
when validation moves to a failure-only path.
|
||
4. Capture the baseline. Existing body omission, explicit-zero, cache-control,
|
||
structured-output, and frozen-attempt tests own their established behavior.
|
||
|
||
**Verify:** `go test ./internal/llm`; run `BenchmarkPrepareGeneration`.
|
||
|
||
## Stage 6 — Encode Valid Extra Parameters Only In The Complete Request
|
||
|
||
**Outcome:** Successful request preparation does not serialize extras twice.
|
||
|
||
**Work:**
|
||
|
||
1. Keep `openAIChatRequestPayload` responsible for body assembly and empty/
|
||
reserved-key checks. Remove its successful-path per-value `json.Marshal`
|
||
calls; place accepted values directly into the payload map.
|
||
2. Marshal the complete assembled map once in `PrepareGeneration`. On success,
|
||
continue authentication resolution and construction of the frozen payload
|
||
exactly as before. Do not change body shapes or omission/presence semantics.
|
||
3. On complete-request marshal failure only, examine the extras with the
|
||
existing per-value marshal check to identify an invalid extra. Return the
|
||
existing `ErrInvalidRequest` classification and `extra_params.<key>` context
|
||
for that failure. If extras are serializable, retain `ErrRequestFailed` for
|
||
the original full-request encoding failure. Cold-path repeated work is
|
||
acceptable; successful requests get the optimization. No new reflective
|
||
validator or stricter accepted-value rules are needed.
|
||
4. Adjust any helper-only tests whose incidental assembly boundary changed,
|
||
retaining protection at `PrepareGeneration`/`Generate`. Preserve frozen
|
||
request bytes across attempts and record Stage 5 comparisons. Update internal
|
||
model-client mechanics; change integration prose only enough to describe
|
||
validation before dispatch without prescribing two encodings.
|
||
|
||
**Verify:** `go test ./internal/llm ./internal/generation`; run encoding
|
||
benchmarks. Invalid requests still dispatch nothing, and retry tests still
|
||
exercise one frozen body per generation.
|
||
|
||
## Stage 7 — Establish Inspection Benchmarks
|
||
|
||
**Outcome:** Owned lookup and assembled requirement-inspection baselines, with
|
||
no production change. This separates fixture work from the selection refactor.
|
||
|
||
**Work:**
|
||
|
||
1. Add `BenchmarkInspectionSelection` in configuration tests using a captured
|
||
snapshot constructed outside timing. Measure existing prompt/profile lookups
|
||
with small and representative mutable nested-extra payloads.
|
||
2. Add root `BenchmarkRequirementInspection` using a real engine with a simple
|
||
prompt and the same small/large-extra distinction. Build the engine and
|
||
request outside timing, supply needed caller variables, and exercise only
|
||
the public inspection call. No artifacts, schemas, or providers are accessed.
|
||
3. Check selected identities and inventories outside timing. Record the
|
||
benchmark-only baseline commit and repeated measurements before routing
|
||
changes. Do not treat long immutable strings as deep-copy workload.
|
||
|
||
**Verify:** `go test ./internal/configuration .`; run the two new benchmark
|
||
families in their owning packages.
|
||
|
||
## Stage 8 — Add Shared Lightweight Configuration Lookups
|
||
|
||
**Outcome:** Captured configuration can provide identity/inspection metadata
|
||
without copying execution payloads. Existing runtime behavior stays unchanged
|
||
until Stages 9 and 10.
|
||
|
||
**Read:** `internal/configuration/prompts.go`, `targets.go`, their tests,
|
||
`internal/usecase/prompt_inspection.go`, `profile_inspection.go`, and public
|
||
inspection GoDoc.
|
||
|
||
**Work:**
|
||
|
||
1. Extract existing prompt ID/version selection into a private
|
||
`lookupPromptEntry` helper. Preserve literal keys, blank checks, omitted
|
||
version ambiguity, not-found mapping, and construction-time hashes. Use it
|
||
from existing owned-definition/inspection construction; do not duplicate
|
||
selection rules.
|
||
2. Add internal `GetPromptInspection(context.Context, string, string)
|
||
(*domain.PromptInspection, error)` returning owned cached inspection data
|
||
with copied inputs,
|
||
without copying template structures. Keep `GetPromptSnapshot` independently
|
||
owned, but stop copying an unused inspection-input slice just to obtain its
|
||
cached hash. Retain the private `prompt` helper where callers need both
|
||
values, composing it from shared lookup/copy helpers.
|
||
3. Extract a shared private indexed-profile lookup used by `GetProfile` and
|
||
new `GetProfileIdentity(ctx, id) (string, error)`. The new method returns the
|
||
selected lookup ID after successful lookup, with no `ExtraParams` copy or
|
||
readiness/credential validation. `GetProfile` retains its owned deep copy.
|
||
Use existing context/not-found handling and the captured target invariant.
|
||
4. Test selection parity, owned returned prompt inputs, missing IDs, and partial
|
||
profiles through configuration APIs. Extend Stage 7's configuration benchmark
|
||
with lightweight lookup cases so old/new paths can be compared with the same
|
||
payload. Root requirement-inspection routing remains unchanged in this stage.
|
||
|
||
**Verify:** `go test ./internal/configuration ./internal/usecase .`; run the two
|
||
benchmark families in their owning packages. No new public methods or ConfigKit
|
||
queries occur during lookup.
|
||
|
||
## Stage 9 — Use Profile Identity Lookup During Requirement Inspection
|
||
|
||
**Outcome:** Requirement inspection no longer deep-copies a target it does not
|
||
use.
|
||
|
||
**Work:**
|
||
|
||
1. Add a narrow optional repository capability in `internal/usecase` matching
|
||
`GetProfileIdentity`. Add `resolveProfileIdentity` beside the existing
|
||
profile selection helper, sharing basic ID/repository checks where useful.
|
||
2. Use the capability for the assembled configuration adapter. Wrap repository
|
||
failures with existing `ErrProfileLoad` and keep exact requested identities.
|
||
For internal repositories lacking the capability, fall back to
|
||
`resolveProfileSelection`, preserving its nil-profile/nil-target checks.
|
||
Do not change existing repository interfaces or public extension contracts.
|
||
3. Route only the identity requirement in `InspectRequirements` through this
|
||
helper. Keep default selection, partial-profile support, cancellation/error
|
||
order, direct-session handling, and analyzer behavior. `InspectProfile` and
|
||
preparation still need complete owned targets and keep their existing path.
|
||
4. Reuse existing selection tests, add focused capability/fallback parity
|
||
coverage, and compare `BenchmarkRequirementInspection` with Stage 7,
|
||
especially the mutable nested-extra case. Update internal runner mechanics.
|
||
|
||
**Verify:** `go test ./internal/configuration ./internal/usecase .`; run the
|
||
requirement-inspection benchmark. No model, credential, admission, schema, or
|
||
provenance work is introduced.
|
||
|
||
## Stage 10 — Select Prompt Inspection Metadata Without Execution-Definition Copies
|
||
|
||
**Outcome:** Prompt and requirement inspection avoid copying template execution
|
||
structures they do not consume.
|
||
|
||
**Work:**
|
||
|
||
1. Add an optional `GetPromptInspection` repository capability in the runner.
|
||
Factor a shared inspection-selection helper from `InspectPrompt`: use the
|
||
captured metadata capability when available, otherwise derive the same
|
||
result from `resolvePromptDefinition` as today. Preserve guards, not-found/
|
||
ambiguity categories, and cached-hash versus fallback-hash behavior.
|
||
2. Let `InspectPrompt` use that helper. Let `InspectRequirements` select the
|
||
same prompt metadata, use its default profile when needed, and request the
|
||
exact requirement snapshot by selected ID/version.
|
||
3. The analyzer currently reads only `definition.ID`/`Version`; pass a local
|
||
identity-only `domain.PromptDefinition` constructed from that metadata rather
|
||
than loading a full execution definition. Do not change analysis semantics
|
||
or enlarge its scope. Keep snapshot metadata/template ownership as today.
|
||
4. Protect exact selectors, default selection, session bypass, caller-owned
|
||
results, and hash equality with preparation through existing tests. Cover
|
||
both optional-capability and fallback paths without pinning helper call
|
||
counts. Compare inspection benchmarks and update runner/source documentation.
|
||
|
||
**Verify:** `go test -race ./internal/configuration ./internal/usecase .`;
|
||
run inspection benchmarks without `-race`. Requirement inventories and public
|
||
prompt metadata remain identical for the same captured context.
|
||
|
||
## Stage 11 — Establish Assembled Workflow Benchmarks
|
||
|
||
**Outcome:** Reproducible end-to-end local benchmarks, including the baseline
|
||
for conditional snapshot optimization. No production change.
|
||
|
||
**Read:** `internal/usecase/runner.go`, `prepared_execution.go`, root
|
||
`prepared_execution.go`, `convert.go`, client adapters, and
|
||
`prepared_execution_contract_test.go`. Locate the actual adapter file rather
|
||
than assuming an adapter filename.
|
||
|
||
**Work:**
|
||
|
||
1. Add root `BenchmarkEnginePreparation`, `BenchmarkEngineRun`, and
|
||
`BenchmarkPreparedExecutionDetails` using a real assembled engine and an
|
||
injected deterministic client. Use no live HTTP, environment credentials,
|
||
or source mutation in timing. Keep engine setup outside timing; discard
|
||
unused prepared handles so the benchmark does not retain every iteration.
|
||
2. Cover a small case and separate large-message and mutable-payload cases.
|
||
Use a few hundred optional schema properties and nested extra-map entries
|
||
within existing JSON-value limits, not merely long immutable strings.
|
||
Use the real schema validator for the assembled structured-output case.
|
||
Separate creation cost from repeated `Details()` cost.
|
||
3. Capture the benchmark-only baseline commit and repeated results. Verify
|
||
that the large-message case measures rendering/output storage while the
|
||
mutable-payload case exercises deep copying. Record the specific schemas,
|
||
input sizes, retained sinks, and handle lifetimes used by the fixtures.
|
||
|
||
**Verify:** `go test .`; run the three new families. Check representative
|
||
content/validation outside timing. Keep preparation and `Run` measurements
|
||
separate from engine construction and provider latency.
|
||
|
||
## Stage 12 — Establish Snapshot Ownership And Live-Heap Evidence
|
||
|
||
**Outcome:** A measured ownership inventory for Stage 13's conditional change.
|
||
No production change; keep this analysis separate from creating the assembled
|
||
workflow benchmarks.
|
||
|
||
**Read:** The preparation, handle, conversion, and adapter files listed in
|
||
Stage 11, plus `internal/domain/message.go` and `internal/jsonvalue/jsonvalue.go`.
|
||
|
||
**Work:**
|
||
|
||
1. Add a focused internal `BenchmarkPreparedSnapshot` using existing fast
|
||
runner collaborators to expose snapshot cost without schema-compilation
|
||
noise. Add opt-in `BenchmarkRetainedPreparedHandles` with ready, completed,
|
||
and discarded subcases, holding a fixed batch under the common live-heap
|
||
evidence method. These measurements are not lifecycle assertions.
|
||
2. Record the ownership inventory and baseline in execution evidence: message
|
||
slices/cache pointers are copied by concatenation; hashes are newly allocated;
|
||
schema is copied from the validation plan; nested extras can still alias an
|
||
internal repository/request after shallow target merging; details must remain
|
||
separate from execution and survive completion/discard; public conversion
|
||
and injected-client adapters create caller-owned values. Confirm these facts
|
||
against current code, including fake/internal collaborators, before Stage 13.
|
||
3. Record readiness/run/discard lifetime assumptions and repeated observations.
|
||
Check whether the candidate below can save mutable schema/message metadata
|
||
copies without transferring an externally accessible map. Do not remove a
|
||
production copy during this analysis stage.
|
||
|
||
**Verify:** `go test ./internal/usecase .`; run the new families. Benchmarks must
|
||
check representative content/validation outside timing, avoid counting immutable
|
||
message length as deep-copied bytes, and document lifetime assumptions.
|
||
|
||
## Stage 13 — Conditionally Consolidate Prepared Creation's First Snapshot Copy
|
||
|
||
**Outcome:** Either one justified creation-copy simplification with preserved
|
||
ownership, or a completed, evidence-backed decision to retain the current
|
||
implementation. This does not authorize skipping measurement or ownership work.
|
||
|
||
**Selected candidate:** Transfer preparation's already-owned messages, hashes,
|
||
and schema into its private execution payload instead of deep-copying them
|
||
again in creation. Still detach nested extras and still make a separate,
|
||
complete details snapshot.
|
||
|
||
**Work:**
|
||
|
||
1. Use Stage 12's inventory to confirm that `operation.run`'s message/cache
|
||
structures, input-hash map, and copied schema have no mutable external owner.
|
||
Immutable string sharing is safe; no read-only promise should be imposed on
|
||
public or injected values to justify removing a copy.
|
||
2. If the inventory supports the candidate, replace the first general
|
||
`clonePreparedRun(operation.run)` with transfer of `operation.run`, after
|
||
deeply copying its `EffectiveModelParams.ExtraParams` using existing
|
||
`jsonvalue.CopyMap` and preserving the invalid-request error boundary.
|
||
The schema already passed through `jsonvalue.Copy` during preparation;
|
||
keep that validation. Leave the subsequent full clone for `details` intact.
|
||
3. Keep `Details()`'s defensive clone and facade conversion, claim/discard
|
||
synchronization, payload cleanup, observers, and generated-client copies.
|
||
Further conversion or borrowed-snapshot redesigns are not this stage's
|
||
candidate. Preserve public availability of details after execution and
|
||
discard, and the separation of mutable details from execution state.
|
||
4. Verify independence from retained internal source-profile, renderer, and
|
||
validation-plan data using existing tests plus missing meaningful nested-
|
||
mutation coverage. Compare Stages 11–12 creation and live-heap evidence. Retain
|
||
the candidate only if ownership remains correct and savings justify it
|
||
without a material retention/complexity trade-off. Otherwise restore the
|
||
candidate edit and record why the current design is retained; keep useful
|
||
benchmarks. Do not substitute a more elaborate refactor merely to claim a
|
||
reduction. Update runner mechanics if the change is retained.
|
||
|
||
**Verify:** `go test -race ./internal/usecase .`; run snapshot/assembled/retained
|
||
benchmarks without `-race`. Existing single-claim, discard races, frozen-source,
|
||
redaction, repair, and details-after-completion behavior must continue to pass.
|
||
|
||
## Stage 14 — Consolidate Evidence And Verify The Complete Local Scope
|
||
|
||
**Outcome:** The local roadmap scope is complete with clear measurements and
|
||
unchanged consumer contracts.
|
||
|
||
**Work:**
|
||
|
||
1. Record a concise before/after summary and revision/command references for
|
||
each changed path in execution evidence. Keep any Stage 13 retain decision
|
||
explicit. Include cumulative-allocation versus retained-heap distinctions
|
||
and small-input trade-offs; do not imply provider-latency improvements.
|
||
2. Review canonical internal source/runner/model-client descriptions against
|
||
the implemented flow. Consumer GoDoc and format semantics should remain
|
||
unchanged; remove incidental duplicated encoding mechanics where Stage 6
|
||
required it. Update the inventory only if responsibilities actually changed.
|
||
3. Verify roadmap coverage: item 1 remains complete without dependency work;
|
||
items 2–5 have implemented local improvements; item 7 uses lightweight
|
||
inspection; item 6 has the selected conditional outcome and evidence;
|
||
production compiled rendering and assembled offline workflows have maintained
|
||
benchmarks. Do not leave a requested item silently unexamined.
|
||
4. Run assembled verification below once on final code, review the complete
|
||
diff and tree for artifacts, and mark stages/roadmap progress accurately.
|
||
Keep this plan as the implementation record until ordinary roadmap
|
||
retirement; do not add release gates or publish a release.
|
||
|
||
**Verify:**
|
||
|
||
```sh
|
||
go test -race -vet=off -mod=readonly ./...
|
||
go vet -mod=readonly ./...
|
||
python3 scripts/check-examples.py
|
||
git diff --check
|
||
```
|
||
|
||
Check local Markdown links and changed-file formatting using existing tooling
|
||
or an appropriate focused check. Reuse successful checks for unchanged content
|
||
rather than rerunning merely to create the final commit.
|
||
|
||
## Execution Evidence
|
||
|
||
### Validation And Compiled-Rendering Baseline
|
||
|
||
The benchmark-only baseline is commit
|
||
`0eb009ad5a3248e2a1e12cad9b3a05906d6b0850` (`Add validation and rendering
|
||
benchmarks`), on top of planning baseline
|
||
`3fde1f6cbbc2cc5b3dc17779ba44e0af6cd8a836`. The measured source is unchanged
|
||
from that commit. No production files changed.
|
||
|
||
Environment: Linux/amd64, Intel Core i5-7260U @ 2.20 GHz, benchmark parallelism
|
||
4; `go1.26.8`, module directive `go 1.26.0`, ConfigKit `v0.5.0`, JSON Schema
|
||
`v6.0.2`, and the backend modules `v3.20260930.0`.
|
||
|
||
Command (five 500 ms samples per benchmark, without the race detector):
|
||
|
||
```sh
|
||
GOTOOLCHAIN=local GOWORK=off GOPROXY=off GONOPROXY=none GOSUMDB=off \
|
||
go test -mod=readonly -run '^$' \
|
||
-bench 'Benchmark(BasicValidation|CompiledRendering|RetainedRenderedOutput|GoRendererArtifactReferences)$' \
|
||
-benchmem -benchtime=500ms -count=5 \
|
||
./internal/validate ./internal/prompt
|
||
```
|
||
|
||
Times below list all five `ns/op` samples in command order, followed by median
|
||
and range. Allocation figures are `B/op; allocs/op` (ranges where they varied).
|
||
|
||
| Benchmark case | `ns/op` samples (median; range) | Allocations |
|
||
| --- | --- | --- |
|
||
| Basic validation, short text | 46.59, 44.98, 43.31, 46.23, 43.19 (44.98; 43.19–46.59) | 0; 0 |
|
||
| Basic validation, 1 MiB nonempty | 193859, 329884, 418896, 386126, 378173 (378173; 193859–418896) | 1048576–1048577; 1 |
|
||
| Basic validation, ~1 MiB whitespace | 1112498, 1100439, 1175913, 1290678, 1108746 (1112498; 1100439–1290678) | 1048592–1048595; 2 |
|
||
| Basic validation, Unicode whitespace | 170.8, 164.2, 158.6, 159.2, 165.2 (164.2; 158.6–170.8) | 16; 1 |
|
||
| Compiled rendering, small message | 3931, 8488, 2041, 1902, 2181 (2181; 1902–8488) | 1632; 20 |
|
||
| Compiled rendering, 256 KiB artifact message | 285812, 380128, 655925, 664387, 537684 (537684; 285812–664387) | 1050656–1050713; 25 |
|
||
| Compiled rendering, repeated across session/messages | 1870972, 1362368, 1142268, 1073474, 1173588 (1173588; 1073474–1870972) | 3945983–3946034; 74 |
|
||
| `NewGoRenderer` fallback, one artifact reference | 407143, 629149, 522549, 562200, 470393 (522549; 407143–629149) | 1056409–1056477; 69–70 |
|
||
| `NewGoRenderer` fallback, repeated references | 1439883, 1535813, 1300176, 1541795, 1416267 (1439883; 1300176–1541795) | 2897171–2897255; 157 |
|
||
| Retained output, four 256 KiB rendered results | 398753, 392206, 384503, 380386, 406691 (392206; 380386–406691) | 1050702–1050724; 25–26 |
|
||
|
||
For the retained-output benchmark, post-GC live-heap deltas above the same
|
||
setup-only fixture were 1048272, 1050176, 1046032, 1049856, and 1049016 bytes
|
||
(median 1049016; range 1046032–1050176). The batch contains four separately
|
||
rendered 256 KiB messages. This is retained-heap evidence, not an inference
|
||
from `B/op`; it includes result structure and allocator/runtime noise. The
|
||
initial exploratory run exposed cross-count sink retention in the measurement
|
||
baseline; the benchmark now clears prior sinks before measuring setup, and the
|
||
recorded values are from the corrected committed fixture.
|
||
|
||
The short validator case allocates nothing, while the 1 MiB nonempty body has
|
||
about one response-sized allocation; the whitespace-only case also retains a
|
||
small failure-result allocation. Compiled rendering provides separate small,
|
||
artifact-sized, and repeated-reference baselines, with the existing parsing
|
||
fallback reported for context. Several render timings show wide ranges on this
|
||
machine, so the median/range—not any single run—are the comparison baseline.
|
||
The retained-output results consistently remain near 1 MiB for four outputs.
|
||
|
||
Focused verification passed: `go test -mod=readonly ./internal/validate
|
||
./internal/prompt`. Record later before/after comparisons, ownership analysis,
|
||
conditional decisions, and final verification in this section as the remaining
|
||
stages complete. Keep the feature roadmap focused on target state and progress
|
||
rather than duplicating the staged procedure.
|
||
|
||
## Open Questions
|
||
|
||
No consumer-policy decisions remain open. File reservation and encoding error
|
||
handling have concrete approaches above. The prepared-copy candidate is
|
||
deliberately conditional on accepted evidence/ownership criteria, with a
|
||
defined retain-current-design outcome; it needs technical verification, not a
|
||
new consumer contract or a stronger ownership guarantee.
|