Document prompt requirement inspection

This commit is contained in:
2026-10-08 20:48:10 +00:00
parent ee0ad99e17
commit a051edd82d
6 changed files with 191 additions and 53 deletions

View File

@@ -154,6 +154,50 @@ rendered messages, and use prepared execution when that work must remain tied
to later execution. The method's [GoDoc](../../engine.go) owns exact fields,
hash, ownership, and error semantics.
## Inspect Requirements Before Preparation
Use [`Engine.InspectRequirements`](../../requirements.go) when the application
needs the prompt's recognizable input and caller-variable dependencies for a
particular context before it has input artifacts. Unlike `InspectPrompt`, it
also walks captured template syntax and selects a profile identity; unlike
`Prepare`, it does not load artifacts or render messages.
```go
requirements, err := engine.InspectRequirements(ctx, promptkit.RequirementInspectionRequest{
PromptID: "meeting.summary",
Vars: map[string]string{"audience": "project leads"},
})
if err != nil {
return err
}
for _, input := range requirements.Inputs {
if input.Kind == promptkit.RequirementRequired {
// Plan to bind this input before preparation.
}
if input.Kind == promptkit.RequirementOptional && input.Explanation != "" {
// A recognizable reference may occur only on a known skipped path.
}
}
for _, variable := range requirements.Variables {
if variable.Kind == promptkit.RequirementRequired && !variable.Supplied {
// The key is required even though this inspection did not receive it.
}
}
if !requirements.Complete {
// Preserve the partial inventory and account for its discovery issues.
}
```
Optional entries can include recognizable inputs and variables found only in
inactive branches. A missing required variable is reported separately from
discovery completeness: missing a value used only for direct text substitution
does not by itself make the inventory incomplete. Conversely, an unresolved
dependency-bearing branch can make discovery incomplete while retaining known
conditional entries. Do not treat an empty partial inventory as proof that no
input bindings are needed. See the method's [GoDoc](../../requirements.go) for
the exact classifications, supported syntax, ownership, and error contract.
## Prepare Without Model Execution
[`Engine.Prepare`](../../engine.go) resolves the selected prompt and profile,

View File

@@ -124,6 +124,12 @@ Optional `session_id` uses the same template variables and input helper. Its
rendered value is trimmed, omitted if empty, and limited to 256 Unicode code
points. A nonblank direct request session ID bypasses the template.
`InspectRequirements` can enumerate recognizable dependencies from captured
templates before artifact content is available. Its conservative syntax
coverage does not change template validity or rendering; see the [consumer
guide](consumers/pkg-promptkit.md#inspect-requirements-before-preparation) and
the method's [GoDoc](../requirements.go) for that inspection boundary.
### Cache Control
Optional `cache_control` has required `type: ephemeral` and optional `ttl`,

View File

@@ -416,6 +416,13 @@ suite solely for this unit; commit.
## Stage 7 — Document And Verify The Assembled Feature
**Status: Complete (2026-10-08).** GoDoc, consumer guidance, framework-format
cross-references, and the maintained preparation example now describe the
implemented discovery boundary. Final offline verification passed:
`go test -race -vet=off -mod=readonly -count=1 ./...`,
`go vet -mod=readonly ./...`, and `python3 scripts/check-examples.py`, with
`GOTOOLCHAIN=local GOWORK=off GOPROXY=off GONOPROXY=none GOSUMDB=off`.
**Outcome:** Implemented semantics and analysis limits are documented in their
canonical owners, with final verification ready for user review.

View File

@@ -1,8 +1,10 @@
# Effective Prompt Requirement Inspection
Status: Implementation planned; the feature is not implemented. The
[implementation plan](implementation.md) owns the ordered implementation steps.
This document owns the purpose, scope, and target end state.
Status: Implemented (2026-10-08). The
[implementation plan](implementation.md) records the completed work units and
final verification. Current public API details live in GoDoc; the consumer
guide, format reference, and internal documents link to those canonical
contracts.
## Purpose
@@ -16,7 +18,7 @@ The originating request is [Scriptorium's feature request](scriptorium-feature-r
Promptkit supplies application-neutral inspection; consumers own presentation,
binding checks, and their own workflows.
## Current Baseline
## Original Review Baseline
At review baseline `b595dca`:
@@ -183,28 +185,19 @@ metadata. A directly resolved input identifier may equal a supplied variable
value; reporting that binding name is necessary, without echoing its originating
expression or variable map.
## Work Needed From The Baseline
## Implemented Outcome
Add request/result values and the engine method through the root facade.
Keep selection coordination internal and place syntax discovery with the
existing prompt-template component. Reuse captured templates and selection
helpers rather than creating another preparation path.
The root API selects a captured prompt and profile identity, then returns
caller-owned input and variable inventories with conservative classifications
and structured discovery limitations. The internal prompt walker uses captured
template syntax and effective metadata; inspection does not enter artifact,
schema, credential, rendering, provider, or execution preparation.
Capture required metadata presence at the final ConfigKit handoff and retain it
alongside inspection snapshots. Apply [ADR 0001](../adr/0001-configuration-boundary.md):
interpret final effective definitions, not raw/intermediate configuration or
history. Operator provenance remains a separate explicit API.
Implement a small syntax walker, simple context tests, classification
aggregation, and safe limitation reporting. Keep request-specific state separate
from captured definitions. Focus tests on representative enumeration,
classifications, scope mistakes, selection, capture/ownership, and absence of
execution effects, under the [testing policy](../policy/testing.md). Do not test
a general interpreter or repeat ConfigKit's source-loading matrix.
When implementation lands, update GoDoc, the consumer guide, relevant format
cross-references, internal documents, and a maintained example. Canonical owners
document the implemented boundary; release notes summarize rather than own it.
The final verification and the commit sequence are recorded in the
[implementation plan](implementation.md). The exact public contract is owned by
[GoDoc](../../requirements.go); consumer usage, framework-format cross-reference,
and internal selection/source descriptions are maintained in their respective
documents.
## Acceptance Outcomes

View File

@@ -36,6 +36,17 @@ func main() {
if _, found := view.Explain(configkit.Keys("profiles", "offline-example", "model")); !found {
exit(fmt.Errorf("configured profile model was not found"))
}
requirements, err := engine.InspectRequirements(context.Background(), promptkit.RequirementInspectionRequest{
PromptID: "example.prepare",
})
if err != nil {
exit(err)
}
if !requirements.Complete || len(requirements.Inputs) != 1 ||
requirements.Inputs[0].Name != "note" ||
requirements.Inputs[0].Kind != promptkit.RequirementRequired {
exit(fmt.Errorf("required note input was not discovered before preparation"))
}
prepared, err := engine.Prepare(context.Background(), promptkit.RunRequest{
PromptID: "example.prepare",

View File

@@ -10,43 +10,74 @@ import (
// RequirementInspectionRequest selects a prompt and supplies caller context
// for requirement discovery. It requires no artifact bindings or credentials.
type RequirementInspectionRequest struct {
PromptID string
// PromptID is the exact, case-sensitive configured prompt ID.
PromptID string
// PromptVersion selects an exact version. An empty value uses the prompt
// selection rule that requires exactly one configured version.
PromptVersion string
ProfileID string
Vars map[string]string
SessionID string
// ProfileID selects the exact effective profile identity. If empty, the
// prompt's default profile is used; the profile need not be ready for
// execution.
ProfileID string
// Vars supplies string values for caller-variable lookups and simple
// conditions. Presence is determined by exact map key, including an empty
// value; values are not returned in the inspection.
Vars map[string]string
// SessionID, when nonblank after the normal session-ID normalization,
// bypasses the prompt's session template during discovery.
SessionID string
}
// RequirementKind describes how a selected prompt may depend on an input or
// caller variable for the supplied context.
// caller variable for the supplied context. It is independent of whether a
// caller-variable value is present.
type RequirementKind string
const (
// RequirementRequired identifies a declaration or definite access.
// RequirementRequired identifies an input declared required or a dependency
// reached by a recognized definite access.
RequirementRequired RequirementKind = "required"
// RequirementConditional identifies a dependency whose reachability is
// unresolved for the supplied context.
// RequirementConditional identifies a dependency whose reachability or
// identity remains unresolved for the supplied context.
RequirementConditional RequirementKind = "conditional"
// RequirementOptional identifies a dependency known not to be required,
// including a reference used only on a known skipped path.
// including a nonrequired declaration, optional lookup, or reference used
// only on a known skipped path.
RequirementOptional RequirementKind = "optional"
)
// InputRequirement describes a declared or template-referenced prompt input.
type InputRequirement struct {
Name string
Kind RequirementKind
Declared bool
// Name is the exact input identifier.
Name string
// Kind is the strongest classification established for this name.
Kind RequirementKind
// Declared reports whether the name appears in the effective input
// declarations; helper-only references have Declared false.
Declared bool
// ContentType preserves effective metadata presence: nil means absent or
// null, while a non-nil pointer may refer to an empty string.
ContentType *string
// Description preserves effective metadata presence independently of
// Explanation, including an explicitly empty string.
Description *string
// Explanation is safe human-readable context, such as why a reference is
// optional because it occurs only on a known skipped path. It is not a
// machine-readable contract.
Explanation string
}
// VariableRequirement describes a caller-map key referenced by a prompt.
type VariableRequirement struct {
Name string
Kind RequirementKind
Supplied bool
// Name is the exact caller-map key, not a template-local name.
Name string
// Kind is the strongest classification established for this key.
Kind RequirementKind
// Supplied reports exact-key presence in the request Vars map. An empty
// string is supplied; the value itself is not returned.
Supplied bool
// Explanation is safe human-readable context and is not a machine-readable
// contract.
Explanation string
}
@@ -54,31 +85,67 @@ type VariableRequirement struct {
type RequirementIssueCode string
const (
RequirementIssueMissingVariable RequirementIssueCode = "missing_variable"
RequirementIssueInputContent RequirementIssueCode = "input_content"
RequirementIssueDynamicInputName RequirementIssueCode = "dynamic_input_name"
// RequirementIssueMissingVariable means an absent caller value prevents
// resolving a dependency decision or input name.
RequirementIssueMissingVariable RequirementIssueCode = "missing_variable"
// RequirementIssueInputContent means unavailable input content affects
// dependency discovery.
RequirementIssueInputContent RequirementIssueCode = "input_content"
// RequirementIssueDynamicInputName means a referenced input name cannot be
// determined for the supplied context.
RequirementIssueDynamicInputName RequirementIssueCode = "dynamic_input_name"
// RequirementIssueUnsupportedConstruct means a dependency-relevant
// expression or scope is outside the supported discovery analysis.
RequirementIssueUnsupportedConstruct RequirementIssueCode = "unsupported_construct"
)
// RequirementIssue describes one discovery limitation using safe summary
// text. Descriptions are not intended as machine-readable details.
type RequirementIssue struct {
Code RequirementIssueCode
InputName string
// Code is the machine-readable limitation category.
Code RequirementIssueCode
// InputName identifies an affected input when one is known; empty means no
// particular input was identified.
InputName string
// VariableName identifies an affected caller key when one is known; empty
// means no particular key was identified.
VariableName string
Description string
// Description is a safe human-readable summary, not a string to parse.
Description string
}
// RequirementInspection contains discovered dependencies for one selected
// prompt context. Complete describes discovery, not renderability or readiness.
// prompt context. Input and variable entries are sorted by exact name. Issues
// are deduplicated by code and associated names, then sorted by that tuple.
// Their descriptions and entry explanations are safe summaries, not
// machine-readable details.
//
// Complete describes the supported analysis's discovery coverage, not whether
// requirements are supplied, templates will render, artifacts or schemas are
// valid, or execution is ready. A complete result can still contain required
// inputs or missing required variables. Valid templates using dependency-
// relevant constructs outside the deliberately small recognition scope may
// produce useful partial inventories with Complete false; this is not a
// template-definition error or an authoring prohibition.
type RequirementInspection struct {
PromptID string
// PromptID is the selected exact prompt ID.
PromptID string
// PromptVersion is the selected exact prompt version.
PromptVersion string
ProfileID string
Inputs []InputRequirement
Variables []VariableRequirement
Complete bool
Issues []RequirementIssue
// ProfileID is the selected profile identity; it does not indicate
// execution readiness.
ProfileID string
// Inputs contains declared inputs and recognizable template references,
// including entries known to be optional.
Inputs []InputRequirement
// Variables contains recognizable caller-map keys, including keys used
// only on known skipped paths.
Variables []VariableRequirement
// Complete reports discovery coverage, not requirement satisfaction or
// execution readiness.
Complete bool
// Issues contains deduplicated, structured discovery limitations.
Issues []RequirementIssue
}
// InspectRequirements discovers inputs and caller variables for one selected
@@ -89,6 +156,14 @@ type RequirementInspection struct {
// captured session template. Vars are copied before analysis, and returned
// slices and metadata pointers belong to the caller.
//
// Discovery recognizes literal input names, direct caller-map keys, literal-
// key root-map lookups, simple caller-derived input names, and if/with
// conditions based on direct values, literals, negation, or two-string
// equality/inequality. It classifies recognizable references on known skipped
// paths as optional. Other dependency-relevant expressions or scopes may leave
// a useful partial result with Complete false. The returned inventories do
// not reject otherwise valid templates or prohibit their use.
//
// This method does not perform artifact, schema, credential, rendering,
// validation, capacity, observation, provider, or execution work. Complete
// describes dependency discovery, not whether every dependency is supplied or
@@ -98,7 +173,9 @@ type RequirementInspection struct {
// selection match ErrInvalidRequest; an absent profile selection also matches
// ErrProfileRequired. Missing prompt/profile IDs match ErrPromptNotFound or
// ErrProfileNotFound. Other selection/snapshot failures match ErrPromptLoad or
// ErrProfileLoad. A nil Engine matches ErrInvalidConfig.
// ErrProfileLoad. When cancellation prevents lookup or analysis, no inspection
// is returned and the context error remains discoverable with errors.Is. A nil
// Engine matches ErrInvalidConfig.
func (e *Engine) InspectRequirements(
ctx context.Context,
req RequirementInspectionRequest,