Files
commandkit/docs/policy/architecture.md

373 lines
22 KiB
Markdown
Raw Normal View History

# Architecture Policy
**Base Policy Version:** 1.3
**Last Updated**: 2026-11-06
**Upstream Source:** [architecture.md](https://gitea.maximumdirect.net/eric/policy/src/commit/d84a38ebab26f048cafbe59a2246700c35d29843/docs/policy/architecture.md)
## Purpose And Ownership
CommandKit is an opinionated Go library for standard inspection commands,
consistent CLI syntax, and reduced application command infrastructure. It provides
useful operator access to capabilities owned by its integrated libraries through
shared command vocabulary, registration, selection, coordination, presentation,
and invocation outcomes. This policy owns normative responsibility boundaries,
dependency direction, and architectural constraints. ADRs own decision rationale; the
[implementation roadmaps](../development.md#task-specific-reading-guide) own
proposals, sequencing, and implementation status.
Architectural constraints govern future implementation. Concrete package
inventory and mechanics belong in internal documentation when implemented.
Exact exported contracts belong in Go declarations and GoDoc; implemented
operator behavior belongs in the CLI reference. Do not describe planned packages
or APIs as existing implementation.
## Ownership Boundaries
Applications and integration handlers choose the operation and report content;
CommandKit core supplies the shared CLI mechanics. Integrated libraries retain
ownership of their domain behavior. These boundaries govern current and planned
capabilities; they do not imply that a planned API is implemented.
| Area | CommandKit Owns | CommandKit Does Not Own |
| --- | --- | --- |
| **CLI output — core** | Format selection, serialization, template execution, and output delivery | Choosing report content or detail level; authoring application-specific text templates |
| **PromptKit/ConfigKit integrations** | Standard commands, flags, and report content that expose useful operator tasks through upstream public APIs | Helpers or convenience interfaces for application-internal use of upstream APIs |
| **CLI inputs** | CLI syntax, parsing, typed bindings, presence tracking, and usage diagnostics | Application business rules or upstream domain-validation semantics |
| **Upstream behavior** | Selecting and invoking supported capabilities, adapting their results, and preserving their meaning | Reimplementing upstream configuration resolution, prompt preparation, validation, or other domain algorithms |
| **Outcomes and errors — core** | Carrying and presenting selected outcomes; handling CommandKit's own parsing, rendering, and delivery failures | Deciding application recovery policy or terminating the process |
| **Application lifecycle** | Command dispatch and invocation reporting lifecycle | Application startup, engine construction, credential provisioning, signal handling, or long-lived application state |
## Success Criteria
Evaluate designs and adoption against both criteria:
1. Applications adopt consistent command vocabulary, syntax, and inspection
behavior for the standard suite.
2. Applications remove substantial repeated CLI infrastructure while keeping
clear ownership of domain behavior.
Existing applications supply evidence of needs, not compatibility requirements
for CommandKit. Adoption is expected to make breaking changes to divergent
consumer conventions. Once CommandKit publishes its own contracts, changes to
those contracts follow the [release policy](release.md).
Use representative simple and complex consumers to substantiate API designs.
Replacing parsing and dispatch plumbing with equally repetitive registration or
callback bookkeeping does not satisfy infrastructure reduction. Keep enough
flexibility to express genuine application requirements within one command model.
## Go Types And Facilities
CommandKit is a Go library for Go applications. Its APIs and implementation use
ordinary Go types, language features, and standard-library facilities. Their
documented semantics and representational limits are accepted design constraints.
CommandKit does not introduce substitute type systems or compatibility machinery
merely to circumvent those limits.
Ordinary parsing, application-defined domain types, and necessary serialization
adapters remain appropriate. Preserve values that the selected Go types can
represent; accepting their limits does not justify accidental rounding or
truncation during conversion. Keep CLI boundary conversions small and focused,
without turning them into general-purpose type-conversion frameworks.
Specific numeric parsing and report serialization contracts remain separate.
A CLI parser's selected numeric types do not establish a blanket restriction on
application-supplied values or silently redefine their serialization contracts.
GoDoc and CLI documentation own implemented behavior; roadmaps own pending work.
## Command And Application Ownership
[ADR 0001](../adr/0001-command-dispatch-and-inputs.md) records the selected
dispatcher, captured declarations, installation boundary, and typed-input model.
CommandKit owns dispatch, common parsing, presence tracking, help, shared syntax
diagnostics, and the handlers and CLI contracts of its standard families.
The integrated libraries own the domain semantics exposed through those handlers.
Applications register domain commands outside those families and own their
meaning, effects, domain validation, artifacts, and operational outcomes.
Namespaces provide the ownership boundary. Core construction reserves help and
version. Optional standard families, including config and promptkit, own their
root namespace when installed; importing an integration or declaring an absent
family claims no namespace. An application may occupy an uninstalled family name.
Installing the standard family then reports a collision, as does application
registration after the standard family is installed. Neither order merges groups
or replaces handlers. Installed standard families cannot acquire private
application operations beneath them. Standard-family declarations are root-only;
nesting a real family beneath another group reports an assembly error.
Applications own their executable entry points, context creation, signals,
stream selection, runtime construction, and final process exit. CommandKit
returns invocation outcomes and must not terminate the process. Its common
mechanics apply to custom commands without taking ownership of their domain
semantics or report content.
CommandKit's grammar applies to commands using its dispatcher and binding APIs.
Its documentation recommends the same grammar and vocabulary for application-owned
commands, including those outside its dispatcher. The
[CLI reference](../cli.md#registered-command-syntax) owns implemented grammar.
Use explicit instance construction and composable command registration, not
package-global or import-time registries. Applications and integrations supply
captured canonical command declarations through the core installation API.
Declaration factories capture metadata, bindings, and callback references eagerly;
construction errors remain attached for installation to report. Capture the
metadata collections needed to keep later edits to a source declaration from
changing a returned command. Application callback objects retain their ownership.
Canonical command declarations own handlers, parsing metadata, and help
metadata. Applications may provide separate routing rules in the same
installation batch; these rules resolve invocations to canonical commands and
do not add command nodes, handlers, or flags.
Validate each installation batch before mutating the instance registry. A failed
construction yields no usable application; a failed later registration leaves the
existing registry intact. Separate successful registration calls are not one
transaction. Duplicate canonical paths and attempts to replace reserved commands
report errors rather than letting registration order select a winner. Identical
routing rules are idempotent; competing targets for the same source and scope
report an error. Command-scoped flag routing refers to an exact canonical
command path.
Explicit absent-command declarations contribute no commands, help entries, or
namespace claims; attached errors are reported before skipping absence. An invalid
zero declaration is not absence. Integrations define their own empty-suite
semantics and retain invalid wiring errors even when capabilities are narrowed.
Standard families are assembled from declared provider capabilities. Each family
specifies its own resources and supported operations; shared verbs retain
consistent meanings across families. Configuration scopes describe their supported
inspection operations. CommandKit installs the supplied handlers and metadata and
reports actual availability through help and discovery. Missing upstream
capabilities leave other supported operations available.
Help and version use identity/build metadata. CommandKit's registration and
metadata paths leave acquisition callbacks uninvoked. Help and version remain
available with missing or invalid configuration. Callers retain their startup and
callback effects. Version reporting defaults to application identity; explicit
component selection reports embedded library identity.
These rules govern CommandKit's own API, dispatcher, and standard handlers.
Provider contracts define required inputs and results for enabled operations;
callback internals and application execution remain consumer choices. Shared
inspection/execution loading and focused acquisition callbacks are recommended
integration practices. Independent consumer use of other CLI frameworks remains
application-owned.
## Invocation Reporting And Outcomes
Use one reporting facility per invocation for command results, immediate messages,
and deferred messages. Core owns shared visibility, formatting, delivery, and
finalization. Applications and integrations choose their report content and
messages; callers choose the destinations.
[ADR 0006](../adr/0006-invocation-reporting.md) records this reporting and outcome
ownership model. Its accepted structured-report extension remains planned in the
[adoption roadmap](../roadmap/promptkit-v0.20.0.md#shared-core-report-output-prerequisite).
For that shared report handoff, core owns format selection, serialization,
template execution, and delivery. Applications and integrations select logical
report content and detail level, and author any report-specific text templates.
Changing format must not implicitly change that content or detail level.
Representations and encoders are core implementation choices; commands should
not maintain parallel rendering pipelines or a separate report tree per format.
Use ordinary Go report values and thin projections where upstream data needs
adaptation. Prefer standard serialization and template facilities to a new public
document model, tag convention, or template language. Shared rendering serves
both invocation reporting and direct writer use, while invocation delivery keeps
the existing visibility, outcome, and presentation-failure lifecycle. Exact
serialization conventions, format options, and API signatures belong in the
roadmap until implemented, then in their API and CLI contracts.
Applications decide how to respond to domain-specific errors. CommandKit may
provide shared helpers for carrying, inspecting, and presenting errors while
leaving those decisions with the application. Supplying an error cause alone
does not select an outcome or generate an operator-facing message. CommandKit
handles its own errors, including routing, parsing, and presentation failures.
Preserve application-defined domain classifications and supplied error identity.
Report rendering and delivery failures separately from the command's explanation
and cause, applying the [CLI reporting contract](../cli.md#presentation-failures).
Intentionally suppressed output is not a delivery failure. Present terminal
failure explanations after normal reporting finishes, avoid duplicate presentation
through core's own paths, and retain the explanation and any supplied cause in
the returned outcome.
Keep reporting state scoped to the invocation's presentation and returned outcome.
Logging and message history remain application concerns.
## Handler Inputs And Invocation State
Handlers receive typed input structs with explicit flag and operand bindings.
Use presence-aware fields where omission matters; preserve explicitly supplied
empty, false, and zero values independently of parser defaults. Context, streams,
and execution dependencies are separate from CLI inputs. Integration suites keep
their typed CLI input separate from shared acquisition state. For a configuration
operation, initialize and populate each selected scope's input independently,
applying only its applicable selector bindings; initialize shared state once for
the selected inspection invocation. Configuration input validation receives the
selected scope identity together with its applicable typed input. Named definition
inspection uses one input and one shared state. Complete applicable preflight
and input checks before shared state initialization and provider acquisition.
Factories need not run after a failure and remain uninvoked during construction,
installation, help, version, and scope discovery.
For two-selection configuration comparison, prepare and check each side's input
separately with its scope identity and selection, applying the same applicable
converted selector values to both. Finish both checks before initializing shared
state once and invoking the integration-owned pair provider. The
[consumer guide](../consumers/guide.md#configkit-scopes) and linked GoDoc own the
provider lifecycle; the [CLI reference](../cli.md#compare-configuration-selections)
owns current selector behavior. Required provider wiring follows declared
capabilities; a comparison-only scope does not require an unrelated ordinary
acquisition callback.
Omitted input and state factories supply the respective type's zero value,
including nil for pointers. Caller factories and callbacks retain responsibility
for objects they return or reference; do not promise arbitrary deep copies,
repair caller-created aliases, or silently allocate arbitrary pointer types.
Core-owned parsed collections stay independent where values are applied to
separate inputs.
Provide typed binding composition through supported public core contracts when
integrations need to apply captured converted values to selected inputs. Preserve
binding identity and presence without exposing private setters or backend state.
Required-value checks apply to the selected binding subset. Missing required
operator input is a usage failure; invalid binding selection or destination is a
wiring failure with operational classification when detected during invocation.
Metadata defects detectable during assembly remain installation errors. Input
composition does not create a second parser or a raw-argument handler model.
CommandKit keeps parsed values, collections, and presence from one invocation out
of subsequent runs. Storage allocation and safe reuse are implementation choices;
the [internal overview](../internal/overview.md#internalcobra) describes the
current backend lifecycle. Caller-owned closures and runtime state
retain their own lifecycle. Concurrent dispatch is a separate contract.
Applications may interpret parsed domain values and provide domain value parsers
through the binding system. Raw-argument parsing is not the primary handler model.
Keep input declarations available without application initialization.
## Public Facade And Dependency Direction
[ADR 0004](../adr/0004-optional-integration-modules.md) selects separate optional
integration modules with their own public package contracts. The facade and
backend isolation principles in
[ADR 0002](../adr/0002-public-facade-and-cli-backend.md) apply within those module
boundaries.
The core owns command declarations, registration, bindings, dispatch, help/version,
shared syntax, reporting mechanics, diagnostics, and outcomes. Each integration
owns its standard handlers, provider contracts, report content, adapters, and
integration-specific public types. Integrations expose operator-facing CLI tasks;
they do not provide convenience interfaces for application-internal upstream APIs.
Dependencies point from integrations toward the core; an integration may
depend on another integration when its responsibility requires that relationship.
Document those dependencies. Core public signatures, implementation imports, and
module requirements remain independent of optional integration libraries,
including dependencies introduced through tests, examples, or tools.
Public handles retain private implementation state and expose explicitly selected
methods. Any forwarding and conversions must preserve type identity, value
ownership, callback lifetime, presence, and supported public error semantics. Do
not alias public types to internal or backend types, anonymously embed those types
to promote methods, or expose implementation bridge methods.
Straightforward implementation may live in an owning public 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.
Public signatures, fields, callbacks, constraints, and method sets must not expose
internal or Cobra/pflag types. 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.
Upstream public types may be selected as deliberate dependencies in the
integrations that own them; that permission does not justify optional library
dependencies in the core. Core owns shared presentation categories,
and integrations adapt upstream diagnostics while preserving selected outcome
and error semantics. Application-owned input types remain application-owned.
Expose narrow core composition contracts for demonstrated integration needs,
without exporting private assembly or backend structures.
## Backend And Library Integration
[ADR 0002](../adr/0002-public-facade-and-cli-backend.md) selects Cobra with pflag
as the sole initial backend. Their types and operations stay inside an internal
adapter that translates CommandKit-owned definitions into backend machinery.
Registry logic, standard handlers, and shared internal representations remain
independent of backend-specific structures.
Reuse backend routing and parsing mechanisms, adapting them to selected
CommandKit semantics. Do not replicate Cobra's entire API through renamed
wrappers. Introduce narrow internal interfaces where actual interactions justify
them; a public backend selection or plugin framework is not part of the design.
ConfigKit and PromptKit own configuration composition, provenance, inspection
semantics, secret classification, and their native inspection representations.
Applications provide schemas, acquisition, domain resolution and validation,
and construction of inspection providers. CommandKit exposes those capabilities through supported
upstream APIs. The public contract of the adopted library version governs their
meaning and disclosure rules. When CommandKit adopts an upstream revision,
changes to those semantics flow through its integration. Adaptation and
documentation follow the adopted contract; descriptions of upstream behavior
do not establish a separate, fixed CommandKit specification of that behavior.
CommandKit owns presentation and may summarize or transform returned safe data
for different operator needs. Reports can evolve in format, organization, and
detail while respecting upstream disclosure rules and the evidence supplied by
the capability. Exact report contracts belong in the CLI reference when
implemented; changes to CommandKit's published contracts follow the release
policy.
Use independently released public contracts; do not import sibling internal
packages or depend on adjacent checkouts.
Use narrow integration contracts, reusing upstream public types where useful and
introducing CommandKit-owned abstractions where they simplify integration.
Concrete integration needs guide provider representations under
[ADR 0003](../adr/0003-configuration-scopes.md).
Use upstream safe inspection data to preserve owning-library meaning and
disclosure rules under [ADR 0004](../adr/0004-optional-integration-modules.md).
Do not reconstruct inspection reports from mutable runtime configuration or source
files when the upstream inspection API supplies captured, safe evidence. Explicit
adaptation into ordinary report values must preserve the selected evidence and
report conversion failures rather than silently changing its meaning.
CommandKit parses and forwards supplied CLI selections and overrides through the
declared acquisition boundary. Upstream libraries retain path validity,
resolution, precedence, conflict handling, and domain validation. Preserve their
errors instead of inventing a parallel override policy or retrying with altered
values. Application loading and application-owned overrides retain their own
responsibilities.
The [CLI reference](../cli.md#show-effective-configuration) owns current native
report formats and operation-specific preparation and delivery timing. Semantic
comparison exposes the adopted upstream comparison API and carries its evidence
through the provider boundary; it does not introduce a CommandKit equality engine.
## Scope And Non-goals
Keep packages and abstractions proportionate to cohesive responsibilities. The
library is not a general application runtime, dependency-injection framework,
logging system, hosted service, or standalone application. Shared vocabulary is
recommended for application-owned commands; their names and domain behavior
remain application choices.
Accepted ADRs and current CLI/API owners define selected contracts; the
[future work record](../roadmap/future.md) preserves remaining proposals.
Backend defaults and incidental consumer behavior do not establish new contracts.
A replacement backend must preserve published CLI behavior or follow deliberate compatibility
changes under the release policy.
Base restrictions and guarantees on selected requirements. Configuration and
prompt libraries retain their own confidentiality contracts; standard output
must use their safe inspection APIs without promising that arbitrary application
content is safe for public disclosure. New threat scope or restrictions require
a deliberate decision under the [agent policy](agents.md).