Simplify requirement inspection plan

This commit is contained in:
2026-10-08 19:58:57 +00:00
parent e459c7d54b
commit a6e0ffea8f
2 changed files with 518 additions and 712 deletions

View File

@@ -1,55 +1,46 @@
# Effective Prompt Requirement Inspection Implementation Plan
Implement the [feature roadmap](inspection.md) in order. Each numbered stage
is scoped for one Luna coding-agent prompt. This plan replaces the completed
ReleaseKit adoption plan; that plan remains available in Git history.
Implement the [feature roadmap](inspection.md) in order. Each numbered stage is
scoped for one Luna coding-agent prompt. This plan replaces the earlier
interpreter-style analysis scope with simple enumeration and conservative
classification. The completed ReleaseKit plan remains available in Git history.
## Baseline And Working Rules
- Baseline: `7827ca043b5de84a18199ac8f4b3056691aacbb5`. There is no effective
requirement-inspection API yet. `InspectPrompt` reports declarations;
`Prepare` needs artifacts. The configured engine captures compiled templates.
- The agreed scope is the practical analysis core, with statically identifiable
inactive undeclared input references included as optional inventory entries
and explained as inactive. Distinguish this inventory from applicable
dependencies; it does not require evaluating skipped expressions or adopting
broader Go-template interpretation.
- Start with [development](../development.md), then read the
- Planning baseline: `e459c7d`. The inspection API is not implemented.
`InspectPrompt` reports declarations, `Prepare` needs artifacts, and the
configured engine already captures compiled templates.
- Start with [development](../development.md), then the
[agent](../policy/agents.md), [architecture](../policy/architecture.md),
[testing](../policy/testing.md), and [documentation](../policy/documentation.md)
policies. Reuse unchanged policies already read. Follow
[ADR 0001](../adr/0001-configuration-boundary.md): interpret final effective
configuration at the ConfigKit handoff, never raw/intermediate sources or
configuration history.
- Inspect the current worktree before each stage; preserve unrelated work.
Validate and commit each completed unit under the agent policy. Keep current
internal documentation accurate as each internal capability lands.
- Preserve all existing inspection, construction, preparation, rendering, and
execution contracts. Discovery limitations must not invalidate templates.
Do not add variable defaults, input schemas, name restrictions, count/size
limits, a branch-expansion budget, or generation-readiness checks.
- No provider calls, actual publication, new dependency, CLI/HTTP layer, upstream
ConfigKit change, or application-specific pipeline concept is needed.
- Public declarations belong in the root facade; analysis belongs in
`internal/prompt`, selection coordination in `internal/usecase`, and metadata
capture in `internal/configuration`. Runtime/domain values remain independent
of ConfigKit. Use the standard parser and `template.IsTrue`; do not implement
a replacement template language or execute whole prompts with fake artifacts.
policies. Reuse unchanged policies already read.
- Follow [ADR 0001](../adr/0001-configuration-boundary.md): interpret only final
effective configuration at the ConfigKit handoff. Operator provenance is
separate; inspection does not examine raw sources or intermediate values.
- Preserve unrelated work and commit completed, verified units under the agent
policy. Keep implemented internal documentation accurate as stages land.
- Preserve existing construction, inspection, preparation, rendering, and
execution contracts. An inspection limitation is not a definition error.
- Use captured parsed syntax, direct traversal, and maps keyed by exact name.
No general evaluator, assignment-value propagation, branch-environment joins,
possible-name sets, branch combinations, analysis budget, or global cache is
needed. Do not add caller limits, defaults, input schemas, or authoring bans.
- The root package owns public declarations/conversion. `internal/prompt`
owns discovery, `internal/usecase` coordinates selection, and
`internal/configuration` supplies captured syntax/metadata. Runtime/domain
values remain independent of ConfigKit.
- No new dependency, upstream ConfigKit change, provider access, CLI/HTTP
feature, application wire format, or publication is part of these stages.
## Concrete API And Result Decisions
## Concrete Public Values
Add root `requirements.go` in Stage 8. The method is:
Add root `requirements.go` in Stage 5:
```go
func (e *Engine) InspectRequirements(
ctx context.Context, req RequirementInspectionRequest,
) (*RequirementInspection, error)
```
Define ordinary root-owned values, mirrored by execution-owned internal values
in `internal/domain/requirements.go`; do not export aliases to internal types:
```go
type RequirementInspectionRequest struct {
PromptID string
PromptVersion string
@@ -58,7 +49,7 @@ type RequirementInspectionRequest struct {
SessionID string
}
type RequirementKind string // constants: required, optional, conditional
type RequirementKind string // values: required, conditional, optional
type InputRequirement struct {
Name string
@@ -70,9 +61,10 @@ type InputRequirement struct {
}
type VariableRequirement struct {
Name string
Kind RequirementKind
Supplied bool
Name string
Kind RequirementKind
Supplied bool
Explanation string
}
type RequirementIssueCode string
@@ -95,495 +87,355 @@ type RequirementInspection struct {
}
```
Use `RequirementRequired`, `RequirementOptional`, and
`RequirementConditional` as the kind constant names. Use these issue constants:
Use root-owned values and internal counterparts in
`internal/domain/requirements.go`; do not expose internal aliases.
Kind constants are `RequirementRequired`, `RequirementConditional`, and
`RequirementOptional`. Retain four useful issue categories:
| Constant | Value | Meaning when discovery is affected |
| Constant | Value | Discovery limitation |
| --- | --- | --- |
| `RequirementIssueMissingVariable` | `missing_variable` | An absent caller value prevents resolving a dependency decision/name. |
| `RequirementIssueInputContent` | `input_content` | Unavailable artifact content affects further dependencies. |
| `RequirementIssueDynamicInputName` | `dynamic_input_name` | A helper's exact selected name remains unresolved. |
| `RequirementIssueUnsupportedConstruct` | `unsupported_construct` | A construct outside the analysis core prevents discovery. |
| `RequirementIssueExpressionUnresolved` | `expression_unresolved` | An expression cannot be evaluated within the supported semantics, including an incompatible operation. |
| `RequirementIssueMissingVariable` | `missing_variable` | An absent value prevents determining a dependency decision/name. |
| `RequirementIssueInputContent` | `input_content` | Unavailable artifact content affects dependencies. |
| `RequirementIssueDynamicInputName` | `dynamic_input_name` | A referenced input name cannot be determined. |
| `RequirementIssueUnsupportedConstruct` | `unsupported_construct` | Analysis does not cover a dependency-relevant expression or scope. |
Use safe, fixed-category descriptions, optionally identifying reported names.
Do not expose the expression, its literals unrelated to identifiers, its
evaluated values, raw template text, or a Go template execution error. An empty
associated name means no particular named entry is identified; it does not
establish that an input/variable is absent.
An empty associated name means that no particular entry was identified.
Descriptions and explanations are safe human-readable summaries, not strings
callers must parse. There is no expression tree, source offset, JSON tag/schema,
or stable diagnostic wording contract.
### Classification, Metadata, And Ownership
### Classification And Ownership
- Always report declarations. A declared required input stays required.
Active template accesses can promote an optional declaration to required.
Include statically identifiable undeclared references on known inactive paths
as optional entries with an inactive-use explanation.
- An entry is required when its use is established for the context, conditional
when a possible use depends on unresolved analysis, and optional when no
applicable use needs it and unresolved effects do not undermine that conclusion.
An established required use dominates conditional/optional uses.
- `Explanation` is a safe human-readable summary of inactive-only use, separate
from declared `Description`. Its wording is not a machine-readable contract.
Do not add an uncertainty issue for known inactivity. Required or conditional
uses elsewhere override inactive-only classification and explanation.
Caller variables used solely on known inactive paths are not requirements.
- Aggregate inputs and variables by exact execution name. Do not trim request
variable keys or discovered helper names. Configuration normalization remains
with the existing configuration adapter.
- Metadata pointers distinguish absent/cleared metadata from an explicit
value, including an explicitly empty value. `Declared` distinguishes a known
declaration from a helper-only reference. No new input-format enforcement.
- `Supplied` is key presence in the caller's variable map, including an empty
string. A required variable's presence satisfies that known presence
requirement; a conditional variable's necessity is unresolved. Optional
lookup absence is not a missing required value. Do not infer Boolean values,
nonblank constraints, or other variable validation from names.
- Sort inputs and variables lexicographically by exact name. Deduplicate issues
by code/input-name/variable-name and sort by that tuple. Diagnostics need no
public expression tree, node offsets, or JSON tags. No stable wire format or
nil-versus-empty slice distinction is promised.
- Return independent slices and metadata pointers; copy request variables at the
facade like `RunRequest`. Never expose or mutate shared template trees.
Callers retain the existing responsibility for concurrent mutation of requests.
- Seed declared inputs from final effective definitions. Required declarations
stay required independently of syntax. Others start optional.
- Collect occurrences with definite, possible, or inactive reachability.
Definite helper/direct required-variable accesses are required; possible ones
are conditional; recognizable inactive references are optional. A lookup
that execution permits to be absent remains optional unless another access
establishes a stronger requirement.
- Aggregate exact names with required > conditional > optional. No proof that
a name in both unresolved branches is required is necessary: conditional is
acceptable until an independent definite access/declaration establishes it.
- Include recognizable inactive inputs and caller variables in both inventories.
Give inactive-only entries an `Explanation`, independent of declared input
`Description`. Any applicable occurrence, including an optional lookup,
removes the inactive-only explanation; required/conditional uses also
override optional classification. Inactivity is not an uncertainty issue.
- Input metadata pointers distinguish absence/null from an explicit string,
including an empty string. `Declared` distinguishes metadata ownership from
a helper-only reference. Do not infer new input formats.
- `Supplied` means presence of the exact caller key, including empty strings.
It is independent of classification and completeness; do not echo values.
- Sort input and variable entries lexically by exact name. Deduplicate issues
by code/input-name/variable-name and sort by that tuple. Do not promise a
nil-versus-empty slice distinction.
- Return independent slices and metadata pointers, copy request variables at
the facade as for `RunRequest`, and keep captured syntax immutable. Retain
callers' existing responsibility for concurrently mutating requests.
### Completeness And Uncertainty
### Deliberately Small Recognition Scope
`Complete` concerns discovery of both input and variable requirements, not
whether they are satisfied. `{{.audience}} {{input "note"}}` can yield complete
discovery with a missing required `audience` and known required `note`.
Reading the body of `note` is unnecessary to identify that dependency.
An empty complete input set, or a complete inventory containing only optional
entries, establishes that no artifact bindings are needed. Nonempty inventory
alone does not establish that artifacts are needed; incomplete discovery does
not establish their absence.
Use the standard template parse tree and `template.IsTrue` for known values.
Only determine a value when needed for a recognized name or simple condition.
Unknown values carry their cause internally. Emit issues and mark incomplete
when uncertainty affects a dependency decision/name, or an opaque construct
can hide requirements. Unknown output text alone does not cause incompleteness.
An unknown branch containing only text need not make discovery incomplete.
For an unresolved branch, analyze both paths once and combine access summaries:
a name definitely needed on both paths can stay required; names possible on
only some paths are conditional. Merge branch-local unresolved issues.
Compare applicable requirement summaries, not inactive inventory or rendered
content, and do not enumerate combinations of independent conditions.
Conservative incompleteness is valid
where the core cannot prove an exact set; do not claim completeness there.
Unresolved dynamic names/opaque input effects must not leave otherwise optional
input entries falsely definitive. Mark affected optional entries conditional,
including previously inactive-only undeclared entries; an unrestricted unknown
input name can affect all such entries. Clear inactive-only explanations where
possible active uses undermine them.
An unknown-name issue also describes potential undiscovered undeclared names.
Known required declarations remain required.
### Initial Analysis Core
Analyze each selected template's executable root. Definitions that are never
invoked do not themselves create requirements.
| Construct | Initial handling |
| Syntax | Initial handling |
| --- | --- |
| Text/comments and sequences | No text output; visit executable actions in order. |
| String, Boolean, nil literals; dot/root | Track known values needed for dependencies; root is the supplied string map. |
| Root-map field access, including `$.name` | Record a required named caller variable; absent key produces an unknown value, not an inspection error. |
| Dot inside `with` | Follow the selected pipeline value; do not reinterpret string fields as caller root keys. |
| `index` on the root map with a known string key | Record an optional named lookup, unless another use requires it; absent lookup yields the Go string zero value. |
| Local variables | Single-variable declarations and assignments, root `$`, aliases, lexical scope, and pipeline results; locals are not caller variables. |
| `input` | Known string names, including direct variable/alias-derived names; record the dependency and return an unknown body value. |
| Pipelines/parenthesized expressions | Respect argument order and the previous value's final-argument position. |
| `and`, `or`, `not` | Go truthiness and short-circuit access semantics; return Go-compatible operand/Boolean values. |
| `eq`, `ne`, `lt`, `le`, `gt`, `ge` | String comparisons, including `eq`'s multiple comparison operands; all ordinary arguments are evaluated as Go does. |
| `if`, `else`, `with` | Known-path selection and unresolved-path joining, with correct dot/local scope. |
| Numeric operations, other built-ins, ranges, invoked named templates, other constructs outside the core | Keep discovered dependencies and expose uncertainty when they affect discovery. Do not reject the definition or execute loops/calls speculatively. |
| Literal `input "name"` | Collect the exact name without reading its body. Parenthesized literal wrappers and a literal piped directly to `input` are equivalent. |
| Direct root `.name` or `$.name` | Collect a required caller key when active; supplied strings are known, absence is unresolved rather than an inspection error. |
| `index` of the root map with one literal string key | Collect an optional lookup; its value is supplied text or the string zero value if absent, following execution. |
| `input .name`, `input $.name`, or `input (index . "name")` | Resolve a directly available string as an input identifier on applicable paths. |
| One simple value piped directly to `input`, or parenthesized simple expressions | Recognize the same bounded forms without evaluating arbitrary pipelines. |
| String/Boolean/nil literals and simple direct values | Use known values for conditions; no coercion of caller strings. |
| `not` of a simple predicate; two-string `eq`/`ne` | Recognize negation and ordinary equality/inequality without adding the full comparison family. |
| `if`/`else`, `with` | Classify branch occurrences using a recognized condition; unresolved branches are possible, not guesses. |
| Locals, changed dot, and root binding | Retain scope identity sufficient to distinguish root-map accesses from local/string/unknown values; no local-value propagation is required. |
| Other pipelines/functions, `and`/`or`, assignments, ranges, invoked named templates, or complex names | Collect safely identifiable references conservatively and record dependency-relevant limitations; do not interpret their execution. |
A numeric literal or unsupported pure output expression is not itself a caller
requirement. Discover its evaluated arguments where that is safe; its value can
remain unknown until a later dependency decision needs it. Known short-circuit
paths must not collect dependencies from skipped operands or branches.
They can contribute optional inactive inventory as described below, without
collecting skipped caller-variable requirements or uncertainty.
An unsupported expression does not automatically make every argument a definite
access: short-circuiting or unknown scopes can make references merely possible.
Keep complex forms outside complete analysis instead of emulating their rules.
Do not interpret `$local` as a caller key or `.field` on a string/unknown dot as
a root lookup. Explicit root accesses remain recognizable only while the root
binding's identity is known. Unsupported changes to a binding make its identity
unknown where affected; no assignment-value or cross-branch reconstruction is
needed.
For a known inactive path, use a non-evaluating syntax walk to collect literal
`input` names, including equivalent parenthesized/piped literal forms. Walk
skipped branch bodies and short-circuit operands without reading caller values,
mutating local environments, or invoking templates/functions. Unresolved
dynamic names and unsupported expressions in these skipped paths do not make
discovery incomplete. Never-invoked definitions and a directly bypassed session
template remain outside this inventory. Track inactive-only occurrences
separately from definite/possible uses so aggregation can classify a shared
name correctly.
Walk known inactive subtrees for syntactically identifiable literal input names
and caller keys. Do not evaluate skipped expressions, resolve skipped dynamic
input names, or produce uncertainty solely from skipped constructs. Names
hidden behind unsupported scopes need not be invented. Exclude bypassed session
templates and never-invoked definitions entirely.
Use a small private known/unknown value model. Branch joins retain identical
known values and can retain finite possible string names from direct
string-valued branch assignments. Different possible names are conditional;
a missing variable/body or opaque value can introduce other unknown names.
Do not evaluate cross-products of possible expression values or build a symbolic
predicate system. Unsupported scopes must not invent root variable names:
preserve explicit root accesses and literal helper references where identifiable,
and report incompleteness for the rest.
### Simple Algorithm And Completeness
## Stage 1 — Retain Effective Declaration Metadata And Inspection Snapshots
1. Seed declaration entries and visit each selected template.
2. Maintain only reachability and the scope identity needed to recognize caller
keys; collect names and minimal classification evidence in indexed summaries.
3. For simple conditions, visit both branches with definite/possible/inactive
reachability as appropriate. A possible parent never becomes definite just
because an inner predicate is known true; a known inactive parent stays
inactive. No branch environment or assignment-value joins are needed.
4. Aggregate/finalize names, metadata, presence, explanations, and limitations.
**Outcome:** Inspection can obtain captured templates and metadata without
changing execution definitions, hashes, or existing public inspection values.
Use shared traversal helpers for active/possible/inactive syntax rather than
separate interpreters. Do not enumerate execution combinations. Prefer a direct
pass through syntax and final aggregation; no special machinery is warranted
by ordinary prompt sizes.
`Complete` means that the supported analysis established the requirement
inventory/classifications. Conservative false is acceptable for difficult cases.
Missing direct text-substitution values alone do not make discovery incomplete.
Unresolved branches with dependency-bearing references return conditional
entries and incomplete status; no proof of identical branches is required.
Unknown pure text without a dependency effect need not change completeness.
An unsupported call that may hide dependencies or an unresolved active input
name makes discovery incomplete. Preserve recognized names; do not create a
wildcard entry. Maintain simple flags for possible unknown input/caller-key
effects, promoting affected optional entries to conditional at finalization,
rather than repeatedly rescanning the inventory. Known required entries remain
required. Clear inactive-only explanations where those unknown effects undermine
the conclusion.
A complete empty or all-optional input inventory needs no artifact bindings.
An incomplete inventory does not prove that no others are needed. A complete
result is not a rendering/readiness/provider guarantee.
## Stage 1 — Capture Effective Metadata And Template Snapshots
**Outcome:** The analyzer can obtain existing compiled templates and declaration
metadata without artifact reads or changing old inspection/execution behavior.
**Read:** `internal/configuration/load.go`, `prompts.go`,
`internal/prompt/template.go`, `internal/domain/domain.go`,
`internal/promptdef/hash.go`, and the source document.
`internal/promptdef/hash.go`, and the internal source document.
**Work:**
1. Add internal metadata/snapshot values. Retain content-type/description
presence alongside `promptEntry`, not inside the JSON-hashed
`domain.PromptDefinition` or the existing public `PromptInputDefinition`.
1. Retain content-type/description presence alongside configuration
`promptEntry`, rather than changing the hashed prompt definition or old
public inspection shape.
2. Capture effective per-input presence from `Resolution.InputInfo` during the
existing Resolve callback before `resolvePrompts` can record normalized
sequences. Use final prompt-ID/version/input-index paths only, following the
existing backend/profile presence-capture pattern. Do not inspect history.
3. Materialize metadata values from the normalized final declaration. Treat
absent/null as no metadata and an explicit empty string as available metadata;
preserve existing accepted/rejected configuration semantics.
4. Define `prompt.RequirementSnapshot` containing compiled templates and
declaration metadata, plus the narrow internal lookup
`GetRequirementSnapshot(id, version string) (RequirementSnapshot, bool)`.
Implement it on the captured configuration adapter. Use exact selected keys
and read-only template access; do not parse/reopen content during lookup.
5. Keep the existing renderer's lookup and all old constructor/API paths intact.
Update the internal source document for the implemented metadata capability.
existing Resolve callback before prompt normalization. Use final
prompt-ID/version/input-index paths, following the existing presence-capture
pattern. Materialize values from the normalized final declaration.
3. Add `prompt.RequirementSnapshot` with captured templates and metadata, and
narrow internal `GetRequirementSnapshot(id, version string)
(RequirementSnapshot, bool)` access on the captured configuration adapter.
Use selected exact keys, without reparsing or reopening resources.
4. Preserve renderer/constructor paths and update internal source documentation.
**Verify:** Focused configuration tests cover absent, empty, nonempty, and
normalization-sensitive metadata, with real final-value layering/resource capture.
Confirm old inspection shape and definition hashes remain unchanged for equivalent
existing definitions. Run `go test ./internal/configuration ./internal/promptdef ./internal/prompt`.
Commit this unit; do not implement the public method yet.
## Stage 2 — Add Internal Result Values And Literal Dependency Discovery
**Outcome:** Straight-line literal helper requirements and declarations are
discoverable through an internal analyzer.
**Work:**
1. Add internal counterparts of the result kinds, metadata, and issue values
defined above in `internal/domain/requirements.go`.
2. Add `internal/prompt/requirements.go` and focused tests. Provide one analyzer
entry taking context, selected definition, captured snapshot, variables, and
whether session-template analysis is bypassed; return internal analysis data.
Selected public identities will be added by the runner later.
3. Seed declarations, walk executable root actions, recognize literal
`input "name"` calls, deduplicate names, and aggregate required accesses.
Analyze the session first when applicable and messages in their existing order.
4. Add aggregation/finalization helpers for classification, independent metadata,
ordering, safe explanations/issue construction, and `Complete`. Keep
inactive-only occurrence information separate from applicable accesses for
Stage 5. Do not produce prompt text.
5. Until later stages implement control flow, return conservative incomplete
results for constructs that can hide dependencies. Temporary partial coverage
is internal implementation status, not a new public promise.
6. Check context at ordinary traversal boundaries; return the context error,
not a successful partial result after cancellation. Do not introduce workers,
hard analysis limits, or stronger interruption promises.
7. Describe implemented analysis ownership in the component/source documents.
**Verify:** Straight-line declared/undeclared references, duplicates, declared
required unused inputs, optional unused declarations, literal body independence,
empty input-free templates, and session-only references. Run
`go test ./internal/prompt ./internal/domain`; commit.
## Stage 3 — Resolve Caller Values, Map Lookups, And Local Aliases
**Outcome:** Caller variable requirements are distinct from input requirements;
simple names and pipeline values can be tracked without rendering.
**Work:**
1. Implement private value/environment records for known strings/Booleans/nil,
root-map identity, local symbols, and unknown values with causes.
2. Handle direct root field accesses and `$.name`, absent versus empty string,
root-map `index` with known string keys, local declarations/assignments, and
parenthesized/pipeline argument binding.
3. Caller requirements arise from accesses to the root variable map, not local
symbol names. Direct field access is required; optional `index` access is
promoted if another active use requires that same caller key.
4. Keep missing values unknown without aborting discovery. Do not mark incomplete
solely because a text-substitution variable is absent.
5. Preserve exact keys and values internally; return only requirement metadata
and key-presence information. Add no coercion, trimming, or field-name limits.
**Verify:** Direct supplied/missing/empty values, optional lookups, duplicate
access promotion, root versus local names, and pipeline argument position.
Use small comparisons with real Go-template execution for presence behavior;
do not duplicate the standard library's entire test matrix. Run
`go test ./internal/prompt`; commit.
## Stage 4 — Add Core Comparisons And Short-Circuit Semantics
**Outcome:** Common context expressions select values and dependencies consistently
with Go templates.
**Work:**
1. Implement `not`, string comparison families, and the operand-returning
`and`/`or` behavior specified in the core table. Use `template.IsTrue`.
2. Evaluate ordinary command arguments in order; `eq`'s multiple comparison
operands are not an `or` shortcut. Short-circuit only the appropriate
`and`/`or` operands.
3. Once a preceding operand leaves access uncertain, treat later potentially
evaluated operands conditionally rather than definitely accessed or skipped.
Preserve unknown causes for later dependency decisions.
Stage 5 adds optional inventory for definitely skipped helper expressions;
these are not applicable input or variable dependencies.
4. Keep unsupported/incompatible evaluation as an internal unknown value with
a safe category. Do not leak values through errors or convert it into a new
construction/request rejection.
5. Inspect safe eager arguments of out-of-core built-ins for dependencies.
Their output may remain unknown; never execute arbitrary functions or whole
templates merely to determine a dependency.
**Verify:** Empty/nonempty strings, the truthiness of the string `"false"`,
comparisons, multi-operand `eq`, and `and`/`or` skips containing helper calls
or missing variables. Compare representative supported expressions with the
existing Go renderer. Run `go test ./internal/prompt`; commit.
## Stage 5 — Analyze Known Branches, Scope, And Inactive Inventory
**Outcome:** Supplied context resolves ordinary `if`/`with` dependencies;
statically identifiable inactive input references remain optional inventory.
**Work:**
1. Add `if`/`else` and `with` traversal for known predicate values.
Evaluate only the selected path for applicable dependencies.
2. Preserve root `$`, `with` dot changes, pipeline-declared symbols, block
shadowing, and assignments to outer symbols. Follow Go's lexical lifetimes.
3. Never reinterpret `.field` against a known string dot as a root-map lookup.
Unsupported field operations have unknown values, not invented variables.
4. Add the non-evaluating inactive syntax walk specified above. Include literal
helper names from skipped branches and short-circuit operands as optional
entries with a safe inactive-use explanation. Preserve declarations and
aggregate other uses of the same name; required/conditional uses dominate.
5. Do not evaluate skipped predicates, resolve skipped dynamic names, mutate
branch environments, or collect skipped caller-variable requirements/issues.
Do not inspect uncalled definitions or a bypassed session template.
6. Keep uncertain predicates using the temporary conservative handling until
Stage 6; do not guess their truth from placeholders.
**Verify:** Both known branches, nested `if`/`with`, root access from changed dot,
declarations versus assignments/shadowing, and inactive helpers/missing variables.
Include declared-required and declared-optional inputs in these cases.
Test inactive undeclared literal and equivalent parenthesized/piped names,
inactive explanations, active reuse overriding inactivity, and skipped dynamic
or unsupported expressions causing no uncertainty. Verify that a complete
all-optional inventory requires no artifacts.
Run `go test ./internal/prompt`; commit.
## Stage 6 — Join Unresolved Paths And Finalize Completeness
**Outcome:** Missing values and content-dependent branching return useful partial
results without inventing definitive classifications.
**Work:**
1. Analyze unknown branches once each in forked local environments. Join their
requirement summaries and any assignments visible after the block. Preserve
identical known values; retain direct possible string names where available.
2. Required accesses shared definitely across both paths can remain required.
Otherwise possible accesses are conditional. An unconditional use elsewhere
or a declared required input still dominates.
3. Emit uncertainty only when it affects discovered requirements. An unresolved
text-only branch can still have a complete requirement set; no output rendering
is needed to establish that. Inactive inventory entries alone do not change
applicable dependency summaries or introduce incompleteness.
4. Support input-body-caused unknown conditions without reading the body:
the predicate's known helper name is required; dependent branches are partial.
5. Handle out-of-core controls/calls conservatively. Inspect identifiable
literal helper and explicit root-variable accesses where scope is known.
Do not follow recursive named calls, unroll ranges, or mark unrelated locals
as caller variables. Opaque hidden requirements must keep the result incomplete.
6. Ensure supported and unsupported analysis does not mutate compiled templates
or change definition acceptance. No new parser/compiler validation rule.
**Verify:** Unknown branch unions and shared accesses, missing text variables
with complete discovery, branch-sensitive missing variables, content predicates,
unknown `with` dot, assignment joins, and valid range/named-template examples
returning incomplete rather than errors. Run `go test ./internal/prompt`; commit.
## Stage 7 — Finish Dynamic Names And Conservative Inventory Aggregation
**Outcome:** Direct variable/alias names resolve exactly; unresolved names
preserve known candidates and correctly affect optional inventory classifications.
**Work:**
1. Resolve `input .input_name`, root `index` results, aliases, parenthesized
arguments, and piped names when the supported value is one known string.
Supplied name values are used as identifiers, never normalized anew.
2. For joined finite possible string names, report their possible input entries
conditionally. For unrestricted unknown names, retain all already known
entries and report undiscovered-name uncertainty.
3. Emit dynamic-name issues together with useful missing-variable, input-content,
unsupported, or expression-unresolved categories from the underlying value.
Do not echo values beyond a resolved/known possible binding identifier.
4. Make potentially targeted optional input entries conditional: candidate-only
uncertainty affects matching entries; unrestricted/opaque input effects
can affect all optional entries, including inactive undeclared inventory.
Remove inactive-only explanations when possible active uses undermine them.
Leave known required inputs required.
5. Finalize deduplication, lexical ordering, metadata propagation, variable
`Supplied` flags, inactive-use explanations, and completeness. No placeholder
`"*"` input name, invented variable default, payload read, or binding-validation API.
**Verify:** Exact dynamic names, empty supplied values versus missing values,
aliases selected by known branches, finite names from unknown assignments,
unrestricted names, repeated uses across messages/session, and optional
declaration/inactive inventory uncertainty. Verify results contain no unrelated
variable values or skipped-variable requirements.
Run `go test ./internal/prompt`; commit.
## Stage 8 — Wire Selection And The Public Engine Facade
**Outcome:** A configured consumer can call the new operation without artifacts,
credentials, schema preparation, or execution collaborators.
**Work:**
1. Add `internal/usecase/requirements_inspection.go` and the internal request.
Check nonblank prompt ID, normalize the direct session ID using the existing
domain helper, and honor context cancellation before selection work.
2. Use `resolvePromptDefinition` for exact version/default ambiguity behavior.
Factor the existing request-profile/default-profile choice into a small
shared private helper at its existing preparation location, retaining its
error identities and preparation error ordering.
3. Use `resolveProfileSelection` only to select identity. Do not call
`resolvePreparation`, target-completeness checks, API-key validation,
credential environment lookups, artifact/schema readers, renderer,
validator, admission, generation, or observation/run-ID machinery.
4. Obtain the captured requirement snapshot through the narrow lookup capability
on the selected repository. A configured production engine always provides
it. Missing internal capability/snapshot is an operational `ErrPromptLoad`,
not successful incomplete discovery or a raw-template fallback.
5. Analyze the messages and applicable session template; direct nonblank session
bypass removes only that template's uses. Build the internal result with the
selected identities and forward through `internal/engine`.
6. Add the public values/method in root `requirements.go`, conversion helpers,
GoDoc, and normal `mapPublicError` translation. Copy mutable values as above.
Document inactive inventory and `Explanation` separately from declaration
metadata and structured uncertainty; do not promise diagnostic wording.
Nil engine matches `ErrInvalidConfig`; operational errors return no partial
result. Successful incomplete discovery returns a result and nil error.
7. Preserve existing public error categories for absent prompt/profile,
ambiguous versions, absent selected/default profile, invalid session ID, and
cancellation. Add no new sentinel merely for uncertainty.
8. Update internal component/runner documentation for the implemented method.
**Verify:** Focused runner tests with failing/counting execution collaborators
prove selection-only behavior. Check explicit/default/partial profiles,
exact selectors and ambiguity, session bypass, cancelled contexts, nil engine,
and incomplete-success behavior. Run
`go test ./internal/usecase ./internal/engine ./...` once for facade wiring;
**Verify:** Focused final-value metadata tests for absent/null, empty, and
nonempty values, plus equivalent-definition hashes and old inspection behavior.
Use actual configuration capture rather than another source-loading matrix.
Run `go test ./internal/configuration ./internal/promptdef ./internal/prompt`;
commit.
## Stage 9 — Protect Public Semantics, Isolation, And Execution Parity
## Stage 2 — Enumerate Literal Inputs And Caller Keys
**Outcome:** Consumer-facing tests cover the acceptance outcomes without
duplicating the analyzer's detailed case matrix.
**Outcome:** An internal syntax walker produces deduplicated inventories and
does not confuse caller variables with template locals.
**Work:**
1. Add external-package `requirements_inspection_contract_test.go` using real
configured engines and minimal synthetic definitions. Use the existing
mapping/profile helpers, not a downstream checkout.
2. Prove exact `transcript` discovery, undeclared literal inputs, required and
optional declarations in inactive branches, context-selected names, and
session-only requirements removed by a direct session ID.
Include optional inactive undeclared entries with explanations, active reuse
overriding inactivity, and bypassed session names absent from the inventory.
3. Cover complete-but-unsatisfied variable requirements, conditional/unknown
dependencies, and empty complete versus empty incomplete input sets.
Cover complete all-optional inventories and no caller requirements from
solely inactive variable accesses.
4. Preserve classifiable errors for selection/cancellation. Test inspection
against a valid partial profile and an explicit direct-key-required profile
without an API key; missing runtime schemas and future artifact files must
not prevent inspection. Inject fail-if-called artifact/model/validator
collaborators where supported to prove the relevant absence of execution.
5. Use a small parity table: known-context applicable inspector requirements
must agree with `Prepare`/the renderer's input and variable semantics when
actual synthetic artifacts are later supplied. Do not use execution failure
order as the definition of all discovered requirements.
Do not compare optional inventory entries to actual renderer helper accesses.
6. Test source capture using a file-backed message changed/deleted after engine
construction. New inspection uses the captured content, like existing runs.
7. Mutate returned metadata/slices and repeat inspection; run representative
simultaneous inspections alongside preparation to verify no shared-state
mutation. Test this ownership risk, not a new scheduling/order guarantee.
8. Verify safe diagnostics/result formatting with marker values: unrelated
variable values, direct session value, template text, and credential values
are absent; input identifiers and declared descriptions are allowed.
Include inactive explanations in the disclosure checks.
9. Keep analyzer tests as the owner of detailed expression/branch semantics;
root tests own public values, errors, ownership, and assembled behavior.
1. Add internal result counterparts in `internal/domain/requirements.go` and
an analyzer entry in `internal/prompt/requirements.go`, taking context,
selected definition/snapshot, supplied variables, and session bypass.
2. Seed declarations, visit executable message roots and the applicable session
root, and collect literal input references and direct/literal-index caller
keys. Exclude uncalled definitions and a bypassed session template.
3. Implement aggregation, exact names, supplied-key presence, independent
metadata, sorting, and safe issues/explanations. Add ordinary traversal
cancellation checks without stronger interruption promises or workers.
4. Track scope identity, not local values: root-map versus nonroot/unknown dot
and local bindings. For unsupported scope changes, keep safely recognizable
references and report incomplete analysis rather than inventing caller keys.
5. Until Stage 3, classify branch references conservatively as possible and
unsupported dependency-bearing expressions as limitations.
6. Update the implemented internal component/source descriptions.
**Verify:** Run focused new contract tests and affected package tests, with
`-race` for the isolation tests. Reuse Stage 8 results for unchanged cases;
commit. Avoid full-suite repetition solely to produce this commit.
**Verify:** Literal declared/undeclared input names, repeated references,
required unused declarations, optional declarations/lookups, missing versus
empty direct variables, root versus local references, and empty input-free
templates. Test unsupported-scope uncertainty, not assignment interpretation.
Run `go test ./internal/prompt ./internal/domain`; commit.
## Stage 10 — Document The Boundary And Verify The Assembled Feature
## Stage 3 — Classify Simple Branches And Inactive References
**Outcome:** Canonical documentation accurately describes the new capability,
and the complete feature is ready for review, not automatically published.
**Outcome:** Ordinary supplied-context branches give useful classifications;
inactive inputs and variables remain optional inventory.
**Work:**
1. Finish GoDoc for every new public field, kind, issue category, method,
ownership/error behavior, and supported discovery boundary. State that
inspection is not generation readiness or future-byte validation.
2. Add task-oriented consumer guidance comparing the three inspection methods
and preparation. Demonstrate required `transcript`, missing variables, and
explicit incomplete discovery without a Promptkit JSON format.
Explain optional inactive inventory and reading requirement classifications
rather than treating every input entry as a binding to prepare.
3. Update relevant format cross-references, internal source/runner/component
documents, and development reading guidance. Do not duplicate the exact
public contract outside GoDoc or describe unsupported constructs as invalid.
4. Extend the maintained preparation example with a small inspection use before
actual artifact preparation, preserving all existing example output
assertions. Verify the example's semantic inspection result locally without
adding a second recurring release gate or replacing the other examples.
5. Check every roadmap acceptance outcome against implementation/tests. Confirm
the old inspection API/hash shapes and execution behavior are retained and
no raw-source/history validation or new caller restriction was introduced.
6. On final changed code, run one assembled offline verification using the
selected local Go and prepared module caches:
1. Add only the simple condition recognition in the scope table: direct values,
literal-key lookups, literals, negation, and two-string equality/inequality.
Use Go truthiness; a supplied `"false"` string is truthy.
2. For `if`/`with`, collect the predicate's references under the parent's
reachability. Visit selected and skipped bodies with the appropriate
reachability and dot identity. In unresolved branches, mark accesses possible.
3. Collect syntactically recognizable inactive input/caller names without
evaluating skipped expressions. Add inactive-only explanations; required
declarations or other definite/possible uses take precedence.
4. Unknown/content-dependent conditions preserve known entries and limitations.
An unresolved text-only branch need not be incomplete; dependency-bearing
unresolved branches can remain incomplete without cross-branch proofs.
5. Do not add assignment environments, alias-value propagation, branch joins,
more comparison families, or short-circuit evaluation for complex conditions.
**Verify:** Known true/false branches, nested reachability, string truthiness,
simple comparisons, `with` dot versus explicit root, missing decision values,
and content-dependent conditions. Include inactive literal input and caller
references, promotion by active reuse, and declared required inputs.
Confirm skipped unsupported/dynamic expressions do not introduce uncertainty
and an all-optional complete inventory needs no artifacts.
Compare a few supported cases with the real renderer.
Run `go test ./internal/prompt`; commit.
## Stage 4 — Resolve Direct Names And Finish Limitation Reporting
**Outcome:** Simple supplied names resolve exactly; complicated names and
constructs return useful partial inventories without speculative analysis.
**Work:**
1. Recognize the direct variable/lookup-derived helper names, simple
parentheses, and direct-to-input pipelines in the scope table.
Use a known string as an identifier without trimming or coercion.
2. Missing decision/name values, input-body dependence, unresolved helper
names, and dependency-relevant unsupported syntax produce the four issue
categories. Collect recognized references conservatively from unsupported
subtrees without executing functions, loops, or named calls.
3. Keep skipped computed input names unresolved without inventing an entry or
marking incompleteness from inactivity alone. The syntactically visible
caller key can still appear as an optional variable.
4. Finalize unknown-name/scope effects on affected optional entries using the
simple flags described above. Required declarations/definite accesses stay
required; uncertainty overrides misleading inactive-only explanations.
5. Finish independent result values and safe diagnostics. No possible-name
sets, underlying expression/value traces, placeholder names, or payload reads.
**Verify:** Exact direct names, optional literal-key lookups, empty versus
absent supplied names, and equivalent simple parenthesized/piped forms.
Use a few complex assignment/pipeline/range/named-call examples to prove
incomplete-success behavior, not broad interpretation coverage.
Check unresolved-name effects on optional entries and absent unrelated values
in results/explanations. Run `go test ./internal/prompt`; commit.
## Stage 5 — Wire Selection And The Public Facade
**Outcome:** Consumers can inspect a selected captured prompt before artifacts
exist, without execution collaborators or readiness checks.
**Work:**
1. Add `internal/usecase/requirements_inspection.go` and an internal request.
Check the nonblank prompt ID, use existing session normalization, honor
context cancellation, and select via `resolvePromptDefinition`.
2. Reuse request-profile/default-profile selection and
`resolveProfileSelection` for identity only. Share a small private selector
helper with preparation if needed, preserving its error identities/order.
3. Obtain the captured snapshot through the narrow repository capability and
invoke the analyzer. Missing required internal capability/snapshot is
operational `ErrPromptLoad`, not a successful partial result or source fallback.
4. Do not call preparation, target-completeness/credential checks, environment
lookups, artifact/schema readers, renderer, validator, admission, generation,
observations, or run-ID machinery.
5. Forward through `internal/engine` and add root `requirements.go` with the
planned values/method, conversion/copy helpers, GoDoc, and `mapPublicError`.
Nil engine follows `ErrInvalidConfig`. Operational errors return no result;
discovery limitations return an incomplete result and nil error.
6. Preserve public selection/session/cancellation error categories. Do not add
an error sentinel for incomplete discovery. Update internal runner/component
documents for the implemented method.
**Verify:** Focused selection tests cover explicit/default/partial profiles,
exact versions/ambiguity, direct-session bypass, cancellation, nil engine,
operational errors, and incomplete success. Use failing/counting execution
collaborators only to prove absence of the relevant effects.
Run affected use-case/engine tests and `go test ./...` for assembled facade
wiring; commit.
## Stage 6 — Protect Public Inventory And Ownership
**Outcome:** Public contract coverage protects the consumer need without
duplicating the syntax walker's detailed cases.
**Work:**
1. Add external-package `requirements_inspection_contract_test.go` with real
configured engines and minimal synthetic definitions.
2. Cover exact required `transcript`, undeclared literals, declaration metadata,
simple context-selected names, and inactive input/variable entries with safe
explanations. Missing required variables and complete discovery are separate.
3. Cover conditional/incomplete results and empty/all-optional complete
inventories. A direct session ID excludes its template-only inventory
entries; declarations/message references remain.
4. Prove inspection before artifacts exist and without credentials/schema
preparation/provider access. Use a valid partial profile and, where useful,
an explicit direct-key-required profile without its key.
5. Compare a small set of supported definite requirements with later preparation
using synthetic artifacts. Optional inactive inventory is not an executed
access; incomplete cases need not satisfy complete-analysis parity.
6. Verify captured file-backed content survives later file change/removal.
Mutate returned slices/metadata and repeat inspection; exercise simultaneous
inspections alongside preparation to protect captured-state ownership.
7. Check disclosure with marker values: unrelated variable values, direct
session values, template text, and credentials stay absent. Binding names,
caller keys, declared descriptions, and safe inactive explanations are allowed.
8. Root tests own public values/errors/assembled behavior; analyzer tests own
recognition/classification. Avoid duplicated interpretation matrices.
**Verify:** Run focused contract/affected package tests and `-race` for
ownership cases. Reuse unchanged prior checks rather than rerunning the full
suite solely for this unit; commit.
## Stage 7 — Document And Verify The Assembled Feature
**Outcome:** Implemented semantics and analysis limits are documented in their
canonical owners, with final verification ready for user review.
**Work:**
1. Finish GoDoc for classifications, presence, metadata, explanations, issues,
completeness, errors/ownership, and the deliberately small supported scope.
Complex valid templates may yield partial results; no authoring ban is added.
2. Update the consumer guide to compare the inspection APIs and preparation.
Show required input discovery, optional inactive input/variable inventory,
missing required variables, and explicit incomplete discovery.
3. Update relevant format cross-references and internal source/runner/component
documents. Keep exact implemented API contracts in GoDoc rather than copying
them into every guide.
4. Add a small inspection use to the maintained preparation example, preserving
its existing assertions. Verify it locally without adding another release gate.
5. Review roadmap acceptance against implementation/tests. Confirm no general
interpreter, stronger caller restriction, raw/intermediate configuration
validation, execution-readiness check, or speculative completeness promise
was introduced.
6. On final changed code, run one assembled offline verification using the local
Go installation and prepared module caches:
`go test -race -vet=off -mod=readonly -count=1 ./...`,
`go vet -mod=readonly ./...`, and
`python3 scripts/check-examples.py`. Set `GOTOOLCHAIN=local`, `GOWORK=off`,
`GOPROXY=off`, `GONOPROXY=none`, and `GOSUMDB=off`.
Check changed Go formatting, local documentation links, and `git diff --check`.
There is no need to rerun release-tool publication fixtures for unchanged tools.
7. Record verification and implemented status in the temporary roadmap/plan
for user review. Do not tag, push, select a release version, write a prospective
release note, or retire the review artifacts in this implementation stage.
Release preparation owns those later decisions.
8. Review the final change, commit the unit, and report remaining issues honestly.
Do not mark unsupported constructs complete merely to make tests pass.
Release-tool fixtures need not rerun when tooling is unchanged.
7. Record implemented status and verification in these temporary review
documents, commit, and report remaining limitations honestly. Release
preparation later owns version selection, notes, retirement, tags, and pushes.
## Completion Criteria
- The root operation and dedicated public values implement the selected
artifact-free, context-sensitive inspection contract.
- Definitions/templates and required metadata presence come from the final
captured configuration; current inspection/execution and hashes remain intact.
- Practical-core complete results agree with execution semantics; uncertainty
remains a structured successful partial result, not guessed absence or error.
- Missing values, conditional requirements, metadata absence, and session bypass
are distinguishable without leaking unrelated content.
- Inactive undeclared references appear as optional entries with explanations;
they do not introduce skipped-variable requirements or uncertainty. Complete
all-optional inventories remain distinguishable from incomplete discovery.
- Public classification and ownership tests pass; no provider/resource preflight,
arbitrary limit, application wire schema, or new template-validity rule was added.
- Canonical documentation and maintained examples reflect the implemented
discovery boundary. Successful checks for the final snapshot are reported.
- The separate public operation returns input and caller-variable inventories
classified required, conditional, or optional for the selected context.
- Recognizable inactive references remain optional entries in both inventories,
with safe explanations and aggregation that preserves stronger requirements.
- Simple branches/direct names agree with execution; complex cases preserve
recognized names and explicit limitations without assignment/path solving.
- Effective metadata/captured templates come from final configuration and remain
immutable; existing inspection, hashes, and execution behavior remain intact.
- Selection, session bypass, missing versus empty caller keys, metadata presence,
classification, and completeness have distinct documented meanings.
- No artifacts, credentials, provider access, schemas, or execution state are
needed for inspection. Results preserve disclosure and public error boundaries.
- Focused tests, the maintained example, canonical docs, and final assembled
checks demonstrate the feature. No significant design questions remain open.

View File

@@ -1,273 +1,227 @@
# Effective Prompt Requirement Inspection
Status: Implementation planned; the feature is not implemented. The
[implementation plan](implementation.md) owns the ordered migration steps and
implements the agreed analysis scope and inventory below. This document owns
the target end state and migration scope.
[implementation plan](implementation.md) owns the ordered implementation steps.
This document owns the purpose, scope, and target end state.
## Purpose
Let consumers discover a selected prompt's caller requirements before its input
artifacts exist. Scriptorium can expose that information to operators and to
Narratio, which can compare its planned bindings with Promptkit's exact input
names before undertaking expensive pipeline work.
Let consumers enumerate a selected prompt's inputs and caller variables before
its input artifacts exist, with each name classified as required, conditional,
or optional for the supplied context. Scriptorium can present that inventory
to operators and Narratio can compare planned bindings with exact input names
before undertaking expensive pipeline work.
The originating request is `docs/roadmap/scriptorium-feature-request.md`.
The capability is application-neutral: Promptkit describes prompt requirements;
consumers interpret them for their own configuration, presentation, and workflow.
The originating request is [Scriptorium's feature request](scriptorium-feature-request.md).
Promptkit supplies application-neutral inspection; consumers own presentation,
binding checks, and their own workflows.
## Current Baseline
At review baseline `b595dca`:
- [InspectPrompt](../consumers/pkg-promptkit.md#inspect-a-prompt-before-preparation)
selects a captured prompt and reports declared inputs and output metadata.
It does not analyze templates or resolve the execution profile.
selects a captured prompt and reports declared inputs and output metadata,
without analyzing templates or resolving an execution profile.
- [InspectProfile](../consumers/pkg-promptkit.md#inspect-a-profile-before-prompt-work)
reports one captured profile's effective target separately.
reports a captured profile's effective target separately.
- [Prepare](../consumers/pkg-promptkit.md#prepare-without-model-execution)
selects the execution context, checks target requirements, loads artifacts
and applicable schemas, and renders messages. It needs actual input data.
- The configuration adapter captures final effective definitions, message
contents, and compiled message/session templates during engine construction.
Runtime rendering uses Go templates, string-valued caller variables, and the
`input` helper. Declared required inputs are checked independently of template
branches; active helper calls also require their named inputs.
- Declared input metadata currently uses string fields for content type and
description. The new result needs to identify a declaration and distinguish
available metadata from absent metadata, including effective presence where
necessary; current empty strings alone do not establish source-field presence.
selects an execution context, loads artifacts and applicable schemas, and
renders messages. It needs actual input data.
- Engine construction captures final definitions and compiled message/session
templates. Rendering uses Go templates, string-valued caller variables, and
the `input` helper. Required input declarations are checked independently of
branches; an executed helper also needs its named input.
- Declared content types and descriptions currently use string fields. The
new inspection needs declaration information and effective metadata presence,
including the difference between an absent field and an explicitly empty one.
The [format reference](../formats.md#messages-and-templates),
[internal source document](../internal/sources.md), and existing GoDoc own the
implemented semantics. No existing operation provides artifact-free,
context-sensitive requirement discovery.
The [format reference](../formats.md#messages-and-templates), existing GoDoc,
and [internal source document](../internal/sources.md) own current behavior.
## Target Public Operation
Add a separate read-only engine operation, planned as
`Engine.InspectRequirements`, with a dedicated request and result. Preserve
`InspectPrompt`, `InspectProfile`, preparation, and execution contracts.
Add `Engine.InspectRequirements` with dedicated public request/result values.
Preserve `InspectPrompt`, `InspectProfile`, preparation, and execution.
The request supplies a prompt ID, optional version, optional profile selector,
supplied template variables, and an optional direct session ID. It needs no
artifact references or contents, output path, API key, or execution settings
unrelated to discovery.
The request supplies a prompt ID, optional version and profile selectors,
caller variables, and an optional direct session ID. It needs no artifact
references or contents, output path, or credentials.
Selection follows equivalent execution semantics: exact identities, omitted
version ambiguity, request profile or prompt default, and direct session-ID
normalization and bypass. Return selected prompt ID/version and effective
profile ID. Profile selection establishes identity, not generation readiness;
profiles that depend on later execution overrides need not be completed here.
Use execution's selection semantics: exact identities, omitted-version
ambiguity, request profile or prompt default, and normalized direct session-ID
bypass. Report the selected prompt ID/version and effective profile ID.
Selecting a profile establishes identity, not generation readiness; partial
profiles remain inspectable.
Use the configured engine's captured definitions. Source discovery and layering
remain consumer choices. Results describe that captured context, not a promise
about a later engine or later execution with changed variables.
Use the configured engine's captured definitions. Source selection remains a
consumer choice. Results describe that captured context, not a later engine or
execution with different variables.
## Result And Requirement Meanings
## Result And Classification
Expose public Go values with documented classifications and predictable ordering.
Exact symbol and field spelling belongs in the implementation plan
and GoDoc. No Promptkit JSON wire schema is part of this feature.
Return an inventory of inputs and a separate inventory of caller variables.
Each entry has one of three requirement classifications:
The result contains:
- **Required:** A declaration or recognized definite access establishes that
the caller must supply it.
- **Conditional:** Its necessity remains unresolved, including references on
paths whose applicability the supported analysis cannot determine.
- **Optional:** It is known not to be required, including recognizable references
used only on known inactive paths and nonrequired declarations or lookups.
- Selected prompt and profile identities.
- Known input names, preserving execution identity, with required, optional,
or conditional classification for the supplied context.
- Declaration information, descriptions, and content-type metadata where
available. A template-discovered undeclared input must not look like a
declared input with known metadata. Report existing media-type metadata,
not an invented input schema or inferred format constraint.
- An inactive-use explanation for statically identifiable undeclared inputs
referenced only on known inactive paths. These are optional inventory entries,
not bindings the current context needs. Keep this explanation separate from
declared descriptions.
- Variable requirements separately from artifact inputs, with requirement
classification and whether supplied values satisfy the known requirement.
Report presence and actual supported semantics, not the supplied values.
- A discovery-completeness indicator and structured uncertainty reasons, with
safe explanations and relevant identifiers where available.
A required declaration stays required even in an inactive branch. A definite
helper use promotes an optional input declaration to required. Aggregate repeated
occurrences by exact name: required takes precedence over conditional, which
takes precedence over optional. Conservative conditional classification is
acceptable where analysis cannot establish necessity; inspection need not prove
equivalence between alternative execution paths.
Required means the caller must supply the input for this context. A declared
required input stays required even if no active template references it. A
declared optional input becomes required when an active template use needs it.
Optional means it is known not to be required for this context; conditional
means its requirement depends on unresolved context. Do not use optional to
hide uncertainty. Known required declarations retain that classification even
when other requirements are conditional or undiscoverable.
Input entries distinguish declarations from discovered undeclared references
and preserve available descriptions/content types. They do not infer input
schemas or introduce format enforcement. Variables identify caller-map keys,
not template-local symbol names, and report whether each key was supplied.
An empty supplied string is present; absence is separate from classification.
Include statically identifiable undeclared references on known inactive paths
as optional entries with an inactive-use explanation. A required or conditional
use elsewhere takes precedence over that inactive-only classification and
explanation. Inactivity itself is not discovery uncertainty.
Include recognizable references on known inactive paths in both inventories
as optional entries with an inactive-use explanation. Keep explanations separate
from declared input descriptions. An applicable required or conditional use
elsewhere overrides the optional classification. Any applicable occurrence,
including an optional lookup, removes an inactive-only explanation. Known
inactivity introduces neither an execution requirement nor discovery uncertainty.
An unresolved dynamic name can refer to an otherwise optional input entry. Its
absence from recognized active uses alone does not establish that it is
definitely optional; classification and uncertainty must account for that
possibility.
Deduplicate names and provide predictable ordering. Results also identify the
selected context and provide discovery completeness with machine-readable
limitation categories and safe explanations. Exact public spellings belong in
the implementation plan and, once implemented, GoDoc. No Promptkit JSON wire
schema is part of this feature.
Known input names should be deduplicated across declarations, messages, and
session templates. Aggregate uses without discarding an established required
use. Conditions need not expose raw expressions; machine-readable reason
categories and safe summaries are sufficient for basic consumer handling.
## Simple Discovery Boundary
Completeness describes discovery, not readiness or satisfaction. For example,
a missing variable used only to insert text can be a known unsatisfied
requirement while the complete input set is still discoverable. If that
variable selects an input name or branch, its absence can also make discovery
incomplete. An empty complete input set means no artifact bindings are needed;
a complete inventory containing only optional entries also needs no artifact
bindings. An empty incomplete set does not establish that fact. Consumers must
distinguish the inventory from applicable requirements, rather than treating
every reported name as a needed binding.
Use parsed template syntax, not raw-text matching or rendering with placeholder
artifacts. The intended algorithm is straightforward:
## Context-Sensitive Discovery Boundary
1. Seed the inventory from input declarations and their effective metadata.
2. Visit the selected message templates and applicable session template,
collecting recognizable input and caller-variable references.
3. Classify occurrences using their surrounding path and a small amount of
supplied-context evaluation.
4. Aggregate entries and report any unresolved discovery.
Analyze parsed templates rather than searching raw text or rendering with
placeholder artifacts. Use supplied variable values where they determine
branches and input names, following execution's presence, truthiness, scope,
and evaluation semantics. An empty string is not an absent variable, and a
nonempty string must not be reinterpreted as a Boolean merely from its spelling.
The source inventory is finite and ordinarily small. Direct traversal and
name-indexed aggregation are sufficient; no size caps, analysis budgets,
branch-combination enumeration, or global analysis cache are needed.
Include dependencies from messages and the applicable session-ID template.
A direct nonblank session ID bypasses that template, removing only requirements
introduced solely by it; declarations and message uses remain relevant.
The bypassed session template is also excluded from the inactive inventory.
Recognize literal input names, direct caller-variable access, literal-key root
lookups, direct variable-derived input names, and ordinary `if`/`with` branches.
Use simple truthiness, negation, and equality/inequality where straightforward.
Empty strings and the nonempty string `"false"` follow actual Go truthiness.
The implementation plan defines this small initial coverage precisely.
Literal `input` references are discoverable without reading bodies, including
undeclared references. Artifact content being unavailable does not by itself
make discovery incomplete: it does so when the content affects which further
requirements apply. Variable-derived names are discoverable when supported
analysis can determine the exact name from the supplied context.
Retain enough lexical/dot scope awareness to distinguish caller variables from
locals. Complicated assignments, pipelines, computed names, ranges, or invoked
named templates can remain outside complete analysis. Preserve identifiable
references and report limitations rather than implementing a general template
interpreter, joining assignment environments, or propagating possible-name sets.
No cross-branch proof of a common required dependency is necessary.
When missing variables, content-dependent decisions, unresolved names, or
analysis limitations prevent determination, preserve known requirements and
return successful incomplete discovery. Structured reasons should distinguish
these situations without requiring diagnostic-text matching. Unsupported
analysis is not a new prompt-validity rule. Completeness is asserted only when
the supported analysis establishes the requirement set; it does not promise
universal analysis of every valid Go template.
On a known inactive path, collect names visible in syntax without evaluating
its expressions. Literal input names and recognizable caller keys remain in
the inventory; skipped dynamic input names need not be resolved. Unsupported
skipped expressions do not themselves make discovery incomplete.
The selected practical analysis core and inactive inventory are described below.
They do not change execution or expand the template language.
A direct nonblank session ID excludes the bypassed session template from both
inventories. Never-invoked template definitions do not create requirements.
Declarations and references in selected messages remain relevant.
Complex computed names may be missed by the inventory. That is an accepted
analysis limitation, not a reason to reject an otherwise valid prompt.
Inspection must expose potential incompleteness rather than imply that unknown
names are absent. Template authors benefit from keeping dependency expressions
simple, but this feature adds no template-authoring prohibition.
## Completeness And Limitations
Completeness describes discovery, not satisfaction or readiness. A missing
variable used only for direct text substitution can be a known required entry
with complete discovery. A missing value used to select a branch or name can
leave dependencies unresolved.
When unavailable input content, unresolved names, missing decision values, or
unsupported analysis affect discovery, return known entries with successful
incomplete status and structured reasons. Conservative incompleteness is
acceptable; inspection need not prove an exact requirement set through complex
expressions. Unsupported pure text with no effect on dependencies need not
introduce uncertainty.
Unresolved names can potentially refer to existing optional entries. Account
for that possibility in classification, while leaving established required
entries required. Do not label a potentially needed entry definitively optional.
An empty complete input inventory, or a complete input inventory containing
only optional entries, establishes that no artifact bindings are needed.
An empty incomplete inventory does not. Consumers must use classifications,
not inventory length, to identify bindings that need preparation.
## Effects, Errors, And Disclosure
Inspection uses captured prompt resources and needs no future artifact files,
credential lookup, model access, capacity admission, or execution artifact
creation. It does not prepare an output-validation schema or validate future
input bytes. Necessary configuration/resource failures can still prevent engine
construction or inspection; this is not a general resource preflight.
Inspection uses captured resources without future artifact reads, schema
preparation, credential lookup, model calls, capacity admission, observations,
run IDs, or execution artifact creation. Necessary configuration/resource
failures can still prevent engine construction or inspection.
Use existing public selection/error identities wherever applicable, including
invalid requests, missing prompt/profile selections, and ambiguous versions.
Preserve ordinary context cancellation behavior. Discovery uncertainty is a
result state, distinct from an operational or selection error. Expose any
additional necessary classifications through the public facade, not diagnostic
text parsing or internal types.
Preserve publicly classifiable selection errors and ordinary context
cancellation. Discovery uncertainty is a successful result, not an operational
error or a new definition-validity rule. A complete result does not promise
successful rendering, generation, future artifact validity, or provider
availability.
Results and safe diagnostics exclude credential values, artifact payloads,
raw/rendered prompt content, and unrelated supplied variable values. Selected
identities, input/variable names, and declared descriptions are intentional
operator metadata. A resolved dynamic input identifier may equal a supplied
variable value; reporting that identifier is necessary to describe the binding.
Keep the disclosure exception limited to its role as an identifier, without
echoing the variable map or the expression that produced it. Scriptorium owns
the presentation and disclosure of this metadata to its callers.
A complete result describes requirements, not successful rendering or generation,
future artifact validity, or provider availability. Incomplete results remain
useful without authorizing consumers to treat unknown dependencies as absent.
Results and diagnostics exclude credential values, artifact payloads,
raw/rendered prompt content, and unrelated supplied values. Selected identities,
input/variable names, and declared descriptions are intentional operator
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
Add public request/result values and the engine method through the existing
facade. Put selection coordination and analysis in internal components with
cohesive ownership. Reuse existing prompt/profile selection, session override,
and template parsing semantics rather than introducing a second preparation
path or a general-purpose template interpreter.
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.
Make the captured templates and effective declaration metadata available to
internal inspection. Retain any required metadata presence at the ConfigKit
handoff. Apply [ADR 0001](../adr/0001-configuration-boundary.md): inspection
interprets final effective definitions, not raw sources, configuration history,
or intermediate artifacts. Operator provenance remains a separate explicit API.
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 requirement aggregation, context-sensitive analysis, and safe public
uncertainty/error conversion. Keep request-specific analysis separate from
shared captured state. Prefer direct traversals and indexed aggregation; avoid
enumerating every possible branch combination or repeated full-template scans.
Predictable result ordering does not require a stronger complexity guarantee
or a new global cache.
Add focused analyzer tests and public-facade contract coverage under the
[testing policy](../policy/testing.md). Use real captured definitions and
templates, synthetic variables, and explicit unavailable artifacts. Compare
complete discovery with representative execution semantics. Test incomplete
results separately from errors and verify disclosure boundaries and absence
of artifact reads/provider calls. Do not retest ConfigKit's loading matrix.
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, and the internal component/source documents. Describe the
analysis boundary and examples of complete, unsatisfied, and incomplete results
in their canonical owners. Release notes explain the new capability; they do
not own its contract. The implementation plan owns implementation sequencing.
cross-references, internal documents, and a maintained example. Canonical owners
document the implemented boundary; release notes summarize rather than own it.
## Acceptance Outcomes
- A required `transcript` is reported exactly, allowing a consumer to detect a
planned binding under the wrong name before the transcript exists.
- Literal undeclared helper references are discoverable without making their
absence from declarations a new definition error.
- Declared required inputs remain required in inactive branches; optional
declarations remain optional where no applicable use makes them required.
- Statically identifiable inactive undeclared references are optional entries
with an inactive-use explanation; active and unresolved uses of the same name
take precedence. These entries do not introduce variable requirements or
incompleteness solely from skipped expressions.
- Supported variable-dependent branches and names agree with execution;
unresolved content/name/analysis cases retain known entries and uncertainty.
- Version/profile/default selection and session-template bypass agree with
execution without credential or generation-readiness checks.
- Variables are reported separately, with absent and empty supplied values
interpreted according to the existing template semantics.
- Artifact-free complete results, including all-optional inventories, are
distinguishable from empty partial discovery.
- Errors remain publicly classifiable; results and safe diagnostics disclose
identifiers and declared metadata without unrelated content or secret values.
- Existing inspection and execution APIs retain their contracts. Pipeline
planning, binding validation, CLI/HTTP exposure, presentation, JSON schemas,
graph rendering, batch interfaces, and provider preflight remain downstream.
## Selected Analysis Scope And Inventory
### Initial Template Analysis Coverage
Implement a documented practical core: literal input calls,
direct caller-variable access, ordinary `if`/`with` control flow, and simple
variable-derived names and comparisons where their values are determinable.
Account for template variable scope and short-circuit behavior for supported
expressions. Additional constructs can be supported where straightforward;
otherwise report incompleteness when they affect requirement discovery. The
implementation plan specifies the exact expression families for this baseline.
This covers the representative consumer needs while keeping completeness
honest and avoiding a second general-purpose interpreter.
### Inactive Undeclared Reference Inventory
Always include declarations and discovered active or unresolved dependencies.
Also include statically identifiable undeclared references on known inactive
paths as optional entries with an inactive-use explanation. This broader
inventory helps operators explore prompt behavior while preserving the
distinction between inventory and applicable dependencies.
Collect inactive names from syntax without evaluating skipped expressions.
The initial scope covers literal helper names and equivalent parenthesized or
piped literal forms. Skipped dynamic names need not be resolved, and skipped
variable accesses do not become caller requirements or uncertainty reasons.
Include known skipped branches and short-circuit operands, without expanding
analysis to never-invoked template definitions or a bypassed session template.
Preserve declared optional metadata and required declarations; when another
use makes a name required or conditional, its inactive occurrence does not
override that classification or imply that the name is inactive-only.
- Required `transcript` is reported exactly before its artifact exists.
- Literal undeclared helper names are discoverable without definition rejection.
- Input and variable references are classified as required, conditional, or
optional, including recognizable inactive references in both inventories.
- Required declarations stay required; definite uses promote optional entries;
other occurrences never erase an established required use.
- Simple supplied-context branches and direct dynamic names follow execution;
complicated or content-dependent cases preserve known entries and limitations.
- Caller keys remain distinct from template locals, and supplied empty values
remain distinct from absent keys.
- Selection and session bypass follow execution without readiness checks.
- Complete artifact-free inventories remain distinguishable from empty partial
discovery; completeness remains separate from supplied-variable presence.
- Results/errors preserve the disclosure and public classification boundaries.
- Existing inspection/execution contracts remain intact. Application APIs,
pipeline planning, binding validation, presentation, provider preflight, input
schemas, and a general-purpose template evaluator remain outside this scope.