--- id: CANP-WP-0002 type: workplan title: "Resolve CPF v0.1 open questions promoted for v0.2" domain: agents repo: canned-prompts status: proposed owner: codex topic_slug: practice created: "2026-09-06" updated: "2026-09-06" reviewed_at: "2026-09-06" reviewed_by: "claude" context_paths: - "INTENT.md" - "CannedPromptFormat-v0.1.md" - "reference/canned_prompts.py" - "examples/pqrst-estimate/" state_hub_workstream_id: "c87c8e27-8b11-5306-b687-86779ef3f1be" --- # Resolve CPF v0.1 open questions promoted for v0.2 `CannedPromptFormat-v0.1.md` § 23 currently lists twelve items as "experience should determine". A review of the seed on 2026-09-06 (spec + reference CLI + `examples/pqrst-estimate`; 3/3 tests pass, full local lifecycle smokes clean) plus an operator interview promoted five of them from "wait and see" to "decide, with a stated leaning". This workplan carries those decisions. Unpromoted § 23 items stay deferred and unchanged: content macros, cryptographic integrity/signing, model capability vocabularies, run manifests and evidence formats, deterministic compilation manifests, trust/reputation signals, federated discovery, richer template syntax. **Guardrail for every task below:** `INTENT.md` § Deliberate boundary. The format may *declare* a requirement; it must not specify the resolver, runtime, or execution engine that satisfies it. ## Optional inputs need defaults ```task id: CANP-WP-0002-T01 status: done priority: high state_hub_task_id: "e49bf036-0aee-5bb2-abbd-3d3f683df298" ``` **Gap found in review.** Rendering rule § 5.1(4) makes any unresolved placeholder an error, and § 6 gives `inputs` no `default` field. An input declared `required: false` and referenced from the template therefore makes `render` fail unless a value is supplied — optional inputs are effectively unusable. The spec's own § 4 manifest example hits this with `repository_context`. `reference/canned_prompts.py` is conformant here; the defect is in the spec. **Decision (operator, 2026-09-06):** add a `default` field to `inputs`, and allow that default to be either - a **static** value, or - a **derived** default — a prompt that produces the value from available context. Unresolved-with-no-default remains an error. **Design note.** § 10 already carried the derivation mechanism: `dependencies` accepts `requirement: generate`, defined as "a resolver MAY satisfy a missing dependency by invoking an appropriate generation process", with resolution left undefined. A derived default therefore did not need a new concept, only a binding — the input's default names the dependency, and the dependency records what may be generated and at which version. **Follow-up decisions (operator, 2026-09-06):** - *Both declaration forms are allowed.* A derived default may reference a declared prompt dependency, or carry an inline `prompt`. The reference form is preferred and the spec says why (an inline prompt is anonymous — unversioned, unprovenanced, un-evaluable), and a validator SHOULD warn when a **published** package derives inline. Inline is for local and draft packages. - *Error unless static fallback.* A derived default MAY declare a static `value`. A consumer that does not derive uses it; with no fallback the input stays unresolved, which is an error. Derivation never silently yields empty content. - *Split render from resolve.* Resolution decides values and may be non-deterministic; rendering substitutes and always is. Rendering MUST NOT derive. This preserves INTENT success criterion 4 verbatim — no change to `INTENT.md` was needed. Work: Delivered: 1. § 5.1 rewritten as "Resolution and rendering" — six resolution rules and four rendering rules, with rendering explicitly deterministic and forbidden from deriving (rule 10). Resolution must report which values were derived (rule 6). A tool that resolves only supplied values and static defaults is stated to be conforming. 2. § 6 gains `default` in the field table plus a new § 6.1 covering both declaration forms, the static fallback, and why the reference form is preferred. 3. § 4 manifest surface updated: `repository_context` — the field that demonstrated the original defect — now carries a derived default with a fallback, backed by a `requirement: generate` prompt dependency. 4. § 10 states the binding between `requirement: generate` and a referenced derived default. 5. § 18 gains validation rules 11–13 and the publish-time inline warning. 6. § 19 gains two MUST NOTs: a derived default's prompt text is not an instruction addressed to the consuming tool, and derivation must be visible to the caller. 7. § 21 documents the new `resolve` verb. 8. `reference/canned_prompts.py`: `Resolution` dataclass, `resolve_inputs` (with `resolve_values` kept as a wrapper), `input_default_kind`, `prompt_dependency_ids`, `validate_input_default`, a `resolve` command, and a specific render-time error naming the underivable inputs. Tests: 3 → 11, all passing. Both READMEs document the split. ## Registry namespaces and ownership ```task id: CANP-WP-0002-T02 status: todo priority: high state_hub_task_id: "a52cd41e-2bcc-50ee-96a1-d645a06df922" ``` An id like `practice/pqrst-estimate` has a namespace prefix with no owner. Two authors publishing `practice/…` into one registry collide today; `add` and `publish` only refuse an exact `id@version` that already exists. Decide, for § 3.2 and § 20: what a namespace means, who may claim one, how a filesystem registry records the claim, and what a consumer does on conflict. Leaning: keep v0.1's local-first stance — namespace ownership is a *registry policy*, declared by the registry rather than by the package — so package semantics do not change when a hosted registry appears later. Signing and trust scoring stay out of scope (§ 23, INTENT non-goals). ## Prompt composition and inheritance ```task id: CANP-WP-0002-T03 status: todo priority: high state_hub_task_id: "0505c790-e57e-568c-9c8c-376957c9b08e" ``` Largest gap between `INTENT.md` and the spec. Principle 9 ("composition without capture") and the `dependencies.prompts` field both promise composition, but v0.1 defines no mechanism — `dependencies` is a declared field with no semantics. T01 has since settled one corner of this: a derived default binds an input to a prompt dependency declared `requirement: generate`, so package-to-package reference already exists for that one case. Build on it rather than around it. Decide what composition means at the *artifact* level: how one package references another, whether references are includes, extends, or plain declared prerequisites, and how versions are pinned. Leaning: declaration only — a package states what it needs, and resolution stays with the consumer, per the INTENT boundary. Do not introduce range resolution (INTENT non-goal). ## Canonical eval schemas ```task id: CANP-WP-0002-T04 status: todo priority: medium state_hub_task_id: "0daf1097-6599-563f-8965-739cbc874ddf" ``` Cheapest item to pin down. `evals/` is a reserved path and `evals:` is a manifest list, but § 12 defines no schema, so an eval file is an unvalidated blob that no tool can act on. Define a minimal eval-spec schema: identity, what is being asserted, the fixture it runs against, and how a result is reported. Keep it declarative and engine-neutral — "universal prompt evaluation" is an explicit INTENT non-goal, so this specifies the *file*, not an evaluation engine. Extend § 18 to validate eval files that declare the schema, and add one eval to `examples/pqrst-estimate` as a worked case. ## Typed context and dependency contracts ```task id: CANP-WP-0002-T05 status: todo priority: medium state_hub_task_id: "5beec7ba-e6c9-58b1-a065-b06aba015034" ``` `dependencies.context` and `dependencies.capabilities` appear in the § 4 manifest surface with no semantics whatsoever in v0.1, and § 9 `compatibility.capabilities` overlaps them without a stated relationship. Decide: what a context dependency declares, how it differs from an input, how it relates to `compatibility.capabilities`, and whether capability names are free strings in v0.2 (model capability vocabularies stay deferred). T01 is now settled and partly answers this: a derived default is a consumer-resolved context requirement expressed through `dependencies.prompts` rather than through `dependencies.context`. Decide whether `context` is still a distinct concept or collapses into the prompt-dependency mechanism. ## Rewrite specification section 23 ```task id: CANP-WP-0002-T06 status: todo priority: medium state_hub_task_id: "06032689-fc19-5fcd-a76e-5dbc87d02cd4" ``` After T01–T05 land, replace § 23's flat twelve-item list with two sections: questions **being decided for v0.2** (each with its scoped question and stated leaning, referencing this workplan) and questions **still deferred** (the eight unpromoted items listed at the top of this file). Bump the spec status line if the format revision warrants it. ## Reference implementation conformance fixes ```task id: CANP-WP-0002-T07 status: todo priority: low state_hub_task_id: "f642abe6-8222-5958-88ee-7a53454f2344" ``` Two defects found in the same review, independent of the format questions: 1. **Prerelease versions sort as newest.** `parse_semver` in `reference/canned_prompts.py` returns `(major, minor, patch, raw_string)`, so `1.0.0-rc1` and `1.0.0` tie on the numeric fields and then compare as strings — `"1.0.0-rc1" > "1.0.0"`. `resolve_installed` with no `--version` therefore selects a prerelease over its own release. Order prerelease below release, or state in § 17 that v0.1 ignores prerelease ordering. 2. **`copy_immutable` copies everything.** `add`/`publish` use `shutil.copytree` over the whole source directory, so a stray `.git`, `.venv`, or scratch file lands in the catalog and registry. § 2 says tools MUST ignore unknown non-reserved files unless a manifest field references them. Decide whether packaging is reserved-paths-only or an explicit ignore list, then align the implementation and § 2.