canned-prompts/README.md
tegwick 8376a00a88 CANP-WP-0002 T06: revision v0.2, and section 23 rewritten
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
2026-09-06 14:22:45 +02:00

202 lines
7.6 KiB
Markdown

# canned-prompts seed
> **Collect, reuse and share prompts and prompt templates.**
This bundle contains a first project seed for `canned-prompts`:
- [`INTENT.md`](INTENT.md) — project mission, boundaries, principles, and success criteria.
- [`CannedPromptFormat.md`](CannedPromptFormat.md) — the package-format specification, currently revision v0.2.
- [`reference/`](reference/) — deliberately small Python CLI implementing the basic lifecycle.
- [`examples/pqrst-estimate/`](examples/pqrst-estimate/) — a real package that can be used to exercise the implementation.
- [`examples/house-style/`](examples/house-style/) — a `type: fragment` package that `pqrst-estimate` composes.
## Try the reference implementation
```bash
cd reference
python -m venv .venv
. .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
# add the included examples to your local catalog
python canned_prompts.py add ../examples/house-style
python canned_prompts.py add ../examples/pqrst-estimate
# find and inspect it
python canned_prompts.py search pqrst
python canned_prompts.py show practice/pqrst-estimate
# see how each input and parameter resolves, and where the value came from
python canned_prompts.py resolve practice/pqrst-estimate \
--set session_summary="Implemented feature X, read unfamiliar code, added tests."
# render it
python canned_prompts.py render practice/pqrst-estimate \
--set session_summary="Implemented feature X, read unfamiliar code, added tests."
# publish it to the local filesystem registry
python canned_prompts.py publish ../examples/pqrst-estimate
# remove the local catalog if you want to simulate another machine, then install
python canned_prompts.py install practice/pqrst-estimate --version 0.1.0
```
Packages added from a path are filed under the registry name `local`; packages
installed from a registry are filed under that registry's name. `search` prints
qualified references:
```text
house:practice/pqrst-estimate@0.1.0 PQRST Estimate
local:practice/pqrst-estimate@0.1.0 PQRST Estimate
```
By default the reference tool uses:
```text
~/.canned-prompts/catalog
~/.canned-prompts/registry
```
Override them with:
```text
CANNED_PROMPTS_HOME=/some/path
```
or command-level `--catalog` / `--registry` options.
## Resolution vs rendering
The format separates the two steps (`CannedPromptFormat.md` § 5.1).
Resolution decides a value for every input and parameter and may be
non-deterministic; rendering substitutes those values and always is. An input
may declare a default that is either a static value or a *derived* one — a
prompt that a capable consumer may run to produce the value, declared without
naming any resolver or model.
This reference tool never calls a model, so it resolves supplied values and
static defaults only, and reports anything it cannot derive instead of
rendering a prompt with a silent hole in it.
## Composition
A package composes another by declaring it as a dependency and binding it to an
input default. There are two kinds:
- **`include`** — inline the other package's *rendered template* as text.
Deterministic, needs no model, and the reference CLI performs it.
- **`derive`** — use the other package's *result*, obtained by running it.
Only a consumer able to run it can supply one.
`examples/pqrst-estimate` includes `examples/house-style`, so a shared style
block is a versioned package rather than copied text:
```yaml
dependencies:
prompts:
- id: practice/house-style
version: ">= 0.1.0"
requirement: required
inputs:
- name: house_style
required: false
default:
include: practice/house-style
```
A dependency pins an exact version by default; `any`, `newest` and a `>= X.Y.Z`
lower bound are explicit opt-ins. These are per-dependency selectors, not
version ranges — there is no solver, and constraint resolution across a
dependency graph remains a non-goal.
There is no template inheritance. A package never extends another or overrides
its parts; composition is by reference only, so a package's content stays
readable without chasing ancestors.
## Evals
An eval file declares what to assess. `evals/` used to hold whatever an author
put there; it now has one schema the tooling understands, split along the same
line as composition:
- **render checks** — deterministic assertions about the *rendered prompt*
(`contains`, `not_contains`, `resolves_all`). No model needed, so the
reference CLI runs them.
- **output criteria** — statements about a good *result*. Declared, not run.
```bash
python canned_prompts.py eval practice/pqrst-estimate
```
```text
local:practice/pqrst-estimate@0.2.0
evals/quality.yaml (pqrst-estimate-quality)
render PASS contains "must sum to exactly 100%"
render PASS resolves_all
output -- 4 criteria declared (not run: judging output needs a model)
```
An eval declares assessment, never results. Results are run evidence and live
outside the immutable package.
## Required versus compatible
Two fields used to say overlapping things about capabilities. They now differ
in one word each:
| Field | Meaning | Absence means |
|-------|---------|---------------|
| `dependencies` | **required** — does not work without it | a consumer should warn or refuse |
| `compatibility` | **observed** — known to work with it | nothing; it is information |
Dependencies come in three kinds, separated by what the format can do about
them: `prompts` are packages it resolves by id and version, `context` names
things it does not package at all (an information space, an API, a corpus the
caller supplies), and `capabilities` are what the environment must be able to
do. `resolve` lists the last two, since the reference tool cannot verify
either:
```text
requires (this tool cannot verify these):
capability web-search
context repository-tree — A listing of the repository under review.
```
## Registries and identity
An id names a package *within a registry* (`CannedPromptFormat.md`
§ 3.2). The same id obtained from two registries may be two different
packages, so the catalog keeps them apart and a bare id that matches more than
one is reported as ambiguous rather than guessed. Qualify it when you need to:
```bash
python canned_prompts.py render house:practice/pqrst-estimate --set ...
```
A registry may describe itself with an optional `registry.yaml` naming it and
recording which namespaces are claimed and under what policy. Those claims are
descriptive: a filesystem registry cannot authenticate a publisher, and
signing and trust scoring are explicit non-goals. Ownership lives with the
registry rather than in the package, so no package carries an unverifiable
assertion of authority.
## Packaging and versions
`add`, `publish` and `install` copy the reserved paths (`prompt.yaml`,
`prompt.md`, `README.md`, `LICENSE`, `examples/`, `evals/`, `assets/`) plus
anything a manifest field references — and nothing else, as § 2 requires. What
was left behind is reported rather than silently dropped:
```text
not packaged (not a reserved path, not referenced by the manifest): .git/, .venv/, notes.txt
```
Version precedence follows SemVer, so `1.0.0` outranks `1.0.0-rc1`, and
`any`, `newest` and `>= X.Y.Z` skip prereleases entirely. Publishing a release
candidate never changes what existing consumers resolve to; name it exactly to
use it.
## Deliberate limitations
This seed has no hosted registry, model execution, authentication, network access, dependency resolver, or social features. `publish` and `install` operate on a filesystem registry so that the package semantics can be tested before infrastructure is built around them.