Closes the workplan. The format becomes `canned-prompt/v0.2`, and packages declaring v0.1 remain valid — everything added across T01-T05 is additive, so a v0.1 package means exactly what it always meant. That is the MINOR case section 17 itself describes. The spec file loses its version suffix: CannedPromptFormat-v0.1.md becomes CannedPromptFormat.md, with the revision stated inside. One stable path that never breaks a link, and no rename per revision; the version belongs in the `format` string where tools actually read it. Section 23 is rewritten into three parts rather than the planned two. "Settled since v0.1" tables the five resolved questions against where each rule now lives. "Still deferred" carries the eight unpromoted items plus pattern-matching render checks. "Decided against" holds template inheritance alone, because calling it deferred would misdescribe it — reopening it means overturning a decision and answering four recorded objections, not filling a gap. Section 23 also names the two habits the five decisions turned out to share, so later revisions follow them rather than rediscover them: separate the deterministic half from the rest, and a package never asserts what it cannot back. The eval-rubric and registry-manifest schemas keep their own v0.1. They are new in this revision and sit on their own version lines. Reference CLI: ACCEPTED_FORMATS; an unknown revision is rejected naming what is accepted. Tests 78 -> 81. Example packages declare v0.2 and are bumped 0.1.0 -> 0.1.1 and 0.2.0 -> 0.2.1 as section 17 PATCH — metadata corrections with behavior unchanged. Also refreshes section 22's worked example, which had drifted: it showed pqrst-estimate at 0.1.0 with no composition, contradicting the package actually in the repo. It now mirrors the real package and doubles as a composition illustration. CANP-WP-0002 is finished. CANP-WP-0003 carries forward the one residual: the default registry's basename-derived name reads as `registry:`. 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
41 KiB
Canned Prompt Format
Revision: v0.2 — packages declare format: canned-prompt/v0.2
Status: Experimental
Project: canned-prompts
Purpose: Portable packaging of reusable prompts and prompt templates.
Compatibility: packages declaring canned-prompt/v0.1 remain valid (§ 3.2).
1. Goals
The Canned Prompt Format (CPF) defines the smallest practical contract for a reusable prompt artifact.
A conforming package should be:
- human-readable;
- filesystem-portable;
- provider-neutral;
- inspectable before use;
- parameterizable where useful;
- versionable;
- extensible with examples, evals, dependencies, and provenance.
CPF specifies the artifact format. It intentionally does not specify model execution, agent orchestration, dependency resolution, registry transport, or evaluation engines.
2. Package layout
The minimum valid package is:
my-prompt/
├── prompt.yaml
└── prompt.md
A richer package may contain:
my-prompt/
├── prompt.yaml
├── prompt.md
├── README.md
├── examples/
│ ├── basic.yaml
│ └── edge-case.yaml
├── evals/
│ └── quality.yaml
└── assets/
└── rubric.md
Reserved paths
| Path | Meaning |
|---|---|
prompt.yaml |
Required package manifest |
prompt.md |
Default prompt template unless overridden by template |
README.md |
Optional human documentation |
LICENSE |
Optional license text |
examples/ |
Optional examples/fixtures |
evals/ |
Optional evaluation specifications |
assets/ |
Optional supporting text/data artifacts |
Tools MUST ignore unknown non-reserved files unless a manifest field explicitly references them.
This governs packaging as well as reading: a tool that copies a package into a
catalog or registry MUST copy the reserved paths and the files a manifest
field references, and nothing else. A working directory's .git, virtualenv or
scratch files are not part of the package and MUST NOT be published with it. A
tool that omits files SHOULD say which, so that an author is never silently
surprised by what did not ship.
LICENSE is reserved because § 14 asks tools to surface licensing on publish
and install, and because dropping the license text while faithfully copying the
license field would misrepresent the package.
3. Manifest
The canonical manifest is UTF-8 YAML named prompt.yaml.
3.1 Minimal manifest
format: canned-prompt/v0.2
id: review/code-review
name: Code Review
version: 1.0.0
summary: Review a change for correctness and maintainability.
template: prompt.md
Required fields are:
formatidnameversionsummarytemplate
3.2 Package identity
format
The current revision is:
format: canned-prompt/v0.2
A package declaring canned-prompt/v0.1 remains valid and MUST still be
accepted. Everything v0.2 adds is additive — input defaults, composition, the
eval schema, registry manifests — so a v0.1 package means exactly what it
always meant. By § 17's own guidance this is a MINOR revision: new capability,
unchanged contract.
A consumer MUST reject a format it does not recognize rather than guessing at
its meaning.
id
A stable, registry-independent logical identifier.
Recommended syntax:
<namespace>/<name>
Examples:
review/code-review
engineering/architecture-review
practice/pqrst-estimate
Rules:
- lowercase ASCII is RECOMMENDED;
/,-,_, and.MAY be used;- whitespace MUST NOT be used;
- the ID MUST NOT contain
..path traversal segments; :MUST NOT be used, so that it remains available as the registry separator in a qualified reference (below);- registry implementations MUST treat the ID as logical metadata rather than an unchecked filesystem path.
Identity is registry-scoped
An id names a package within a registry, the way a path names a file
within a repository. CPF does not claim that an id is globally unique:
practice/pqrst-estimate obtained from two different registries may be two
different packages, and a consumer that draws on more than one registry MUST
keep track of which registry each package came from.
This follows from what the format actually guarantees. A local-first format with no signing, no federation and no central authority (all explicit non-goals) cannot enforce global uniqueness, and a guarantee that cannot be enforced is worse than none — it invites consumers to conflate two packages that merely share a name.
Where a consumer must distinguish them, a qualified reference names the registry:
<registry>:<id>
For example:
house:practice/pqrst-estimate
upstream:practice/pqrst-estimate
An unqualified id is acceptable wherever it is unambiguous for that consumer. A tool that finds the same id in more than one registry MUST report the ambiguity rather than choosing for the caller.
name
Human-readable display name.
version
A package version. Semantic Versioning (MAJOR.MINOR.PATCH) is RECOMMENDED and used by the reference implementation.
Behavior-changing edits SHOULD create a new version rather than overwrite a published package.
summary
A short description of the intended purpose. A consumer SHOULD be able to decide whether a package is potentially relevant from name + summary alone.
template
Relative path to the primary prompt template inside the package. The path MUST remain within the package directory.
type
Declares what kind of artifact the package is. Optional; defaults to
template.
| Value | Meaning |
|---|---|
template |
A complete prompt, intended to be used on its own |
fragment |
A reusable block intended for inclusion in other packages (§ 10.4) |
type is advisory. A fragment is a perfectly valid package and MAY be
rendered on its own; the field records the author's intent so that a consumer
can warn when a package is used against it — rendering a fragment as a
standalone prompt, or including a whole template where a fragment was meant.
4. Complete manifest surface
format: canned-prompt/v0.2
id: review/code-review
name: Code Review
version: 1.2.0
summary: >
Review a change for correctness, maintainability,
security and test coverage.
type: template
template: prompt.md
inputs:
- name: change
type: content
required: true
description: The code, diff, or change to review.
- name: repository_context
type: content
required: false
description: Optional surrounding repository context.
default:
derive: context/repository-summary
value: "(no repository context provided)"
parameters:
depth:
type: enum
values: [quick, normal, thorough]
default: normal
description: Desired review depth.
include_security:
type: boolean
default: true
output:
format: markdown
description: A structured review with findings and recommendations.
compatibility:
capabilities:
- long-context
models: []
providers: []
dependencies:
prompts:
- id: context/repository-summary
version: 1.0.0
requirement: generate
context:
- name: repository-tree
description: A listing of the repository under review.
requirement: optional
capabilities:
- code-analysis
examples:
- examples/basic.yaml
evals:
- evals/review-quality.yaml
license: CC-BY-4.0
tags:
- code-review
- engineering
provenance:
author: Example Author
source: https://example.invalid/original
derived_from: []
extensions: {}
All fields other than the required fields in section 3.1 are optional.
5. Prompt template syntax
CPF uses deliberately small placeholder semantics:
{{ variable_name }}
A placeholder name MUST correspond to either:
- a declared
input, or - a declared
parameter.
Whitespace immediately inside {{ and }} is insignificant.
Examples:
Review the following change at {{ depth }} depth.
{{ change }}
5.1 Resolution and rendering
Producing a final prompt is two steps:
- Resolve — determine a value for every declared input and parameter.
- 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:
- Call-supplied values override defaults.
- A declared parameter default is used when no call value is supplied.
- A declared static input default is used when no call value is supplied.
- A required input without a value is an error.
- 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
valuewhen one is declared, and otherwise leaves the input unresolved. - An included input default is satisfied by rendering the included package
(§ 10.4). This is deterministic, so a consumer that can render can satisfy
it; one that cannot locate the package uses the static fallback
valuewhen declared, and otherwise leaves the input unresolved. - Resolution MUST report which values were derived or included, so that a caller can see what was added on its behalf before the prompt is used.
Rendering rules:
- Values are substituted as text.
- A placeholder with no resolved value is an error.
- Template evaluation MUST NOT execute arbitrary code.
- 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. Inclusion likewise happens during resolution; rendering only substitutes.
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 9 makes an error.
CPF 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 template as executable code.
6. Inputs
inputs is an optional ordered list.
inputs:
- name: document
type: content
required: true
description: Document to summarize.
Fields:
| Field | Required | Meaning |
|---|---|---|
name |
yes | Placeholder/input identifier |
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 input types are:
contenttexturlpathjson
These are descriptive hints. 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:
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):
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:
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.
Included default
A default may instead include another package's rendered template as text (§ 10.4). Unlike a derived default this is deterministic and needs no model, so every implementation that can render can also include:
dependencies:
prompts:
- id: style/house
version: 1.0.0
requirement: required
inputs:
- name: house_style
type: content
required: false
default:
include: style/house
include names a package declared in dependencies.prompts, exactly as a
derive reference does. A single default MUST declare at most one of
include or derive.
An included default MAY also declare a static fallback value, used by a
consumer that cannot locate the included package.
Static fallback
value inside a derived or included 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.
The same holds for an inclusion that cannot be resolved.
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.
Supported descriptive parameter types:
stringintegernumberbooleanenum
Example:
parameters:
tone:
type: enum
values: [neutral, friendly, formal]
default: neutral
max_items:
type: integer
default: 10
A tool SHOULD validate enum values. Other type validation is RECOMMENDED but not mandatory for a minimal implementation.
8. Output contract
output describes the intended result, not an execution protocol.
output:
format: markdown
description: Concise structured findings.
Suggested format values include:
textmarkdownjsonyamlxmlcode
Registries MAY index output format for discovery.
9. Compatibility
compatibility records observations, never requirements.
Everything here describes where the package has been seen to work. Nothing here is a precondition for using it, and a consumer MUST NOT refuse to run a package because its environment is absent from these lists.
compatibility:
capabilities:
- long-context
aliases:
web-search: [browsing, tool-search]
models:
- example/model-family
providers: []
Semantics:
capabilities: capabilities the package is known to work well with, whether or not it requires them;aliases: the same capability as known by other names, so that a consumer can recognize a requirement its environment labels differently;models: model identifiers known to be compatible or evaluated;providers: provider identifiers when provider-specific behavior matters.
An empty list means "not constrained/unspecified", not "compatible with nothing".
9.1 Required versus compatible
Earlier drafts described compatibility as recording "requirements or
observations" and dependencies as recording what a prompt "expects", so both
appeared to say the same thing about capabilities. They do not, and the
distinction is worth stating in one word each:
| Field | Meaning | Absence means |
|---|---|---|
dependencies |
required — the package does not work without it | a consumer SHOULD warn or refuse before running |
compatibility |
observed — the package is known to work with it | nothing; it is information, not a gate |
A capability the prompt cannot function without belongs in
dependencies.capabilities. A capability it merely benefits from, or has been
evaluated against, belongs in compatibility.capabilities. The same name may
legitimately appear in both: required to run at all, and separately observed to
work well on particular models.
10. Dependencies
Dependencies describe what a package requires in order to work (§ 9.1). There are three kinds, separated by what CPF can do about them:
| Kind | Names | CPF can resolve it |
|---|---|---|
prompts |
other CPF packages | yes — by id and version (§ 10.3) |
context |
things CPF does not package | no — the consumer supplies them |
capabilities |
what the environment must be able to do | no — the environment either can or cannot |
dependencies:
prompts:
- id: context/repository-summary
version: 1.0.0
requirement: optional
context:
- name: repository-tree
kind: information-space
description: A listing of the repository under review.
requirement: required
capabilities:
- web-search
10.1 Context dependencies
context names something the package needs that CPF neither packages nor
resolves: a live information space, an API, a corpus, a document the caller
supplies. Anything CPF can package is a package — a reusable policy or style
block belongs in prompts as a type: fragment package (§ 3.2), not here.
Entries use name rather than id, precisely because they are not package
identifiers and nothing can look them up.
| Field | Required | Meaning |
|---|---|---|
name |
yes | What the context is called, in kebab-case |
description |
yes | What it is and what it must contain |
kind |
no | Free-form hint, e.g. information-space, api, document, corpus |
requirement |
no | required or optional; defaults to required |
description is required because nothing else can explain an unpackaged
dependency. A consumer meeting this entry has only the text to go on, and
INTENT principle 3 puts hidden context at odds with reuse.
A context dependency is not an input. An input is content the caller passes for one use (§ 6); a context dependency is a standing fact about the environment the package needs to be run in.
10.2 Capability dependencies
capabilities lists what the execution environment must be able to do, as
free-form lowercase kebab-case names — validated for shape, not for membership
in any vocabulary, exactly as tags are (§ 15). Standardizing model capability
vocabularies remains deferred; no real usage has yet asked for one.
A capability is not an artifact: it cannot be fetched, pinned or generated, so
it takes no version and no requirement: generate. It is either present or it
is not. A consumer that knows it lacks a required capability SHOULD say so
before running rather than after.
Where an environment knows the same capability by another name,
compatibility.aliases (§ 9) can record the correspondence.
Recommended requirement values:
requiredoptionalgenerate
generate means that a resolver MAY satisfy a missing dependency by invoking an appropriate generation process. CPF does not define how generation or dependency resolution works.
10.3 Naming and pinning a dependency
A dependency's id MAY be a qualified <registry>:<id> reference (§ 3.2).
An unqualified id is resolved by the consumer against whatever registries it
draws on, and an id matching packages from more than one registry MUST be
reported as ambiguous rather than chosen.
version selects which version satisfies the dependency:
version |
Selects |
|---|---|
a semver literal, e.g. 1.2.0 |
exactly that version |
any |
any available version; a consumer SHOULD prefer one it already holds |
newest |
the newest available version |
>= 1.2.0 |
the newest available version that is at least 1.2.0 |
Prereleases are excluded from any, newest and >= (§ 17.1); name one
exactly to select it.
An exact pin is the expected form. The other three are explicit opt-ins, visible in the manifest, so that looseness is always something an author wrote rather than something absence implied.
version is REQUIRED for any dependency referenced by a composition or a
derived default (§ 6.1), and RECOMMENDED otherwise.
This is deliberately not semantic version range resolution, which remains a non-goal. There are no unions, no caret or tilde operators, and above all no solver: each dependency is selected independently against what is available, and no consumer is expected to satisfy constraints across a dependency graph. A single lower bound is a selector, not a constraint system.
10.4 Composition
A package composes another in one of two ways, both expressed as an input default (§ 6.1) so that composition reuses the resolution machinery rather than adding a second one:
| Form | Produces | Deterministic |
|---|---|---|
include |
the other package's rendered template, inlined as text | yes |
derive |
the other package's result, obtained by running it | no |
include is text composition: a shared preamble, rubric or house-style block
becomes a real versioned package instead of copied text, and inlining it needs
no model. derive is output composition: the value is whatever running the
other package produces, and only a consumer able to run it can supply one.
When rendering an included package, its placeholders are resolved from the including package's already-resolved values by name, falling back to the included package's own defaults. An included package with a required input that the including package does not supply is an error naming both packages.
Implementations MUST detect inclusion cycles and report them rather than recursing.
CPF does not define template inheritance. A package does not extend
another, override its sections, or inherit its inputs. Composition is by
reference only, for four reasons drawn from this specification and from
INTENT.md: hidden context defeats reuse (INTENT principle 3); § 19 asks that
package contents be visible before execution, which an inheritance chain
prevents; § 17 asks that behavior changes produce a new version, which an
inherited change would bypass; and resolving an override chain is the kind of
graph problem the non-goals exclude.
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.
An example file is not normative but SHOULD make intended usage obvious.
Suggested YAML shape:
name: basic review
values:
change: |
def add(a, b):
return a + b
depth: quick
Tools MAY render examples directly.
12. Evals
evals is a list of relative paths to evaluation specifications.
CPF does not standardize a universal evaluation language, which remains a
non-goal. Every eval file MUST declare a schema, and a consumer MUST ignore a
schema it does not recognize rather than guessing at its meaning.
CPF defines exactly one schema, so that evals/ holds something tools can act
on rather than opaque blobs. Other schemas remain legal and are simply not
interpreted here.
12.1 canned-prompts/eval-rubric/v0.1
schema: canned-prompts/eval-rubric/v0.1
name: pqrst-estimate-quality
description: The estimate reads as a post-session audit, not a plan.
example: examples/basic.yaml
render:
- contains: "must sum to exactly 100%"
- not_contains: "{{"
- resolves_all: true
output:
criteria:
- Percentages sum to exactly 100%.
- Categories are not silently renamed or merged.
- Rationale cites evidence from the session summary.
| Field | Required | Meaning |
|---|---|---|
schema |
yes | MUST be canned-prompts/eval-rubric/v0.1 |
name |
yes | Identifies this eval within the package |
description |
no | What the eval is for |
example |
no | Fixture to run against; MUST be a path declared in the manifest's examples |
render |
no | Deterministic checks on the rendered prompt |
output |
no | Model-judged criteria for the result |
An eval declaring neither render nor output asserts nothing and SHOULD be
rejected.
Render checks
Render checks assert properties of the rendered prompt text. They need no model, so any implementation that can render can run them.
| Check | Passes when |
|---|---|
contains: <text> |
the rendered prompt contains that text |
not_contains: <text> |
it does not |
resolves_all: true |
every placeholder resolved to a value |
Each entry is a single-key mapping, so a check may appear more than once.
Output criteria
output.criteria is a list of statements about a good result. Judging them
requires running the prompt and assessing what comes back, which CPF does not
specify and most consumers cannot do. They are declared, not run — the
same division as include and derive in § 10.4, and for the same reason.
Results are not part of the package
An eval file declares what to assess; it MUST NOT record outcomes. Results are
run evidence, which lives outside the immutable package (§ 17) and may be
associated with <id>@<version> by a registry or evaluation system without
mutating it. A tool reporting results SHOULD identify the package version, the
eval name, and each check's outcome.
13. Provenance and lineage
provenance:
author: Ada Example
source: https://example.invalid/source
derived_from:
- id: review/code-review
version: 1.1.0
The field is descriptive. Registries SHOULD preserve provenance when publishing or mirroring packages.
14. Licensing
A package MAY declare an SPDX license identifier or other clear license expression:
license: CC-BY-4.0
Absence of a license MUST NOT be interpreted as permission to redistribute or modify the package.
Tools SHOULD surface licensing metadata during publishing and installation.
15. Tags
tags:
- architecture
- review
- agentic-coding
Tags are free-form discovery hints. Registries MAY normalize or enrich tags while preserving package metadata.
16. Extensions
extensions is the designated escape hatch for experimental or implementation-specific metadata.
extensions:
org.example.canned-prompts:
maturity: experimental
Extension keys SHOULD be namespaced to avoid collisions.
A consumer MUST ignore unknown extension entries unless it explicitly claims support for them.
17. Immutability and versioning
Published <id>@<version> pairs SHOULD be immutable.
A registry SHOULD reject publication of a package when the same ID/version already exists with different contents unless an explicit administrative override mechanism exists.
Immutability is scoped to a registry, because identity is (§ 3.2). The same
<id>@<version> held by two registries is two packages, and a consumer holding
both is not in a conflict — it is holding two things whose names happen to
coincide. Only a repeat publication within one registry violates
immutability.
17.1 Precedence and prereleases
Where versions are compared, precedence follows Semantic Versioning: numeric
fields first, and a prerelease version ranks below its own release, so
1.0.0-rc1 precedes 1.0.0. Build metadata does not affect precedence.
A prerelease is not selected by any, newest, or a >= lower bound
(§ 10.3). Only an exact pin selects one. Publishing a release candidate
therefore never changes what existing consumers resolve to — which is the
point of marking it a candidate.
Suggested versioning guidance:
- PATCH: wording/metadata correction with intended behavior unchanged;
- MINOR: backward-compatible behavior or parameter additions;
- MAJOR: changed contract, renamed/removed inputs, or materially different intended behavior.
This guidance is intentionally advisory because prompt behavior is probabilistic and cannot be versioned as mechanically as an API.
18. Package validation
A validator SHOULD verify at least:
prompt.yamlexists and parses as YAML;- required manifest fields exist;
formatnames a recognized revision (§ 3.2);idis non-empty and contains no path traversal;versionis non-empty;templateresolves to a regular file inside the package;- referenced example/eval paths do not escape the package;
- required inputs and parameter names are unique;
- every template placeholder resolves to a declared input or parameter;
- no required value is silently omitted during rendering;
- no input declares both
defaultandrequired: true; - a derived default declares either a
derivereference or an inlinederive.prompt, never both; - a
deriveorincludereference names a package declared independencies.prompts; - no default declares both
includeandderive; - every dependency referenced by a composition or derived default declares a
version, and that version is a semver literal,any,newest, or a>=lower bound (§ 10.3); - inclusion does not form a cycle;
- every eval file parses and declares a
schema; - an eval declaring
canned-prompts/eval-rubric/v0.1has aname, asserts something viarenderoroutput, uses only known render checks, and — when it declares anexample— names a path listed in the manifest'sexamples; - every
dependencies.contextentry declares anameand adescription, and arequirementofrequiredoroptionalif present; - capability names in
dependencies.capabilitiesandcompatibility.capabilitiesare lowercase kebab-case.
A validator SHOULD additionally warn when a package intended for publication declares an inline derivation prompt (§ 6.1).
A validator that is given a registry rather than a package SHOULD verify,
when registry.yaml is present, that format is
canned-prompt-registry/v0.1, that name is a non-empty string usable in a
qualified reference, and that each namespaces entry declares a policy of
open or closed.
19. Security requirements
Prompt packages are content, not trusted code.
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.
Implementations SHOULD:
- inspect all referenced paths for traversal;
- make package contents visible before execution;
- surface provenance and license metadata;
- treat remote content referenced by a package as untrusted input;
- separate package installation from model/tool authorization.
20. Registry model
CPF does not mandate registry transport.
A valid registry may be:
- a filesystem directory;
- a Git repository;
- an object store;
- an HTTP service;
- a federated catalog.
Conceptually, a registry stores immutable package versions keyed by:
<id>@<version>
20.1 Registry identity and namespace ownership
A registry MAY declare itself with a registry.yaml at its root:
format: canned-prompt-registry/v0.1
name: house
description: Internal prompt registry.
namespaces:
practice:
owner: Ada Example
policy: closed
scratch:
policy: open
Fields:
| Field | Required | Meaning |
|---|---|---|
format |
yes | MUST be canned-prompt-registry/v0.1 |
name |
yes | Short registry name, used in qualified references (§ 3.2) |
description |
no | Human-readable explanation |
namespaces |
no | Mapping of namespace to its claim |
Within namespaces, owner is a human-readable claim and policy is
closed (only the owner publishes) or open (anyone may). Both are
descriptive: a filesystem registry has no way to authenticate a publisher, and
authentication, signing and trust scoring are all explicit non-goals.
The file is optional. A bare directory remains a valid registry; a consumer that finds no manifest SHOULD take the registry's name from how it was addressed — for the reference implementation, the registry directory's basename.
Ownership is a property of the registry, not of the package. A package never declares who owns its namespace. This keeps the claim where it can actually be acted on — the registry decides what it admits — and keeps packages free of unverifiable assertions of authority, consistent with § 19's position that a package is content rather than a trusted actor. It also means package semantics do not change when a filesystem registry is later replaced by a hosted one.
A registry SHOULD refuse to publish into a closed namespace it does not
consider the publisher to own. How it decides is registry policy and is
outside this specification.
20.2 Consumer-side layout
A consumer drawing on more than one registry MUST keep packages from different
registries distinct, because identity is registry-scoped (§ 3.2). Installing
the same <id>@<version> from two registries is not an error and MUST NOT
overwrite: both are retained and addressed by qualified reference.
The reference implementation stores its catalog as:
catalog/
└── <registry name>/
└── <id path>/
└── <version>/
The reference implementation uses the filesystem layout:
registry/
└── <id path>/
└── <version>/
├── prompt.yaml
└── ...
For example:
registry/
├── registry.yaml
└── practice/
└── pqrst-estimate/
└── 0.1.0/
├── prompt.yaml
└── prompt.md
A registry's own layout is not namespaced by registry name: within one registry, an id is unambiguous by definition.
21. Reference CLI semantics
The reference tool uses two stores:
- catalog — packages locally available for search/show/render;
- registry — packages available for publish/install.
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
Every command that takes an ID accepts either a bare id or a qualified
<registry>:<id> reference (§ 3.2). A bare id that matches packages installed
from more than one registry is reported as ambiguous, listing the candidates,
rather than resolved by guessing.
add takes a package from a path rather than from a registry, so it files the
package under the reserved registry name local.
The reference tool satisfies include defaults, because inclusion is
deterministic and needs no model, and reports derive defaults it cannot
satisfy.
eval ID run an installed package's render checks
eval runs the deterministic render checks of every recognized eval file and
reports output criteria as declared but not run.
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
This is examples/pqrst-estimate in the canned-prompts repository, abridged.
It composes a shared style fragment, so it also demonstrates § 10.4.
prompt.yaml:
format: canned-prompt/v0.2
id: practice/pqrst-estimate
name: PQRST Estimate
version: 0.2.1
summary: Estimate how session effort was distributed across PQRST categories.
template: prompt.md
dependencies:
prompts:
- id: practice/house-style
version: ">= 0.1.0"
requirement: required
inputs:
- name: house_style
type: content
required: false
default:
include: practice/house-style
- name: session_summary
type: content
required: true
parameters:
include_rationale:
type: boolean
default: true
output:
format: markdown
compatibility:
capabilities: [long-context]
license: MIT
examples: [examples/basic.yaml]
evals: [evals/quality.yaml]
tags: [retrospective, agentic-coding, pqrst]
prompt.md:
{{ house_style }}
Review the following coding-session summary and estimate the distribution of
session effort across PQRST. Percentages must sum to 100%.
P = main problem
Q = quality and tests
R = research and context clarification
S = security and credentials
T = task organization
Session:
{{ session_summary }}
Include rationale: {{ include_rationale }}
23. Open questions
Settled since v0.1
CANP-WP-0002 promoted five items from "experience will decide" to decisions.
Each is now specified. This table records where the rule lives and what was
chosen, so a later reader can find the reasoning and not just the rule.
| Question | Decision | Where |
|---|---|---|
| Optional inputs were unusable | default on inputs, static or derived; resolution separated from rendering |
§ 5.1, § 6.1 |
| Registry namespaces and ownership | Identity is registry-scoped; ownership is registry policy, never a package's own claim | § 3.2, § 20.1, § 20.2 |
| Prompt composition and inheritance | Composition by reference in two kinds, include and derive; no inheritance |
§ 10.3, § 10.4 |
| Canonical evaluation schemas | One schema, canned-prompts/eval-rubric/v0.1; render checks run, output criteria are declared |
§ 12.1 |
| Typed context and dependency contracts | dependencies means required, compatibility means observed; context names what CPF cannot package |
§ 9.1, § 10.1, § 10.2 |
Two habits recur across those decisions, and later revisions should follow them rather than rediscover them:
Separate the deterministic half from the rest. include and derive,
render checks and output criteria, resolve and render. Each pair splits at the
same seam: what a tool can do with the package alone, and what needs a model. A
consumer that cannot call a model is never second-class — it does the
deterministic half completely and reports the rest honestly, rather than
guessing or silently producing something incomplete.
A package never asserts what it cannot back. Ownership belongs to the
registry that admits a package, not to the package that would like to claim a
namespace. compatibility records observation, never permission. A derived
default is a request to a consumer, not an instruction the package executes.
This is why § 19 can treat a package as content rather than as an actor.
Still deferred
Experience should still determine whether later revisions standardize:
- content macros;
- cryptographic integrity and signing;
- model capability vocabularies — § 10.2 keeps capability names free-form, validated for shape and not for membership;
- run manifests and evidence formats;
- deterministic compilation manifests;
- trust and reputation signals;
- federated discovery;
- richer template syntax;
- pattern-matching render checks — § 12.1 stops at
contains,not_containsandresolves_allrather than adding a matching language to a format whose § 5 rests on not having one.
Decided against
Template inheritance (§ 10.4) is not deferred; it was considered and refused. Reopening it means overturning a decision, not filling a gap, and would have to answer the four objections recorded there.
Until practical usage forces these questions, the format should remain intentionally small.