Motivation
§6.3 says <Content /> "inside a component body" is replaced by the caller's children. The engine substitutes it only at a body's top level, or directly inside <Output>. Nested anywhere else it survives substitution, reaches component resolution, and fails — Content is reserved, so nothing can supply it.
The failure message describes a real rule ("it means something only inside a component's body") but the element is inside a component's body, which makes the diagnosis misleading as well as the behavior surprising.
Reproduction
All three fail identically on 511776e. The wrapper's kind does not matter — a Markdown component, a core function component, and a structural construct all reproduce it.
<!-- components/Echo.md -->
<Output>ECHO[<Content />]</Output>
<!-- components/Host.md — nested inside another invocation -->
<Echo as="e">prefix <Content /> suffix</Echo>
<Output>host got: {e}</Output>
<!-- components/HostTemp.md — nested inside a core function component -->
<Capture as="c"><TempDir>dir wrapped: <Content /></TempDir></Capture>
<Output>host got: {c}</Output>
<!-- components/HostCap.md — nested inside a structural construct -->
<Capture as="c">captured: <Content /></Capture>
<Output>host got: {c}</Output>
Invoked as <Host>MATERIAL</Host>, each exits 1 with:
<Content /> renders the content its invocation was given, so it means something
only inside a component's body. <Content> is reserved: it never resolves a
component, so a repository file named Content cannot supply it.
Working control: <Output><Content /></Output> as the whole body, and <Content /> as a top-level body segment, both substitute correctly.
Cause
substituteSegmentList (packages/core/src/expand.ts:2668) maps over a body's segments, handles Content and interpolates text, and for any other component segment returns [segment] — it never recurses into segment.children. buildBody calls it once per top-level segment and once for each <Output> region's direct children, which is exactly the set of positions that work.
Why it matters
Passing caller-supplied material into a prompt is the natural use, and it is unavailable:
<Prompt as="answer">
Material to assess:
<Content />
</Prompt>
The workaround is to take the material as a prop and interpolate {props.material}, which works anywhere. That is a fine interface, but it is a different one, and a component author has no way to learn the restriction except by hitting it — the spec does not state it and the error does not describe it.
Acceptance criteria
<Content /> and <Content slot="…" /> substitute wherever they appear in a component body, including inside another component's invocation, inside a structural construct, and nested several levels deep.
- Slot partitioning, the once-only slot errors of §6.3.3, and the content-scope rule of §6.3.4 are unchanged — a nested projection still runs in the invocation's content scope and still stops before the component releases its own.
- Two
<Content /> elements at different depths behave the same as two at top level.
- §6.3 states the resolved rule, and a conformance row covers a nested position.
- If the restriction is instead deliberate, the spec says so and the error message names the actual rule, so the failure is diagnosable.
Impact
Found while synchronizing the #181 living end-goal target (#292). Two authored components used the pattern and could not expand; both now take the material as a prop and the target classifies this as missing.
Motivation
§6.3 says
<Content />"inside a component body" is replaced by the caller's children. The engine substitutes it only at a body's top level, or directly inside<Output>. Nested anywhere else it survives substitution, reaches component resolution, and fails —Contentis reserved, so nothing can supply it.The failure message describes a real rule ("it means something only inside a component's body") but the element is inside a component's body, which makes the diagnosis misleading as well as the behavior surprising.
Reproduction
All three fail identically on
511776e. The wrapper's kind does not matter — a Markdown component, a core function component, and a structural construct all reproduce it.Invoked as
<Host>MATERIAL</Host>, each exits 1 with:Working control:
<Output><Content /></Output>as the whole body, and<Content />as a top-level body segment, both substitute correctly.Cause
substituteSegmentList(packages/core/src/expand.ts:2668) maps over a body's segments, handlesContentand interpolates text, and for any other component segment returns[segment]— it never recurses intosegment.children.buildBodycalls it once per top-level segment and once for each<Output>region's direct children, which is exactly the set of positions that work.Why it matters
Passing caller-supplied material into a prompt is the natural use, and it is unavailable:
The workaround is to take the material as a prop and interpolate
{props.material}, which works anywhere. That is a fine interface, but it is a different one, and a component author has no way to learn the restriction except by hitting it — the spec does not state it and the error does not describe it.Acceptance criteria
<Content />and<Content slot="…" />substitute wherever they appear in a component body, including inside another component's invocation, inside a structural construct, and nested several levels deep.<Content />elements at different depths behave the same as two at top level.Impact
Found while synchronizing the #181 living end-goal target (#292). Two authored components used the pattern and could not expand; both now take the material as a prop and the target classifies this as missing.