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
3 KiB
| id | type | title | domain | repo | status | owner | topic_slug | created | updated | state_hub_workstream_id |
|---|---|---|---|---|---|---|---|---|---|---|
| CANP-WP-0005 | workplan | Document and detect inclusion diamonds | agents | canned-prompts | finished | codex | practice | 2026-09-06 | 2026-09-06 | 773f08a3-f899-53fc-87c8-037f8ffbd24d |
Document and detect inclusion diamonds
Residual from CANP-WP-0004 (origin: residual, origin_ref: CANP-WP-0004).
Warn about repeated inclusion
id: CANP-WP-0005-T01
status: done
priority: medium
state_hub_task_id: "74234a7a-c3bd-5bf7-a40d-21ddf208cfb5"
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:
- Document it. State in § 10.4 that inclusion is textual and repeated, not deduplicated, and give the factoring pattern that avoids a diamond.
- 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
addtime. 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.
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.