CANP-WP-0002 T03: composition by reference, two kinds
T01 had already delivered half of composition without naming it: a derived default binds an input to a prompt dependency, which is transclusion — run package B, use its output. What was missing was the deterministic half. `include` inlines another package's rendered template as text. No model is involved, so the reference CLI can actually perform it, and a shared preamble, rubric or style block becomes a versioned package instead of copied text. This is the concrete way to honor INTENT principle 9 without any runtime. `derive` stays as it was. Both are input defaults, so composition reuses the resolution machinery rather than adding a second one. No template inheritance. Four of this repo's own documents argue against it: INTENT principle 3 (hidden context defeats reuse), section 19's "make package contents visible before execution", section 17's requirement that behavior changes produce a new version, and the non-goal on range resolution. Version selectors: an exact pin is the expected form, with `any`, `newest` and `>= X.Y.Z` as explicit opt-ins so looseness is written rather than implied by absence. Selectors are evaluated per dependency against what is available — no solver, no cross-dependency constraint satisfaction — which is what keeps them outside the range-resolution non-goal, and the spec says so. Also defines `type` (template | fragment), which appeared once in the section 4 manifest surface and was specified nowhere. Spec: 3.2 (type), 5.1 (inclusion resolution rule, renumbered), 6.1 (included default), 10.1 and 10.2 (new), 18 (rules 14-16), 21. Reference CLI: validate_version_selector, select_version, prompt_dependencies replacing prompt_dependency_ids, check_composition_reference, CatalogComposer with cycle detection, and resolve_inputs gaining composer= and inherited=. Tests 21 -> 42. Examples: house-style is a real fragment package; pqrst-estimate composes it and is bumped 0.1.0 -> 0.2.0 per section 17. Fixes an ordering bug found while testing: inputs resolved before parameters, so an included package could not see the including package's parameters and silently fell back to its own defaults — the fragment rendered tone=neutral where the including package said blunt. Parameters now resolve first; the report still lists inputs first. 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
This commit is contained in:
parent
352ed5b30a
commit
4a56f20209
11 changed files with 697 additions and 58 deletions
|
|
@ -179,7 +179,7 @@ non-goals and were not touched.
|
|||
|
||||
```task
|
||||
id: CANP-WP-0002-T03
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "0505c790-e57e-568c-9c8c-376957c9b08e"
|
||||
```
|
||||
|
|
@ -189,15 +189,62 @@ 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.
|
||||
**Found during the work.** T01 had already delivered half of composition
|
||||
without it being named as such: a derived default binds an input to a prompt
|
||||
dependency, which is transclusion — run package B, use its output. What was
|
||||
missing was the *deterministic* half. Separately, `type: template` appeared
|
||||
once in the § 4 manifest surface and was defined nowhere in the specification.
|
||||
|
||||
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).
|
||||
**Decisions (operator, 2026-09-06):**
|
||||
|
||||
- *No inheritance.* A package never extends another, overrides its sections,
|
||||
or inherits its inputs. Four of this repo's own documents argue against it:
|
||||
INTENT principle 3 (hidden context defeats reuse); § 19's "make package
|
||||
contents visible before execution", which an inheritance chain prevents;
|
||||
§ 17's requirement that behavior changes produce a new version, which an
|
||||
inherited change bypasses; and the non-goal on range resolution, which an
|
||||
override chain would drag in.
|
||||
- *Two composition kinds.* `include` inlines another package's **rendered
|
||||
template** as text — deterministic, no model, and the reference CLI performs
|
||||
it. `derive` (T01) uses another package's **result** — non-deterministic and
|
||||
only available to a consumer that can run it. Both are expressed as input
|
||||
defaults, so composition reuses the resolution machinery instead of adding a
|
||||
second one.
|
||||
- *Version selectors.* An exact pin is the expected form; `any`, `newest` and
|
||||
`>= X.Y.Z` are explicit opt-ins, so looseness is always written rather than
|
||||
implied by absence. These are per-dependency selectors evaluated
|
||||
independently against what is available. There is no solver and no
|
||||
cross-dependency constraint satisfaction, which is the line that keeps this
|
||||
outside the range-resolution non-goal — the spec says so explicitly.
|
||||
|
||||
Delivered:
|
||||
|
||||
1. § 3.2 defines `type` (`template` | `fragment`), closing the undefined-field
|
||||
gap. It is advisory: a fragment is a valid package and may be rendered
|
||||
alone; the field records intent so a consumer can warn.
|
||||
2. § 10.1 (new) covers naming and pinning: qualified dependency ids, the four
|
||||
version selectors, `version` required for anything composed, and an
|
||||
explicit statement of why this is not range resolution.
|
||||
3. § 10.2 (new) defines both composition kinds, parameter pass-through into an
|
||||
included package, mandatory cycle detection, and the reasoned refusal of
|
||||
inheritance.
|
||||
4. § 6.1 gains the included-default form; § 5.1 gains resolution rule 6 for
|
||||
inclusion (rules renumbered); § 18 gains rules 14–16; § 21 notes that the
|
||||
reference tool satisfies inclusions and reports derivations.
|
||||
5. `reference/canned_prompts.py`: `validate_version_selector`,
|
||||
`select_version`, `prompt_dependencies` (replacing
|
||||
`prompt_dependency_ids`), `check_composition_reference`, `CatalogComposer`
|
||||
with cycle detection, and `resolve_inputs(composer=, inherited=)`.
|
||||
Tests 21 → 42.
|
||||
6. `examples/house-style/` (new) is a real `type: fragment` package, and
|
||||
`examples/pqrst-estimate` composes it — bumped 0.1.0 → 0.2.0 per § 17,
|
||||
since including a style block changes intended behavior.
|
||||
|
||||
**Ordering bug found and fixed during the work.** Inputs were resolved before
|
||||
parameters, so an included package could not see the including package's
|
||||
parameters and silently fell back to its own defaults — the fragment rendered
|
||||
`tone: neutral` where the including package said `blunt`. Parameters now
|
||||
resolve first; the report still lists inputs before parameters.
|
||||
|
||||
## Canonical eval schemas
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue