269 lines
15 KiB
Markdown
269 lines
15 KiB
Markdown
# Computational efficiency roadmap
|
||
|
||
Status: implemented; PF-01–PF-06 and selected approaches OD-1/OD-2 are complete.
|
||
|
||
Date: 2026-10-09.
|
||
|
||
## Purpose and authority
|
||
|
||
Reduce repeated work and avoidable allocations in CommandKit's command dispatch,
|
||
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 target state, priorities, and selected implementation
|
||
scope. The completed [implementation record](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
|
||
[testing policy](../policy/testing.md) for validation. These proposals do not
|
||
establish new public performance or concurrency guarantees.
|
||
|
||
## Reviewed baseline
|
||
|
||
The efficiency review examined commit `54cfbd8` with a clean worktree. Its
|
||
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 | 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. | 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
|
||
within the existing root facade and Cobra adapter; no additional public package
|
||
or consumer registration API is proposed.
|
||
|
||
## Target behavior and ownership
|
||
|
||
Optimizations preserve the existing contracts rather than changing operator or
|
||
consumer workflows:
|
||
|
||
- Keep Cobra/pflag objects and operations inside the internal adapter, following
|
||
[ADR 0003](../adr/0003-public-facade.md).
|
||
- Keep parsed values, collections, and presence independent between invocations.
|
||
[ADR 0002](../adr/0002-cobra-backend.md) permits safe reuse; rebuilding backend
|
||
storage is an implementation choice. Caller-owned state retains its lifecycle.
|
||
- Preserve registration validation, reserved command ownership, and complete
|
||
batch validation before changing the registry, as documented in
|
||
[command declarations](../../command.go) and [registration](../../registry.go).
|
||
- Keep metadata construction, help, and version independent of provider
|
||
acquisition under the architecture policy.
|
||
- Preserve owning-library rendering and confidentiality semantics, aggregate
|
||
document order, and acquisition of all selected views before `config show`
|
||
output under [ADR 0007](../adr/0007-native-yaml-reports.md).
|
||
- Preserve cancellation, diagnostic delivery, outcome classification, and
|
||
reporting of partial output and write failures under
|
||
[ADR 0005](../adr/0005-invocation-outcomes.md).
|
||
|
||
Registration and command metadata belong to the root implementation. Backend
|
||
indexes and invocation machinery belong to `internal/cobra`. Report formatting
|
||
and scope preparation remain with the corresponding root handlers. Acquisition,
|
||
configuration resolution, and provider lifetime remain with the existing
|
||
integration contracts and consuming applications.
|
||
|
||
## Target improvements
|
||
|
||
### PF-01: Render only the requested PromptKit format
|
||
|
||
In [PromptKit handlers](../../promptkit.go), `runPromptKitPrompt` calls
|
||
`formatPromptText` before `writePromptKitReport` chooses a format.
|
||
`runPromptKitProfile` similarly calls `formatProfileText` unconditionally. The
|
||
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.
|
||
|
||
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
|
||
|
||
In the [Cobra adapter](../../internal/cobra/parse.go), `declarationFor` scans the
|
||
declaration-to-backend map to find a selected backend command. `pathFor` calls
|
||
`parentOf` from the root for every ancestor. For N command nodes and selected
|
||
depth D, repeated parent searches can take O(N × D) work; a chain can produce
|
||
quadratic work.
|
||
|
||
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.
|
||
|
||
### PF-03: Reduce command-tree reconstruction
|
||
|
||
[Metadata translation](../../parser.go) copies and sorts the registered tree on
|
||
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.
|
||
|
||
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.
|
||
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
|
||
each invocation, reuse that state within the invocation, and cache immutable
|
||
metadata between invocations. Reusing mutable backend trees across invocations
|
||
is deferred beyond this target. Measurements may motivate reconsidering it in
|
||
future work. No public freeze step, backend selector, or concurrent-dispatch
|
||
guarantee is needed for these improvements.
|
||
|
||
### PF-04: Reduce aggregate YAML preamble scanning
|
||
|
||
In [configuration display](../../show.go), `hasYAMLDirective` and
|
||
`frameEffectiveYAML` split the complete document into lines before examining its
|
||
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.
|
||
|
||
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
|
||
and write-failure handling straightforward. The full document copy remains an
|
||
accepted cost of this target; the optimization removes the document-wide line
|
||
index and redundant preamble scanning.
|
||
|
||
Preserve native bytes, directive placement, inline document content and block
|
||
scalars, scope-label escaping, and boundary newlines according to the CLI
|
||
contract. Keep single-scope native delivery direct. Aggregate acquisition remains
|
||
complete before output; the current code buffers one rendered document at a time,
|
||
not every scope's rendered bytes simultaneously.
|
||
|
||
This target removes avoidable scans and allocations; complete rendering and
|
||
delivery still require work proportional to output size.
|
||
|
||
### PF-05: Avoid repeated registry copying
|
||
|
||
[Registration](../../registry.go) copies all existing top-level entries for each
|
||
`Register` call. Registering R top-level commands individually therefore produces
|
||
O(R²) cumulative map-copy work, apart from validation and declaration cloning.
|
||
This is a construction cost, not work performed by a registered handler.
|
||
|
||
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.
|
||
|
||
Batch registration remains an optional consumer optimization. Both individual
|
||
and batch registration are supported by this target.
|
||
|
||
### PF-06: Prepare stable configuration scope metadata
|
||
|
||
[Scope selection](../../selectors.go) sorts scope declarations and repeatedly
|
||
searches the scope slice. Aggregate selection and handler acquisition each look
|
||
up every scope by name, producing quadratic scope-search work. Selection also
|
||
occurs during preflight and again in the handler. In
|
||
[suite normalization](../../config.go), selector duplicate checking compares each
|
||
selector with all previous selectors; PromptKit already uses name and short-name
|
||
maps for the equivalent check.
|
||
|
||
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.
|
||
|
||
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.
|
||
|
||
## Delivery scope and evidence
|
||
|
||
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.
|
||
|
||
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.
|
||
|
||
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.
|
||
|
||
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
|
||
concrete motivation and review rather than following automatically from a
|
||
performance optimization.
|
||
|
||
## Resolved Decisions
|
||
|
||
The following approaches were selected on 2026-10-09. Both are implementation
|
||
choices within the existing public contracts. No significant open decisions
|
||
remain for this roadmap's scope.
|
||
|
||
### OD-1: How far should backend reuse extend?
|
||
|
||
**Selected approach:** Adopt the recommended approach: reuse the prepared backend
|
||
within one invocation and cache immutable metadata between invocations, while
|
||
constructing fresh mutable backend state for each `Execute`.
|
||
|
||
**Reasoning:** This removes repeated help/usage work and metadata translation
|
||
while keeping invocation isolation straightforward. Measure the remaining
|
||
backend construction cost to inform future work. The selection defines the
|
||
PF-03 target without establishing a permanent architectural restriction.
|
||
|
||
**Alternative considered:** Retain a mutable backend tree per `App` and reset it
|
||
before each invocation. This can remove more command and flag-set construction
|
||
work for consumers that execute repeatedly. It also retains more memory and requires
|
||
careful reset of parsed values, presence, operands, contexts, streams, and other
|
||
backend state, plus refresh after registration. The added lifecycle complexity
|
||
is deferred until measurements justify reconsideration and behavioral evidence
|
||
establishes independent invocations. A CLI that executes only once receives
|
||
little benefit from this additional reuse.
|
||
|
||
### OD-2: How much buffering should aggregate YAML retain?
|
||
|
||
**Selected approach:** Adopt the previously listed alternative: retain the
|
||
complete framed buffer after replacing the line splits with prefix scanning.
|
||
|
||
**Reasoning:** This keeps delivery simple and avoids additional writes for each
|
||
document. It accepts the second document allocation and copy while removing the
|
||
line index and unnecessary scanning. Measure the resulting allocation cost,
|
||
particularly for large reports, without making segmented delivery part of this
|
||
implementation target.
|
||
|
||
**Alternative considered:** Retain only the native render buffer and deliver
|
||
framing and native slices as separate segments. This can reduce peak allocation
|
||
and copying while keeping native rendering complete before delivery. It requires
|
||
accounting for cancellation and partial output across segment writes and may
|
||
increase writer-call overhead. It remains a viable future optimization if the
|
||
selected approach's measured memory cost warrants the additional delivery logic.
|