Files
commandkit/docs/roadmap/remediation.md

309 lines
16 KiB
Markdown

# Initial implementation review and remediation
Status: remediation in progress.
Date: 2026-10-07.
## Review baseline and scope
Reviewed committed implementation at `85f44cd`, including the changes since
planning baseline `e5c361d`. The review covered production code, public contracts,
the internal adapter, integration helpers, maintained examples, and existing
behavioral tests against the accepted ADRs, architecture policy, and selected
[initial target](initial.md) and [implementation plan](implementation.md).
These are static review findings. No tests were added or executed during this
review; prior completion notes are historical evidence, not a new validation
result. The triggering cases below follow from the current code paths. Focused
regression checks are proposed completion evidence for subsequent fixes.
The findings below describe the review baseline. Completion is tracked here as
implementation fixes land.
## Remediation progress
- R-01 complete: required selectors are checked after scope selection; unrelated
scopes remain usable, and help describes conditional requiredness.
## Architecture assessment
The main architecture is implemented:
- The root `commandkit` package owns public named types, opaque handles,
per-instance registration, typed bindings, outcomes, standard handlers, scope
assembly, and ConfigKit/PromptKit integration.
- `internal/cobra` owns Cobra/pflag operations behind a neutral internal bridge.
Public signatures do not expose the backend. Each invocation rebuilds backend
state; configuration and engine acquisition remain lazy.
- Standard families own their namespaces. Applications retain custom commands,
loaders, domain behavior, executable entry points, streams, and process exit.
- Scope capabilities and prompt/profile callbacks can be supplied independently.
Native YAML writers and public upstream inspection APIs are used. Profile
reports include `ExtraParams` without additional redaction.
- The module uses published dependencies rather than sibling internal packages
or local replacement directives. Simple and composed consumer examples exist.
No package restructuring or additional implementation framework is recommended.
Diff, catalogs, JSON output, consumer migrations, publication tooling, and
concurrent-dispatch guarantees are deliberate deferrals, not missing initial
architecture. The issues below affect behavior within the implemented structure.
- R-02 complete: inline document starts are recognized and native content is
retained, including block scalars.
- R-03 complete: standard inspection handlers check cancellation before runtime
construction/acquisition, after acquisition, and before prepared report delivery.
- R-04 complete: every help entry form finalizes report failures through the
common diagnostic presentation path once.
- R-05 complete: validation header/record write failures retain `DeliveryError`
separately from acquisition failures and combined diagnostic-stream failures.
## Findings and priority
P1 means a supported invocation is blocked or its report content is corrupted.
P2 means a selected behavior or public contract is incomplete or unnecessarily
restricted. All listed items need resolution before calling the initial target
fully aligned.
| ID | Priority | Finding | Review category |
| --- | --- | --- | --- |
| R-01 | P1 | Required selectors block scopes to which they do not apply | Strengthened requirement; behavior defect |
| R-02 | P1 | Aggregate YAML framing inserts empty documents for inline start markers | Report correctness |
| R-03 | P2 | Standard inspection handlers omit cancellation boundary checks | Target gap |
| R-04 | P2 | Help shortcuts bypass diagnostic presentation | Target gap; inconsistent delivery |
| R-05 | P2 | Validation report failures omit `DeliveryError` | Public outcome contract gap |
| R-06 | P2 | Zero outcomes and empty delivery state are not normalized | Target gap |
| R-07 | P2 | Nil diagnostic writers are rejected despite the documented no-output behavior | Strengthened prohibition; contract inconsistency |
| R-08 | P2 | Unknown/default absent scopes omit available choices | Operator contract gap |
| R-09 | P2 | PromptKit selectors reserve flag names absent from installed commands | Strengthened prohibition |
| R-10 | P2 | Parent help omits registered groups containing only help metadata | Discovery gap |
### R-01: enforce selector requiredness only where applicable
**Evidence:** [selectors.go](../../selectors.go), `adaptContextSelector` and
`configInputFlags`; [binding.go](../../binding.go), `prepareInput`.
All configuration operation commands receive every suite selector. Adaptation
copies `FlagMeta.Required` unchanged, and generic input preparation checks it
before scope selection or applicability checks.
With an app-only required `--campaign` selector and app/PromptKit validation
scopes, `config validate --scope promptkit` fails because campaign is missing.
Supplying campaign instead fails because it does not apply to PromptKit. There
is no successful invocation for that independently supported scope.
**Authority:** selected scope-specific routing in INT-01 and the initial
roadmap's selector section. A required selector for one scope does not authorize
requiring it for every other scope.
**Remediation:** apply selector requiredness after selecting applicable scopes.
Keep ordinary custom-command required flags unchanged. Preserve requiredness
and applicability in help metadata without an unconditional preparation check.
**Completion evidence:** an unrelated scope succeeds without the selector; an
applicable scope fails before acquisition when it is missing; `all` enforces
the requirements of its selected scopes; help remains available.
### R-02: recognize inline YAML document-start markers
**Evidence:** [show.go](../../show.go), `frameEffectiveYAML`.
The framer recognizes only a line equal to `---` or starting with `--- #`.
A native document such as `--- {setting: value}\n` already has a valid document
start, but receives another opening marker and scope comment. The result starts
with an empty document followed by the actual value, so document count and scope
association are wrong even though execution returns success.
**Authority:** [ADR 0007](../adr/0007-native-yaml-reports.md) and CFG-03 require
native YAML preservation and one framed document per selected scope.
**Remediation:** recognize the document-start token with supported whitespace and
inline content. Preserve native bytes; a parallel decode/remarshal pipeline or
a new prohibition on valid native YAML is unnecessary.
**Completion evidence:** aggregate decoding yields exactly one value per scope
for inline mapping/scalar starts, ordinary marked documents, unmarked documents,
and directive-bearing documents, while preserving native content.
### R-03: check cancellation at standard inspection boundaries
**Evidence:** `runConfigValidation`, `runConfigShow`, `runConfigSources`, and
`runConfigExplanation` in [validate.go](../../validate.go),
[show.go](../../show.go), [sources.go](../../sources.go), and
[explain.go](../../explain.go); definition handlers and engine loading in
[promptkit.go](../../promptkit.go).
The handlers pass context to callbacks but do not check `Context.Err()` at their
own boundaries. With an already canceled context and a successful loader that
does not itself inspect context, configuration inspection can acquire and emit a
successful report. Direct PromptKit suite callbacks have the same exposure;
the engine helper also loads before delegating to context-aware named inspection.
**Authority:** INV-01/OUT-01 and implementation Stage 5 explicitly select standard
handler checks at operation/acquisition boundaries, with supported cancellation
identity preserved. ADR 0005 classifies cancellation as operational failure.
**Remediation:** check context at standard acquisition/report boundaries and
between aggregate acquisitions. Retain `context.Canceled`/deadline identity and
existing diagnostics/partial-report information. This does not require a
goroutine cancellation runner, interruption of callbacks, or new restrictions on
application-owned custom command semantics.
**Completion evidence:** cancellation before acquisition and during acquisition
returns operational failure with the context cause, and prevents subsequent
standard work at the next boundary. Include an acquisition callback that ignores
context so the test protects CommandKit's responsibility.
### R-04: finalize help outcomes through the presentation path
**Evidence:** [execute.go](../../execute.go), `Execute`; [builtins.go](../../builtins.go),
`renderHelpOutcome`.
Root help, non-runnable group help, and `--help`/`-h` return directly from
`renderHelpOutcome`. A failed help report write returns an operational outcome
but bypasses `present`, so it produces no operational diagnostic and cannot
record a simultaneous diagnostic-stream failure. Explicit `help PATH` goes
through the normal handler finalization and behaves differently.
**Authority:** ADR 0005's diagnostic channels and INV-01's single presentation path.
**Remediation:** route these outcomes through the same finalization path exactly
once, preserving existing report failure fields and lazy metadata behavior.
**Completion evidence:** all help entry forms have consistent failure delivery;
broken report and diagnostic streams retain both errors without double delivery.
### R-05: retain validation report errors in `DeliveryError`
**Evidence:** [validate.go](../../validate.go), `runConfigValidation` and
`validationFailure`; [outcome.go](../../outcome.go), `Outcome`.
Both validation header and record write failures call `validationFailure`, which
sets cause and incomplete-report state but never sets `DeliveryError`. With a
working diagnostic stream, the returned delivery error remains nil despite a
failed report write. Other standard report paths record this field.
**Remediation:** distinguish report-writing failures from acquisition failures
and retain the former in `DeliveryError`, alongside the operational cause.
Do not misclassify every validation acquisition error as a delivery error.
**Completion evidence:** header/record errors and short writes populate both
relevant error fields and retain incomplete-report state. Combined stdout/stderr
failures preserve both errors. Existing validation short-write checks inspect
the report cause but do not check its `DeliveryError`.
### R-06: implement the selected outcome normalization
**Evidence:** [execute.go](../../execute.go), `present`; [outcome.go](../../outcome.go).
A handler returning `Outcome{}` retains an empty kind and delivery state because
`present` returns it unchanged when there are no diagnostics. An explicit domain
outcome with an omitted diagnostic state likewise retains an empty state.
**Authority:** the selected outcome model, specified in implementation Stage 1,
normalizes a zero outcome to success/0 and an empty delivery state to
`DeliveryNone`. The latter is also stated in public GoDoc.
**Remediation:** normalize these selected defaults during invocation coordination.
Preserve explicit domain classification, status, cause, and delivery information;
do not introduce inference of domain meaning from numeric status.
**Completion evidence:** a zero handler outcome returns success/0/none; explicit
domain outcomes retain their values with an omitted delivery state normalized.
### R-07: honor nil diagnostic writers as no-output diagnostics
**Evidence:** [outcome.go](../../outcome.go), `Diagnostic` GoDoc;
[execute.go](../../execute.go), `present` and `hasErrorDiagnostic`.
GoDoc says a nil `Write` function has no output. Coordination instead turns it
into an operational delivery failure with `diagnostic has no writer`. Thus an
otherwise successful result containing a zero diagnostic becomes a failure.
An error-severity diagnostic with a nil writer also suppresses automatic cause
presentation because deduplication checks severity alone.
**Remediation:** treat nil writers as no-output entries, consistent with the
documented behavior, and exclude them from cause-presentation deduplication and
delivery-attempt bookkeeping. Requiring every entry to have a writer would be a
new API restriction requiring a deliberate decision.
**Completion evidence:** nil writers do not change success/domain classifications
or suppress a real cause; active diagnostic writer failures remain observable.
### R-08: include available scope choices in selection failures
**Evidence:** [selectors.go](../../selectors.go), `selectConfigScopes`.
Unknown selection returns only `unknown configuration scope NAME`. This also
occurs when only a PromptKit scope exists and the default `app` scope is absent.
The operator receives no available choices.
**Authority:** CFG-01 expressly requires useful choices for unsupported scopes
and absent `app`; ADR 0009 requires available choices for unsupported selections.
**Remediation:** include registered scope choices in stable scope order. Retain
the usage classification, default selection, strict aggregate coverage, and
pre-acquisition failure; do not silently select an alternative.
**Completion evidence:** explicit unknown scope and omitted selection with absent
`app` identify available choices without invoking runtime callbacks.
### R-09: reserve selector names for actual command collisions
**Evidence:** [promptkit.go](../../promptkit.go), `normalizePromptKitSuite` and
`promptKitInputFlags`.
Normalization unconditionally rejects a selector called `version`, even when
only profile inspection is supplied. `--version` is installed only on prompt
inspection, so there is no flag collision in a profile-only suite. It also
reserves `scope` despite not installing a scope-selection flag on definitions.
**Authority:** capability-based family assembly and the agent policy's prohibition
on inventing restrictions. The selector rules in Stage 7, reused by Stage 12,
call for detecting collisions with standard local flags, not a reservation of
absent flags for hypothetical future use.
**Remediation:** detect collisions against flags actually installed on commands
receiving the selector. Keep genuine collisions rejected. If a broader vocabulary
reservation is desired, record it as a new decision rather than a parser necessity.
**Completion evidence:** a profile-only selector named `version` can register;
the same selector is rejected when it collides with installed prompt inspection.
Review the absent `scope` reservation against the same rule.
### R-10: expose registered metadata groups in parent help
**Evidence:** [command.go](../../command.go), `Group`;
[internal/cobra/parse.go](../../internal/cobra/parse.go), `buildTree` and
`usageTemplate`; pinned Cobra v1.10.1 `Command.IsAvailableCommand`.
CommandKit accepts a non-runnable group with no children, and direct invocation
renders its help. However, parent help filters commands with Cobra's
`IsAvailableCommand`, which requires a runnable command or an available
descendant. A metadata-only group, or a chain containing only such groups,
disappears from the parent's command inventory despite being registered and
addressable. The custom template provides no additional help-topic inventory.
**Authority:** HELP-01 and the architecture policy select discovery from registered
metadata; Cobra's availability defaults do not independently select a CommandKit
visibility restriction.
**Remediation:** derive help inventory from CommandKit's declared commands,
including groups that expose help. Do not prohibit empty groups or add fake
application handlers solely to satisfy a backend visibility rule.
**Completion evidence:** parent/root help discovers metadata-only groups and
their nesting; direct help works and leaves acquisition callbacks uninvoked.
## Follow-up boundaries
Address the behavioral findings with focused fixes and regression evidence at
the public invocation boundary or the narrow framing boundary as appropriate.
Update GoDoc/CLI references where needed to match the selected contracts.
Do not expand this work into upstream redaction changes, general framework
construction, new consumer mandates, blanket validation gates, or new concurrency
guarantees. Recheck the completed target after remediation; this review does not
claim that the existing tests establish these missing cases.