Story
As an executable-document author, I want to run independent children at the
same time and wait for all of them, so unrelated work can make progress without
making the document's output or replay depend on scheduling.
Example
<All>
<Spawn>
<Session name="planner">
<Prompt>Draft the implementation plan.</Prompt>
</Session>
</Spawn>
<Spawn>
<Session name="reviewer">
<Prompt>Review the current change.</Prompt>
</Session>
</Spawn>
</All>
Both spawned children can be live together. <All> waits for both and renders
their Markdown in the order the spawns were written, even when the second child
finishes first.
Current gap
Executable Markdown expands structural bodies sequentially. A long-running
component, Agent turn, durable wait or command in the first sibling prevents a
later sibling from starting. Calling Agent operations from TypeScript does not
close the gap: it bypasses the ordinary authored component and journal
boundaries.
Issue #854 needs ordinary <Session><Prompt /></Session> paths to coexist so
one turn can stream while another waits for permission. That product behavior
depends on a general document-language boundary rather than a private REPL or
Agent helper.
Contract
<All> is engine-owned structural syntax. It has no props or result and
requires at least two direct, paired <Spawn> children. Whitespace between
spawns is ignored. Any other direct content, a self-closing spelling, an
unknown prop, or a <Spawn> outside its direct parent refuses before any child
starts. Repository components, registrations and Plugins cannot answer
either reserved name.
Every spawned child starts from the same incoming binding snapshot in a fresh
environment and owns a child eval scope. Bindings and retained resources made
inside it remain available to that child's later work and escape neither to a
sibling nor to work after </All>. Each <Spawn> owns its output buffer,
block counter, expansion path and failure ledger.
The document execution owns <All>, and <All> owns its spawned children.
The children are durable coroutines assigned in source order. Durable
effects inside them keep their ordinary records, and a successful child closes
with its rendered string. After every child succeeds, those strings render in
source order. Completion order may determine journal append order but never the
rendered result.
A completed replay runs no spawned work. A partial replay restores completed
child output and starts only unrecorded work, with the same child identities,
block IDs and expansion paths. Inserting, removing or reordering spawns is a
definition change and follows the existing divergence contract.
Failure is fail-fast. The first child failure observed by the join cancels and
joins unfinished siblings, emits no <All> output, and leaves only events
already acknowledged by the journal. Cancelling the parent cancels and joins
every child. No task, subscription, resource or output buffer survives its
spawned scope.
<Spawn> is a control-flow boundary. A <Return> inside it cannot select a
value owned outside <All>, and a <Break> cannot exit a loop outside the
spawned child. Both are refused during structural validation. A value component
invoked inside the child owns its own <Return>, and a loop wholly inside the
child owns its own <Break>, as they do elsewhere.
Generated XMD receives the same built-in syntax and whole-fragment preflight.
Every spawn is checked before the generated fragment performs its first
effect.
Acceptance
- Two authored spawns reach a barrier before either is released. Releasing
the second first appends its durable operation first, while final Markdown is
rendered in authored order.
- Child coroutine identities, block IDs and expansion identities follow source
order and reproduce on replay regardless of completion order. Parent work
after <All> keeps the parent counter it would have had without child
scheduling.
- Partial replay restores completed child output and executes only the first
unrecorded frontier. Full replay performs no spawned effect.
- A child binding or retained eval resource is available later in that child
and absent from its sibling and parent.
- A child failure cancels and joins a held sibling, emits no
<All> output
and fabricates no successful record for interrupted work. Parent cancellation
likewise leaves nothing alive.
- Malformed structure refuses before either child begins.
<Return> and
<Break> cannot cross the spawn boundary, while nested owners still work.
- The same authored construct works inside admitted generated XMD and is listed
by the canonical syntax surface.
Negative controls fail if spawns run sequentially, output follows completion
order, children share the parent coroutine or one mutable block counter, a
sibling survives failure, a child binding escapes, malformed structure starts
one child, or non-local Return/Break is admitted.
Evidence
Use one focused Core suite that drives the ordinary scanner, structural
validator, expansion engine and durable stream. Use gated durable effects rather
than timing. Keep the canonical syntax and generated-XMD suites beside it so a
new reserved construct cannot exist only on the direct execution path.
Relationships
Out of scope
- Returning child values, capturing
<All> with as, sharing child
bindings, choosing a concurrency limit, racing spawns, or continuing after
one fails.
- Changing
<Prompt>, the Agent API, REPL state, permission handling, terminal
presentation or CLI grammar.
- A test-only concurrency component, a scheduler API exposed to documents, or a
new durable record family.
Story
As an executable-document author, I want to run independent children at the
same time and wait for all of them, so unrelated work can make progress without
making the document's output or replay depend on scheduling.
Example
Both spawned children can be live together.
<All>waits for both and renderstheir Markdown in the order the spawns were written, even when the second child
finishes first.
Current gap
Executable Markdown expands structural bodies sequentially. A long-running
component, Agent turn, durable wait or command in the first sibling prevents a
later sibling from starting. Calling Agent operations from TypeScript does not
close the gap: it bypasses the ordinary authored component and journal
boundaries.
Issue #854 needs ordinary
<Session><Prompt /></Session>paths to coexist soone turn can stream while another waits for permission. That product behavior
depends on a general document-language boundary rather than a private REPL or
Agent helper.
Contract
<All>is engine-owned structural syntax. It has no props or result andrequires at least two direct, paired
<Spawn>children. Whitespace betweenspawns is ignored. Any other direct content, a self-closing spelling, an
unknown prop, or a
<Spawn>outside its direct parent refuses before any childstarts. Repository components, registrations and Plugins cannot answer
either reserved name.
Every spawned child starts from the same incoming binding snapshot in a fresh
environment and owns a child eval scope. Bindings and retained resources made
inside it remain available to that child's later work and escape neither to a
sibling nor to work after
</All>. Each<Spawn>owns its output buffer,block counter, expansion path and failure ledger.
The document execution owns
<All>, and<All>owns its spawned children.The children are durable coroutines assigned in source order. Durable
effects inside them keep their ordinary records, and a successful child closes
with its rendered string. After every child succeeds, those strings render in
source order. Completion order may determine journal append order but never the
rendered result.
A completed replay runs no spawned work. A partial replay restores completed
child output and starts only unrecorded work, with the same child identities,
block IDs and expansion paths. Inserting, removing or reordering spawns is a
definition change and follows the existing divergence contract.
Failure is fail-fast. The first child failure observed by the join cancels and
joins unfinished siblings, emits no
<All>output, and leaves only eventsalready acknowledged by the journal. Cancelling the parent cancels and joins
every child. No task, subscription, resource or output buffer survives its
spawned scope.
<Spawn>is a control-flow boundary. A<Return>inside it cannot select avalue owned outside
<All>, and a<Break>cannot exit a loop outside thespawned child. Both are refused during structural validation. A value component
invoked inside the child owns its own
<Return>, and a loop wholly inside thechild owns its own
<Break>, as they do elsewhere.Generated XMD receives the same built-in syntax and whole-fragment preflight.
Every spawn is checked before the generated fragment performs its first
effect.
Acceptance
the second first appends its durable operation first, while final Markdown is
rendered in authored order.
order and reproduce on replay regardless of completion order. Parent work
after
<All>keeps the parent counter it would have had without childscheduling.
unrecorded frontier. Full replay performs no spawned effect.
and absent from its sibling and parent.
<All>outputand fabricates no successful record for interrupted work. Parent cancellation
likewise leaves nothing alive.
<Return>and<Break>cannot cross the spawn boundary, while nested owners still work.by the canonical syntax surface.
Negative controls fail if spawns run sequentially, output follows completion
order, children share the parent coroutine or one mutable block counter, a
sibling survives failure, a child binding escapes, malformed structure starts
one child, or non-local Return/Break is admitted.
Evidence
Use one focused Core suite that drives the ordinary scanner, structural
validator, expansion engine and durable stream. Use gated durable effects rather
than timing. Keep the canonical syntax and generated-XMD suites beside it so a
new reserved construct cannot exist only on the direct execution path.
Relationships
acceptance unchanged.
a403cc57170514db95722ce7926aec5515f92453.Out of scope
<All>withas, sharing childbindings, choosing a concurrency limit, racing spawns, or continuing after
one fails.
<Prompt>, the Agent API, REPL state, permission handling, terminalpresentation or CLI grammar.
new durable record family.