Consolidate and renumber architectural decision records
This commit is contained in:
@@ -1,209 +1,78 @@
|
||||
# ADR 0001: Own dispatch through an explicit registry and typed inputs
|
||||
# ADR 0001: Own dispatch through captured declarations and typed inputs
|
||||
|
||||
## Status
|
||||
|
||||
Accepted.
|
||||
|
||||
[ADR 0011](0011-captured-declarations-and-installation.md) refines
|
||||
declaration construction, installation, optional-family namespace reservation,
|
||||
and integration input/state lifetimes.
|
||||
Accepted. Consolidated during the [alpha ADR cleanup](README.md#alpha-consolidation).
|
||||
|
||||
## Date
|
||||
|
||||
2026-10-06
|
||||
2026-11-07; consolidates decisions made on 2026-10-06 and 2026-10-10.
|
||||
|
||||
## Context
|
||||
|
||||
Several downstream consumers of the ConfigKit and PromptKit libraries include
|
||||
duplicated or near-duplicated code relating to configuration inspection,
|
||||
routing, parsing, help, and error handling. However, their command vocabulary
|
||||
and syntax have diverged. CommandKit aims to provide a single, unified CLI
|
||||
implementation that will reduce duplication, provide consistent vocabulary and
|
||||
grammar, and reduce the amount of code each individual application must own.
|
||||
|
||||
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.
|
||||
Applications repeat routing, flag parsing, help, and inspection commands while
|
||||
also needing their own domain commands. Sharing only isolated command handlers
|
||||
would leave much of that infrastructure and its inconsistent behavior with each
|
||||
application. Optional integrations also need to assemble commands through the
|
||||
same public mechanisms as applications.
|
||||
|
||||
## Decision
|
||||
|
||||
### Ownership and namespace structure
|
||||
CommandKit owns dispatch, shared parsing, presence tracking, help, syntax
|
||||
failures, and standard command mechanics. Applications own their domain commands,
|
||||
validation, effects, runtime construction, and executable lifecycle. CommandKit
|
||||
accepts caller context and streams and returns outcomes rather than terminating
|
||||
the process.
|
||||
|
||||
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 an explicitly constructed, per-instance registry. Applications and
|
||||
integrations supply captured declarations containing routing, parsing, help
|
||||
metadata, and callback references. One declaration supplies those views of the
|
||||
command. Captured metadata is insulated from later edits to its source;
|
||||
application objects referenced by callbacks remain caller-owned.
|
||||
|
||||
Use namespace-first syntax for the standard suite, which provides a clear
|
||||
family ownership boundary. The initial standard command families are `config`
|
||||
for ConfigKit inspection and validation, and `promptkit` for PromptKit inspection
|
||||
and utilities. CommandKit integrates these libraries through their supported public
|
||||
semantics.
|
||||
Declarations share one installation boundary. Construction issues are retained
|
||||
for installation, and each installation batch is validated before registry
|
||||
mutation. Collisions are errors rather than order-dependent replacement.
|
||||
Optional standard families claim their namespaces when installed, not when
|
||||
imported. Their canonical operations remain integration-owned; application
|
||||
commands live outside installed standard families.
|
||||
|
||||
CommandKit accepts custom commands outside its reserved standard families and
|
||||
reports registration errors for attempts to replace standard handlers or add
|
||||
private operations beneath them. Its input extension mechanism accepts context
|
||||
selectors and typed overrides for standard operations.
|
||||
Handlers receive typed inputs through explicit flag and operand bindings.
|
||||
Presence is separate from value so omission differs from supplied empty, false,
|
||||
or zero values. Domain parsers and validation remain application-owned. Core
|
||||
provides typed composition for integrations without exposing parser state or
|
||||
requiring a second argument parser.
|
||||
|
||||
CommandKit accepts caller-supplied context, streams, handlers, and inspection
|
||||
providers and returns invocation outcomes. It leaves process termination to the
|
||||
caller. Executable startup, signals, runtime construction, schemas, acquisition,
|
||||
and domain resolution remain application choices.
|
||||
Parsed state is independent between invocations. Integration inputs for selected
|
||||
scopes or comparison sides are distinct from invocation-wide acquisition state.
|
||||
Applications own the objects and caches returned by their factories. CommandKit
|
||||
coordinates its own preparation and calls without promising arbitrary deep copies
|
||||
or concurrent dispatch.
|
||||
|
||||
### Explicit registry and capability installation
|
||||
|
||||
Each explicitly constructed CommandKit instance owns a registry. Its API accepts
|
||||
individual commands and composable groups. Registration uses explicit instance
|
||||
state with caller-controlled construction and composition.
|
||||
|
||||
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. CommandKit assembles standard families from declared provider
|
||||
capabilities, installing their supported handlers and metadata together. Each
|
||||
family defines its own resource model, supported verbs, and per-operation data
|
||||
contracts. Configuration scopes declare their available inspection operations.
|
||||
|
||||
The API allows supported capabilities to be integrated independently. Adding
|
||||
comparison, catalogs, or other inspection features extends the installed surface
|
||||
according to the supplied providers. Shared verbs have consistent purposes across
|
||||
families, while the available subsets reflect their resources and capabilities.
|
||||
Help and discovery expose those subsets. Registration validates the wiring of
|
||||
declared capabilities and reports unsupported selections before acquisition.
|
||||
|
||||
CommandKit registration validates declarations with acquisition callbacks left
|
||||
uninvoked. Its construction, help, and version paths use metadata. The selected
|
||||
handler requests dependencies when its operation needs them. Invalid configuration
|
||||
returned by an acquisition callback is an invocation-time failure. Caller code
|
||||
executed before registration and effects inside callbacks remain caller-owned.
|
||||
|
||||
CommandKit installs handlers for supplied capabilities and describes actual
|
||||
availability. Each enabled operation retains its standard meaning and output
|
||||
contract. The exact operation inventory and remaining command contracts are
|
||||
separate design decisions.
|
||||
|
||||
### Typed inputs with explicit bindings
|
||||
|
||||
The public binding contract accepts application-defined input structs for custom
|
||||
commands and uses CommandKit-defined inputs for standard handlers. Explicit
|
||||
registration binds flags and operands to fields and supplies parsing and help
|
||||
metadata. CommandKit delivers parsed typed inputs to registered handlers.
|
||||
|
||||
Bindings support ordinary typed fields and presence-aware fields where omission
|
||||
has a separate meaning. CommandKit records supplied state independently of values
|
||||
and defaults, preserving explicitly supplied empty, false, and zero values.
|
||||
|
||||
CommandKit keeps parsed values, collections, and presence from one invocation out
|
||||
of subsequent invocations. Storage allocation and reuse are implementation
|
||||
choices. Handler closures and application state retain their own lifecycle;
|
||||
concurrent dispatch is a separate contract.
|
||||
|
||||
The invocation API supplies context and streams separately from CLI inputs.
|
||||
CommandKit creates its declarations and bindings from metadata and defers its
|
||||
acquisition calls to the selected handler. The binding system accepts domain value
|
||||
parsers; domain interpretation and validation belong to registered callbacks.
|
||||
|
||||
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, prepare independent invocation inputs, apply shared parsing and
|
||||
operand checks, then invoke the typed handler. Return its invocation outcome to
|
||||
the application.
|
||||
|
||||
CommandKit classifies shared usage errors and standard-family outcomes. Its
|
||||
handler result contract accepts application-specific outcomes and preserves their
|
||||
supplied classification. Scriptorium output validation and Notarius artifact
|
||||
outcomes, for example, remain domain results. CommandKit translates results from
|
||||
explicit classification and emits usage guidance for usage 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.
|
||||
|
||||
### Contract scope and consumer guidance
|
||||
|
||||
This decision governs CommandKit's public contracts and implemented behavior.
|
||||
Each operation specifies the inputs, result semantics, callback lifetimes, and
|
||||
metadata its provider supplies. Consumers choose how to implement those callbacks
|
||||
and how to structure loading, execution, startup, and process status mapping.
|
||||
|
||||
Sharing acquisition and resolution between inspection and execution, and keeping
|
||||
acquisition focused on producing inspection views, are recommended practices.
|
||||
CommandKit supports those patterns through reusable inputs and lazy provider
|
||||
hooks. Its API preserves declared results and coordinates its own calls; arbitrary
|
||||
application internals remain application-owned.
|
||||
|
||||
Cobra isolation applies to CommandKit's exports and internal adapter. Independent
|
||||
consumer use of another CLI library remains an application choice. Additional
|
||||
families and source models can use capability contracts appropriate to their
|
||||
resources, with standard meanings preserved for operations that are installed.
|
||||
Construction, installation, help, version, and capability discovery use metadata
|
||||
without acquiring application state. The selected operation acquires what it
|
||||
needs after applicable input checks. Caller startup and callback effects retain
|
||||
their own lifecycle.
|
||||
|
||||
## 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 would need to describe its separate parsing and help contract. |
|
||||
| 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` for PromptKit | 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. CommandKit's invocation-state contract remains separate from caller-owned object lifetimes; objects are an application option. |
|
||||
| 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. |
|
||||
- Application dispatchers forwarding standard families would share handlers but
|
||||
leave custom-command parsing, root help, and diagnostics duplicated.
|
||||
- Raw-argument handlers or string-keyed input access would leave typed input
|
||||
assembly and presence handling with every consumer.
|
||||
- Reflection, struct tags, or generated bindings could shorten declarations but
|
||||
add another declaration language or toolchain.
|
||||
- Import-time registries or a separate integration registration framework would
|
||||
introduce hidden state or parallel assembly conventions.
|
||||
|
||||
## 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.
|
||||
Applications use one command system while retaining domain ownership. Explicit
|
||||
bindings and captured declarations require deliberate assembly, but integrations
|
||||
can supply complete families through the same public contracts.
|
||||
|
||||
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, independent invocation inputs, cancellation,
|
||||
and domain outcomes.
|
||||
CommandKit releases document intentional compatibility changes. Coordinated
|
||||
application documentation and subprocess-caller updates are recommended migration
|
||||
practices.
|
||||
|
||||
The [architecture policy](../policy/architecture.md) owns normative boundaries.
|
||||
[ADR 0002](0002-cobra-backend.md) selects the backend, and
|
||||
[ADR 0003](0003-public-facade.md) defines its public/internal boundary.
|
||||
[GoDoc](../../command.go) owns declaration, installation, and callback contracts;
|
||||
the [consumer guide](../consumers/guide.md) explains their use. Exact grammar,
|
||||
commands, and outcomes belong in the [CLI reference](../cli.md). Binding identity,
|
||||
absence handling, factory defaults, and operation-specific preparation timing
|
||||
can evolve within this architecture through deliberate API changes.
|
||||
|
||||
Reference in New Issue
Block a user