# 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. All stages below are pending. Update their status and evidence as work 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 **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.` 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 Implementation has not started. Record benchmark-only baseline revisions, comparisons, ownership analysis, conditional decisions, and final verification here as the stages are completed. 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.