Adopt shared policies and record CommandKit architecture
This commit is contained in:
179
docs/adr/0001-command-dispatch-and-inputs.md
Normal file
179
docs/adr/0001-command-dispatch-and-inputs.md
Normal file
@@ -0,0 +1,179 @@
|
||||
# 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.
|
||||
95
docs/adr/0002-cobra-backend.md
Normal file
95
docs/adr/0002-cobra-backend.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# ADR 0002: Use Cobra and pflag as the initial internal CLI backend
|
||||
|
||||
## Status
|
||||
|
||||
Accepted.
|
||||
|
||||
## Date
|
||||
|
||||
2026-10-06
|
||||
|
||||
## Context
|
||||
|
||||
CommandKit's selected dispatcher model needs nested routing, flags and operands,
|
||||
generated help, usage diagnostics, and invocation execution. Reimplementing this
|
||||
generic infrastructure would add work beyond the suite-specific inspection and
|
||||
consistency behavior that motivates the library.
|
||||
|
||||
Cobra and urfave/cli v3 were the finalists. The latter's destination bindings and
|
||||
duplicate controls offered a close fit for the explicit-input model. The author
|
||||
strongly preferred Cobra because of its broader adoption. Familiarity and the
|
||||
availability of contributors who know the framework are useful maintenance
|
||||
considerations across several related applications.
|
||||
|
||||
The selection was based on documentation and API review. Ecosystem adoption is
|
||||
the selection rationale, not evidence that every backend default is an appropriate
|
||||
CommandKit contract.
|
||||
|
||||
## Decision
|
||||
|
||||
Use Cobra with pflag as the sole initial CLI backend. CommandKit translates its
|
||||
registrations into backend command trees, flag bindings, and execution callbacks.
|
||||
Reuse their routing, option parsing, help, context, and stream mechanisms and add
|
||||
adaptation where selected CommandKit semantics require it.
|
||||
|
||||
CommandKit retains its own public registration API, typed inputs, standard
|
||||
families, and observable contracts. Applications do not construct Cobra commands
|
||||
or use pflag getters in handlers. [ADR 0003](0003-public-facade.md) isolates the
|
||||
backend behind an internal adapter and root-owned public contracts.
|
||||
|
||||
Applications retain process lifecycle and domain execution. ConfigKit and
|
||||
PromptKit retain configuration and inspection semantics. Framework selection
|
||||
does not transfer those responsibilities to Cobra or CommandKit.
|
||||
|
||||
Build fresh backend command/flag state and input storage per invocation. A fresh
|
||||
command tree constructed from registered definitions is the initial approach.
|
||||
Tree construction must not load configuration or create application runtime.
|
||||
|
||||
Implement one concrete backend. There is no initial public backend selector or
|
||||
pluggable framework. A future replacement can change internal implementation
|
||||
while preserving the public API and selected behavioral contracts.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
| Alternative | Advantages | Reason not selected |
|
||||
| --- | --- | --- |
|
||||
| urfave/cli v3 | Destination bindings for flags and named operands, presence queries, and duplicate controls fit the chosen input model closely. | Credible alternative; Cobra's broader adoption is the preferred maintenance tradeoff. |
|
||||
| pflag alone | Established parsing with a smaller integration surface. | Leaves more routing, help integration, and command infrastructure to implement. |
|
||||
| Kong | Compact typed-struct and tag-driven declarations. | Its main declaration model differs from the explicit-binding API; adaptation reduces its benefit. |
|
||||
| Standard `flag` alone | Familiar variable binding and custom value parsing. | First-operand stopping requires adaptation for the intended syntax, with more custom command infrastructure. |
|
||||
| Expose Cobra as the public API | Less adapter code and the full framework surface. | Couples consumers to backend structures and framework-specific configuration. |
|
||||
| Interchangeable backends immediately | Future integration flexibility. | Adds abstraction and behavior surfaces without a current requirement. |
|
||||
|
||||
## Consequences
|
||||
|
||||
The framework supplies established generic CLI machinery. CommandKit still owns
|
||||
registration conflicts, reserved families, typed bindings and presence, standard
|
||||
help and diagnostics, provider integration, and selected reports and outcomes.
|
||||
|
||||
The adapter must accommodate explicit empty/false/zero values, fresh state,
|
||||
selected repetition and operand rules, and help without configuration. Backend
|
||||
features do not expand the standard automatically: for example, completion
|
||||
commands remain deferred unless deliberately selected.
|
||||
|
||||
Backend error handling must preserve application outcomes, avoid duplicate
|
||||
diagnostics, and return control to the application's entry point. Native YAML
|
||||
inspection and multi-document scope display remain integration work using the
|
||||
owning libraries. Choosing a framework introduces no JSON report requirement.
|
||||
|
||||
Exact dependency versions and attribution are selected when dependencies are
|
||||
incorporated. Meaningful integration evidence must exercise the real adapter
|
||||
against selected contracts. A dependency update or replacement can change CLI
|
||||
behavior even with unchanged Go signatures and is subject to compatibility policy.
|
||||
|
||||
The [architecture policy](../policy/architecture.md) owns normative boundaries;
|
||||
the [backend roadmap](../roadmap/03-backend.md) owns implementation tasks and status.
|
||||
[ADR 0001](0001-command-dispatch-and-inputs.md) records the dispatcher and input
|
||||
model. Remaining command and grammar proposals belong to the CLI roadmap.
|
||||
|
||||
Reviewed primary references from the selection:
|
||||
|
||||
- [Cobra API](https://pkg.go.dev/github.com/spf13/cobra).
|
||||
- [Cobra command and help guide](https://github.com/spf13/cobra/blob/main/site/content/user_guide.md).
|
||||
- [Cobra flag bindings](https://cobra.dev/docs/how-to-guides/working-with-flags/).
|
||||
- [pflag syntax and extension points](https://github.com/spf13/pflag).
|
||||
- [urfave/cli v3 API](https://pkg.go.dev/github.com/urfave/cli/v3).
|
||||
109
docs/adr/0003-public-facade.md
Normal file
109
docs/adr/0003-public-facade.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# ADR 0003: Own public contracts through a root facade and internal CLI adapter
|
||||
|
||||
## Status
|
||||
|
||||
Accepted.
|
||||
|
||||
## Date
|
||||
|
||||
2026-10-06
|
||||
|
||||
## Context
|
||||
|
||||
CommandKit needs room to change implementation while consumers retain stable
|
||||
registration, input, and invocation contracts. Exposing backend assembly or
|
||||
extension structures would let applications depend on details that maintainers
|
||||
may later replace.
|
||||
|
||||
ConfigKit, PromptKit, and ReleaseKit demonstrate facades that own public types and
|
||||
delegate through private state and explicit conversion. ConfigKit also translates
|
||||
upstream YAML parser structures into an independent internal document model.
|
||||
ReleaseKit has a second facade for a distinct lightweight runtime responsibility;
|
||||
CommandKit has no corresponding requirement for another public package initially.
|
||||
|
||||
The Cobra selection is compatible with dependency isolation. Public API
|
||||
independence needs a genuine contract boundary, and keeping backend structures
|
||||
out of shared internal representations also makes future replacement easier.
|
||||
|
||||
## Decision
|
||||
|
||||
Use the module root `commandkit` as the sole initial supported public facade.
|
||||
Define CommandKit's public named types there and expose only selected operations,
|
||||
extension contracts, and documented methods. Handles keep implementation state in
|
||||
private fields.
|
||||
|
||||
Implementation packages belong under Go's `internal/` boundary. Public code
|
||||
forwards inward with explicit wrappers and conversions; internal production
|
||||
packages do not import the root. Shared internal contracts live inside that
|
||||
boundary. Additional public packages require a deliberate decision based on a
|
||||
durable consumer responsibility.
|
||||
|
||||
Do not alias public types to internal or backend types, anonymously embed those
|
||||
types to promote methods, or expose implementation bridge methods. Exported
|
||||
fields, signatures, variables, callbacks, interface methods, constraints, and
|
||||
method sets must not reveal internal or Cobra/pflag structures. Loosely typed
|
||||
values and returned errors must not provide indirect backend-object access.
|
||||
|
||||
Standard contracts such as `context.Context` and `io.Writer` remain appropriate.
|
||||
ConfigKit or PromptKit public types may be selected as deliberate integration
|
||||
dependencies. Application input types remain application-owned. Go declarations
|
||||
and GoDoc own exact exported contracts.
|
||||
|
||||
Confine Cobra/pflag types and operations to an internal adapter. Registry logic,
|
||||
standard handlers, and shared definitions use CommandKit-owned representations.
|
||||
The adapter translates paths, descriptions, bindings, presence, callbacks, and
|
||||
selected help/outcome semantics into backend machinery. Do not reproduce Cobra's
|
||||
complete API through renamed wrappers.
|
||||
|
||||
Typed input factories and handlers can cross the facade through private callbacks
|
||||
without internal packages importing public declarations. Preserve fresh input and
|
||||
backend state per invocation. Introduce narrow internal interfaces where actual
|
||||
engine/adapter interaction justifies them; exact package names and splits remain
|
||||
implementation choices.
|
||||
|
||||
Use one concrete backend initially. There is no public backend interface, plugin
|
||||
registry, or requirement for a second implementation. Replacement is a future
|
||||
maintainer change.
|
||||
|
||||
Translate backend-owned syntax failures into CommandKit diagnostics and outcomes.
|
||||
Preserve useful public identity for application handler errors and cancellation
|
||||
according to the invocation contract. Explicit conversions must preserve value
|
||||
ownership, callback lifetime, presence, and supported error semantics.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
| Alternative | Analysis |
|
||||
| --- | --- |
|
||||
| Keep all machinery in the public root | Less forwarding, but harder consumer navigation and easier accidental public expansion. |
|
||||
| Alias or embed implementation types | Less conversion code, but couples public type identity, fields, or method sets to internal choices. |
|
||||
| Hide Cobra only behind the public facade | Hides types from applications but allows backend structures to spread through internal logic, complicating replacement. |
|
||||
| Mirror Cobra's API with wrappers | Removes dependency names while preserving its conceptual coupling and compatibility surface. |
|
||||
| Public packages for registry, bindings, backend, and other mechanisms | Adds supported imports and assembly contracts without a distinct consumer responsibility. |
|
||||
| Selectable/pluggable backends immediately | Adds selection and extension machinery before a concrete need exists. |
|
||||
|
||||
## Consequences
|
||||
|
||||
Consumers depend on deliberate root contracts while internal algorithms,
|
||||
collaborators, and package boundaries can evolve. Useful application registration
|
||||
capabilities still need a proportionate API; minimizing accidental exports must
|
||||
not recreate repetitive consumer infrastructure.
|
||||
|
||||
Explicit forwarding and conversion require maintenance. Type identity, ownership,
|
||||
method sets, callback lifetime, and error semantics must be protected by evidence,
|
||||
not inferred from package relocation.
|
||||
|
||||
Compiled public API checks protect aliases, fields, signatures, constraints, and
|
||||
promoted methods. Import checks keep the backend in its adapter and internal
|
||||
production code independent of root. External-package tests protect consumer
|
||||
semantics through the real adapter. ConfigKit and ReleaseKit provide precedents
|
||||
for these checks without requiring their full validation workflows.
|
||||
|
||||
Backend replacement can preserve the public Go API but must also preserve selected
|
||||
CLI behavior or follow the release policy for intentional incompatibilities.
|
||||
Backend defaults do not independently establish new CommandKit contracts.
|
||||
|
||||
The [architecture policy](../policy/architecture.md) owns normative boundaries.
|
||||
The [facade roadmap](../roadmap/04-facade.md) owns implementation status, package
|
||||
layout illustrations, and pending work. [ADR 0001](0001-command-dispatch-and-inputs.md)
|
||||
records the dispatcher/input model, and [ADR 0002](0002-cobra-backend.md) selects
|
||||
the initial backend. The CLI roadmap retains individual command approval status.
|
||||
Reference in New Issue
Block a user