105 lines
5.8 KiB
Markdown
105 lines
5.8 KiB
Markdown
# ADR 0003: Own public contracts through a root facade and internal CLI adapter
|
|
|
|
## Status
|
|
|
|
Partially superseded by [ADR 0010](0010-optional-integration-modules.md).
|
|
|
|
ADR 0010 narrows the root facade and public-type ownership decision to the core
|
|
module and places optional library dependencies in integration modules. The
|
|
remaining principles continue to apply; this record's decision text is retained.
|
|
|
|
## Date
|
|
|
|
2026-10-06
|
|
|
|
## Context
|
|
|
|
CommandKit needs room to change its internal implementation details while
|
|
consumers retain access to a stable external API. Exposing backend assembly or
|
|
extension structures would let applications ossify around implementation
|
|
details that maintainers may later decide to 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.
|
|
|
|
The selection of Cobra as an internal dependency is compatible with this
|
|
decision. 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.
|
|
|
|
Straightforward implementation may live in the root package without expanding
|
|
its exports. Introduce internal packages, interfaces, and conversions where they
|
|
simplify implementation or protect the backend boundary. Package organization and
|
|
the amount of internal layering follow implementation needs. 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. CommandKit does
|
|
not expose its internal backend objects or backend-specific syntax errors through
|
|
its public API, including through loosely typed values or returned errors.
|
|
Caller-supplied values and errors retain their documented handling; this boundary
|
|
does not require inspecting or transforming arbitrary application objects.
|
|
|
|
Standard contracts such as `context.Context` and `io.Writer` remain appropriate.
|
|
Public types from integration libraries, such as ConfigKit and PromptKit, may
|
|
be selected as deliberate 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 remain independent of backend structures.
|
|
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.
|
|
|
|
Preserve independent parsed state per invocation under
|
|
[ADR 0001](0001-command-dispatch-and-inputs.md). Factories, callbacks, and shared
|
|
representations remain API and implementation design choices.
|
|
|
|
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. Any 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. |
|
|
| Require a forwarding-only root and separate internal representations | Enforces a uniform layering scheme. It can add duplicate types and callback plumbing before those mechanisms simplify implementation. |
|
|
| 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.
|
|
|
|
Forwarding and conversion add maintenance where used. Proportionate checks should
|
|
protect the public surface, backend isolation, and consumer behavior through the
|
|
real adapter. The implementation determines which checks provide useful evidence;
|
|
package relocation alone does not establish those properties.
|
|
|
|
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.
|
|
[ADR 0001](0001-command-dispatch-and-inputs.md) records the dispatcher/input model,
|
|
and [ADR 0002](0002-cobra-backend.md) selects the initial backend.
|