180 lines
11 KiB
Markdown
180 lines
11 KiB
Markdown
# ADR 0001: Own dispatch through an explicit registry and typed inputs
|
|
|
|
## Status
|
|
|
|
Accepted.
|
|
|
|
## Date
|
|
|
|
2026-10-06
|
|
|
|
## Context
|
|
|
|
Audita, Narratio, Notarius, and Scriptorium repeat configuration inspection,
|
|
routing, parsing, help, and error handling. Their command vocabulary and syntax
|
|
have diverged. CommandKit aims to provide one implementation of standard
|
|
inspection, consistent vocabulary and mechanics, and less application
|
|
infrastructure. Application migrations are expected to make breaking changes.
|
|
|
|
Applications have genuine domain commands, such as Narratio's `session restore`,
|
|
that remain outside the standard suite. Merely forwarding standard families from
|
|
separate application dispatchers would leave custom-command mechanics and root
|
|
help dependent on each application's implementation.
|
|
|
|
Input handling also needs a common boundary. Raw argv handlers or string-based
|
|
flag retrieval leave repeated parsing and input assembly with applications.
|
|
Presence matters for configuration overrides: omitted values differ from
|
|
explicitly supplied empty strings, false, and zero.
|
|
|
|
## Decision
|
|
|
|
### Ownership and namespace structure
|
|
|
|
CommandKit owns the dispatcher, shared parsing and presence tracking, help,
|
|
syntax diagnostics, and standard-family handlers and outcomes. Applications own
|
|
custom command names, domain meaning, validation, effects, artifacts, and
|
|
operational outcomes.
|
|
|
|
Use namespace/resource-first syntax for the standard suite. The `config` family
|
|
and an installed `promptkit` family belong to CommandKit. PromptKit definition
|
|
inspection uses the single `promptkit` namespace; root `prompt` and `profile`
|
|
names remain available to applications. This provides a clear family ownership
|
|
boundary without spreading a namespace across root verbs.
|
|
|
|
Applications register commands outside reserved families. They cannot replace
|
|
standard handlers or add private operations underneath a reserved family.
|
|
Standard-family context selectors and typed overrides use the defined input
|
|
extension mechanism rather than introducing application-owned operations there.
|
|
|
|
Applications retain `main`, signal handling, context creation, stream selection,
|
|
runtime construction, and final process exit. CommandKit returns invocation
|
|
outcomes and does not terminate the process. Configuration schemas, acquisition,
|
|
domain resolution and validation, and provider construction remain application
|
|
integration responsibilities. ConfigKit and PromptKit retain their own
|
|
configuration, provenance, safe inspection, and native rendering semantics.
|
|
|
|
### Explicit registry and capability installation
|
|
|
|
Each explicitly constructed application instance owns a registry. Applications
|
|
register individual commands and composable groups. There is no package-global
|
|
registry or import-time `init` registration.
|
|
|
|
Definitions provide paths, descriptions, flags and defaults, repetition and
|
|
presence metadata, operand requirements, and execution handlers. One definition
|
|
supplies routing, parsing, and help. Duplicate paths and attempts to replace
|
|
reserved commands return registration errors; order does not select a winner.
|
|
|
|
Help and version are installed automatically using supplied application identity
|
|
and build metadata. Configuration and PromptKit families are installed when the
|
|
application supplies their providers. Supplying a capability installs its complete
|
|
standard family, not a collection requiring manual operation-by-operation wiring.
|
|
|
|
Registering providers does not execute them. Construction may diagnose missing
|
|
or invalid wiring without reading configuration, resolving credentials, or
|
|
building engines. Invalid configuration is an invocation-time failure. Command
|
|
declarations, help, and version remain available independently of application
|
|
initialization. The selected handler acquires dependencies when it needs them.
|
|
|
|
Do not install placeholder commands. Omitting the configuration provider does
|
|
not make a partial integration a complete core adopter. PromptKit is optional;
|
|
its selected family is installed as a complete capability. The CLI roadmap owns
|
|
the proposed command inventory and the remaining choices for that standard.
|
|
|
|
### Typed inputs with explicit bindings
|
|
|
|
Use application-defined input structs for custom commands and CommandKit-defined
|
|
inputs for standard handlers. Explicit registration binds flags and operands to
|
|
their input fields. The declarations also supply parsing and help metadata.
|
|
Handlers do not retrieve primitive values by flag name or parse the complete argv.
|
|
|
|
Use ordinary typed fields where omission has no separate meaning. Where it does,
|
|
keep supplied/not-supplied state beside the value in a presence-aware field.
|
|
Defaults do not mark input as explicitly supplied. Preserve explicit empty,
|
|
false, and zero values.
|
|
|
|
Create fresh input storage per invocation. Registered commands must not capture
|
|
one mutable input permanently or retain prior values, collections, or presence.
|
|
This decision addresses lifetime without establishing concurrent dispatch.
|
|
|
|
Supply context and stream access separately from CLI inputs. Input declarations
|
|
and binding setup must not construct execution dependencies. Applications perform
|
|
domain validation and may interpret parsed domain values or supply a custom value
|
|
parser through the binding system.
|
|
|
|
The public factory, binding, registration, and handler signatures and presence
|
|
type names remain API design choices. No illustrative declaration establishes
|
|
an exact public API.
|
|
|
|
### Outcomes and domain behavior
|
|
|
|
Resolve the command and handle help/version before loading application state.
|
|
For execution, allocate fresh inputs, apply shared parsing and operand checks,
|
|
then invoke the typed handler. Return its invocation outcome to the application.
|
|
|
|
Shared usage errors and standard-family outcomes belong to CommandKit. Custom
|
|
handlers must communicate application-specific outcomes without silent generic
|
|
reclassification. For example, Scriptorium output validation and Notarius artifact
|
|
outcomes remain application concerns. Do not classify domain errors by matching
|
|
message strings or print usage unconditionally for runtime failures.
|
|
|
|
Common mechanics do not require a common success-report format for custom
|
|
commands. The application owns its artifact and operational output contracts.
|
|
Exact result/error interfaces and unresolved grammar choices are separate design
|
|
work, not approvals implied by this decision.
|
|
|
|
## Alternatives considered
|
|
|
|
### Dispatcher and ownership
|
|
|
|
| Alternative | Analysis |
|
|
| --- | --- |
|
|
| Application-owned dispatcher forwarding standard families | Small integration surface and convenient migration, but custom syntax, root help, and errors still need cross-dispatcher coordination; removes less infrastructure. |
|
|
| Standard definitions mounted into an application's framework | Reuses routing infrastructure, but application configuration can preserve inconsistent behavior outside mounted families. Internal framework reuse remains compatible with this decision. |
|
|
| Shared metadata/help with separate routing | Unifies discovery but adds synchronization among metadata, routing, and parsing. One dispatcher avoids it. |
|
|
| Both full dispatch and embeddable family handlers | Serves established external CLI architectures but adds integration surfaces and maintenance. Add embedding only for a concrete need. |
|
|
| Raw-argument handlers for custom commands | Eases migration but leaves parsing and help responsibilities with applications. A future transition adapter needs explicit limitations and is not full adoption. |
|
|
| Universal application runtime framework | Could absorb startup and operational services, but expands beyond the demonstrated CLI boundary and domain ownership. |
|
|
| Verb-first standard paths | Groups actions but distributes namespace ownership among individual routes. Namespace/resource-first paths keep family ownership clear. |
|
|
| Reserve root `prompt` and `profile` | Provides short resource paths but consumes application namespaces. A single `promptkit` family gives the library one ownership boundary. |
|
|
|
|
### Registration
|
|
|
|
| Alternative | Analysis |
|
|
| --- | --- |
|
|
| Declarative tree passed to construction | Makes assembly visible together; larger applications still need group composition. May become a convenience over the same registry. |
|
|
| Constructor options for every command/group | Compact setup, but excessive options can obscure commands versus application settings. Option conventions remain API design work. |
|
|
| Global registry populated by `init` | Less entry-point code, but hidden import effects, shared mutable state, and unclear ownership. Explicit instances avoid those costs. |
|
|
|
|
### Handler inputs
|
|
|
|
| Alternative | Analysis |
|
|
| --- | --- |
|
|
| Parsed-value accessor object | Uniform handlers, but string-based lookup, runtime type errors, and repeated assembly remain. |
|
|
| Individual typed parameters | Clear for small commands; grows into long signatures and adapter machinery. Typed structs scale more naturally. |
|
|
| Reflection and struct tags | Compact declarations, but adds a tag language, field-discovery rules, and runtime declaration validation. Explicit bindings keep declarations visible. |
|
|
| Typed command objects | Can organize complex handlers; must separate fresh input from dependency and execution lifetimes. Applications may use objects without making them mandatory. |
|
|
| Generated bindings/adapters | Can combine compact authoring and typing, but adds tooling and generated-file maintenance without demonstrated need. |
|
|
| Raw arguments | Simple routing integration, but returns common parsing and help to applications. |
|
|
|
|
## Consequences
|
|
|
|
One command system removes repeated routing, parser setup, help assembly, and
|
|
shared error plumbing as well as standard inspection handlers. Domain handlers
|
|
and their declarations remain recognizable application code.
|
|
|
|
The public registration surface is larger than family delegation, and explicit
|
|
bindings require connecting declarations to the correct fields. Those costs
|
|
support consistent mechanics without requiring reflection or generated code.
|
|
|
|
Shared parsing defects can affect every adopter. Consumer and backend integration
|
|
evidence must protect selected semantics, including ownership conflicts, help
|
|
without configuration, presence, fresh input, cancellation, and domain outcomes.
|
|
Intentional migration changes require coordinated application documentation and
|
|
subprocess-consumer updates.
|
|
|
|
The [architecture policy](../policy/architecture.md) owns normative boundaries.
|
|
The [dispatcher roadmap](../roadmap/02-dispatch.md) owns implementation status and
|
|
pending API design. The [CLI roadmap](../roadmap/01-cli-plan.md) retains command
|
|
proposals and their approval status. [ADR 0002](0002-cobra-backend.md) selects the
|
|
backend, and [ADR 0003](0003-public-facade.md) defines its public/internal boundary.
|