Simplify ADRs and relax premature constraints

This commit is contained in:
2026-10-07 02:45:09 +00:00
parent 984d159676
commit 7e453a2df2
9 changed files with 165 additions and 310 deletions

View File

@@ -10,19 +10,19 @@ Accepted.
## Context
CommandKit needs room to change its internal implementation details while
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
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
decision. Public API independence needs a genuine contract boundary, and
keeping backend structures out of shared internal representations also makes
future replacement easier.
## Decision
@@ -32,38 +32,39 @@ 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.
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. Loosely typed
values and returned errors must not provide indirect backend-object access.
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.
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 use CommandKit-owned representations.
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.
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.
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. Explicit conversions must preserve value
according to the invocation contract. Any conversions must preserve value
ownership, callback lifetime, presence, and supported error semantics.
## Alternatives considered
@@ -71,6 +72,7 @@ ownership, callback lifetime, presence, and supported error semantics.
| 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. |
@@ -84,15 +86,10 @@ collaborators, and package boundaries can evolve. Useful application registratio
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.
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.