canned-prompts/reference/README.md
tegwick c580bf63c9 CANP-WP-0002 T05: required versus observed, and typed context dependencies
The answer to the capability question was conditional: keep both fields if
they carry the required/observed distinction, fix the terminology if they do
not. They did not.

Section 9 opened with "records known requirements or observations", mixing
both in one field — `models` was observational ("known to be compatible or
evaluated") while `capabilities` was prescriptive ("expected from the
execution environment"). Section 10 then described dependencies as what a
prompt "expects". Both fields said expected, so the overlap was real
ambiguity rather than redundancy, and the fix is terminology.

`dependencies` now means **required**; `compatibility` means **observed**. A
consumer must not refuse to run a package because its environment is absent
from a compatibility list. The same capability name may legitimately appear in
both: required to run at all, and separately observed to work well on
particular models. `compatibility.aliases` records the same capability under
other names, so a consumer can recognize a requirement its environment labels
differently.

Dependencies now have three kinds, separated by what the format can do about
them: `prompts` it resolves by id and version; `context` names what it does
not package at all; `capabilities` are what the environment must be able to
do. Context entries use `name` rather than `id`, because nothing can look them
up, and `description` is required because nothing else can explain an
unpackaged dependency. A capability takes no version and no
`requirement: generate` — it is not an artifact and cannot be fetched, pinned
or generated. Capability names are free-form kebab-case, validated for shape
and not membership, exactly as tags are.

Section 10.1 also draws the line the format had never stated: an input is
content the caller passes for one use; a context dependency is a standing fact
about the environment.

Spec: 9 rewritten, 9.1 and 10.1 and 10.2 new, 10 reframed, 18 (rules 19-20),
4 updated. Former 10.1/10.2 renumbered to 10.3/10.4 with cross-references.

Reference CLI: validate_capabilities, validate_context_dependencies, and a
`resolve` section listing required capabilities and context under "this tool
cannot verify these" rather than implying it checked. Tests 51 -> 65.

Also drops an invented `session-review` capability from the example package in
favour of an honest `long-context` observation.

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
2026-09-06 08:11:13 +02:00

3.2 KiB

canned-prompts reference CLI

This is intentionally a small reference implementation, not the intended final architecture.

It demonstrates eight verbs:

add      PATH
search   QUERY
show     ID
resolve  ID --set key=value
render   ID --set key=value
eval     ID
install  ID [--version VERSION]
publish  PATH

The implementation uses a local catalog plus a filesystem registry and performs no model calls.

resolve and render are separate because the specification separates them (§ 5.1): resolution decides each value and may be non-deterministic, rendering substitutes and always is. resolve prints where every value came from — supplied, default, or fallback — before any prompt is produced.

Stores

Default locations:

~/.canned-prompts/catalog
~/.canned-prompts/registry

A registry stores packages flat, because an id is unambiguous within one registry:

<registry>/<id path>/<version>/...

A catalog is namespaced by registry, because identity is registry-scoped (§ 3.2) and the same id may be installed from more than one place:

<catalog>/<registry name>/<id path>/<version>/...

For example:

~/.canned-prompts/catalog/house/practice/pqrst-estimate/0.1.0/
~/.canned-prompts/catalog/local/practice/pqrst-estimate/0.1.0/

A registry's name comes from its optional registry.yaml, and otherwise from its directory basename. add takes a package from a path rather than a registry, so it files it under local (override with --as).

Commands that take an ID accept a bare id or a qualified <registry>:<id>. A bare id installed from more than one registry is reported as ambiguous rather than resolved by guessing.

Design choices

  • YAML manifest via PyYAML.
  • {{ name }} template substitution only.
  • No arbitrary expression/code execution.
  • Published versions are immutable by default.
  • install copies from registry to catalog.
  • add copies a package directly to catalog.
  • search, show, resolve, and render operate on catalog packages, and print qualified <registry>:<id> references.
  • include defaults are satisfied (inclusion is deterministic); derive defaults are reported, not run. Inclusion cycles are detected and named.
  • eval runs the deterministic render checks of any eval declaring the canned-prompts/eval-rubric/v0.1 schema, and reports output criteria as declared but not run. Unrecognized schemas are skipped, not rejected. A failed render check exits non-zero.
  • resolve lists required capabilities and context dependencies, which this tool cannot verify, rather than implying it checked them.
  • An optional registry.yaml names a registry and records namespace claims. publish warns when a namespace is declared closed — it cannot authenticate a publisher, and says so rather than implying it checked.
  • Static input defaults are applied; derived defaults (§ 6.1) are not. This tool never calls a model, so a derived default is satisfied only by its static fallback value. Without one, resolve reports the input as unresolved and render refuses rather than substituting empty text.

Use this implementation to challenge the format. Replace it once real usage reveals the right architecture.