Document the optional integration modules roadmap

This commit is contained in:
2026-10-09 12:54:28 +00:00
parent 9ee6ca43ef
commit 06859b1b09

261
docs/roadmap/modules.md Normal file
View File

@@ -0,0 +1,261 @@
# Optional integration modules
Status: proposed implementation roadmap; no module extraction has begun.
## Purpose and authority
Make CommandKit useful to consumers that use both ConfigKit and PromptKit,
ConfigKit alone, or neither library. Consumers using only CommandKit's core
should acquire neither ConfigKit nor PromptKit through CommandKit's package or
module dependencies. ConfigKit-only consumers should not acquire PromptKit.
This roadmap records the targeted end state and migration work. Module names,
repository layout, and other recommendations under [Open Decisions](#open-decisions)
remain proposals. It does not supersede accepted ADRs or describe these changes
as implemented. The [architecture policy](../policy/architecture.md) and
[ADR 0003](../adr/0003-public-facade.md) govern the current boundary; an accepted
successor decision is needed before changing it. Current contracts remain in
GoDoc, the [CLI reference](../cli.md), and the
[consumer guide](../consumers/guide.md).
## Current baseline
At the start of this roadmap, CommandKit has one Go module and one supported
public root package. It requires ConfigKit v0.5.0 and PromptKit v0.12.0 and
declares Go 1.26.0. Its internal Cobra adapter implements routing and parsing.
The root combines core command infrastructure with configuration and PromptKit
inspection. Coupling extends beyond constructors:
- `NewConfigKitScope` accepts ConfigKit results and maps their diagnostics.
- `SourceReader` returns ConfigKit source records; `ExplanationReader` accepts
ConfigKit paths and returns ConfigKit explanations.
- `PromptKitSuite` callbacks return PromptKit inspection types.
- `NewPromptKitScope` adapts engine configuration into the configuration suite.
- Standard handlers share root-private helpers and state. For example,
configuration registration accesses the application's registry and records
whether the suite has already been installed.
The [internal overview](../internal/overview.md) describes the implemented
inventory. The maintained examples and integration tests currently belong to the
root module. ReleaseKit v0.3.0 is also selected there as a Go tool dependency;
the [release procedure](../release.md) assumes one module and one version tag.
Optional command registration currently controls behavior, not dependencies.
Moving integration code into packages sharing the root `go.mod` would separate
compiled imports but retain integration requirements in the module graph.
Go's [module graph rules](https://go.dev/ref/mod#module-graph-pruning) explain
that distinction.
## Targeted end state
Provide three consumer modules with dependencies directed toward the core.
Names below identify responsibilities rather than final import paths.
| Module | Responsibility | Integration dependencies |
| --- | --- | --- |
| Core | Application construction, command/group registration, typed bindings, dispatch, help/version, common grammar, outcomes, and diagnostics | Neither ConfigKit nor PromptKit |
| Configuration integration | Standard configuration family, scope composition, reports, and ConfigKit adapters | Core and ConfigKit; no PromptKit |
| PromptKit integration | Named prompt/profile inspection and the PromptKit configuration-scope adapter | Core, PromptKit, and the configuration integration where needed for scope composition |
The core remains an opinionated command library with its existing internal
Cobra boundary. The dependency goal is independence from ConfigKit and PromptKit,
not a standard-library-only implementation.
Integrations remain maintained parts of CommandKit. They install commands into
the same application through supported public core operations and follow its
grammar, help, invocation, and outcome conventions. Applications retain domain
commands, acquisition, runtime construction, streams, and process exit.
The recommended configuration boundary moves the existing ConfigKit-shaped
contracts with their handlers. It preserves one configuration suite into which
applications compose `app`, `promptkit`, and other supported scopes. PromptKit
configuration inspection and named-definition inspection remain independently
selectable. A module dependency does not automatically install a command family.
Core public signatures and implementation imports contain no ConfigKit or
PromptKit types. Integration-specific types and helpers move to their owning
modules. Do not retain root forwarding wrappers that restore those imports.
Shared helpers stay private unless a concrete cross-module interaction needs a
supported contract; extraction is not a reason to expose every existing helper.
Preserve current operator behavior during extraction: command vocabulary,
selection and scope semantics, native reports, diagnostics, error identity,
outcomes, and help without provider acquisition. Any necessary CLI change must
be identified and decided separately. Public Go import and symbol relocation
is an intentional consumer migration.
## Work sequence
### 1. Settle boundaries and record the decision
- Resolve the significant choices under Open Decisions and select module paths
and package names before moving public symbols.
- Create a successor ADR for the changed facade/module boundary and mark
ADR 0003 superseded according to the
[documentation policy](../policy/documentation.md#architecture-decision-records).
Preserve its decision history. Align the architecture policy and review other
ADRs for affected ownership statements without reopening unrelated decisions.
- Inventory root exports, imports, private helper use, and family registration
behavior. Assign each to core or an integration; document the consumer-facing
old-to-new API mapping rather than freezing a full inventory in this roadmap.
- Sketch three representative consumers: core only, ConfigKit only, and both
libraries. Confirm that combined configuration scopes remain straightforward
and that moving code does not replace removed boilerplate with new plumbing.
Completion: the dependency direction, public migration, and registration approach
are concrete enough to implement without inventing a general plugin framework.
### 2. Extract the integrations and isolate the core
- Establish the selected module layout. Keep the existing root import path for
the core under the recommended layout.
- Move configuration declarations, registration, handlers, reports, and
ConfigKit adapters together. Move PromptKit declarations, handlers, reports,
and its configuration-scope adapter into the PromptKit integration.
- Replace access to root-private registry state and helpers with the smallest
supported interactions needed by those integrations. Preserve duplicate
registration handling and standard-family ownership; audit these boundaries
rather than assuming package relocation preserves them automatically.
- Keep core outcomes, diagnostics, bindings, and command declarations as shared
contracts. Place report encoding and upstream diagnostic conversion with their
owners. Avoid copying ConfigKit's data model into the core merely to remove
imports.
- Remove integration dependencies from the core module, including dependencies
reintroduced by its tests, examples, tools, or remaining public declarations.
Review tool dependencies for that specific objective; broad removal of all
maintainer dependencies is not a prerequisite.
- Use released inter-module requirements for consumer builds. Local workspace
composition may support development, but published modules must not depend
on sibling checkouts or consumer-applied `replace` directives.
Completion: core-only and ConfigKit-only consumers have the intended dependency
boundaries, while the composed consumer retains the existing standard commands.
### 3. Move examples and validate the consumer boundaries
- Keep a genuine core-only example with no configuration-library imports. Place
ConfigKit and composed examples, fixtures, and integration tests in modules
that can legitimately depend on those integrations. Choose their exact home
with the module layout; examples in the root module must not defeat isolation.
- Move existing behavioral tests with their owners. Retain coverage for typed
presence, repeated invocations, registration collisions, provider errors,
report delivery, and preserved CLI behavior without duplicating every suite.
- Add focused consumer checks for the three adoption paths. Verify both package
dependencies and the selected module graph, including transitive requirements.
Core-only checks exclude both libraries; ConfigKit-only checks exclude
PromptKit. Merely observing a smaller executable is insufficient.
- Exercise combined `app` and `promptkit` configuration scopes and independent
installation of named PromptKit inspection through public APIs.
- Run each module's relevant suite. Verify ordinary module consumption with
workspace mode disabled so local workspace resolution cannot hide missing
requirements. Root `go test ./...` alone will not cover nested modules.
Completion: representative public consumers demonstrate isolation and retained
composition. These checks protect the selected boundary without requiring an
exhaustive matrix of historical dependency versions.
### 4. Prepare migration documentation and module releases
- Update GoDoc, the consumer guide, maintained examples, README, internal
inventory, and contributor reading guide as the implementation lands. Keep
the CLI reference unchanged except where actual behavior or relevant ownership
changes require an update. Link this roadmap from contributor navigation when
integrating it into the implementation workflow.
- Explain moved imports and constructors and the selection of optional modules.
Release the incompatible root API change according to the
[release policy](../policy/release.md#versioning-and-compatibility); do not
publish it as a compatible patch. Inspect actual publication state when
selecting versions and preserve existing published tags and notes.
- Adapt release profiles, module discovery, validation, note locations, version
reporting, and publication commands to the selected release model. Verify
ReleaseKit's supported capabilities before choosing the mechanics; the current
single-module profile is not evidence of multi-module support.
- For nested modules, use Go's module-subdirectory tag prefixes and make
release-note paths and immutable links unambiguous. Review licensing and
attribution visibility in each distributed module under the
[OSS policy](../policy/oss.md).
- Establish publication order from module dependencies and record compatible
requirements explicitly. Published consumers must resolve actual released
versions. A local workspace is not a substitute for that release sequence.
Preserve the existing successful-source-publication completion model.
Completion: every consumer module can be released and consumed independently of
local workspace state, with a documented migration from the combined API.
## Scope and follow-up
This work changes packaging and integration boundaries. It does not select a
new backend, concurrent-dispatch contract, dynamic plugin system, or replacement
configuration engine. It does not lower the existing Go minimum.
Keep the PromptKit v0.13.0 and ConfigKit v0.6.0 adoption identifiable as a separate
change after extraction unless an implementation dependency justifies combining
them. Catalogs and requirement discovery are subsequent integration features.
Semantic comparison retains the upstream-contract dependency recorded in
[deferred work](deferred.md). None is needed to demonstrate module isolation.
## Open Decisions
### Repository layout and module paths
**Recommendation:** keep the core at the existing repository root and create
nested integration modules in this repository, for example under
`integrations/configkit` and `integrations/promptkit`. These paths are proposals.
One repository makes coordinated API changes and shared maintenance convenient
while separate `go.mod` files provide consumer dependency isolation. The cost is
explicit multi-module testing, prefixed tags, and release orchestration.
**Alternative:** publish the integrations from separate repositories. Each can
use a simpler single-module release procedure and evolve independently. This is
viable if ReleaseKit or maintainer preferences make nested-module publication
awkward, but coordinated changes, examples, and documentation require more
cross-repository work. Settle this before publishing integration import paths.
### Ownership of the configuration family
**Recommendation:** place the existing configuration family and its
ConfigKit-shaped provider contracts in the configuration integration. Let the
PromptKit integration depend on it to supply a compatible scope. This follows
the current code and avoids a speculative common data model. The tradeoff is
that non-ConfigKit providers using that suite still depend on ConfigKit contracts;
core-only custom commands remain available without them.
**Alternative:** define library-independent configuration reports and provider
contracts in core or a separate configuration module, then adapt both owning
libraries. This could benefit consumers wanting standard configuration commands
over unrelated libraries. It adds conversion, public contract ownership, and
possibly another module before such a consumer has demonstrated the need.
Select it only if representative usage justifies those costs and preserves native
inspection semantics.
### Registration across the new public boundary
**Recommendation:** first reuse public command/group registration and duplicate
path checks. Add only a narrow core registration capability if preserving the
existing standard-family ownership contract requires it. This keeps integrations
ordinary Go libraries and avoids exporting root-private state. Its suitability
must be checked against reserved paths and registration atomicity during the API
inventory.
**Alternative:** introduce an explicit public family declaration and installation
API for all standard suites. This can give ownership and collision handling a
clear shared representation if ordinary registration proves insufficient. It
also creates a new long-lived abstraction that may duplicate command groups;
prefer it only when the concrete extraction shows a distinct responsibility.
### Versioning and publication cadence
**Recommendation:** version modules independently, declare their supported
minimum dependency versions through ordinary module requirements, and coordinate
releases when changes span modules. Core-only consumers then avoid unrelated
integration releases. This requires clear per-module notes and publication
ordering; exact tooling and note placement need verification before adoption.
**Alternative:** release all three modules with matching version numbers on a
coordinated cadence. A single documented release set simplifies communication
and initial testing, while still retaining separate dependency graphs. It causes
unchanged modules to receive releases and still needs distinct Go tags and
inter-module dependency ordering; matching numbers alone do not enforce
compatibility. Neither choice implies support for every possible version
combination.