diff --git a/docs/roadmap/modules.md b/docs/roadmap/modules.md index 54c924f..57b8078 100644 --- a/docs/roadmap/modules.md +++ b/docs/roadmap/modules.md @@ -2,8 +2,10 @@ Status: module boundary accepted in ADR 0010; implementation planning remains in progress and no module extraction has begun. The consumer API is finalized -and its ADR/architecture-policy alignment is complete. The next minor ReleaseKit -release will support individual module releases; CommandKit adoption is pending. +and its ADR/architecture-policy alignment is complete. The internal registration +and execution design is finalized, including both composition-question resolutions. +The next minor ReleaseKit release will support individual module releases; +CommandKit adoption is pending. ## Purpose and authority @@ -15,7 +17,9 @@ module dependencies. ConfigKit-only consumers should not acquire PromptKit. This roadmap records the targeted end state, selected module layout, and migration work. The finalized [registration API](registration-api.md) owns the selected declarations, installation semantics, and representative consumer -examples. Neither document describes these changes as implemented. +examples. The finalized [registration backend design](registration-backend.md) +owns internal structure, shared installation/execution mechanics, and the resolved +composition decisions. These documents describe the implementation target. [ADR 0010](../adr/0010-optional-integration-modules.md) owns the accepted module boundary and convenient registration principles; it partially supersedes [ADR 0003](../adr/0003-public-facade.md), whose remaining principles still apply. @@ -143,6 +147,16 @@ shapes merely because they already exist. Built-in help/version remain reserved; optional integration roots are owned when their real family is installed. ADR 0011 and the architecture policy now record that lifetime; implementation remains pending. +- Use the finalized [internal registration and execution design](registration-backend.md). + Built-ins, application commands, and integration families share one + [installation path](registration-backend.md#one-installation-path-for-every-command) + and typed compiler. Captured metadata and invocation state have distinct owners; + the backend document defines the capture, binding, routing, and presentation + mechanics without adding another integration installation API. +- Apply the resolved [unused-binding validation boundary](registration-backend.md#resolved-unused-binding-validation-oq-1) + and [scope-error handoff](registration-backend.md#resolved-scope-error-handoff-oq-2). + The registration API incorporates the selected runnable-parent and scope-error + attachment declarations. No absence-validation overload is selected. - Core diagnostics use a small CommandKit-owned severity type. Upstream diagnostic conversion belongs in the integrations, preserving selected classification, delivery, and error semantics without copying ConfigKit's @@ -205,10 +219,13 @@ with refinements recorded in ADR 0011. ADR 0010's consumer-migration consequence is superseded for the unreleased prototype transition. The architecture policy reflects the selected core/integration facades, installed-family ownership, and scope-aware configuration input validation. Earlier accepted decision text remains -intact. +intact. The internal backend design is also finalized; its runnable-child, +scope-error attachment, and unused-binding decisions are incorporated into the +API target. Both composition questions are resolved. - Use the finalized [registration API](registration-api.md) and selected module - layout as the implementation target before moving public symbols. + layout together with the [backend design](registration-backend.md) as the + implementation target before moving public symbols. - Inventory root exports, imports, private helper use, and family registration behavior. Use the inventory as evidence of responsibilities and coupling, not as a list of APIs to preserve. Implement final exports and ownership from the @@ -218,7 +235,8 @@ intact. core only, ConfigKit only, and both libraries. They define the target wiring, including combined configuration scopes and independent definition inspection; port them into maintained examples as implementation lands. -- Prepare a staged implementation plan from this roadmap and the finalized API, +- Prepare a staged implementation plan from this roadmap, the finalized API, + and the finalized [backend design](registration-backend.md), preserving the unreleased-prototype organizing principle above. Completion: the dependency direction, final API, and registration approach @@ -227,14 +245,21 @@ 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. + path for the core. Use the backend document's [internal ownership boundaries](registration-backend.md#selected-internal-ownership) + when organizing private declarations, bindings, registry, execution, neutral + metadata, and the Cobra adapter. - 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. +- Use the shared installer and typed compiler for every command source, including + built-in help/version. Replace direct registry writes, duplicated family + collision checks, and integration-specific core state with the selected + captured-declaration path. Integration factories use public core contracts, + and `ckprompt` uses the selected scope-error attachment contract in `ckconfig`. +- Follow the backend document's [integration preparation design](registration-backend.md#integration-preparation-without-parallel-execution-frameworks) + for selected inputs and shared state. Preserve each operation's acquisition + and report timing while removing repeated scope selection and mixed-context + projection. - Implement core outcomes, diagnostics, bindings, and command declarations using the finalized API. Replace ConfigKit severity in core diagnostics with the selected CommandKit-owned type. Place report encoding and upstream diff --git a/docs/roadmap/registration-api.md b/docs/roadmap/registration-api.md index 56c1af3..e7ab62a 100644 --- a/docs/roadmap/registration-api.md +++ b/docs/roadmap/registration-api.md @@ -1,7 +1,9 @@ # Unified command registration API Status: finalized API design; implementation is pending. The declarations, -consumer examples, and design checks below define the selected target API. +consumer examples, and design checks below define the selected target API, +including the additions and clarifications selected in the finalized backend +design. No implementation is authorized by this document alone. ## Purpose and authority @@ -23,7 +25,10 @@ boundaries and convenient integration registration. captured-declaration model, optional-family namespace lifetime, and separated input/shared-state lifetimes. The [architecture policy](../policy/architecture.md) reflects those boundaries. The [module roadmap](modules.md) tracks extraction -work; this API design remains a target rather than implemented behavior. +work. The finalized [backend design](registration-backend.md) owns internal +structure and shared implementation mechanics. Its resolved composition choices +are incorporated below; this API design remains a target rather than implemented +behavior. Design for the best consumer API without preserving current implementation signatures or machinery. Existing code can supply evidence and reusable work, @@ -109,8 +114,10 @@ A failed `New` yields no usable application. A failed `Register` leaves the existing registry intact. No rollback guarantee spans separate registration calls. Declaration construction, installation, help, and version do not invoke provider -acquisition or invocation input/context factories. They do not validate files, -credentials, or application configuration. Invalid wiring fails at installation; +acquisition or application/suite input/state factories. Built-in help may prepare +its own private typed input through the common compiler, without initializing the +target command or invoking its validation/execution callbacks. These paths do not +validate files, credentials, or application configuration. Invalid wiring fails at installation; an upstream error returned by a valid provider remains an invocation result. An originally nil loader remains invalid wiring even if an adapter would wrap @@ -171,8 +178,14 @@ The selected empty-suite behavior is: Use an explicit `commandkit.NoCommand()` declaration for intentional absence. Core installation skips it. A zero-value `Command` remains invalid. Integration -factories validate or retain wiring errors before representing an empty family -as a no-op; absence must not swallow errors. A no-op does not reserve a namespace. +factories retain adapter-construction errors before representing an empty family +as a no-op. Validate bindings needed by generated commands; unused selector +bindings are ignored when they contribute to no generated inspection command. +Integration-owned scope identifiers, applicability references, and enabled-provider +wiring still undergo validation. Ignoring unused bindings does not erase retained +adapter errors. This is the selected [unused-binding resolution](registration-backend.md#resolved-unused-binding-validation-oq-1). +A no-op does not reserve a namespace, and `NoCommand()` needs no validation-bearing +overload or separate public declaration validator. ## Capture, reuse, and invocation inputs @@ -273,6 +286,7 @@ type CommandSpec[I any] struct { Name string Summary string Description string + Children []Command NewInput func() I Flags []Flag[I] Arguments []Argument[I] @@ -367,6 +381,17 @@ installed tree, extract its handlers, or append children after capture. Collecti in `CommandSpec`, groups, and defaults are captured as appropriate; runtime closure state and diagnostic writer effects remain caller-owned. +### Runnable parents and captured children + +`CommandSpec.Children` combines a typed handler with child declarations. Capture +its child-handle slice and apply the same immediate composition, collision, and +installation placement checks used by `Group`. A runnable parent follows its +handler and operand requirements on bare invocation; help describes the parent +and children under [ADR 0006](../adr/0006-command-grammar.md). `Group` remains the +convenience for a non-runnable parent. Neither form permits mutation or child +append operations after capture. The [backend design](registration-backend.md#immutable-declaration-records) +uses the same captured representation and installation path for both. + ### Retained errors, placement, and no-ops `WithError` returns a new captured declaration retaining a non-nil error alongside @@ -565,6 +590,8 @@ func FromConfigKit[I, S, T any]( load func(context.Context, Selection, I, S) (*configkit.Result[T], error), ) Scope[I, S] +func WithScopeError[I, S any](scope Scope[I, S], err error) Scope[I, S] + func Command[I, S any](suite Suite[I, S]) commandkit.Command func AppCommand[T any]( @@ -581,9 +608,11 @@ validation diagnostics at invocation time, preserving current selected reports and classifications rather than implementing a second configuration engine. An empty selector scope list applies to all registered scopes; a nonempty list -identifies its applicable registered scopes. Unknown identifiers and reserved -standard-flag collisions are declaration errors. Scope identifiers, capability -selection, defaults, and aggregate availability follow +identifies its applicable registered scopes. Unknown scope identifiers are +declaration errors. Bindings used by generated commands undergo core validation, +including standard-flag collisions; unused selector bindings follow the selected +absence/unused-binding rule above. Scope identifiers, capability selection, +defaults, and aggregate availability follow [ADR 0009](../adr/0009-configuration-scopes.md). Non-applicable selectors are not applied to a scope's input; this adds no new unused-selector rejection policy. `CheckInput` receives the registered name of each requested scope alongside its @@ -605,6 +634,21 @@ arguments nor demands a forwarding wrapper from the caller. A nil loader becomes a retained `config`/`app` declaration error. The full suite is used for composition; there is no incremental scope append operation after installation. +### Retained scope errors + +`ckconfig.WithScopeError` returns a scope retaining a non-nil construction error +alongside earlier causes, leaving the original scope unchanged. A nil error +preserves the scope. Retained causes preserve supported `errors.Is`/`errors.As` +identity across scope copies and narrowing of `Operations`. The family factory +collects those issues into its command declaration before capability selection +can discard them; no acquisition callback runs to discover a construction error. + +`ckprompt.ConfigScope` uses this public operation to retain original loader errors +without accessing private `ckconfig` state. It otherwise declares the ordinary +scope fields and capabilities. The [scope-error resolution](registration-backend.md#resolved-scope-error-handoff-oq-2) +records the rationale and rejected alternative; the operation adds no new scope +installation lifecycle. + ## Selected PromptKit integration declarations The `ckprompt` package uses `ckconfig` selections and scope contracts through its @@ -662,7 +706,9 @@ maps the owning library's public diagnostics into core-owned `Diagnostic` values Definition providers preserve diagnostics even when a named lookup fails. Consumers may leave either definition callback nil to omit that capability. -A valid suite with both nil is an explicit no-op after eager declaration validation. +A suite with both callbacks nil contributes no commands and ignores unused +selector bindings. It returns a no-op retaining any adapter-construction errors; +those errors still fail installation. A nil loader supplied to `FromEngine` is invalid wiring, not missing capability; setting its callbacks nil does not erase the retained error. For prompt lookup, the adapter passes the requested version value to PromptKit, with omission using @@ -706,7 +752,18 @@ succeed. The no-call rows remain unconditional for CommandKit-owned paths. | Configuration inspection of several scopes | Once per selected scope | Once, shared by selected scopes | Requested scopes, in selected operation order | | Named definition inspection | Once for the selected command | Once for that invocation | Selected inspection callback | +The factory counts describe application inputs and suite input/state. Built-in +help may prepare its own private input through the common compiler; it still +leaves the target application's and suites' factories, validation/execution +callbacks, and providers uninvoked. + Input conversion and scope/capability preflight use metadata without acquisition. +All supplied occurrences on an installed command undergo conversion, including +selectors inapplicable to the requested scope and values supplied with help. +Scope filtering affects value application and required-value checks, not +conversion; an earlier malformed singleton occurrence remains an error even if a +later one is valid. + For configuration execution, initialize and populate each requested scope input, then run its `CheckInput`; initialize the shared state before acquisition after input checks succeed. The input/state callbacks do not run for unselected scopes @@ -1126,7 +1183,7 @@ of arbitrary effects inside application callbacks. | --- | --- | | `ckconfig.Command(ckconfig.Suite[struct{}, struct{}]{})` | Error: no configuration scopes | | One named scope with empty operations and nil acquisition | Success; `config scopes` describes it, no inspection verbs supplied by that scope | -| Valid definition suite with no callbacks | Explicit absence, no namespace claimed | +| Definition suite with no callbacks and unused selector bindings | Explicit absence, unused bindings ignored; retained adapter errors still fail | | `NoCommand()` beside ordinary children | Skipped, no placeholder help item | | `WithError(NoCommand(), sentinel)` | Error retaining sentinel identity | | Zero `Command{}` at the root or as a child | Declaration error | @@ -1219,10 +1276,13 @@ The documentation alignment is complete: | [ADR 0001 status](../adr/0001-command-dispatch-and-inputs.md#status) | Remains accepted, with refinements recorded in ADR 0011 and its decision text preserved | | [ADR 0010](../adr/0010-optional-integration-modules.md) | Core/integration dependency boundaries remain in effect; ADR 0011 refines registration and supersedes the migration-guide requirement for this unreleased prototype | | [Architecture policy](../policy/architecture.md) | Uniform installed-family ownership, optional integration facades, and declaration/input boundaries | -| [Module roadmap](modules.md) | Final API selected; documentation alignment completed; extraction and release adoption still pending | +| [Module roadmap](modules.md) | Final API and backend design selected; extraction and release adoption still pending | +| [Backend design](registration-backend.md) | Shared internal pipeline, runnable children, scope-error attachment, unused-binding validation, and both composition-question resolutions | A staged module implementation plan remains to be written from this finalized -API and the module roadmap. It should specify eager capture, error retention, -neutral bindings, scope isolation, maintained examples, and release adoption. +API, the finalized [backend design](registration-backend.md), and the module +roadmap. Both composition questions are resolved. The plan should specify eager +capture, error retention, neutral bindings, scope isolation, maintained examples, +and release adoption. The documentation alignment neither implements these changes nor authorizes implementation by itself. diff --git a/docs/roadmap/registration-backend.md b/docs/roadmap/registration-backend.md index 17c2e4a..78f161a 100644 --- a/docs/roadmap/registration-backend.md +++ b/docs/roadmap/registration-backend.md @@ -1,8 +1,8 @@ # Registration and execution internals -Status: proposed internal design for the finalized consumer API; no implementation -has begun. Runnable-command children and scope-error attachment are selected API -additions to incorporate into the registration API document. Both composition +Status: finalized internal design; no implementation has begun. Runnable-command +children, scope-error attachment, and the unused-binding validation resolution are +incorporated into the [registration API](registration-api.md). Both composition questions below are resolved; the staged implementation plan remains to be written. ## Purpose and authority @@ -18,8 +18,9 @@ capture, installation, namespace, and input/state decisions. [ADR 0010](../adr/0010-optional-integration-modules.md) owns module boundaries, and the [architecture policy](../policy/architecture.md) governs implementation. The [module roadmap](modules.md) owns the broader extraction and release work. -This document proposes internal mechanics and identifies gaps; it neither changes -the finalized API silently nor establishes additional consumer guarantees. +This document defines selected internal mechanics and records the resolved +composition decisions. Public declarations and consumer examples belong in the +registration API document; these changes remain unimplemented. The prototype has no compatibility priority. Replace its parallel paths and bookkeeping where they obstruct the selected design. Preserve deliberately @@ -46,7 +47,7 @@ opportunities and structural risks, not measured performance regressions. | `bridgeTree` in [parser.go](../../parser.go) caches neutral metadata; [internal/cobra/parse.go](../../internal/cobra/parse.go) creates a session per invocation | The useful metadata/session split should survive the refactor | Share captured metadata, refresh it after registry changes, and keep mutable backend values invocation-local | | `commandAt` traverses names again after the adapter has already resolved a command | Duplicate route lookup can become another source of dispatch disagreement | Return a private command key from the adapter and resolve the captured runner directly | -## Proposed internal ownership +## Selected internal ownership Keep most cohesive core machinery private in the root package. A facade does not require forwarding-only files or duplicate representations for every public type. @@ -58,7 +59,7 @@ Use an internal package where it protects an actual dependency boundary. | Core bindings | Binding identity, typed conversion, presence/defaults, converted-value application, binding metadata validation | Scope names, ConfigKit/PromptKit types, application object cloning | | Core registry | Validate/stage/commit installation batches and invalidate derived metadata | Integration-specific installed flags, command factories, report rendering | | Core execution | Invoke the selected compiled runner, share the current presentation context, normalize/present outcomes | Application startup, provider schemas, process exit | -| `internal/commandmeta` (proposed) | Narrow backend-neutral routing/help DTOs, private command keys, parse result shapes | Public contracts, typed handlers, registry policy, upstream library types | +| `internal/commandmeta` (planned) | Narrow backend-neutral routing/help DTOs, private command keys, parse result shapes | Public contracts, typed handlers, registry policy, upstream library types | | `internal/cobra` | Translate neutral metadata into one invocation session; route, collect raw occurrences, render help | Typed value conversion, declaration validation, family ownership, acquisitions | | `ckconfig` private implementation | Capture scopes, compile applicable selectors, prepare selected inputs, acquire views, perform each configuration operation | Core registry state or private binding access | | `ckprompt` private implementation | Capture definition providers, adapt engine inspection, prepare definition input/state, render definition reports | A second dispatcher/parser or configuration-suite installation | @@ -164,9 +165,8 @@ Keep payload and children independent in the private model. Do not introduce a new prohibition on runnable commands with children merely because separate leaf and group constructors are convenient. -The selected public addition is `Children []Command` on `CommandSpec[I]`. The -registration API currently has no way to combine a handler with children; the -private model alone cannot expose the behavior described by +The selected public addition is [children on runnable commands](registration-api.md#runnable-parents-and-captured-children) +through `CommandSpec.Children`. It exposes the behavior described by [ADR 0006](../adr/0006-command-grammar.md). Capture the child-handle slice using the same ownership and immediate composition checks as `Group`, and enforce the same placement rules at installation. `Group` remains a convenience for non-runnable @@ -434,8 +434,8 @@ metadata-only, while the original invalid loader must remain an error. Wrapping the nil loader in a non-nil acquisition closure would incorrectly defer it to invocation. A core `WithError` only accepts a `Command`, not a scope. -**Selected:** add the integration-owned operation -`ckconfig.WithScopeError[I, S](scope Scope[I, S], err error) Scope[I, S]`. +**Selected:** use the integration-owned [retained scope-error operation](registration-api.md#retained-scope-errors), +`ckconfig.WithScopeError`, declared in the registration API document. It returns a scope retaining the original issue, preserving supported error identity. Repeated attachments preserve earlier causes and leave the original scope unchanged; a nil error preserves the scope. Retained causes survive scope