diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 9ed00f6..3729d15 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -1,532 +1,571 @@ -# ReleaseKit adoption implementation plan +# Performance implementation plan -Status: complete; Stages 1–6 complete. +Status: ready for implementation; Stages 1–11 have not started. -Date: 2026-10-08. +Date: 2026-10-09. ## Scope and authority -Implement the target in the [ReleaseKit feature roadmap](releasekit.md). -Read this entire plan before starting, then complete the numbered stages in -order. Each stage is one Luna implementation assignment. This plan supplies -concrete execution choices; the feature roadmap owns the target and user intent. -The [release policy](../policy/release.md), -[architecture policy](../policy/architecture.md), and accepted ADRs control -their respective boundaries. +Implement PF-01 through PF-06 from the [performance roadmap](performance.md). +Read this entire plan before starting and complete the numbered stages in order. +Each stage is a separate Luna implementation assignment and must leave the +repository buildable. The roadmap owns the target and selected OD-1/OD-2 +approaches; this plan owns execution choices, sequencing, and implementation +evidence. -This file replaces the completed initial library implementation record. That -record remains available at -[its reconciled source revision](https://gitea.maximumdirect.net/eric/CommandKit/src/commit/3871d837489b9955998b362587a52b47282dc021/docs/roadmap/implementation.md). -Historical documents still linking here concern that record, not these new -assignments. Stage 5 repairs those references. Do not restart the completed -library implementation or its remediation work. +Follow the [architecture policy](../policy/architecture.md), accepted ADRs, +[agent policy](../policy/agents.md), [testing policy](../policy/testing.md), and +[documentation policy](../policy/documentation.md). Public Go declarations and +the [CLI reference](../cli.md) own existing contracts. Private names proposed +below identify implementation responsibilities, not new public APIs or durable +internal interfaces. -The implementation sprint delivers maintainer tooling, not CommandKit's first -published release. Do not push to the real repository, create release tags in -CommandKit, prepare a placeholder release note, or configure hosted delivery as -part of these stages. Local disposable Git repositories are the publication -test boundary. Publication of a real candidate remains separately authorized -work under the runbook. +This file replaces the completed ReleaseKit plan. Release tooling is already +implemented and is outside this sprint. Stage 11 repairs navigation that still +identifies this file as the ReleaseKit plan. The previous record is available +through Git history; do not restore its assignments here. -## Baseline and selected implementation choices +No required product or architecture questions remain open. Measurements assess +the selected improvements; they do not authorize substituting persistent mutable +backends or segmented YAML delivery for the user's choices. -At planning time, HEAD is `0690ff6`, the worktree is clean, and the feature -roadmap is committed. CommandKit's initial library and documentation -reconciliation are complete. Root `go.mod` declares Go 1.25.5 and has no -ReleaseKit dependency. There is no root release profile, publication launcher, -release runbook, or release-note history. The feature roadmap records the -remote and source-only release scope. +## Baseline and implementation choices -Use ReleaseKit v0.2.0, reviewed at -`e030794969fb7787e647a90bbc4eb81bd0610f07`. Its -[CLI reference](https://gitea.maximumdirect.net/eric/ReleaseKit/src/commit/e030794969fb7787e647a90bbc4eb81bd0610f07/docs/cli.md), -[profile reference](https://gitea.maximumdirect.net/eric/ReleaseKit/src/commit/e030794969fb7787e647a90bbc4eb81bd0610f07/docs/config.md), and -[publication guide](https://gitea.maximumdirect.net/eric/ReleaseKit/src/commit/e030794969fb7787e647a90bbc4eb81bd0610f07/docs/consumers/publish.md) -own upstream behavior. `../ReleaseKit` is optional review evidence, not a build -dependency or a source tree to modify. +The planning baseline is `6612b54`, with a clean worktree. Production code still +matches the efficiency review at `54cfbd8`; intervening changes are documentation. +The root facade, internal Cobra adapter, inspection families, and release tooling +are implemented. No performance benchmarks were found in the indexed code, and +this planning pass has not executed tests, benchmarks, or profiles. Reinspect +current state before implementation and record the actual measurement revisions. -The execution choices are: +Use the following concrete choices: -- Track the executable with a root Go `tool` directive and a v0.2.0 module - requirement. Build from that selection, without an independent script pin. -- Author a small POSIX shell launcher in `scripts/release`, and use ignored - `.releasekit/bin/releasekit` for its native executable. -- Author the launcher and runbook for CommandKit, using upstream as behavioral - guidance rather than copying substantive source or prose. This avoids an - unnecessary copied-material attribution obligation. Tool use alone does not - require new component entries under the existing OSS policy; changes to - incorporated runtime dependencies still receive their applicable treatment. -- Use a minimal source-only JSON profile with the default suite and explicit - product, remote, and expected push URL. Leave optional checks and delivery - unselected. -- Keep fast launcher tests in a tests-only `scripts` Go package. Keep a real - local publication exercise explicitly opt-in in that same package, so ordinary - release validation does not repeatedly bootstrap and retest the publisher. -- Push the real candidate's branch before its tag when following the future - runbook. ReleaseKit owns validation and tag publication; the launcher does not - implement its own guards or branch push. +- Render PromptKit's requested format in its handler, then pass completed bytes + to a shared delivery helper. Preserve the existing report DTOs and serializers. +- Index backend commands directly to neutral declarations. Walk Cobra's existing + parent links inside the adapter to reconstruct only the selected path. +- Scan YAML opening lines with byte offsets. Return complete framed bytes plus + whether the native document has directives, so the aggregate writer can use + one analysis. Retain the full framed buffer selected in OD-2. +- Prepare and clone pending command registrations before updating the existing + registry. Keep batch validation atomic and preserve owned declaration storage. +- Lazily cache one neutral bridge tree per `App`, invalidating it after a + successful nonempty registration. Construct fresh mutable backend state for + each invocation, then reuse it for parsing, help, and usage within that + invocation, as selected in OD-1. +- Represent registered configuration metadata with a private generic prepared + suite containing normalized declarations, ordered scopes, name lookup, and + discovery inventory. Use name/short-name maps for selector duplicate checks. + Keep scope selection in preflight and handlers as inexpensive indexed passes. + Selected-scope memoization and additional selector-membership indexes are + deferred; their mutable bookkeeping is unnecessary for removing the observed + quadratic scope searches. -These choices add no production Go package, runtime ReleaseKit import, public -API, standard command family, or changes to application version semantics. -Hosted notes, binary targets, CI delivery, and additional release gates remain -deferred as specified in the feature roadmap. +PF-05 and the bounded PF-06 changes above are included in the sprint despite their +lower priority. Implement the straightforward algorithms and measure their cost; +do not silently omit either coverage ID. Escalate concrete correctness conflicts +or material regressions rather than add a new consumer restriction or change +the selected scope. ## Instructions for every stage -1. Follow [developer orientation](../development.md) and the - [agent policy](../policy/agents.md). Inspect current state and prior stage - evidence; preserve unrelated work. Prefer knowledge-graph tools for code - discovery. Read policies relevant to the assigned edits. -2. Implement only the assigned stage and its necessary supporting documentation. - Do not treat later stages or existing historical plans as already completed. - If evidence contradicts a selected choice, report the concrete issue rather - than silently substitute another version, integration, or release policy. -3. Prepare dependencies separately when needed. Checks below are implementation - evidence, not additional permanent release gates. No real credentials, - hosted-service requests, or live publication are needed for them. -4. Format changed Go files. Use focused checks for the stage, and reuse evidence - for unchanged work. The default suite is run in Stages 1, 3, and the final - audit to cover the changed module, new tests, and completed integration; - prose-only stages need document review, not another suite. This is development - validation across changing commits, not a requirement to test a real release - candidate repeatedly before `publish` tests it. -5. Update this plan's stage status and evidence with actual results. Commit each - completed stage under the local commit policy and report its commit, checks, - and remaining work. Review the staged diff and whitespace before committing. +1. Start with [developer orientation](../development.md), inspect Git state, and + read the assigned sources and prior stage evidence. Prefer knowledge-graph + tools for code discovery. Preserve unrelated work and read applicable policies. +2. Implement the assigned stage and necessary supporting tests/documentation. + Preserve public signatures and the fields of value structs such as + `Invocation`, `CommandSpec`, and `ConfigSuite`; do not add execution state to + them. The `App` handle may gain private implementation fields. Cobra/pflag + objects and errors stay inside their adapter. +3. Preserve grammar, conversion of every supplied singleton occurrence, explicit + presence, collection ownership, callback lifecycle, supported error identity, + diagnostic delivery, and report semantics. Reuse existing coverage; add only + missing protection for the change's realistic risks. Do not assert private + helper counts or fixed timings in behavioral tests. +4. Format changed Go files. Run the stage's focused validation and measurements. + Dependency preparation, if needed, is separate from offline measurement. + Apply the measurement protocol's workspace, local-toolchain, and offline + environment settings to Go validation commands as well. + Tests and benchmarks added by this plan are development evidence, not new + release gates. Leave the opt-in ReleaseKit publication exercise unselected. +5. Record actual commands, results, fixture dimensions, relevant comparisons, + and unresolved issues in the stage's evidence field. Never replace pending + evidence with an expected result. Investigate a regression before proceeding; + absence of a timing speedup is not permission to invent a faster contract. +6. Review staged changes and commit each completed stage under the agent policy. + Report its commit, checks, and remaining work. Proceed only when its completion + conditions hold. Update the roadmap's overall status when implementation + begins and ends; detailed execution results belong here. + +No concurrency guarantee, public freeze step, new parser, provider cache, library +version change, or release publication is needed. Application callbacks retain +their effects. Do not forbid registration from callbacks to simplify caching; +refresh metadata-dependent presentation when successful registration has made +the invocation's prepared metadata stale, without repeating input callbacks. + +## Measurement protocol + +Use standard Go benchmarks named with prefix `BenchmarkPerformance`. Use the +same fixtures and measurement scope before and after a change. Record Go version, +GOOS/GOARCH, CPU/machine description, source revision, and fixture dimensions. +Separate app construction plus first execution from execution on an existing +app. Exclude fixture construction from repeated-execution measurements and include +it only when explicitly measuring construction/registration. + +Use in-memory provider fixtures and `io.Discard` for ordinary delivery +measurements. Observe outcomes so invalid fixtures cannot appear as fast success. +Actual ConfigKit/PromptKit loading is covered by existing integration tests; +these measurements isolate CommandKit overhead and do not contact providers. +Keep a few representative shapes rather than a Cartesian product of dimensions. + +With module caches prepared, the common measurement command is: + +```sh +GOWORK=off GOTOOLCHAIN=local GOPROXY=off GONOPROXY=none GOSUMDB=off go test -mod=readonly -run '^$' -bench '^BenchmarkPerformance' -benchmem -benchtime=200ms -count=5 . ./internal/cobra +``` + +Narrow `-bench` and the package list for each stage. Save raw output outside the +worktree and summarize representative ns/op, B/op, and allocs/op results here, +including variation or inconclusive differences. When measurements span a +different machine or Go version, repeat the baseline in an isolated checkout +with the same benchmark sources before making a timing claim. Profile CPU or +allocations only when needed to explain a remaining cost or regression. No new +benchmark-analysis dependency or permanent numeric pass threshold is selected. ## Stage inventory | Stage | Deliverable | Roadmap coverage | | --- | --- | --- | -| 1 | Released Go tool dependency and applicable licensing review | RK-01, RK-05 | -| 2 | Source-only profile and native publication launcher | RK-02, RK-03 | -| 3 | Fast deterministic launcher tests | RK-03, RK-06 | -| 4 | Opt-in publication exercise against disposable local Git repositories | RK-06 | -| 5 | Maintainer runbook, policy status, and navigation reconciliation | RK-04 | -| 6 | Final adoption audit and completion record | RK-01–06 | +| 1 | Dispatch and registration baseline benchmarks | PF-02, PF-03, PF-05 | +| 2 | Inspection and scope baseline benchmarks | PF-01, PF-04, PF-06 | +| 3 | PromptKit rendering for the selected format | PF-01 | +| 4 | Direct declaration lookup and ancestry traversal | PF-02 | +| 5 | YAML preamble analysis with complete framed delivery | PF-04, OD-2 | +| 6 | Registration without copying existing entries | PF-05 | +| 7 | Cached immutable bridge metadata | PF-03, OD-1 | +| 8 | An invocation-owned adapter session | PF-03, OD-1 | +| 9 | Root dispatch, help, and usage integration | PF-03, OD-1 | +| 10 | Prepared configuration suite metadata | PF-06 | +| 11 | Final behavioral/performance audit and documentation | PF-01–06 | -## Stage 1: declare and prepare the released tool dependency +## Stage 1: establish dispatch and registration measurements -**Status:** complete. **Prerequisite:** repository orientation and this plan. +**Status:** not started. **Prerequisite:** orientation and this plan. -**Files:** `go.mod`, `go.sum`; affected notices only if existing incorporated -dependencies change; stage evidence in this plan. +**Files:** new `performance_dispatch_test.go`, +`internal/cobra/performance_test.go`, and evidence here. Production code stays +unchanged in this stage. -1. From the repository root, add the executable tool dependency: +1. Create small test-only command fixtures: a small flat registry with 8 custom + commands, a wide registry with 128 custom commands, and a nested path of + depth 24 with sibling commands. Add one flag-heavy leaf case with 24 flags. + These dimensions are benchmark inputs, not supported-size limits. +2. Add root benchmarks for repeated success, local `--help`, explicit + `help COMMAND`, parser usage failure, and handler-returned usage. Use the + small fixture for the full path set and the wider/deeper fixtures for success + and help. Add construction plus first-execution cases for small and wide apps. + Use real routing and bindings with a cheap typed handler and stable streams. +3. Add adapter `Parse` benchmarks for shallow/wide and deep neutral trees to + isolate PF-02 from root report work. Keep the neutral declarations outside + the timed region; include backend construction in the `Parse` measurement. +4. Add registration benchmarks comparing individual and batch registration of + 8 and 128 preconstructed declarations. Include fresh `New` plus registration + in both timed paths so the comparison has the same construction overhead. +5. Run and record the baseline under the measurement protocol. Preserve benchmark + inputs for subsequent stages. Verify setup and expected outcome classes; + fix the fixture if it unexpectedly measures an error path. + +**Validation:** compile both packages with `go test -mod=readonly -run '^$' +. ./internal/cobra`, then execute the new benchmarks. No timing assertions, +production instrumentation, or full dependency test matrix is needed. + +**Completion:** reproducible baseline results distinguish registration, cold +execution, repeated execution, parsing, and help/usage costs. + +**Evidence:** pending. + +## Stage 2: establish report and scope measurements + +**Status:** not started. **Prerequisite:** Stage 1. + +**Files:** new `performance_reports_test.go` and evidence here. Production code +stays unchanged in this stage. + +1. Use direct `PromptKitSuite` callbacks returning fixed inspection records. + Measure prompt text/YAML with 4 and 128 inputs, and profile text/YAML with + small and larger nested `ExtraParams` maps. Construct fixtures outside the + timed loop; use `App.Execute` so parsing and delivery are represented. +2. Add isolated framing and aggregate `config show --scope all` benchmarks with + two native document fixtures: approximately 4 KiB and 1 MiB with many short + lines. Keep native content and provider callbacks fixed. Include an ordinary + preamble and a directive-bearing preamble, without multiplying every size by + every marker variation. Measure single-scope display as a reference case. +3. Measure `config scopes`, named selection, and aggregate validation with 2 and + 32 scopes. Give every aggregate fixture the requested capability and use cheap + acquisition callbacks. Add suite registration with 4 and 64 distinct selectors + to expose duplicate-check cost without using duplicate declarations. +4. Run and record these baselines before handler or scope changes. Label isolated + helper results separately from complete command execution. Adjust internal + benchmark calls after later signature refactors only as necessary to measure + the same logical operation and fixtures. + +**Validation:** compile the root package and run the new report/scope benchmarks +under the common protocol. Confirm the fixtures produce the intended formats and +success outcomes outside timed setup; existing integration coverage remains the +owner of actual library loading semantics. + +**Completion:** PF-01, PF-04, and PF-06 have comparable baseline measurements. + +**Evidence:** pending. + +## Stage 3: render only the selected PromptKit format + +**Status:** not started. **Prerequisite:** Stage 2. + +**Files:** [promptkit.go](../../promptkit.go), +[promptkit_test.go](../../promptkit_test.go), relevant report benchmarks, and +evidence here. + +1. In `runPromptKitPrompt` and `runPromptKitProfile`, select YAML or text before + calling either text formatter. Preserve report DTO construction and all + acquisition, cancellation, nil-result, and diagnostic handling. +2. For YAML, encode into a `bytes.Buffer` and use its bytes directly. For text, + format once and convert the completed string to bytes without copying it + through an additional builder. Preserve profile text-formatting errors. +3. Reduce `writePromptKitReport` to shared delivery of completed bytes plus + diagnostics. Keep the pre-delivery cancellation check, checked writer, and + incomplete-report outcome handling. Both formats finish rendering before + this delivery step. No lazy public formatter API is needed. +4. Reuse the real inspection tests for report fields, input order, nil parameter + maps, nested `ExtraParams`, cancellation, and output failures. Add focused + missing cases rather than collaborator-call assertions or broad snapshots. +5. Rerun prompt/profile measurements and compare them with Stage 2. Explain any + text-path regression or unexpectedly unchanged YAML allocation result. + +**Validation:** root PromptKit inspection and cancellation tests, followed by +the applicable report benchmarks. Use `go test -mod=readonly -count=1 .` for +root behavioral validation. Preserve public error identity and existing +report confidentiality; this change introduces no redaction policy. + +**Completion:** YAML requests do not construct text reports, and text delivery +has no redundant intermediate builder. + +**Evidence:** pending. + +## Stage 4: index backend declarations and traverse parents + +**Status:** not started. **Prerequisite:** Stage 3. + +**Files:** [adapter](../../internal/cobra/parse.go), +[adapter tests](../../internal/cobra/parse_test.go), relevant benchmarks, and +evidence here. + +1. Change the construction index to map backend command pointers directly to + neutral declarations. Update `Parse`, `RenderHelp`, and `UsageLine` to use + direct lookup instead of scanning declaration-to-backend entries. +2. Reconstruct a selected path by walking the backend's `Parent()` links and + reading each mapped declaration's name, excluding the root. Reverse the + collected names once. Remove recursive root-based parent discovery. +3. Keep neutral failures for missing trees/unmapped selections and exact-path + verification. Do not rely on Cobra prefix matching to define accepted paths. +4. Extend adapter coverage only where needed for a deep path, wide siblings, + unknown paths, root selection, and help/usage path resolution. Existing + parsing and flag-state tests remain applicable. +5. Compare adapter and complete dispatch benchmarks with Stage 1. The tree still + rebuilds here; distinguish reduced path-search cost from that remaining cost. + +**Validation:** `go test -mod=readonly -count=1 ./internal/cobra .`, then the applicable +parse and dispatch benchmarks. + +**Completion:** declaration lookup uses the index and ancestry traversal is +proportional to selected depth after backend construction. + +**Evidence:** pending. + +## Stage 5: scan YAML preambles without splitting documents + +**Status:** not started. **Prerequisite:** Stage 4. + +**Files:** [show.go](../../show.go), [show_test.go](../../show_test.go), +[configkit_test.go](../../configkit_test.go), framing benchmarks, and evidence. + +1. Introduce a private preamble analysis using `bytes.IndexByte` to locate line + boundaries and byte trimming for classification. Track marker positions, + preamble end, directives, and inline marker content. Stop when the opening + framing decision is complete. Preserve Unicode whitespace behavior and + accepted space/tab marker forms; do not add a general YAML parser or limits. +2. Have `frameEffectiveYAML` return complete framed bytes and a directive flag + from that analysis. Update its internal callers/tests for the result pair. + Remove the independent full-document split in `hasYAMLDirective` and use the + same analysis for aggregate completion-marker decisions. +3. Retain the complete native buffer and framed buffer per document. Preserve + placement of labels around inline content/block scalars, authored comments, + directives, native byte slices, quoted scope labels, and boundary newlines. + Keep the existing aggregate document-end marker where directives require it. +4. Leave acquisition-before-output, direct single-scope writing, cancellation + boundaries, and write-error outcomes intact. Do not introduce segmented native + delivery, indentation wrappers, or reserialization of provider documents. +5. Reuse `TestEffectiveYAMLFramingPreservesOneNativeDocument` and aggregate + acquisition/delivery tests. Fill meaningful gaps for absent trailing newline, + CRLF, directive preambles, and control characters in labels. Verify native + content preservation as well as decoded values and document count. +6. Compare framing/aggregate measurements with Stage 2, emphasizing allocation + growth with line count. Report the full framed copy as an intentional retained + cost under OD-2. + +**Validation:** root framing/configuration behavior with +`go test -mod=readonly -count=1 .`, then the relevant report benchmarks. + +**Completion:** each document has one opening analysis, no document-wide line +index, and the selected complete-buffer delivery model. + +**Evidence:** pending. + +## Stage 6: install validated registrations without copying the registry + +**Status:** not started. **Prerequisite:** Stage 5. + +**Files:** [registry.go](../../registry.go), +[registry_test.go](../../registry_test.go), registration benchmarks, and evidence. + +1. Preserve the nil-app check. Validate each declaration and check its name + against both existing registrations and pending entries. Clone declarations + into private pending storage before modifying the app. +2. After the whole batch succeeds, initialize the app's map if needed and add + the pending entries to it. Leave existing entries untouched. An empty batch + remains a successful no-op on a non-nil app. +3. Preserve batch failure behavior for invalid later declarations, duplicates + within a batch, existing paths, and reserved help/version paths. Keep existing + cloning boundaries rather than sharing caller-owned child slices. +4. Reuse atomicity and callback-free registration tests. Add a missing late-failure + or within-batch collision case only if existing coverage is insufficient. +5. Compare individual/batch registration with Stage 1. Sorting and metadata + preparation remain separate until Stage 7. + +**Validation:** registry/declaration behavior with +`go test -mod=readonly -count=1 .`, then registration benchmarks. + +**Completion:** registration copies only pending declarations and preserves +all-or-nothing validation without rebuilding the existing registry map. + +**Evidence:** pending. + +## Stage 7: cache immutable bridge metadata per app + +**Status:** not started. **Prerequisite:** Stage 6. + +**Files:** [app.go](../../app.go), [parser.go](../../parser.go), registry invalidation, +root parser/registry tests, dispatch benchmarks, and evidence. + +1. Add a private cached neutral tree pointer to `App`. Split bridge translation + into a cache accessor and an uncached builder. Build lazily on first use; + sort root names and child lists once per registry state while creating owned + flag/operand metadata. The cached neutral tree is read-only thereafter. +2. Invalidate the pointer at successful nonempty registration's completion point. + Failed and empty registrations retain valid cached metadata. Built-in + construction remains before first use; do not create metadata eagerly for + each individual command registration. +3. Keep this cache free of contexts, streams, parsed values, provider views, and + Cobra/pflag objects. Adapter construction reads the neutral tree without + mutating it. A pointer to the neutral tree identifies a prepared metadata + generation for Stage 9, avoiding a separate counter. +4. Protect observable behavior after success, failure, and empty registration + following earlier execution/help. Check that newly installed configuration + and PromptKit families appear after an earlier help request without provider + loading. Preserve owned declarations and sorted metadata. +5. Compare cold and repeated execution with Stage 1. Account for the one retained + neutral tree per app and confirm rebuilding occurs only when needed through + source review and measurements, not public allocation/count assertions. + +**Validation:** root registry/parser/help tests and dispatch benchmarks. Each +invocation still creates fresh backend values at this stage. Use +`go test -mod=readonly -count=1 .` for root behavioral validation. + +**Completion:** unchanged registrations reuse translated metadata; successful +registration is visible to later parsing and help. + +**Evidence:** pending. + +## Stage 8: introduce an invocation-owned adapter session + +**Status:** not started. **Prerequisite:** Stage 7. + +**Files:** the adapter, its tests/benchmarks, and evidence. Root execution remains +functional through the existing package-level adapter helpers during this stage. + +1. Add an internal `Session` handle with private backend root, neutral metadata, + and reverse declaration index. Use `Prepare(*Command, ParseOptions) *Session`; + construction normalizes options and builds a fresh mutable tree. A missing + tree is represented by a session whose operations return neutral failures. + Methods are `Parse([]string) Result`, `RenderHelp([]string) (string, *Failure)`, + and `UsageLine([]string) (string, *Failure)`. They expose no backend types. +2. Move existing parse/help/usage logic into those methods. Reuse the prepared + tree and Stage 4's index/path traversal. Help renders to a private buffer; + temporarily redirect and restore the selected backend's output/error streams + so generated bytes remain under root delivery control. +3. Keep package-level helpers as thin one-session conveniences for current root + calls and focused adapter benchmarks/tests. They must delegate to the same + methods, not maintain parallel parsing or help implementations. +4. Keep malformed/nil-tree results neutral. Metadata remains read-only; parsed + values belong to the session. Test parse followed by help and usage on one + session, and fresh values/streams on separately prepared sessions. Do not + add a public session API, pool, persistent backend cache, or reset framework. +5. Add a small parse-plus-help session benchmark using an existing fixture. + Label it as a new internal measurement; existing end-to-end benchmarks are + still the before/after evidence for root integration in Stage 9. + +**Validation:** `go test -mod=readonly -count=1 ./internal/cobra .`, then adapter benchmarks. + +**Completion:** the adapter can perform parsing and metadata presentation on +one prepared invocation tree while legacy root entry points still compile. + +**Evidence:** pending. + +## Stage 9: reuse the adapter session throughout root execution + +**Status:** not started. **Prerequisite:** Stage 8. + +**Files:** [execute.go](../../execute.go), [builtins.go](../../builtins.go), +private parser coordination, [execute_external_test.go](../../execute_external_test.go), +dispatch benchmarks, and evidence. + +1. Introduce private execution coordination local to `App.Execute`, retaining + the app, parse options, neutral tree pointer, and session. Prepare after the + existing context/stream checks. Keep early nil-app/context outcomes and + generic usage fallbacks functional without a usable session. +2. Route parse, root/group/local help, usage after parse/input failures, and + usage returned by handlers through that local coordination. Keep the existing + input preparation and callback order, including supplied-value validation + for help. Normal domain handlers still receive the public `Invocation`. +3. Handle the reserved top-level `help` command explicitly in the dispatcher + after its ordinary typed preparation/checks: use its private `helpInput`, + resolve the target with `helpCommandTarget`, and render through the local + session. Share target/render logic with its registered built-in handler. + A one-off private convenience may prepare a session for direct internal + helper calls; the `Execute` path must use its existing session. Preserve + runnable help metadata and the semantics of `help --help` and unknown targets. +4. Do not store the active session on `App`, add it to public `Invocation`, or + put it in caller contexts. For help/usage presentation, compare the current + cached metadata pointer with the session's pointer. If successful registration + by a callback changed metadata, prepare presentation against the new tree + without rerunning parsing, factories, checks, or handlers. This preserves + registration freedom without caching stale presentation. +5. Preserve outcome normalization, cause/error identity, diagnostic ownership, + delivery errors, and incomplete-report reporting. Remove obsolete duplicate + preparation paths once all root execution branches are integrated. +6. Reuse the existing help-entry and fresh-invocation tests. Fill gaps for a + successful flag-bearing invocation followed by help and omitted/false/empty + values, distinct streams/contexts, handler usage, and registration followed + by metadata presentation. Test contracts, not exact session-construction counts. +7. Compare complete help/error/repeated/cold execution with Stage 1. Stable + metadata should need one backend preparation through each ordinary invocation, + including explicit `help`; verify structurally and through measurements. + +**Validation:** root dispatcher/help/outcome/cancellation tests, adapter tests, +with `go test -mod=readonly -count=1 . ./internal/cobra`, then complete dispatch +benchmarks. Use existing facade boundary coverage/review +to confirm that signatures and backend isolation are unchanged. + +**Completion:** every normal `Execute` path uses the selected OD-1 lifecycle and +all existing public handler and presentation semantics remain intact. + +**Evidence:** pending. + +## Stage 10: prepare configuration scope metadata once + +**Status:** not started. **Prerequisite:** Stage 9. + +**Files:** [config.go](../../config.go), [selectors.go](../../selectors.go), +configuration handler files, configuration/cancellation tests, scope benchmarks, +and evidence. Keep public `ConfigSuite`/`ConfigScope` unchanged. + +1. Introduce private `preparedConfigSuite[C]` with the normalized public suite, + ordered scope slice, `map[string]ConfigScope[C]` lookup, and discovery DTO. + Prepare it once in `RegisterConfig` after normalization. Use the existing + ordering comparator: `app`, `promptkit`, then other identifiers alphabetically. + Normalize/copy metadata before preparation so it remains instance-owned. +2. Replace the previous-selector loop in `normalizeConfigSuite` with seen-name + and seen-short maps, retaining validation order, scope-reference deduplication, + and the same collision categories. Preserve all existing naming rules. +3. Pass the private prepared suite into standard command constructors and + preflight/run helpers. Use its ordered slice for aggregate selection and + its map for named lookup, capability preflight, and acquisition. Update + validate/show/sources/explain together; no public integration signature changes. +4. Construct the discovery inventory from declared/installed operations during + registration and retain the private DTO. Encode text/YAML at invocation time. + This preparation must not call `NewContext`, acquisition, input checking, + native writers, or other runtime callbacks. +5. Keep selection validation in preflight and handlers, now using prepared + metadata. Keep selector applicability scans and fresh context projection. + Do not add mutable selected-scope memoization or public/internal input hooks + solely to avoid a second inexpensive validation pass. +6. Reuse discovery ordering/capability, selector projection/requiredness, unknown + scope, provider-error, and cancellation coverage. Ensure aggregate operations + check every selected capability before acquisition; discovery remains + metadata-only. Add only missing post-registration ownership/collision cases. +7. Compare scope execution and suite registration with Stage 2, including the + normal two-scope case. Record the retained slice/map/DTO cost and any timing + tradeoff; extra metadata is private to the registered suite. + +**Validation:** root configuration and cancellation tests plus scope/selector +benchmarks. Use `go test -mod=readonly -count=1 .` for root behavioral validation. +Preserve native reporting and per-operation output timing. + +**Completion:** scope sorting/discovery preparation occurs at registration, +scope lookup is indexed, and duplicate selector checks are linear in selector +count plus declared scope references. + +**Evidence:** pending. + +## Stage 11: audit the completed target and reconcile documentation + +**Status:** not started. **Prerequisite:** Stages 1–10. + +**Files:** this plan, `performance.md`, +[internal overview](../internal/overview.md), developer/README navigation, +and stale references to the retired ReleaseKit plan. Focused corrections only +where the audit proves an incomplete target. + +1. Audit PF-01–06 against source and observed behavior: selected formatting, + indexed ancestry, prefix-only YAML analysis, full framed buffering, pending + registrations, immutable cache invalidation, invocation sessions across all + help/usage paths, and prepared configuration metadata. Identify authoritative + evidence for each target rather than infer completion from stage statuses. +2. Run the default suite once for the complete sprint: ```sh - GOWORK=off GOTOOLCHAIN=local go get -tool gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit@v0.2.0 - GOWORK=off GOTOOLCHAIN=local go mod tidy + GOWORK=off GOTOOLCHAIN=local GOPROXY=off GONOPROXY=none GOSUMDB=off go test -mod=readonly -count=1 ./... ``` -2. Review the resulting graph. Retain the tool declaration and released v0.2.0 - requirement, with its necessary indirect requirements and checksums. Preserve - CommandKit's Go 1.25.5 directive; ReleaseKit and its reviewed Goldmark and - `x/mod` requirements fit that minimum. Review any existing dependency version - changes rather than silently upgrade unrelated libraries. Do not add a local - replacement, a blank runtime import, a separate tools module, or a duplicate - version constant to retain the executable. -3. Apply [OSS policy](../policy/oss.md) to the actual result. ReleaseKit, - Goldmark, and `x/mod` are selected for development tooling, with no source copy - or tool binary distribution in this integration. Tool use alone does not - require new attribution entries. If module resolution changes an existing - incorporated dependency, update its actual notices and pinned source links - with that change. Record the review result here, not as an editorial paragraph - in the attribution document. -4. Prepare caches through normal Go access before offline checks: + Keep live services, the optional release publication exercise, and additional + race/platform/coverage gates unselected. Review production imports and exported + declarations using existing boundary checks and source evidence. +3. Rerun representative benchmarks with unchanged fixtures and comparable + environment. Summarize before/after data by PF ID, separate cold/repeated and + isolated/whole-command costs, and record remaining full-tree/full-buffer work. + Reuse a recent identical measurement when source and environment are unchanged. + Explain material regressions or collect more evidence before declaring success. +4. Update internal documentation to describe implemented cache/session ownership + and prepared metadata. Current CLI/GoDoc contracts should remain unchanged; + update them only for an actual clarification without inventing a new promise. +5. Update README and `docs/development.md` so current implementation navigation + points to this performance plan and roadmap. In `releasekit.md`, `initial.md`, + and `remediation.md`, remove or replace obsolete ReleaseKit-plan references + with the owning completed roadmap/runbook as appropriate. Remove its obsolete + final-stage fragment references. Do not preserve the retired stage assignments + or restart completed release work. +6. Check affected document links/fragments, prose status, and whitespace. Mark + the roadmap implemented only when every selected target has evidence. Record + the stage commits, measurement revisions, check results, and any qualifications + here, then commit the audit and leave a clean workspace. - ```sh - GOWORK=off GOTOOLCHAIN=local go mod download - ``` +**Validation:** the default suite, comparative measurements, facade/import +review, and affected documentation/link checks described above. Reuse unchanged +stage evidence where it directly proves the final target. -**Evidence:** inspect `go list -m gitea.maximumdirect.net/eric/ReleaseKit` with -workspace discovery disabled; it selects v0.2.0. Run `go mod tidy -diff` with -the same workspace/toolchain settings; it reports no further changes. Build -`gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit` with `-mod=readonly` -into a disposable directory outside the worktree and invoke only `version`. -Confirm that the build leaves `go.mod` and `go.sum` unchanged. Run the current -default suite once with `GOWORK=off GOTOOLCHAIN=local GOPROXY=off -GONOPROXY=none GOSUMDB=off go test -mod=readonly -count=1 ./...`. +**Completion:** all six target improvements and both selected approaches are +implemented with relevant behavioral and efficiency evidence; maintained docs +identify their current owners. No performance guarantee or additional release +gate has been introduced. -Observed 2026-10-08: the tool selection resolves to ReleaseKit v0.2.0, with -Goldmark v2.1.6 and `golang.org/x/mod` v0.40.0 in the tooling graph. The prior -graph selected `x/mod` v0.8.0 through `x/text`; `go mod why` showed no package -use in CommandKit, and the current graph uses `x/mod` through ReleaseKit only. -No incorporated runtime dependency changed, so the OSS policy requires no new -attribution entry or license text. No source was copied and the tool binary is -not distributed. `go mod tidy -diff` reported no changes; `go mod verify` -reported all modules verified. The readonly CLI build succeeded, its `version` -subcommand reported v0.2.0, and `go.mod` and `go.sum` were unchanged by the build. -The default offline `go test -mod=readonly -count=1 ./...` passed for the root -and internal Cobra packages; both example packages compiled. - -**Completion:** the root module retains its released tool selection through -ordinary maintenance, the executable builds, affected notices are correct, and -existing library checks pass. Commit the stage before proceeding. - -## Stage 2: implement the source-only publication entry point - -**Status:** complete. **Prerequisite:** Stage 1. - -**Files:** root `releasekit.json`, `scripts/release`, `.gitignore`, and stage -evidence in this plan. - -1. Add the root profile with exactly this initial configuration: - - ```json - { - "product": "CommandKit", - "remote": "origin", - "expected_push_url": "https://gitea.maximumdirect.net/eric/CommandKit.git" - } - ``` - - Omit `checks`, `validation`, `build`, and `delivery`. Their omission selects - the upstream defaults without installing extra gates or deliverables. -2. Write `scripts/release` with `#!/bin/sh` and `set -eu`. Require exactly one - argument; otherwise print `usage: ./scripts/release VERSION` to stderr and - exit 2 before invoking Go. Pass the version argument unchanged to ReleaseKit; - ReleaseKit owns semantic version validation. -3. Resolve the repository root from the launcher's directory, then change to - that root. Use quoted path expansions and a task-specific variable such as - `commandkit_root`. Resolve the directory with `CDPATH=` for the directory - change so an ambient `CDPATH` does not corrupt captured path output. Support - ordinary invocation from another working directory and paths containing - spaces; no general symbolic-link resolution framework is required. -4. Create `.releasekit/bin`. Query Go's `GOHOSTOS` and `GOHOSTARCH` using the - local toolchain with workspace discovery disabled, storing them separately - without replacing the version argument. Build the tool using those native - target values, `GOWORK=off`, `GOTOOLCHAIN=local`, and this argument vector: - - ```text - go build -mod=readonly -o .releasekit/bin/releasekit gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit - ``` - - This builds the executable for the host that must run it, even when the - caller has selected an application cross-compilation target. Dependency - preparation remains separate; no launcher `go get`, `go mod tidy`, alternate - module file, linker injection, or independently selected tool version is - needed. Bootstrap can use normal module access; the candidate suite itself - is offline under ReleaseKit's environment. -5. Replace the shell process with the built executable using `exec` and this - argument vector, quoting both absolute paths and the version: - - ```text - .releasekit/bin/releasekit publish --repo ROOT --config ROOT/releasekit.json VERSION - ``` - - The flags precede `VERSION` as required by ReleaseKit. Preserve its streams - and exit status directly. Do not intercept its publication states, run `check` - first, auto-push `main`, or use `go run` as the publication process. -6. Mark the launcher executable in Git. Add `/.releasekit/` to the root ignore - file; this local bootstrap output is not a declared release artifact. - -**Evidence:** review the profile against upstream, run `sh -n scripts/release`, -and verify the executable mode and ignore rule. Exercise zero and multiple -arguments without a candidate; both return 2 without tool bootstrap. Do not -invoke a valid version against CommandKit's real repository as a smoke check. - -Observed 2026-10-08: reviewed the v0.2.0 profile and CLI references at the -selected upstream commit. The profile contains only the product, remote, and -effective push URL. `sh -n scripts/release` and `python3 -m json.tool -releasekit.json` passed. Git reports the launcher executable, and -`git check-ignore -v .releasekit/bin/releasekit` matches the root ignore rule. -Zero and two arguments both printed the required usage line and returned status -2 with a nonexistent `PATH`, confirming that Go was not invoked; no bootstrap -directory was created. No valid version was sent to CommandKit's repository. - -**Completion:** the profile and launcher match the selected target and delegate -publication to the module-selected native CLI. Commit the stage. - -## Stage 3: cover launcher behavior with fast offline tests - -**Status:** complete. **Prerequisite:** Stage 2. - -**Files:** `scripts/release_test.go`, plus launcher corrections found by the -tests and stage evidence in this plan. Use a tests-only external Go package such -as `release_test`; add no production Go file or exported helper API. - -1. Use standard-library Go tests with temporary directories. Copy the real - launcher and profile into a temporary repository-shaped directory whose path - contains spaces. Invoke the copied launcher from a different directory. - A controlled `go` executable placed first on the child process's `PATH` - supplies the two host environment answers and writes a fake executable at - the requested build output path. The fake executable logs received arguments, - writes distinct stdout/stderr messages, and exits with a test-selected status. - Keep all test controls inside fixture subprocess environments, not launcher - options or product configuration. -2. Cover these observable integration risks: - - | Case | Required observation | - | --- | --- | - | Zero or multiple arguments | Usage on stderr and status 2; Go is not invoked. | - | Valid invocation from another directory | Build occurs at the copied root; publication receives `publish`, explicit absolute repository/profile paths, and the supplied version as separate arguments. | - | Module-selected native bootstrap | The tool package has no `@version` suffix; readonly mode and the expected output are used; the local toolchain, disabled workspace, and queried host target govern the build. | - | Executable returns 0, 1, 2, or 3 | Each status is preserved, with stdout/stderr delivered to their corresponding streams. These cases test delegation, not upstream recovery semantics. | - | Bootstrap failure | The launcher stops with the build failure and never invokes the fake publication executable. | - -3. Test argument values and outcomes rather than exact shell source or private - call counts. Do not add a signal-management framework or a new guarantee - merely to test `exec`; source review plus direct process replacement establish - the selected signal handoff. The script remains a POSIX maintainer tool: - skip shell execution tests with an explicit reason if a POSIX shell is - unavailable, rather than add a Windows publisher. -4. Use per-command working directories and environments. Do not modify process - global working directories or the real repository's remote configuration. - -**Evidence:** format the test file, run the focused scripts package tests, then -the default offline suite with the Stage 1 environment. None of these tests -compiles a real publisher or contacts a service. - -Observed 2026-10-08: `gofmt -d scripts/release_test.go` reported no changes. -`go test -mod=readonly -count=1 ./scripts` passed. The offline default -`go test -mod=readonly -count=1 ./...` passed for the library, internal adapter, -examples, and scripts package. The tests use only temporary fixtures and a -controlled Go executable; they do not build the real publisher or contact a -service. - -**Completion:** the small launcher has regression coverage for its own parsing, -path, build, and delegation responsibilities. Commit the stage. - -## Stage 4: exercise real publication against disposable Git repositories - -**Status:** complete. **Prerequisite:** Stage 3 and prepared dependency caches. - -**Files:** `scripts/release_integration_test.go`, supporting fixture helpers in -that tests-only package if needed, and stage evidence in this plan. - -1. Add `TestReleaseIntegration`, skipped unless - `COMMANDKIT_RELEASE_INTEGRATION=1`. This explicit development opt-in is not - a profile check or a required step before ordinary releases. Keep fast tests - enabled normally. Explain the opt-in and cache prerequisite in the test; - Stage 5 exposes its command in the developer guide. -2. Construct a disposable worktree on `main` and a separate local bare Git - repository. Supply fixture-local identity and disable signing and hooks - through isolated Git configuration; do not alter the maintainer's global - settings. Clear inherited Git directory/worktree overrides for fixture - commands so every operation addresses the fixture. -3. Copy CommandKit's current `go.mod`, `go.sum`, launcher, and ignore file into - the fixture. Copy the real JSON profile, changing only `expected_push_url` - to the local bare repository's absolute path. Keep product `CommandKit` and - remote `origin`. Do not copy CommandKit's tests: the fixture is a tiny Go - package, avoiding recursive execution of these integration tests. -4. Write a fixture-only note `docs/releases/v0.0.1.md` headed - `# CommandKit v0.0.1`, and a small standard-library Go test that appends one - marker to a path supplied through its environment outside the worktree. - This marker demonstrates one candidate-suite run without adding a product - check. Commit fixture contents, configure `origin` to the local bare path, - and push fixture `HEAD:refs/heads/main`. These are local fixture operations, - not publication of CommandKit. -5. Invoke the copied launcher from another directory with the actual Go tool - and module-selected ReleaseKit. Make dependency and toolchain downloads - unavailable during the exercise. Check that it returns success, reports the - fixture candidate/version, publishes an annotated `v0.0.1` tag pointing at - that commit, leaves remote `main` at the candidate, writes one suite marker, - and leaves fixture candidate status clean. Check that only the intended tag - was added and no binary delivery outputs were produced. The ignored local - bootstrap executable is expected. -6. On that fixture, prepare and commit a second note for `v0.0.2` and change - `expected_push_url` to a different local path. Push the new fixture `main`, - then invoke the launcher. Expect known failure status 1 with an endpoint - mismatch diagnostic, no new local or remote tag, and no additional suite - marker. This protects profile wiring without rebuilding upstream's entire - remote-conflict and failed-push matrix. Status 3 propagation is already - covered by the Stage 3 executable double. -7. Use `t.TempDir` cleanup and an overall subprocess context deadline to avoid a - stalled fixture. Avoid timing-sensitive assertions and output snapshots. - Missing shell/Git support can cause an explicit platform skip; when opt-in is - selected, missing dependency caches or a real execution failure must be - reported as failure, not disguised as a successful skip. - -**Evidence:** prepare caches using the Stage 1 preparation command, then run: - -```sh -COMMANDKIT_RELEASE_INTEGRATION=1 GOWORK=off GOTOOLCHAIN=local GOPROXY=off GONOPROXY=none GOSUMDB=off go test -mod=readonly -count=1 ./scripts -run '^TestReleaseIntegration$' -``` - -Record the passed publication and preflight observations. This exercise uses -no real remote, credentials, provider, binary target, or hosted page. Retain the -opt-in test for future tooling changes, without placing it in `releasekit.json`. - -Observed 2026-10-08: after `GOWORK=off GOTOOLCHAIN=local go mod download`, this -offline publication exercise passed: - -```sh -COMMANDKIT_RELEASE_INTEGRATION=1 GOWORK=off GOTOOLCHAIN=local GOPROXY=off GONOPROXY=none GOSUMDB=off go test -mod=readonly -count=1 ./scripts -run '^TestReleaseIntegration$' -``` - -The local publication produced an annotated -`v0.0.1` tag whose object matched the bare remote and pointed to the candidate; -remote `main` retained that commit, the candidate suite wrote one marker, the -worktree was clean, and no `dist` output appeared. A second candidate with a -mismatched expected endpoint returned status 1 with the endpoint diagnostic, -added no tag, and did not run the suite again. The exercise used only temporary -local Git repositories and the real cached module-selected publisher. - -**Completion:** the actual module-selected executable and copied integration -publish a source tag locally and respect the configured endpoint guard. Commit -the stage. - -## Stage 5: document operation and reconcile release navigation - -**Status:** complete. **Prerequisite:** Stage 4. - -**Files:** new `docs/release.md`; `docs/policy/release.md`, README, -`docs/development.md`; historical roadmap references listed below; and stage -evidence in this plan. Apply documentation and OSS policy without changing -accepted ADR decisions or policy provenance. - -1. Author `docs/release.md` as the canonical, concise maintainer runbook. Name - `main`, `origin`, the expected effective push endpoint, a local compatible - Go toolchain (at least the module's 1.25.5 requirement), Git with push access, - and a POSIX shell. Describe source tag plus committed note as the selected - deliverables, with no hosted page, binary matrix, or delivery credentials. -2. Give copyable preparation commands with a task-specific version variable. - Explain stable SemVer selection and pre-1.0 compatibility policy. Prepare - `docs/releases/.md` with its product/version heading, useful summary, - compatibility/upgrade information, and exact immutable-tag footer URLs: - - ```text - https://gitea.maximumdirect.net/eric/CommandKit/src/tag/VERSION/COPYRIGHT - https://gitea.maximumdirect.net/eric/CommandKit/src/tag/VERSION/LICENSE - https://gitea.maximumdirect.net/eric/CommandKit/src/tag/VERSION/docs/oss/attribution.md - ``` - - The visible footer labels are `COPYRIGHT | LICENSE | ATTRIBUTION`. Preparation - review checks intended paths in the candidate; it does not require future - tag URLs to resolve or run a separate note checker. -3. Document cache preparation with `GOWORK=off GOTOOLCHAIN=local go mod download`. - If preparation changes module metadata, review and commit it as part of the - candidate before publication. Commit the final candidate on `main`, push - with `git push origin HEAD:refs/heads/main`, then invoke - `./scripts/release "$commandkit_version"`. State that the command bootstraps - the tool, runs the offline candidate suite once after preflight, and publishes - only the annotated tag. Do not prescribe a required `check` followed by - `publish`; earlier development checks do not replace publication validation. -4. Describe completion at successful intended-tag push or an exact-object - reconciliation confirming acceptance. Link to upstream for its CLI/status - contract. Provide a short operational summary of statuses 0 (confirmed), 1 - (known failure/conflict), 2 (input/profile), and 3 (unresolved push), keeping - this operational summary out of CommandKit's application CLI reference. -5. Include concrete recovery instructions using the endpoint, version, and tag - object reported by the failed invocation. Show the upstream pattern that - verifies `git ls-remote --get-url -- "$releasekit_endpoint"` still equals the - captured endpoint before querying its exact `refs/tags/...` ref. A failed - query or changed URL resolution leaves the result unresolved. Compare the - exact annotated tag object, not just its peeled commit. Exact match confirms - publication; a different object is conflict; proven absence permits deliberate - cleanup of an unpublished local tag only after confirming it has never been - published or used by another release channel. Explain that retained local - tags block an ordinary retry. Never move or overwrite a published tag or - treat an unreadable query as absence. Published source corrections require - a new version; transport failures alone do not. -6. Replace only stale integration-status prose in the release policy with the - implemented ReleaseKit selection and a runbook link. Retain source-only - scope, substantive rules, adopted base version, and upstream provenance; - update the local last-updated date for the edit. -7. Add runbook navigation in README and the developer guide. Add a current - release-tooling reading row and the optional Stage 4 exercise command/cache - prerequisite to developer guidance. Replace the obsolete statement that - release tooling still awaits introduction. Keep commands owned by the - runbook and use links from non-owning documents. -8. Repair links to the replaced completed implementation record. For historical - initial-library references, use the immutable snapshot linked in this plan's - introduction. Inspect and update README, `docs/development.md`, - `docs/roadmap/01-cli-plan.md`, `02-dispatch.md`, `03-backend.md`, `04-facade.md`, - `initial-plan.md`, `initial.md`, and `remediation.md`. Preserve their completed - status and historical statements. Link current ReleaseKit work separately to - `releasekit.md` and this plan. Do not copy the old plan into another maintained - implementation specification or add temporary-plan links to ADRs. - -**Evidence:** review the runbook against the implemented launcher/profile and -the selected upstream source. Check local destinations/fragments, preserved -licensing footer navigation, intended release URL paths, changed historical -references, provenance, and Markdown whitespace. No publisher invocation on -CommandKit or another full test suite is needed for these prose edits. - -Observed 2026-10-08: added `docs/release.md` with source-only preparation, -publication, status, and exact-object recovery instructions. The runbook was -reviewed against the profile, launcher, release policy, OSS policy, and ReleaseKit -v0.2.0 references. The policy retains its base version and upstream provenance -with an updated local date. README and developer navigation link to the runbook; -the developer guide records the optional local exercise. Historical references -now use the immutable initial-plan snapshot, while current ReleaseKit work links -to its roadmap and this plan. A link check found 228 valid local destinations and -fragments across 11 affected documents; Markdown whitespace checks passed. The -README's licensing and attribution footer remains intact. No real publication -or full test suite was run for this prose stage. - -**Completion:** implemented tooling has one canonical runbook, policy status is -accurate, and old library history is distinguishable from current ReleaseKit -work. Commit the stage. - -## Stage 6: audit and record adoption completion - -**Status:** complete. **Prerequisite:** Stages 1–5. - -**Files:** this plan and `releasekit.md` for completion evidence; focused fixes -only if the audit discovers a real incomplete deliverable. - -1. Audit every RK-01–06 result against current state: module/tool selection, - minimal profile, launcher and executable mode, ignored output, dependency - metadata preservation, licensing treatment, implemented runbook/navigation, - fast tests, and recorded local publication evidence. Read actual files and - results rather than infer completion from checked boxes. -2. Confirm production imports and exported declarations have not acquired - ReleaseKit APIs. The production package boundary remains root `commandkit` - and `internal/cobra`; `scripts` is tests-only. Use package/dependency - inspection or existing boundary evidence, not a new general API checker. -3. Run the default offline suite once for the completed implementation using - the Stage 1 environment. Stage 4 stays opt-in. Reuse its successful result - if its launcher, profile, tool selection, and test inputs remain unchanged; - otherwise rerun that local exercise for the changed integration. Review - affected prose, links, module diffs, and whitespace. -4. Mark stage and feature completion only when their evidence is present. Record - exact commands/outcomes and any platform skips. Keep historical baseline - descriptions historical; update the roadmap's current status and link its - completion evidence here. No real release note or tag is required to close - adoption, and no deferred capability is promoted by this audit. -5. Commit the completed audit and status updates, report all stage commits and - checks, and leave a clean workspace. The handoff is tooling ready for separately - authorized preparation of the first real release. - -**Evidence:** record the final default-suite result, the applicable Stage 4 -result, the reviewed deliverable and boundary checks, and the final clean Git -status. Correct any missing result before declaring adoption complete. - -Observed 2026-10-08: the final offline suite passed: - -```sh -GOWORK=off GOTOOLCHAIN=local GOPROXY=off GONOPROXY=none GOSUMDB=off go test -mod=readonly -count=1 ./... -``` - -It passed for the root library, internal Cobra adapter, scripts, and both -examples; the opt-in publication test remained skipped in the default suite. -The opt-in disposable-repository exercise passed earlier and is unchanged by -subsequent edits. These module and package checks passed: - -```sh -GOWORK=off GOTOOLCHAIN=local go list -m gitea.maximumdirect.net/eric/ReleaseKit -GOWORK=off GOTOOLCHAIN=local go mod tidy -diff -GOWORK=off GOTOOLCHAIN=local go mod verify -GOWORK=off GOTOOLCHAIN=local go list -deps -test ./... -GOWORK=off GOTOOLCHAIN=local go list -f '{{.ImportPath}} GoFiles={{.GoFiles}} TestGoFiles={{.TestGoFiles}} XTestGoFiles={{.XTestGoFiles}}' ./scripts -``` - -The module selection is v0.2.0; tidy reports no changes and all modules verify. -The readonly CLI build and version check passed without changing module -metadata. The profile has exactly the selected three fields; -`git ls-tree HEAD scripts/release` reports mode 100755, and -`git check-ignore -v .releasekit/bin/releasekit` matches the root ignore rule. -No ReleaseKit Go package appears in the dependency listing; `scripts` reports -`GoFiles=[]` and only the two external test files. Those are the only Go files -added since the completed initial implementation. No source was copied, no -runtime dependency attribution changed, and no exported production API changed. -The runbook, navigation, historical references, release policy provenance, and -README licensing footer were reviewed. All 243 local links across 13 documents -passed; staged Markdown whitespace checks passed. No platform skips occurred. -Stage commits: `ada2021`, `012af5e`, `f9ec143`, `d3f0891`, and `8c5fea0`. The final -workspace was clean after the completion commit. - -**Completion:** all six stages and all roadmap coverage IDs have authoritative -implementation evidence. Source publication itself remains unperformed. +**Evidence:** pending. diff --git a/docs/roadmap/performance.md b/docs/roadmap/performance.md index 7b72315..1c47fc1 100644 --- a/docs/roadmap/performance.md +++ b/docs/roadmap/performance.md @@ -1,6 +1,6 @@ # Computational efficiency roadmap -Status: proposed improvements; OD-1 and OD-2 resolved; implementation has not begun. +Status: ready for implementation; OD-1 and OD-2 resolved; implementation has not begun. Date: 2026-10-09. @@ -11,8 +11,10 @@ registration, and inspection reports. Favor straightforward changes with clear benefits, and use measurements to decide whether more complex optimizations are worth maintaining. -This document owns the proposed improvements, priorities, and completion -evidence. The [architecture policy](../policy/architecture.md), accepted +This document owns the target state, priorities, and selected implementation +scope. The [implementation plan](implementation.md) owns ordered stages, +measurement fixtures, validation, and execution evidence. The +[architecture policy](../policy/architecture.md), accepted [ADRs](../adr/), [Go declarations](../../app.go), and [CLI reference](../cli.md) continue to govern behavior. Follow the [agent policy](../policy/agents.md) for proportionate implementation and the @@ -26,14 +28,14 @@ findings come from source inspection; no benchmarks or allocation profiles were run. Complexity observations identify scaling costs, not measured bottlenecks or promised speedups. Recheck the affected implementation before starting work. -| ID | Observed work | Proposed result | Priority | +| ID | Observed work | Target result | Primary benefit | | --- | --- | --- | --- | -| PF-01 | PromptKit text reports are built even for YAML requests. | Render only the requested format and avoid redundant report buffers. | First pass | -| PF-02 | Selected-command lookup scans a map; each ancestor is rediscovered from the root. | Direct declaration lookup and ancestry traversal proportional to path depth. | First pass | -| PF-03 | Every invocation translates and sorts metadata; help and usage construct another backend tree. | Reuse immutable metadata and the invocation's prepared tree. | Measure and improve after the first pass | -| PF-04 | Aggregate YAML framing splits every line and copies the complete native document. | Inspect only the opening lines and retain a complete framed buffer for delivery. | First pass | -| PF-05 | Each registration copies the existing top-level registry. | Prepare new entries separately and install a validated batch atomically. | Lower priority | -| PF-06 | Scope ordering, lookup, and selection repeat; selector duplicate checks scan previous entries. | Prepare stable scope metadata once and use direct duplicate checks. | Lower priority | +| PF-01 | PromptKit text reports are built even for YAML requests. | Render only the requested format and avoid redundant report buffers. | Inspection CPU and allocation reduction | +| PF-02 | Selected-command lookup scans a map; each ancestor is rediscovered from the root. | Direct declaration lookup and ancestry traversal proportional to path depth. | Wide or deeply nested command trees | +| PF-03 | Every invocation translates and sorts metadata; help and usage construct another backend tree. | Reuse immutable metadata and the invocation's prepared tree. | Repeated execution and help/usage paths | +| PF-04 | Aggregate YAML framing splits every line and copies the complete native document. | Inspect only the opening lines and retain a complete framed buffer for delivery. | Large or newline-dense reports | +| PF-05 | Each registration copies the existing top-level registry. | Prepare new entries separately and install a validated batch atomically. | App construction with many registrations | +| PF-06 | Scope ordering, lookup, and selection repeat; selector duplicate checks scan previous entries. | Prepare stable scope metadata once and use direct duplicate checks. | Growing scope and selector sets | The implemented package inventory remains owned by the [internal overview](../internal/overview.md). The target is internal improvement @@ -68,7 +70,7 @@ and scope preparation remain with the corresponding root handlers. Acquisition, configuration resolution, and provider lifetime remain with the existing integration contracts and consuming applications. -## Proposed improvements +## Target improvements ### PF-01: Render only the requested PromptKit format @@ -79,15 +81,11 @@ profile text formatter serializes `ExtraParams` as nested YAML; a YAML request then serializes those parameters again as part of the structured report. Text delivery also copies an already completed report into another builder. -Choose the requested formatter before rendering. Use a straightforward buffer or -completed report representation that avoids the redundant text-buffer copy. -Continue using the existing structured report representation and YAML encoder; -the optimization concerns when rendering occurs and how its result is delivered. - -Completion evidence should show unchanged text and YAML report semantics, -including `ExtraParams`, diagnostics, and delivery failures, with eliminated -unused text construction for YAML requests. Measure prompt input collections and -profile parameter maps large enough to expose the redundant work. +The target selects the requested formatter before rendering and delivers the +completed report without a redundant intermediate text buffer. It retains the +existing structured report representation and YAML encoder, including +`ExtraParams`, diagnostics, and delivery-failure semantics. YAML requests produce +only the structured report; text requests produce only the text report. ### PF-02: Index command declarations and ancestry @@ -97,14 +95,10 @@ declaration-to-backend map to find a selected backend command. `pathFor` calls depth D, repeated parent searches can take O(N × D) work; a chain can produce quadratic work. -Record a backend-to-declaration mapping during construction. Build parent links -alongside it, or traverse Cobra's existing parent relationships internally. -Prefer an implementation that reconstructs only the selected path rather than -storing a separate complete path for every node. Declaration lookup should be -constant time, with path construction proportional to D after tree preparation. - -Exercise shallow, wide, and deeply nested declarations through dispatch, help, -and usage. Preserve exact registered-path matching and error classification. +The target provides direct backend-to-declaration lookup and reconstructs only +the selected path from parent relationships inside the adapter. Declaration +lookup is constant time, with path construction proportional to D after tree +preparation. Exact registered-path matching and error classification are retained. This improvement removes redundant searches; constructing the complete backend tree still has a separate cost addressed by PF-03. @@ -115,19 +109,16 @@ every invocation, including flag and operand declarations. The adapter then builds backend commands and flag sets for all nodes. Help and usage rendering build another complete backend tree after parsing has already built one. -Pursue two improvements: - -1. Retain the invocation's prepared backend tree inside the adapter and use it - for parsing, help, and usage. The root coordinates these operations without - receiving Cobra/pflag objects. Keep the internal coordination API narrow. -2. Prepare or lazily cache immutable bridge metadata and sorted child order per - `App`. Refresh that derived state after successful registration. Failed - registration must leave the previously registered commands usable, including - their help and dispatch behavior. +The target retains the invocation's prepared backend tree inside the adapter for +parsing, help, and usage. The root coordinates these operations through a narrow +internal handle without receiving Cobra/pflag objects. Immutable bridge metadata +and sorted child order are cached per `App` and refreshed when registration +changes the declared tree. Failed registration leaves existing commands usable, +including their help and dispatch behavior. Metadata caching principally benefits repeated `Execute` calls. Avoiding a second backend construction also benefits a single CLI help or usage request. -Measure both cases separately, including the cost of app construction and +Efficiency evidence distinguishes both cases, including app construction and registration for consumers that execute only once. Under the selected OD-1 approach, construct fresh mutable backend parse state for @@ -145,10 +136,10 @@ opening content. These splits scan all bytes and allocate a slice entry for each line, even when the framing decision can be made immediately. Framing then allocates a complete copy alongside the native buffer. -First, scan the preamble with byte offsets, stopping when the directive or marker -decision is complete. Share the resulting framing analysis where useful so -directive detection and scope-label placement do not independently scan the same -preamble. Avoid a document-wide line index and unnecessary string conversions. +The target analyzes the preamble with byte offsets and stops when the directive +or marker decision is complete. Directive detection and scope-label placement +share that analysis. Framing needs no document-wide line index or unnecessary +string conversions. Under the selected OD-2 approach, retain the native render buffer and assemble a complete framed buffer for each document before delivery. This keeps delivery @@ -162,9 +153,8 @@ contract. Keep single-scope native delivery direct. Aggregate acquisition remain complete before output; the current code buffers one rendered document at a time, not every scope's rendered bytes simultaneously. -Measure documents with many short lines as well as large values. The initial -target removes avoidable scans and allocations; complete rendering and delivery -will still require work proportional to output size. +This target removes avoidable scans and allocations; complete rendering and +delivery still require work proportional to output size. ### PF-05: Avoid repeated registry copying @@ -173,15 +163,14 @@ will still require work proportional to output size. O(R²) cumulative map-copy work, apart from validation and declaration cloning. This is a construction cost, not work performed by a registered handler. -Validate declarations, check collisions against both existing and pending -entries, and prepare owned new entries in temporary storage. Install them only -after the complete batch succeeds. Preserve declaration ownership and atomic -validation without copying every existing map entry. +The target prepares owned pending declarations and checks collisions against +existing and pending entries before changing the registry. Successful batch +validation installs the pending entries without copying existing map entries, +while preserving declaration ownership and atomic validation. Its completion +point also invalidates derived metadata for PF-03 when the tree changes. -Compare individual and batch registration at representative sizes. Batch -registration is already available to consumers; adopting it is an optional -consumer optimization, not a new requirement. Coordinate this work with PF-03's -metadata refresh so registration has one clear completion point. +Batch registration remains an optional consumer optimization. Both individual +and batch registration are supported by this target. ### PF-06: Prepare stable configuration scope metadata @@ -193,76 +182,41 @@ occurs during preflight and again in the handler. In selector with all previous selectors; PromptKit already uses name and short-name maps for the equivalent check. -Prepare stable scope order and a name lookup during suite registration, keeping -that derived data private. Use direct name and short-name checks for selector -duplicates. Assess retaining the selected scopes in invocation-owned input after -preflight if repeated selection remains significant; keep selector applicability -and per-invocation contexts fresh. +The target retains normalized declarations, stable scope order, direct name +lookup, and discovery inventory as private registered-suite metadata. Selector +duplicate validation uses direct name and short-name checks. Context projection +and selection validation remain fresh for each invocation; both preflight and +handlers can consult the same prepared metadata without repeated sorting or +quadratic scope searches. -Measure the normal small scope set before adopting additional indexes for -selector applicability. Preserve scope order, declared operation availability, -and scope-aware selector validation under -[ADR 0009](../adr/0009-configuration-scopes.md). Do not invoke acquisition while -preparing this metadata. +Scope order, declared operation availability, and scope-aware selector validation +remain governed by [ADR 0009](../adr/0009-configuration-scopes.md). Metadata +preparation leaves acquisition and runtime context callbacks uninvoked. -## Recommended sequence and measurement +## Delivery scope and evidence -1. Establish focused before-change measurements for PF-01, PF-02, and PF-04, - then implement conditional formatting, direct command lookup/ancestry, and - YAML prefix scanning as independently reviewable changes. -2. Measure complete dispatch paths, then implement PF-03's reuse within an - invocation and immutable metadata caching. Compare the resulting runtime - gains with the coordination cost and retained memory. -3. Evaluate PF-05 and PF-06 using the first-pass evidence and expected consumer - sizes. Record a deferral if a proposed change adds complexity without a - worthwhile benefit. -4. Record remaining costs after the selected improvements. Mutable backend reuse - across invocations and segmented YAML delivery remain future possibilities - beyond this target; any reconsideration should use the measurements and a - deliberate change to the selected approach. +PF-01 through PF-06 are included in the implementation target. Conditional +formatting, indexed ancestry, and YAML preamble analysis address the clearest +redundant work. Registry and scope preparation are lower-priority, bounded +changes that remove observed scaling costs without adding consumer requirements. +The [implementation plan](implementation.md) specifies the concrete mechanisms +and divisions of work. -Use Go benchmarks with controlled, offline fixtures when implementation work is -authorized. Keep external loading and service latency out of isolated CommandKit -measurements, and identify when an integration measurement includes provider -work. Include: +Efficiency evidence compares elapsed cost, bytes allocated, and allocations per +operation for representative dispatch, reports, registration, and scope sizes. +It distinguishes cold and repeated execution, isolated and complete command +costs, provider work, and memory retained by caches. Source analysis alone does +not establish a measured speedup. Measurements establish no fixed timing +threshold, public allocation budget, or additional release gate. -| Area | Representative comparisons | -| --- | --- | -| Dispatch | Small and larger registries; wide and deep trees; few and many flags; successful commands, help, and usage errors. | -| Lifecycle | Construction, registration, and first execution versus repeated execution on an existing `App`. | -| Reports | Prompt text/YAML; profile text/YAML with varied parameter maps; aggregate configuration documents with varied size and line count. | -| Registration and scopes | Individual/batch registration; small and larger scope and selector sets. | +Implementation preserves existing public API and CLI contracts. Internal +documentation describes the changed mechanics when they land; detailed +validation and before/after evidence belong in the implementation plan. -Record elapsed cost, bytes allocated, and allocations per operation with the Go -version, machine, fixture dimensions, and benchmark scope. Use repeated samples -for comparisons; profile CPU or allocation behavior when it helps explain a -remaining cost. Account for memory retained by caches as well as allocation -reductions. Avoid combining every fixture dimension into a large benchmark -matrix without a specific question. - -Choose behavioral validation according to each change's risks and existing -coverage. Important cases include fresh presence and collections across runs, -registration after earlier execution, failed registration, help without provider -loading, YAML framing, cancellation, and partial output. Behavioral tests should -protect the existing contracts; benchmark results assess efficiency without -locking in private data structures or exact helper-call sequences. - -These measurements guide implementation choices. They do not establish fixed -timing thresholds, allocation budgets, or additional release gates. - -## Completion and documentation - -For each PF item, record whether it was implemented, deferred, or narrowed, -alongside its change reference, relevant behavioral validation, and measurement -results. An unmeasured improvement must be described as such. Completion means -the selected changes preserve the existing contracts and have evidence supporting -their claimed benefit; it does not require implementing every speculative reuse -option. - -Update [internal documentation](../internal/overview.md) when implemented -mechanics change. Update public GoDoc or the CLI reference only when their owning -contracts need clarification. Keep proposal status and performance evidence in -this roadmap or a subsequent implementation plan. +Selected-scope memoization and additional selector-membership indexes are +deferred pending evidence that their bookkeeping is worthwhile. Mutable backend +reuse across invocations and segmented YAML delivery are outside the selected +target; future reconsideration needs a deliberate change to OD-1 or OD-2. Provider caching, consumer loader redesign, removing copies required for value ownership, and replacing Cobra are outside this target. They require their own