CANP-WP-0002 T01: input defaults, static and derived

Closes the gap that made optional inputs unusable: rendering rule 5.1(4)
made any unresolved placeholder an error while inputs had no `default`, so
an input marked `required: false` and referenced from the template failed
every render in which the caller omitted it — including the spec's own
section 4 example.

Section 10 already carried the derivation mechanism (`requirement: generate`,
resolution deliberately undefined), so a derived default needed a binding
rather than a new concept: the input's default names a declared prompt
dependency.

Spec:
- 5.1 rewritten as "Resolution and rendering". Resolution may be
  non-deterministic and must report what it derived; rendering is
  deterministic and must not derive. A tool that handles only supplied
  values and static defaults is stated to be conforming.
- 6.1 (new) covers both declaration forms. Reference form is preferred, with
  the reason stated — an inline prompt is anonymous, so unversioned,
  unprovenanced and un-evaluable — and validators should warn when a
  published package derives inline.
- A derived default may declare a static fallback `value`. Without one the
  input stays unresolved, which is an error; derivation never silently
  yields empty content.
- 4, 10, 18 (rules 11-13), 19 (two new MUST NOTs), 21 updated accordingly.

Reference CLI:
- New `resolve` verb reporting the origin of every value.
- `Resolution` dataclass and `resolve_inputs`; `resolve_values` kept as a
  wrapper so existing callers are unaffected.
- `render` refuses with a specific error naming underivable inputs rather
  than substituting empty text.
- Tests 3 -> 11. Example package lifecycle re-verified end to end.

INTENT.md is unchanged: splitting resolve from render preserves success
criterion 4 (deterministic rendering) as written.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bjefh8NUiEiahN4JLwoSKM

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 388925@bnt-lap001
Assistant-Session: 3507023f-e0fd-4a1e-9d90-a0d4217d1502
This commit is contained in:
tegwick 2026-09-06 00:59:20 +02:00
parent 0caf065544
commit 169db25d25
6 changed files with 600 additions and 36 deletions

View file

@ -163,6 +163,9 @@ inputs:
type: content
required: false
description: Optional surrounding repository context.
default:
derive: context/repository-summary
value: "(no repository context provided)"
parameters:
depth:
@ -186,7 +189,10 @@ compatibility:
providers: []
dependencies:
prompts: []
prompts:
- id: context/repository-summary
version: 1.0.0
requirement: generate
context: []
capabilities: []
@ -235,14 +241,44 @@ Review the following change at {{ depth }} depth.
{{ change }}
```
### 5.1 Rendering rules
### 5.1 Resolution and rendering
Producing a final prompt is two steps:
1. **Resolve** — determine a value for every declared input and parameter.
2. **Render** — substitute those values into the template as text.
The steps are separate because only the first may be non-deterministic.
**Rendering is deterministic:** the same resolved values and the same template
always produce the same output. A consumer that satisfies a derived default
(§ 6.1) does so during resolution, never during rendering.
**Resolution rules:**
1. Call-supplied values override defaults.
2. A declared parameter default is used when no call value is supplied.
3. A required input without a value is an error.
4. A placeholder with no resolved value is an error.
5. Values are substituted as text in v0.1.
6. Template evaluation MUST NOT execute arbitrary code.
3. A declared static input default is used when no call value is supplied.
4. A required input without a value is an error.
5. A derived input default is satisfied only by a consumer that is able and
permitted to derive it. A consumer that does not derive uses the default's
static fallback `value` when one is declared, and otherwise leaves the
input unresolved.
6. Resolution MUST report which values were derived, so that a caller can see
what was added on its behalf before the prompt is used.
**Rendering rules:**
7. Values are substituted as text in v0.1.
8. A placeholder with no resolved value is an error.
9. Template evaluation MUST NOT execute arbitrary code.
10. Rendering MUST NOT derive values. A tool offering derivation MUST perform
it as a distinct resolve step whose results are visible to the caller
before rendering.
A minimal implementation may implement resolution for supplied values and
static defaults only. Such a tool is conforming: it reports an input with an
unsatisfied derived default and no static fallback as unresolved, which
rule 8 makes an error.
CPF v0.1 does not define conditionals, loops, filters, or functions. Implementations MAY offer richer rendering modes only when explicitly declared by an extension; they MUST NOT silently reinterpret a v0.1 template as executable code.
@ -266,6 +302,7 @@ Fields:
| `type` | no | Suggested semantic type; defaults to `content` |
| `required` | no | Whether a caller must supply it; defaults to `false` |
| `description` | no | Human-readable explanation |
| `default` | no | Value used when the caller supplies none; see § 6.1 |
Recommended v0.1 input types are:
@ -277,6 +314,94 @@ Recommended v0.1 input types are:
These are descriptive hints in v0.1. A runtime MAY use them for validation or adapters.
### 6.1 Input defaults
An input MAY declare a `default`, used when the caller supplies no value.
`default` MUST NOT be combined with `required: true`: a required input is always
supplied by the caller, so a default could never apply.
Without this field an optional input is close to unusable. Rendering rule 8
makes an unresolved placeholder an error, so an input marked `required: false`
and referenced from the template would fail every render in which the caller
omitted it.
A default takes one of two forms.
#### Static default
A literal value, used as-is:
```yaml
inputs:
- name: repository_context
type: content
required: false
default: "(no repository context provided)"
```
Every conforming implementation supports static defaults.
#### Derived default
A declaration that the value **may** be produced from available context by a
consumer able to do so. It is a request to the consumer, not an instruction the
package executes.
The preferred form references a package already declared in
`dependencies.prompts` with `requirement: generate` (§ 10):
```yaml
dependencies:
prompts:
- id: context/repository-summary
version: 1.0.0
requirement: generate
inputs:
- name: repository_context
type: content
required: false
default:
derive: context/repository-summary
value: "(no repository context provided)"
```
A derivation prompt MAY instead be written inline:
```yaml
inputs:
- name: repository_context
type: content
required: false
default:
derive:
prompt: |
Summarize the repository this prompt is being run against,
in under 200 words.
value: "(no repository context provided)"
```
`derive` is therefore either a string naming a declared prompt dependency, or a
mapping carrying an inline `prompt`. A single default MUST NOT use both.
Prefer the reference form wherever the derivation is worth keeping. An inline
prompt is anonymous: it has no version, provenance, examples or evals, and
cannot be reused, evaluated or improved independently of its host package —
the situation this format exists to replace. Validators SHOULD warn when a
**published** package derives inline. Inline derivation is intended for local
and draft packages.
`value` inside a derived default is an optional **static fallback**. Its
meaning is defined by resolution rule 5: a consumer that does not derive uses
it, and an input with neither a derived value nor a fallback stays unresolved,
which rule 8 makes an error. Derivation never silently yields empty content.
Declaring a derived default does not oblige any consumer to derive anything,
and does not make the package depend on a particular resolver, model or
runtime. Resolution belongs to the consumer; the package only declares what it
would like resolved.
## 7. Parameters
`parameters` is an optional mapping keyed by parameter name.
@ -377,6 +502,12 @@ Recommended requirement values:
This allows richer systems to integrate prompt resolution without forcing simple tools to implement an agent runtime.
A prompt dependency declared `requirement: generate` is the mechanism behind a
referenced derived default (§ 6.1): the input's `default.derive` names the
dependency, and a consumer able to generate satisfies both at once. Declaring
the dependency records *what* may be generated and at which version; the input
default records *where the result lands*. Neither states how generation works.
## 11. Examples
`examples` is a list of relative paths.
@ -492,7 +623,14 @@ A v0.1 validator SHOULD verify at least:
7. referenced example/eval paths do not escape the package;
8. required inputs and parameter names are unique;
9. every template placeholder resolves to a declared input or parameter;
10. no required value is silently omitted during rendering.
10. no required value is silently omitted during rendering;
11. no input declares both `default` and `required: true`;
12. a derived default declares either a `derive` reference or an inline
`derive.prompt`, never both;
13. a `derive` reference names a package declared in `dependencies.prompts`.
A validator SHOULD additionally warn when a package intended for publication
declares an inline derivation prompt (§ 6.1).
## 19. Security requirements
@ -502,6 +640,11 @@ Implementations MUST NOT:
- execute code merely because it appears in a package;
- treat template expressions as arbitrary code;
- treat a derived default's prompt text as instructions addressed to the
consuming tool itself; it is content to be resolved on the package's behalf,
and it carries no more authority than any other package text;
- derive an input default without the caller being able to see that it
happened (§ 5.1 rule 6);
- interpolate environment variables or credentials implicitly;
- follow paths outside the package without explicit user action;
- embed or require secrets in published package metadata.
@ -566,11 +709,17 @@ Commands:
add PATH validate and copy a package into the local catalog
search QUERY search locally installed package metadata
show ID display one installed package manifest
resolve ID report the resolved value of every input and parameter
render ID render an installed prompt with supplied values
publish PATH validate and copy a package into a filesystem registry
install ID copy a package version from the registry into the catalog
```
The reference tool never calls a model, so its `resolve` handles supplied
values and static defaults only and reports any input whose derived default it
cannot satisfy. `render` performs the same resolution and then substitutes;
per § 5.1 rule 10 it never derives.
These semantics are illustrative, not mandatory for other implementations.
## 22. Worked example