15 KiB
Optional integration modules
Status: module boundary accepted in ADR 0010; implementation planning remains in progress and 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, selected module layout, and migration work. Recommendations under Open Decisions remain proposals. It does not describe these changes as implemented. ADR 0010 owns the accepted module boundary and convenient registration principles; it partially supersedes ADR 0003, whose remaining principles still apply. Alignment of the architecture policy remains part of the work below. Current implemented contracts remain in GoDoc, the CLI reference, and the consumer guide.
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:
NewConfigKitScopeaccepts ConfigKit results and maps their diagnostics.SourceReaderreturns ConfigKit source records;ExplanationReaderaccepts ConfigKit paths and returns ConfigKit explanations.PromptKitSuitecallbacks return PromptKit inspection types.NewPromptKitScopeadapts 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 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 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 explain
that distinction.
Targeted end state
Provide three consumer modules in this repository with dependencies directed
toward the core. Keep the core at gitea.maximumdirect.net/eric/CommandKit and
give each integration its own go.mod:
| Directory | Module path | Package name |
|---|---|---|
| Repository root | gitea.maximumdirect.net/eric/CommandKit |
commandkit |
integrations/ckconfig |
gitea.maximumdirect.net/eric/CommandKit/integrations/ckconfig |
ckconfig |
integrations/ckprompt |
gitea.maximumdirect.net/eric/CommandKit/integrations/ckprompt |
ckprompt |
The integrations/ grouping keeps optional modules together as their number
grows; the anticipated three to ten integrations are planning context, not a
limit or delivery commitment. Distinct package names avoid requiring local
aliases when these integrations are imported alongside ConfigKit and PromptKit.
Separate module files establish dependency isolation; directory naming alone
does not. The shared repository requires explicit multi-module testing and
publication orchestration.
| Module | Responsibility | Integration dependencies |
|---|---|---|
| Core | Application construction, command/group registration, typed bindings, dispatch, help/version, common grammar, outcomes, and diagnostics | Neither ConfigKit nor PromptKit |
| ConfigKit 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 ConfigKit integration |
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 selected configuration boundary moves the existing ConfigKit-shaped
contracts with their handlers into the ConfigKit integration. Only ConfigKit
support is needed for now; a generic configuration interface is outside this
migration. The PromptKit integration may depend on the ConfigKit integration,
consistent with PromptKit's own ConfigKit dependency. Document that relationship
in consumer guidance: ckprompt depends on ckconfig.
This boundary 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. Align policy and settle implementation boundaries
Completed: ADR 0010 is accepted and ADR 0003 is marked partially superseded, with its decision text preserved. The partial status follows the explicitly authorized exception to the documentation policy for this decision.
- Resolve the remaining choices under Open Decisions before moving public symbols, using the selected module paths and package names above.
- Align the architecture policy with ADR 0010 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 registration meets ADR 0010's convenience principle without replacing removed boilerplate with new plumbing. Use these examples to evaluate the registration API; no fixed call count or particular options pattern is selected.
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, preserving the existing root import path for the core.
- 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
replacedirectives.
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
appandpromptkitconfiguration 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; 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.
- 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. None is needed to demonstrate module isolation.
Open Decisions
Registration across the new public boundary
ADR 0010 selects convenient family registration with specialized composition where needed. The remaining decision is how to support it across module boundaries.
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.