18 KiB
PromptKit concrete request validation
Status: accepted adoption and feature roadmap; implementation complete. Command placement, adapter composition, request construction, artifact syntax, and initial presentation are settled. This document defines the target and required work; the implementation plan sequences the historical implementation work.
Purpose
Adopt PromptKit v0.15.0 and expose its concrete request validation capability as
app promptkit requirements validate PROMPT_ID, alongside the delivered
app promptkit requirements show PROMPT_ID command.
The organizing principle is CommandKit provides useful, consistent access to capabilities owned elsewhere. CommandKit owns vocabulary, registration, selection, coordination, presentation, and invocation outcomes. PromptKit owns what concrete request validation means. Applications supply their domain mapping, defaults, artifacts, and runtime dependencies.
The command answers whether the constructed request passes native preparation at the time of the invocation. It complements discovery without making discovery a prerequisite for validation or validation a prerequisite for application execution.
Current baseline
The baseline is CommandKit commit 700f10d78ed0e410b0cc1d754fdd98415cd5817a.
- The optional ckprompt module requires PromptKit v0.14.0, ConfigKit v0.7.1, and Go 1.26.0.
- Its suite and engine adapters
support independent prompt/profile catalogs, named definition inspection,
and requirements discovery.
FromEnginesupplies those callbacks using an application-owned engine loader. There is no concrete validation callback. - The requirements command accepts a prompt ID, configuration selection, typed suite selectors, version/profile/session context, and repeatable variable assignments. It returns the native discovery evidence as YAML. A partial inventory or missing variable is not itself an invocation failure.
ConfigScopeseparately exposes engine configuration inspection and comparison through ckconfig. It does not install the PromptKit command family.- The composed consumer demonstrates discovery and application-owned configuration/engine loading. It does not yet demonstrate construction or validation of a concrete request.
PromptKit v0.15.0 adds
Engine.ValidateRequest
for an ordinary public RunRequest. Its
consumer guide
and release note
describe the addition. The reviewed release leaves the upstream minimum Go
version, module dependencies, and licensing material unchanged from v0.14.0.
Targeted end state
Command placement and meaning
The standard PromptKit family can expose both operations independently:
| Command | Operator purpose | Owning native capability |
|---|---|---|
app promptkit requirements show PROMPT_ID |
Discover input and variable requirements for a prompt context. | Engine.InspectRequirements |
app promptkit requirements validate PROMPT_ID |
Check the actual constructed request, including supplied values and artifact bindings. | Engine.ValidateRequest |
The new handler delegates to concrete native validation. It does not infer
validity from the discovery report's Complete, inventory, or issues, and does
not independently interpret templates or decide which inputs are required.
Native validation shares Prepare's request conversion and preparation path.
It may resolve prompt/profile resources, load artifacts and schema resources,
render messages and session context, and perform the applicable configuration
and credential checks. All supplied inputs follow native preparation behavior,
including inputs unused by the selected template branch. Errors are returned
as encountered rather than as an exhaustive inventory.
There is no model call or retained prepared snapshot. Success neither certifies provider acceptance nor guarantees that a later execution will succeed. A later run prepares independently. Applications needing execution tied to a checked snapshot can use PromptKit's existing prepared-execution API in their own workflow; that workflow is outside this feature.
Operator inputs
Use the existing requirements command's common syntax for:
- One required prompt ID operand.
--configand--overlay, with existing explicit-presence handling.- Typed suite selectors.
--version,--profile, and--session-id, forwarding their native empty-string semantics.- Repeatable
--var KEY=VALUE, preserving the existing first-separator split, exact keys and values, and last assignment for a duplicate key.
Keep singleton repetition and selector binding behavior consistent with the
current CLI contract. PromptKit owns semantic interpretation of the values.
The namespace identifies the library; configuration scope selection belongs
to the separate config family.
Add repeatable --input NAME=PATH for file-backed bindings, translated to native
promptkit.File(PATH) references without eagerly reading files. Split at the
first =, preserve exact names and paths, and use the last binding for a
duplicate name. Every occurrence is parsed; a later valid binding does not
erase an earlier malformed one.
| Invocation fragment | Result |
|---|---|
--input notes=notes.txt |
Bind notes to promptkit.File("notes.txt"). |
--input notes=dir/a=b.txt |
Preserve the path dir/a=b.txt. |
--input notes= |
Forward an empty file path to native validation. |
--input =notes.txt |
Forward the empty input name to native validation. |
--input notes=first.txt --input notes=second.txt |
Bind notes to second.txt. |
--input notes |
Usage error because the separator is missing. |
PromptKit owns semantic artifact validation and loading. CommandKit adds no input-name whitelist, path restrictions, normalization, shell expansion, or configuration-relative rebasing. Paths are preserved after shell processing.
Use --format yaml for the initial success report, with YAML as the default.
Application-specific settings continue to use typed application input and
callbacks. Applications can supply inline and domain-derived artifacts through
their builder; a standard inline-input flag is outside this delivery. The
command need not offer a built-in flag for every field in RunRequest to permit
applications to use those fields through their builder.
Public integration and request construction
Add an independently optional concrete validation capability to ckprompt.Suite.
Installing validation alone must be useful; supplying discovery alone must
continue to install only discovery. Both operations belong to the same
requirements group when present. The existing installed-family ownership,
absent-suite behavior, and retained wiring errors apply.
Use a ckprompt-owned envelope containing configuration selection and the native seed request populated from the standard command inputs:
type ValidationRequest struct {
Selection ckconfig.Selection
Request promptkit.RunRequest
}
Add this optional callback field to Suite[I, S]:
ValidateRequest func(
context.Context, ValidationRequest, I, S,
) (commandkit.Acquired[struct{}], error)
The acquisition container carries diagnostics; validation has no native result value to invent. Custom callbacks remain available for application-owned runtime arrangements.
Provide a suite-returning WithRequestValidation helper that takes a suite, the
existing engine-loader shape, and an optional typed builder:
func WithRequestValidation[I, S any](
suite Suite[I, S],
load func(context.Context, ckconfig.Selection, I, S) (*promptkit.Engine, error),
build func(context.Context, ValidationRequest, I, S) (promptkit.RunRequest, error),
) Suite[I, S]
The helper attaches the engine-backed validation callback and retains construction errors, including a nil loader, in the returned suite for installation to report. Existing suite capabilities and attached issues are preserved. Narrowing the capabilities afterward does not discard retained wiring errors. Returning a suite gives these errors the same treatment as existing adapters.
For application-defined loadEngine and buildRequest callbacks, composition is:
suite := ckprompt.FromEngine(loadEngine)
suite = ckprompt.WithRequestValidation(suite, loadEngine, buildRequest)
A validation-only consumer can start from an empty suite. Given its application
types Input and State, it can use the standard command-populated request
directly:
suite := ckprompt.WithRequestValidation(
ckprompt.Suite[Input, State]{}, loadEngine, nil,
)
A nil builder passes the seed request unchanged to native validation. Standard
flags already supply useful prompt context, variables, and file bindings;
application mapping is optional. FromEngine alone continues to supply its
existing inspection callbacks, so validation remains an explicit wiring choice.
When present, the builder receives invocation context, the selection and seed request, selected application input, and shared invocation state. It returns the concrete native request. It can supply application artifacts, inline content, execution/output-contract overrides, appended messages, credentials, or other supported native settings. The adapter forwards that result without reapplying or merging fields afterward.
Builders should start from the seed, preserve the selected prompt identity and operator values, and add application defaults deliberately. Map defaults respect key presence, including a supplied empty value. For example, within a builder:
request := seed.Request
if request.Vars == nil {
request.Vars = make(map[string]string)
}
if _, supplied := request.Vars["audience"]; !supplied {
request.Vars["audience"] = "internal"
}
return request, nil
Here seed is the builder's ValidationRequest; --var audience= remains an
explicit empty value. Version, profile, and session fields retain their native
empty-string semantics, including equivalence between omitted and explicitly
empty scalar values. Configuration selection retains its existing presence
information. CommandKit documents these responsibilities without adding a second
native-field validation layer. Reusing a request builder between validation and
application execution is recommended consumer guidance.
Invocation lifecycle and outcomes
Reuse the integration's current typed input preparation, selector application, input checking, invocation state, configuration diagnostic adaptation, and outcome/delivery machinery. Construction, installation, help, and version must leave acquisition and request-building callbacks uninvoked, under the existing architecture policy.
For the bundled adapter, use this order:
- Complete CLI preflight and application input checks.
- Create shared invocation state.
- Invoke the builder, if supplied; otherwise use the seed request unchanged.
- Load the engine using the selected configuration, application input, and state.
- Invoke native validation with the concrete request.
- Deliver diagnostics and the success report through the established paths.
Both builder and loader receive the same invocation state and context. Applications can explicitly share configuration acquisition through that state. A builder failure prevents engine loading. Check cancellation between stages, preserve diagnostics already acquired, and leave later stages uninvoked after failure. Preserve supported native error identity and configuration diagnostics, including failures during engine loading.
Use the existing classification model: malformed CLI syntax and missing
operands are usage failures. Application input-check failures retain their
existing handling. Builder, engine-loader, and native-validation failures are
operational failures with supported error identity preserved. Returned provider
errors do not carry arbitrary usage or domain outcome classifications;
application-owned handlers can return their own outcomes. Preserve
native cancellation error categories; do not add a promise that every native
failure matches ctx.Err().
Successful native validation produces the selected success presentation only after it succeeds. Diagnostics and output failures follow the established delivery contract. The presentation reports the validation outcome without dumping the request, artifact content, credential values, or rendered messages.
Initial success presentation
Emit one small YAML report, defaulting to YAML and accepting explicit
--format yaml as the only format for this delivery:
prompt_id: briefing
valid: true
The ID identifies the command's requested prompt. valid means that validation
of the concrete request returned nil at this invocation; with the bundled
adapter, this is the result of native preparation. Builder guidance preserves
the selected prompt identity so the confirmation describes the operator's
selection. CommandKit adds no independent identity-validation layer.
Native validation supplies neither resolved version/profile metadata nor a prepared snapshot. The report therefore contains no such claims or discovery inventories. Failures use the existing diagnostic/error outcome path. Output delivery is part of invocation success. This initial presentation provides an explicit confirmation for operators and automation; additional formats can follow concrete needs.
Package and dependency ownership
All new provider contracts, request adaptation, standard handlers, and
presentation belong in integrations/ckprompt. Core continues to own generic
declarations, bindings, dispatch, and outcomes. ckconfig remains responsible
for its configuration adaptation; ckprompt can reuse it where appropriate.
Adopt the released PromptKit v0.15.0 module. Preserve the current Go minimum and dependency direction: ckprompt may depend on ckconfig; ckconfig and core do not acquire a PromptKit dependency. Use public upstream APIs and released module versions, without sibling-checkout requirements in committed manifests.
Work needed from the baseline
Dependency adoption
Update ckprompt's PromptKit requirement and checksums to v0.15.0. Review the
resulting selected module graph, and update the PromptKit attribution entry's
selected version and source/notice links to release commit
635674c066d0b432187f96e72b9c03d085443ac5. Update other attribution entries only
where the selected graph or incorporated material actually changes.
Integration API and native adapter
Add ValidationRequest, the optional suite callback, and the suite-returning
WithRequestValidation helper. Reuse existing engine-loading diagnostics and
supported error handling.
Support concrete request construction from typed application input and standard
CLI values, without copying PromptKit's preparation logic. Account for builder
failure and invalid loader wiring using the established integration conventions;
a nil builder uses the seed unchanged.
Keep current discovery and configuration adapters available independently.
FromEngine keeps its current capabilities. Validation is explicitly enabled
through the helper or a custom suite callback, with an application builder only
when additional mapping or defaults are needed.
Command assembly, bindings, and presentation
Extend ckprompt.Command to assemble the requirements group from its supplied
show and validate capabilities. Add the validate handler, file-binding syntax,
and YAML success report. Share common binding/report mechanics where
that simplifies the code, preserving the delivered discovery contract.
Maintained consumer and documentation
Extend the composed consumer with a synthetic concrete request builder and offline validation examples for a successful request and a missing required binding. Demonstrate the existing typed domain selectors alongside the new standard bindings. The example should make the different questions answered by show and validate clear without requiring a model service or real credential. Document the nil-builder path and a builder that preserves supplied map entries while adding defaults.
When implemented, update Go declarations/GoDoc, the CLI reference, consumer guide, and affected orientation/internal descriptions under their canonical owners. Describe native resource work and validation limits accurately, so statements about metadata inspection do not accidentally claim that concrete validation performs no rendering, artifact loading, or credential checks.
This capability fits the accepted optional-family and owning-library boundaries. No new ADR or revision to accepted ADRs is needed. The selected API and CLI details belong in their canonical documentation when implemented.
Completion evidence for implementation
Use proportionate public-boundary evidence for independent capability availability, help without acquisition, standard/typed input mapping, fresh invocation state, nil-builder forwarding, builder default precedence, retained wiring errors, builder-before-loader behavior, native validation error identity, and diagnostic/output delivery. Cover builder and loader failures and cancellation between stages. Use a real PromptKit engine for representative successful validation and a missing binding, with a model-call double that detects accidental execution. Protect continued successful discovery of a partial inventory; do not turn it into a validation failure.
Check semantic output rather than YAML key ordering or native error wording. Avoid duplicating PromptKit's complete preparation test suite. Validate the owning integration and affected composed consumer; check isolated module resolution and dependency boundaries for the upgrade, using the established workspace and disposable-module practices in the developer guide. Implementation evidence must not rely solely on a workspace that silently substitutes a sibling PromptKit checkout.
The decisions needed for an implementation plan are settled.