diff --git a/docs/roadmap/registration-backend.md b/docs/roadmap/registration-backend.md index 9e57f39..6c32a26 100644 --- a/docs/roadmap/registration-backend.md +++ b/docs/roadmap/registration-backend.md @@ -1,8 +1,9 @@ # Registration and execution internals Status: proposed internal design for the finalized consumer API; no implementation -has begun. Two public composition seams identified below need resolution before -the staged implementation plan is finalized. +has begun. Runnable-command children and scope-error attachment are selected API +additions to incorporate into the registration API document. OQ-1 remains open +for later discussion before the staged implementation plan is finalized. ## Purpose and authority @@ -163,6 +164,14 @@ 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 +[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 +parents. This addition requires no separate registration mechanism. + ### Divide validation by responsibility and lifetime Use one implementation of each core metadata rule: @@ -211,6 +220,11 @@ template, initialize suite input, or invoke a parser a second time. `FlagValues.ApplyTo` selects the original identities and applies their prepared values to the supplied destination. +All supplied occurrences undergo conversion, including selectors inapplicable to +the selected scope and values supplied alongside help. Scope filtering governs +value application and required-value checks, not conversion. An earlier malformed +singleton occurrence remains an error even if a later occurrence is valid. + Recommend a private identity token per original binding and an invocation-local lookup from token to prepared entry. Handle copies preserve identity; lookalike bindings get a different token. A private nonzero-sized token object avoids @@ -231,8 +245,11 @@ usage outcomes. An integration may wrap those causes with scope/family context without parsing error messages or inventing another outcome protocol. Help still converts supplied occurrences and rejects malformed values through the -same preparation logic, but does not materialize command/suite input or enforce -execution-required fields. Presence, singleton repetition, and repeated-value +same preparation logic, but does not materialize the target command's application +or suite input, invoke its validation or execution callbacks, or enforce its +execution-required fields. Suite state factories and providers also remain +uninvoked. The built-in help command may prepare its own private typed input +through the common compiler. Presence, singleton repetition, and repeated-value semantics remain those of the finalized API and selected grammar. ## One compiled execution path, including help @@ -350,7 +367,7 @@ configuration views with their proper owners. `ConfigScope` constructs a public `ckconfig.Scope` and delegates configuration command generation to `ckconfig`. It must not construct another config family or reach into `ckconfig`'s private prepared suite. Its construction-error handoff is -the second unresolved seam below. +the selected `ckconfig.WithScopeError` operation described below. ### Share mechanics where ownership supports them @@ -391,21 +408,58 @@ three command sources: built-ins, application declarations, and integrations. Include invalid bindings, conflicting roots in either order, an invalid final member of a batch, nested root-only declarations, absence with retained errors, source metadata edits after capture, and declaration reuse across apps. +Include runnable parents with children through `CommandSpec.Children`, applying +the same capture, collision, and placement checks as ordinary groups. For execution, check wrapped wiring versus input failures, fresh repeatable collections, pointer-shaped scope inputs with shared state, scope-aware validation, -help without factories/providers, and metadata refresh without callback replay. +help without target application/suite factories or providers, and metadata refresh +without callback replay. Check that supplied but inapplicable selectors still +undergo conversion, including on help paths, while the built-in help command can +prepare its own private input through the common compiler. Evidence for the internal path itself can inspect the shared installer/compiler; do not turn a private function name or exact allocation count into a public contract. These are review targets for the future implementation, not a request to add or run tests during this planning pass. +## Resolved scope-error handoff (OQ-2) + +The selected `Scope[I, S]` includes private adapter-construction issues owned by +`ckconfig`. Before the selected addition below, `ckprompt.ConfigScope` could not +attach its original nil-loader error through supported public contracts. An +`Acquire == nil` check alone is insufficient: narrowing `Operations` to empty makes a scope +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]`. +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 +copies and narrowing of `Operations`. The family factory collects those issues +into its command declaration before capability selection or absence can discard +them. `ConfigScope` uses this for its original loader validation and +otherwise fills the ordinary public scope fields. + +This mirrors the accepted core retained-error model at the boundary that needs +it. It does not add scope installation, expose private representations, or move +PromptKit types into the core. Scope capture still occurs at the existing family +factory; this operation is evidence attachment rather than a new lifecycle. + +**Alternative considered:** give the public scope declaration an explicit +construction-error field, consumed by the family factory alongside provider metadata. This is simple +and permits application-created adapters to supply errors directly. It expands +mutable public representation and requires defining preservation/combination of +multiple errors; the opaque attachment operation keeps those details private. + ## Open Questions -The main internal design can use the existing finalized API. The following two -seams need an explicit API clarification or small approved extension. Do not -resolve them with private-module imports, dummy providers, reflection, or hidden -global state. +OQ-1 remains unresolved; its recommendation and alternatives below are retained +for later discussion. The selected API additions above need to be reflected in +the registration API document before implementation planning is finalized. Do not +resolve the remaining question with private-module imports, dummy providers, +reflection, or hidden global state. ### OQ-1: Retaining core binding errors when a family is absent @@ -448,35 +502,3 @@ This needs no new API and is proportionate to installed behavior, but limits the meaning of suite validation before absence to integration metadata and retained adapter issues rather than all unused bindings. Choose it deliberately, rather than silently making empty capabilities swallow invalid declarations. - -### OQ-2: Retaining errors in a scope constructed by another integration - -The selected `Scope[I, S]` includes private adapter-construction issues owned by -`ckconfig`. `ckprompt.ConfigScope` returns that scope type, but cannot attach its -original nil-loader error through supported public contracts. An `Acquire == nil` -check alone is insufficient: narrowing `Operations` to empty makes a scope -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. - -**Recommendation:** approve an integration-owned operation, schematically -`ckconfig.WithScopeError[I, S](scope Scope[I, S], err error) Scope[I, S]`. -It returns a scope retaining the original issue, preserving supported error -identity; a nil error preserves the scope. The family factory collects those -issues into its command declaration before capability selection or absence can -discard them. `ConfigScope` uses this for its original loader validation and -otherwise fills the ordinary public scope fields. - -This mirrors the accepted core retained-error model at the boundary that needs -it. It does not add scope installation, expose private representations, or move -PromptKit types into the core. Scope capture still occurs at the existing family -factory; this operation is evidence attachment rather than a new lifecycle. - -**Alternative:** give the public scope declaration an explicit construction-error -field, consumed by the family factory alongside provider metadata. This is simple -and permits application-created adapters to supply errors directly. It expands -mutable public representation and requires defining preservation/combination of -multiple errors; the opaque attachment operation keeps those details private. - -Neither seam requires a general plugin system. Confirm their resolutions before -turning this design and the module roadmap into ordered implementation stages.