2026-10-10 16:19:45 +00:00
# ADR 0011: Capture command declarations and install optional families explicitly
## Status
Accepted. Refines [ADR 0001 ](0001-command-dispatch-and-inputs.md ) for declaration
construction, installation, optional-family namespace reservation, and integration
input/state lifetimes. Partially supersedes
[ADR 0010 ](0010-optional-integration-modules.md ) only for its consumer-migration
consequence during the unreleased prototype transition, and refines its
convenient composition principle.
2026-10-11 18:59:57 +00:00
Refined by [ADR 0012 ](0012-typed-configuration-comparison-pairs.md ) for
two-selection configuration comparison and its input/state lifetime. The
single-selection lifecycle and other decisions remain in force.
2026-10-10 16:19:45 +00:00
## Date
2026-10-10
## Context
2026-10-11 18:59:57 +00:00
Applications declare domain commands, while optional integrations assemble
standard command families. A common construction and installation model reduces
repeated error checks and registration plumbing. Integrations need public typed
composition contracts to assemble those families while preserving core and
backend boundaries.
Declaration and installation have different responsibilities. An optional family
claims a namespace when installed; selecting a dependency alone should leave the
application's command surface unchanged. During execution, scope-specific CLI
inputs and application-owned shared state also have distinct lifetimes. Making
those lifetimes explicit avoids accidental aliasing between selected scopes.
At the time of this decision, the implementation was an unreleased prototype
with no downstream consumers. Its API shapes carried no compatibility obligations
that would constrain the selected design.
2026-10-10 16:19:45 +00:00
## Decision
2026-10-11 18:59:57 +00:00
### Declaration capture and reuse
2026-10-10 16:19:45 +00:00
Applications and integrations construct the same opaque core command declarations
and install them through core application construction or registration. Initial
2026-10-11 18:59:57 +00:00
integrations provide complete-family factories and focused conveniences through
that common installation API. Suite and provider types remain integration-owned.
Factories eagerly capture metadata, bindings, capability selections, selector
applicability, and callback references. They copy the declaration collections
needed to isolate returned commands from later edits to source metadata.
Construction errors remain attached for installation to report. This model uses
eager declarations with public typed composition; a deferred preparation lifecycle
and untyped integration payload are outside the selected API.
Declarations may be reused across applications with independent registry and
invocation state. Callback references and captured application objects retain
caller ownership. Reuse provides no deep-copy or concurrent-dispatch guarantee.
Application helpers performing other fallible work may still return errors.
### Installation and retained errors
Core validates each installation batch before registry mutation, checking
2026-10-10 16:19:45 +00:00
retained errors, bindings, placement, and collisions. Failed application
2026-10-11 18:59:57 +00:00
construction returns no usable application; failed later registration preserves
the existing registry. Atomicity covers one installation call. Separate calls
and arbitrary callback effects retain their own lifetimes.
Report retained errors with useful command context and preserve supported wrapped
error identity. The aggregate-error representation remains an implementation
choice.
An explicit absence declaration contributes no command, help entry, or namespace.
Installation checks attached errors before skipping it. A zero command declaration
remains invalid. Integrations define their own empty-suite semantics: the initial
configuration suite needs at least one named scope, and metadata-only scopes
remain discoverable. A definition suite with no capabilities contributes nothing.
Binding validation applies to generated command declarations. Unused selector
bindings in an absent family are ignored; explicitly retained adapter-construction
errors still reach installation. A nil loader supplied to an adapter remains
invalid wiring even if its capabilities are subsequently cleared.
### Namespace ownership and placement
Core construction reserves help and version. An optional standard family owns
its complete root namespace upon installation. Imports and absent declarations
claim nothing. An application may occupy an uninstalled family name; installing
a conflicting declaration then fails in either registration order. Installation
preserves existing handlers and group ownership. Private application operations
remain outside installed standard families.
Standard families carry root-only placement metadata, which core enforces when
they are nested beneath another group. Placement is a public declaration property;
its enforcement is independent of package identity or a list of integration names.
A real empty root-only group claims its namespace and obeys placement checks.
Explicit absence contributes no node to place.
### Input and shared-state lifetimes
2026-10-10 16:19:45 +00:00
Integration suites distinguish typed CLI input from typed shared acquisition
2026-10-11 18:59:57 +00:00
state. Ordinary configuration inspection initializes one input per selected scope,
applies its applicable selector bindings, and initializes shared state once for
the inspection invocation. Input validation receives the scope identity and its
typed input. Named definition inspection uses one input and one shared state.
Providers receive both explicitly. ADR 0012 refines this lifecycle for comparison
pairs with two selections of one scope.
2026-10-10 16:19:45 +00:00
Availability preflight and applicable input checks precede shared-state
2026-10-11 18:59:57 +00:00
initialization and acquisition. Failures may stop later input factories.
Construction, installation, help, version, and scope discovery leave suite
input/state factories and acquisition providers uninvoked.
An omitted factory supplies its type's zero value, including nil pointers.
Applications provide initialization needed by their accessors and providers;
providers may intentionally accept nil. Allocation of application objects,
cloning, and aliases returned by factories remain caller responsibilities.
Core-owned parsed collections stay independent when applied to separate inputs.
Caller-owned objects and callback effects retain their own lifetimes.
### Typed binding composition
Core provides a narrow typed contract to capture converted values and apply a
selected binding subset to initialized input. It preserves binding identity,
defaults, and presence while keeping private setters, parser state, and backend
objects behind their existing boundaries. A copied binding retains its identity;
a newly constructed lookalike has its own identity.
Execution-required checks apply to the selected subset. Missing required input
is a usage failure; invalid binding selection or destination is a wiring failure
with operational classification at invocation. These categories remain identifiable
through supported wrapping and stop execution before shared-state initialization
and acquisition. Metadata defects detectable during assembly of generated
commands remain installation errors under the validation boundary above.
2026-10-10 16:19:45 +00:00
### Relationship to earlier decisions
2026-10-11 18:59:57 +00:00
For the unreleased prototype transition covered by this decision, compatibility
wrappers and a downstream migration guide were unnecessary. This exception to
ADR 0010's migration consequence is limited to that transition. Changes to
published contracts follow the [release policy ](../policy/release.md ).
ADR 0001 retains dispatcher, registry, typed-input, and application-ownership
principles. ADR 0010 governs module boundaries and dependency direction, with
[ADR 0003 ](0003-public-facade.md ) governing deliberate exports and backend isolation
within that scope. ADRs 0004 through 0009 retain their operation, grammar, report,
and outcome decisions. Go declarations and GoDoc own exact implemented signatures.
2026-10-10 16:19:45 +00:00
## Alternatives considered
| Alternative | Analysis |
| --- | --- |
| Fallible constructors plus integration-specific registration helpers | Makes each construction failure immediate, but creates several assembly conventions and repeated consumer checks. Captured errors keep one installation boundary. |
| Deferred preparation callbacks | Allows installation to construct a family, but adds lifecycle and error timing beyond eager capture. No demonstrated assembly need requires it. |
| Reserve all known optional family names at core construction | Prevents later name conflicts, but consumes namespaces for integrations the application has not selected. Installed-family ownership keeps conflicts explicit without that restriction. |
| Separate family/plugin registration framework | Could encode ownership and composition, but a captured command with root-only placement and batch validation expresses the required behavior without another installation model. |
| One shared input/state object | Uses fewer type parameters and can share a cache through references in separate input values, but leaves the distinct lifetimes implicit and scope-input isolation to application conventions. Explicit input and state types make those lifetimes visible. |
| Clone hooks or automatic pointer allocation | Can hide input construction, but adds object-graph and initialization responsibilities that belong to the application. Explicit factories keep those choices visible. |
| Public binding getters/setters or an untyped value bag | Enables integration assembly, but exposes private mechanics or returns typed-input assembly to string lookup. Narrow typed composition addresses the actual need. |
## Consequences
2026-10-11 18:59:57 +00:00
Consumers assemble domain commands and optional families through one installation
boundary. Eager assembly errors reach that boundary before provider acquisition.
Namespace collisions remain explicit choices for the application to resolve.
Separate input and state types make initialization and sharing visible. Focused
conveniences support simple applications, while typed composition gives
integrations the public contracts needed to assemble their families.
The [architecture policy ](../policy/architecture.md ) owns normative boundaries;
roadmaps own implementation status and sequencing. Application loading, cache
design, startup, domain validation, callback effects, and process exit remain
consumer responsibilities.