Refine backend composition and input lifecycle decisions

This commit is contained in:
2026-10-10 22:56:40 +00:00
parent 14c304b6f9
commit 3b69cf85e7

View File

@@ -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.