Add the catalog index, and package a real prompt collection
CANP-WP-0004. The operator asked canned-prompts to build a database of
versioned prompts recording where each came from and when — the first step
toward a platform for collaborative prompting.
The format had nowhere to put that. `provenance` records who wrote a prompt and
where the idea came from; nothing recorded how a copy arrived in a particular
store. Section 20.3 now specifies an `index.yaml` as store metadata rather than
package data: how a copy arrived differs for every consumer, and recording an
arrival must never rewrite the package that arrived.
`add`, `install` and `publish` record registry, id, version, name, source,
method, first-inclusion date, and the package's declared author, source and
licence — the last three copied so a listing is readable without opening every
package. A new `index` verb lists it. `included_at` is never overwritten; a
re-run updates `last_seen_at`, because when a package first entered a
collection is a fact about history rather than about the last command run.
Tests 84 -> 90.
Also records two findings from actually using the format:
CANP-WP-0004-T03 — inclusion has no deduplication, so a diamond dependency
renders shared content once per path. Found by composing a real collection.
Not fixed here: deduplicating means choosing which occurrence survives and
deciding what happens when two paths resolve different versions, which is
resolver behaviour that section 10.4 deliberately avoids. Handed to
CANP-WP-0005 with a leaning: document it, warn at validation time, do not
deduplicate.
The add_ons workaround in practice/pqrst-estimate is evidence about section
23's deferred "richer template syntax" — an optional appendix has to be an
input with an empty default, because CPF has no conditionals.
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 17:14:21 +02:00
|
|
|
---
|
|
|
|
|
id: CANP-WP-0005
|
|
|
|
|
type: workplan
|
|
|
|
|
title: "Document and detect inclusion diamonds"
|
|
|
|
|
domain: agents
|
|
|
|
|
repo: canned-prompts
|
CANP-WP-0005: document and detect inclusion diamonds
Section 10.4 said nothing about inclusion being textual and repeated, so an
author met the behaviour only by reading rendered output carefully. That
silence was the defect.
Writing it up got the mechanism wrong at first. I documented a dependency
"reached by two paths" through two include chains, and wrote a detector that
counted composer calls to match. Tested it against the real case that motivated
the task — helix/repo-advance — and it did not fire, though the conventions
block still rendered twice.
The actual mechanism is subtler. helix/commit-sync declares its own
`conventions` input; when composed, resolution passes the including package's
already-resolved values down by name, so it *inherits* the outer text rather
than resolving its own include, and renders it again. Nothing was included
twice; the text appeared twice regardless.
Both mechanisms are now documented with worked diagrams. duplicate_inclusions
detects both — included values that are equal, and an included value contained
within another — and resolve, render and eval warn.
Nothing is deduplicated, as leaned. Deduplicating means choosing which
occurrence survives, since position in a prompt carries meaning, and deciding
what happens when two paths select different versions of the same dependency,
which section 10.3 permits. That is resolver behaviour and section 10.4 keeps
composition declarative.
Verified by reintroducing the diamond in a scratch copy of repo-advance, which
warns, and confirming the shipped factored collection stays silent.
Tests 90 -> 95.
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 19:23:16 +02:00
|
|
|
status: finished
|
Add the catalog index, and package a real prompt collection
CANP-WP-0004. The operator asked canned-prompts to build a database of
versioned prompts recording where each came from and when — the first step
toward a platform for collaborative prompting.
The format had nowhere to put that. `provenance` records who wrote a prompt and
where the idea came from; nothing recorded how a copy arrived in a particular
store. Section 20.3 now specifies an `index.yaml` as store metadata rather than
package data: how a copy arrived differs for every consumer, and recording an
arrival must never rewrite the package that arrived.
`add`, `install` and `publish` record registry, id, version, name, source,
method, first-inclusion date, and the package's declared author, source and
licence — the last three copied so a listing is readable without opening every
package. A new `index` verb lists it. `included_at` is never overwritten; a
re-run updates `last_seen_at`, because when a package first entered a
collection is a fact about history rather than about the last command run.
Tests 84 -> 90.
Also records two findings from actually using the format:
CANP-WP-0004-T03 — inclusion has no deduplication, so a diamond dependency
renders shared content once per path. Found by composing a real collection.
Not fixed here: deduplicating means choosing which occurrence survives and
deciding what happens when two paths resolve different versions, which is
resolver behaviour that section 10.4 deliberately avoids. Handed to
CANP-WP-0005 with a leaning: document it, warn at validation time, do not
deduplicate.
The add_ons workaround in practice/pqrst-estimate is evidence about section
23's deferred "richer template syntax" — an optional appendix has to be an
input with an empty default, because CPF has no conditionals.
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 17:14:21 +02:00
|
|
|
owner: codex
|
|
|
|
|
topic_slug: practice
|
|
|
|
|
created: "2026-09-06"
|
|
|
|
|
updated: "2026-09-06"
|
2026-09-06 17:14:49 +02:00
|
|
|
state_hub_workstream_id: "773f08a3-f899-53fc-87c8-037f8ffbd24d"
|
Add the catalog index, and package a real prompt collection
CANP-WP-0004. The operator asked canned-prompts to build a database of
versioned prompts recording where each came from and when — the first step
toward a platform for collaborative prompting.
The format had nowhere to put that. `provenance` records who wrote a prompt and
where the idea came from; nothing recorded how a copy arrived in a particular
store. Section 20.3 now specifies an `index.yaml` as store metadata rather than
package data: how a copy arrived differs for every consumer, and recording an
arrival must never rewrite the package that arrived.
`add`, `install` and `publish` record registry, id, version, name, source,
method, first-inclusion date, and the package's declared author, source and
licence — the last three copied so a listing is readable without opening every
package. A new `index` verb lists it. `included_at` is never overwritten; a
re-run updates `last_seen_at`, because when a package first entered a
collection is a fact about history rather than about the last command run.
Tests 84 -> 90.
Also records two findings from actually using the format:
CANP-WP-0004-T03 — inclusion has no deduplication, so a diamond dependency
renders shared content once per path. Found by composing a real collection.
Not fixed here: deduplicating means choosing which occurrence survives and
deciding what happens when two paths resolve different versions, which is
resolver behaviour that section 10.4 deliberately avoids. Handed to
CANP-WP-0005 with a leaning: document it, warn at validation time, do not
deduplicate.
The add_ons workaround in practice/pqrst-estimate is evidence about section
23's deferred "richer template syntax" — an optional appendix has to be an
input with an empty default, because CPF has no conditionals.
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 17:14:21 +02:00
|
|
|
---
|
|
|
|
|
|
|
|
|
|
# Document and detect inclusion diamonds
|
|
|
|
|
|
|
|
|
|
Residual from `CANP-WP-0004` (`origin: residual`, `origin_ref: CANP-WP-0004`).
|
|
|
|
|
|
|
|
|
|
## Warn about repeated inclusion
|
|
|
|
|
|
|
|
|
|
```task
|
|
|
|
|
id: CANP-WP-0005-T01
|
CANP-WP-0005: document and detect inclusion diamonds
Section 10.4 said nothing about inclusion being textual and repeated, so an
author met the behaviour only by reading rendered output carefully. That
silence was the defect.
Writing it up got the mechanism wrong at first. I documented a dependency
"reached by two paths" through two include chains, and wrote a detector that
counted composer calls to match. Tested it against the real case that motivated
the task — helix/repo-advance — and it did not fire, though the conventions
block still rendered twice.
The actual mechanism is subtler. helix/commit-sync declares its own
`conventions` input; when composed, resolution passes the including package's
already-resolved values down by name, so it *inherits* the outer text rather
than resolving its own include, and renders it again. Nothing was included
twice; the text appeared twice regardless.
Both mechanisms are now documented with worked diagrams. duplicate_inclusions
detects both — included values that are equal, and an included value contained
within another — and resolve, render and eval warn.
Nothing is deduplicated, as leaned. Deduplicating means choosing which
occurrence survives, since position in a prompt carries meaning, and deciding
what happens when two paths select different versions of the same dependency,
which section 10.3 permits. That is resolver behaviour and section 10.4 keeps
composition declarative.
Verified by reintroducing the diamond in a scratch copy of repo-advance, which
warns, and confirming the shipped factored collection stays silent.
Tests 90 -> 95.
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 19:23:16 +02:00
|
|
|
status: done
|
Add the catalog index, and package a real prompt collection
CANP-WP-0004. The operator asked canned-prompts to build a database of
versioned prompts recording where each came from and when — the first step
toward a platform for collaborative prompting.
The format had nowhere to put that. `provenance` records who wrote a prompt and
where the idea came from; nothing recorded how a copy arrived in a particular
store. Section 20.3 now specifies an `index.yaml` as store metadata rather than
package data: how a copy arrived differs for every consumer, and recording an
arrival must never rewrite the package that arrived.
`add`, `install` and `publish` record registry, id, version, name, source,
method, first-inclusion date, and the package's declared author, source and
licence — the last three copied so a listing is readable without opening every
package. A new `index` verb lists it. `included_at` is never overwritten; a
re-run updates `last_seen_at`, because when a package first entered a
collection is a fact about history rather than about the last command run.
Tests 84 -> 90.
Also records two findings from actually using the format:
CANP-WP-0004-T03 — inclusion has no deduplication, so a diamond dependency
renders shared content once per path. Found by composing a real collection.
Not fixed here: deduplicating means choosing which occurrence survives and
deciding what happens when two paths resolve different versions, which is
resolver behaviour that section 10.4 deliberately avoids. Handed to
CANP-WP-0005 with a leaning: document it, warn at validation time, do not
deduplicate.
The add_ons workaround in practice/pqrst-estimate is evidence about section
23's deferred "richer template syntax" — an optional appendix has to be an
input with an empty default, because CPF has no conditionals.
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 17:14:21 +02:00
|
|
|
priority: medium
|
2026-09-06 17:14:49 +02:00
|
|
|
state_hub_task_id: "74234a7a-c3bd-5bf7-a40d-21ddf208cfb5"
|
Add the catalog index, and package a real prompt collection
CANP-WP-0004. The operator asked canned-prompts to build a database of
versioned prompts recording where each came from and when — the first step
toward a platform for collaborative prompting.
The format had nowhere to put that. `provenance` records who wrote a prompt and
where the idea came from; nothing recorded how a copy arrived in a particular
store. Section 20.3 now specifies an `index.yaml` as store metadata rather than
package data: how a copy arrived differs for every consumer, and recording an
arrival must never rewrite the package that arrived.
`add`, `install` and `publish` record registry, id, version, name, source,
method, first-inclusion date, and the package's declared author, source and
licence — the last three copied so a listing is readable without opening every
package. A new `index` verb lists it. `included_at` is never overwritten; a
re-run updates `last_seen_at`, because when a package first entered a
collection is a fact about history rather than about the last command run.
Tests 84 -> 90.
Also records two findings from actually using the format:
CANP-WP-0004-T03 — inclusion has no deduplication, so a diamond dependency
renders shared content once per path. Found by composing a real collection.
Not fixed here: deduplicating means choosing which occurrence survives and
deciding what happens when two paths resolve different versions, which is
resolver behaviour that section 10.4 deliberately avoids. Handed to
CANP-WP-0005 with a leaning: document it, warn at validation time, do not
deduplicate.
The add_ons workaround in practice/pqrst-estimate is evidence about section
23's deferred "richer template syntax" — an optional appendix has to be an
input with an empty default, because CPF has no conditionals.
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 17:14:21 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
CPF inclusion (§ 10.4) does not deduplicate: when one package reaches the same
|
|
|
|
|
dependency by two paths, the included text renders once per path. This was found
|
|
|
|
|
by building a real collection — `helix/repo-advance` rendered its conventions
|
|
|
|
|
block twice — and worked around downstream by factoring the shared routine into
|
|
|
|
|
its own fragment.
|
|
|
|
|
|
|
|
|
|
§ 10.4 says nothing about it, so an author meets the behaviour only by reading
|
|
|
|
|
the output carefully. That silence is the defect.
|
|
|
|
|
|
|
|
|
|
Two questions, and the second depends on the first:
|
|
|
|
|
|
|
|
|
|
1. **Document it.** State in § 10.4 that inclusion is textual and repeated,
|
|
|
|
|
not deduplicated, and give the factoring pattern that avoids a diamond.
|
|
|
|
|
2. **Detect it.** Decide whether validation should warn when a package reaches
|
|
|
|
|
the same dependency by more than one path. A warning is cheap and catches the
|
|
|
|
|
mistake at `add` time. Silently deduplicating is a different proposition
|
|
|
|
|
entirely — it would mean choosing which occurrence survives, and deciding what
|
|
|
|
|
happens when two paths resolve *different versions* of the same dependency.
|
|
|
|
|
That is resolver behaviour, and § 10.4 keeps composition declarative on
|
|
|
|
|
purpose.
|
|
|
|
|
|
|
|
|
|
Leaning: document it now, warn at validation time, and do not deduplicate.
|
CANP-WP-0005: document and detect inclusion diamonds
Section 10.4 said nothing about inclusion being textual and repeated, so an
author met the behaviour only by reading rendered output carefully. That
silence was the defect.
Writing it up got the mechanism wrong at first. I documented a dependency
"reached by two paths" through two include chains, and wrote a detector that
counted composer calls to match. Tested it against the real case that motivated
the task — helix/repo-advance — and it did not fire, though the conventions
block still rendered twice.
The actual mechanism is subtler. helix/commit-sync declares its own
`conventions` input; when composed, resolution passes the including package's
already-resolved values down by name, so it *inherits* the outer text rather
than resolving its own include, and renders it again. Nothing was included
twice; the text appeared twice regardless.
Both mechanisms are now documented with worked diagrams. duplicate_inclusions
detects both — included values that are equal, and an included value contained
within another — and resolve, render and eval warn.
Nothing is deduplicated, as leaned. Deduplicating means choosing which
occurrence survives, since position in a prompt carries meaning, and deciding
what happens when two paths select different versions of the same dependency,
which section 10.3 permits. That is resolver behaviour and section 10.4 keeps
composition declarative.
Verified by reintroducing the diamond in a scratch copy of repo-advance, which
warns, and confirming the shipped factored collection stays silent.
Tests 90 -> 95.
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 19:23:16 +02:00
|
|
|
|
|
|
|
|
## Outcome
|
|
|
|
|
|
|
|
|
|
Done as leaned, with one correction found by testing.
|
|
|
|
|
|
|
|
|
|
**The documented mechanism was wrong at first.** § 10.4 was written describing a
|
|
|
|
|
dependency "reached by two paths" through two `include` chains, and the detector
|
|
|
|
|
counted composer calls to match. Tested against the real case that motivated the
|
|
|
|
|
task — `helix/repo-advance` — and it did not fire, even though the conventions
|
|
|
|
|
block still rendered twice.
|
|
|
|
|
|
|
|
|
|
The actual mechanism is different and more subtle. `helix/commit-sync` declares
|
|
|
|
|
its own `conventions` input; when composed, resolution passes the including
|
|
|
|
|
package's already-resolved values down by name (§ 10.4 inheritance), so it
|
|
|
|
|
*inherits* the outer text rather than resolving its own include — and renders it
|
|
|
|
|
a second time. Nothing was included twice. The text appeared twice regardless.
|
|
|
|
|
|
|
|
|
|
Both mechanisms are now documented with worked diagrams, and
|
|
|
|
|
`duplicate_inclusions` detects both: included values that are equal, and an
|
|
|
|
|
included value contained within another. `resolve`, `render` and `eval` warn;
|
|
|
|
|
nothing is deduplicated.
|
|
|
|
|
|
|
|
|
|
Verified by reintroducing the diamond in a scratch copy of `repo-advance` — it
|
|
|
|
|
warns — and confirming the shipped factored collection stays silent.
|
|
|
|
|
Tests 90 → 95.
|