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, to later execution. The method's [GoDoc](../../engine.go) owns exact fields,
hash, ownership, and error semantics. 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 ## Prepare Without Model Execution
[`Engine.Prepare`](../../engine.go) resolves the selected prompt and profile, [`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 rendered value is trimmed, omitted if empty, and limited to 256 Unicode code
points. A nonblank direct request session ID bypasses the template. 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 ### Cache Control
Optional `cache_control` has required `type: ephemeral` and optional `ttl`, 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 ## 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 **Outcome:** Implemented semantics and analysis limits are documented in their
canonical owners, with final verification ready for user review. canonical owners, with final verification ready for user review.

View File

@@ -1,8 +1,10 @@
# Effective Prompt Requirement Inspection # Effective Prompt Requirement Inspection
Status: Implementation planned; the feature is not implemented. The Status: Implemented (2026-10-08). The
[implementation plan](implementation.md) owns the ordered implementation steps. [implementation plan](implementation.md) records the completed work units and
This document owns the purpose, scope, and target end state. final verification. Current public API details live in GoDoc; the consumer
guide, format reference, and internal documents link to those canonical
contracts.
## Purpose ## Purpose
@@ -16,7 +18,7 @@ The originating request is [Scriptorium's feature request](scriptorium-feature-r
Promptkit supplies application-neutral inspection; consumers own presentation, Promptkit supplies application-neutral inspection; consumers own presentation,
binding checks, and their own workflows. binding checks, and their own workflows.
## Current Baseline ## Original Review Baseline
At review baseline `b595dca`: 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 value; reporting that binding name is necessary, without echoing its originating
expression or variable map. expression or variable map.
## Work Needed From The Baseline ## Implemented Outcome
Add request/result values and the engine method through the root facade. The root API selects a captured prompt and profile identity, then returns
Keep selection coordination internal and place syntax discovery with the caller-owned input and variable inventories with conservative classifications
existing prompt-template component. Reuse captured templates and selection and structured discovery limitations. The internal prompt walker uses captured
helpers rather than creating another preparation path. 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 The final verification and the commit sequence are recorded in the
alongside inspection snapshots. Apply [ADR 0001](../adr/0001-configuration-boundary.md): [implementation plan](implementation.md). The exact public contract is owned by
interpret final effective definitions, not raw/intermediate configuration or [GoDoc](../../requirements.go); consumer usage, framework-format cross-reference,
history. Operator provenance remains a separate explicit API. and internal selection/source descriptions are maintained in their respective
documents.
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.
## Acceptance Outcomes ## Acceptance Outcomes

View File

@@ -36,6 +36,17 @@ func main() {
if _, found := view.Explain(configkit.Keys("profiles", "offline-example", "model")); !found { if _, found := view.Explain(configkit.Keys("profiles", "offline-example", "model")); !found {
exit(fmt.Errorf("configured profile model was not 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{ prepared, err := engine.Prepare(context.Background(), promptkit.RunRequest{
PromptID: "example.prepare", PromptID: "example.prepare",

View File

@@ -10,43 +10,74 @@ import (
// RequirementInspectionRequest selects a prompt and supplies caller context // RequirementInspectionRequest selects a prompt and supplies caller context
// for requirement discovery. It requires no artifact bindings or credentials. // for requirement discovery. It requires no artifact bindings or credentials.
type RequirementInspectionRequest struct { 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 PromptVersion string
ProfileID string // ProfileID selects the exact effective profile identity. If empty, the
Vars map[string]string // prompt's default profile is used; the profile need not be ready for
SessionID string // 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 // 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 type RequirementKind string
const ( 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" RequirementRequired RequirementKind = "required"
// RequirementConditional identifies a dependency whose reachability is // RequirementConditional identifies a dependency whose reachability or
// unresolved for the supplied context. // identity remains unresolved for the supplied context.
RequirementConditional RequirementKind = "conditional" RequirementConditional RequirementKind = "conditional"
// RequirementOptional identifies a dependency known not to be required, // 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" RequirementOptional RequirementKind = "optional"
) )
// InputRequirement describes a declared or template-referenced prompt input. // InputRequirement describes a declared or template-referenced prompt input.
type InputRequirement struct { type InputRequirement struct {
Name string // Name is the exact input identifier.
Kind RequirementKind Name string
Declared bool // 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 ContentType *string
// Description preserves effective metadata presence independently of
// Explanation, including an explicitly empty string.
Description *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 Explanation string
} }
// VariableRequirement describes a caller-map key referenced by a prompt. // VariableRequirement describes a caller-map key referenced by a prompt.
type VariableRequirement struct { type VariableRequirement struct {
Name string // Name is the exact caller-map key, not a template-local name.
Kind RequirementKind Name string
Supplied bool // 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 Explanation string
} }
@@ -54,31 +85,67 @@ type VariableRequirement struct {
type RequirementIssueCode string type RequirementIssueCode string
const ( const (
RequirementIssueMissingVariable RequirementIssueCode = "missing_variable" // RequirementIssueMissingVariable means an absent caller value prevents
RequirementIssueInputContent RequirementIssueCode = "input_content" // resolving a dependency decision or input name.
RequirementIssueDynamicInputName RequirementIssueCode = "dynamic_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" RequirementIssueUnsupportedConstruct RequirementIssueCode = "unsupported_construct"
) )
// RequirementIssue describes one discovery limitation using safe summary // RequirementIssue describes one discovery limitation using safe summary
// text. Descriptions are not intended as machine-readable details. // text. Descriptions are not intended as machine-readable details.
type RequirementIssue struct { type RequirementIssue struct {
Code RequirementIssueCode // Code is the machine-readable limitation category.
InputName string 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 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 // 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 { type RequirementInspection struct {
PromptID string // PromptID is the selected exact prompt ID.
PromptID string
// PromptVersion is the selected exact prompt version.
PromptVersion string PromptVersion string
ProfileID string // ProfileID is the selected profile identity; it does not indicate
Inputs []InputRequirement // execution readiness.
Variables []VariableRequirement ProfileID string
Complete bool // Inputs contains declared inputs and recognizable template references,
Issues []RequirementIssue // 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 // 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 // captured session template. Vars are copied before analysis, and returned
// slices and metadata pointers belong to the caller. // 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, // This method does not perform artifact, schema, credential, rendering,
// validation, capacity, observation, provider, or execution work. Complete // validation, capacity, observation, provider, or execution work. Complete
// describes dependency discovery, not whether every dependency is supplied or // 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 // selection match ErrInvalidRequest; an absent profile selection also matches
// ErrProfileRequired. Missing prompt/profile IDs match ErrPromptNotFound or // ErrProfileRequired. Missing prompt/profile IDs match ErrPromptNotFound or
// ErrProfileNotFound. Other selection/snapshot failures match ErrPromptLoad 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( func (e *Engine) InspectRequirements(
ctx context.Context, ctx context.Context,
req RequirementInspectionRequest, req RequirementInspectionRequest,