Adopt shared policies and record CommandKit architecture
This commit is contained in:
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