--registry now accepts an http(s):// URL as well as a path, implemented with urllib so reference/ keeps PyYAML as its only dependency. This was the first real test of INTENT principle 10's claim that a hosted registry layers on without changing package semantics. Verified against the running service, almost everything survived the transport unaltered: identity and its ambiguity rules (a bare id in two registries returns 409 over HTTP just as it does locally), immutability of a published id@version (identical content accepted, changed content refused, version bump accepted), strict packaging, validation, and the index. A package published and then installed over HTTP was byte-identical to its source — diff -r clean — and its canonical-fidelity eval still passed after the round trip. One thing did not survive: a URL is not a registry. A filesystem registry IS one registry and section 20.1 names it from its directory; an HTTP service HOSTS SEVERAL behind one base URL. The address therefore cannot name the registry, so it must be named separately — --as when publishing, a qualified reference when installing. Recorded as section 20.4 rather than worked around silently in the client, because the gap is in the specification's list of registry kinds, not in the CLI. Publishing to an HTTP registry without --as fails with that explanation rather than guessing a registry name. Registry responses are treated as untrusted input (section 19): decode_files refuses path traversal, with a test. The wire shape round-trips binary content through base64, also tested. Reference tests 99 -> 105. 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
254 lines
9.5 KiB
Markdown
254 lines
9.5 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/default # the default 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.
|
|
|
|
## Registries over HTTP
|
|
|
|
`--registry` takes a URL as readily as a path:
|
|
|
|
```bash
|
|
export CANNED_PROMPTS_PUBLISH_TOKEN=...
|
|
python canned_prompts.py publish ../examples/pqrst-estimate \
|
|
--registry https://registry.example --as helix
|
|
python canned_prompts.py install helix:practice/pqrst-estimate \
|
|
--registry https://registry.example
|
|
```
|
|
|
|
Publishing needs `--as`, because a URL addresses a service that hosts several
|
|
registries and so does not name one the way a directory does (§ 20.4). That is
|
|
the *only* place the transport is visible: identity and ambiguity, immutability
|
|
of a published version, strict packaging, validation and the index all behave
|
|
the same over HTTP as on a filesystem.
|
|
|
|
## The index
|
|
|
|
Every store keeps an `index.yaml` recording which package versions entered it,
|
|
from where, and when:
|
|
|
|
```bash
|
|
python canned_prompts.py index
|
|
```
|
|
|
|
```text
|
|
local:helix/repo-orient@0.1.0 2026-09-06T13:31:02Z add
|
|
from /home/worsch/helix-forge/prompts/repo-orient
|
|
declares source personal prompt collection, contributed 2026-09-06
|
|
```
|
|
|
|
This is store metadata, not package data. `provenance` records who wrote a
|
|
prompt; the index records how a copy arrived *here* — which differs for every
|
|
consumer, and which must never rewrite the package it describes. `included_at`
|
|
is first arrival and is never overwritten; a re-run updates `last_seen_at`.
|
|
|
|
## Dependencies are reported, not fetched
|
|
|
|
Installing a package that composes another warns when the dependency is
|
|
absent, including when it is present but no version matches:
|
|
|
|
```text
|
|
declared dependencies not in this catalog: practice/house-style@>= 0.1.0
|
|
— install them, or composition referencing them will not resolve
|
|
```
|
|
|
|
Nothing is fetched automatically. Dependency resolution is left to the
|
|
consumer, but a package that installs cleanly and then cannot render is worse
|
|
than one that says what it is waiting for.
|
|
|
|
## 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.
|