Skip to content

Run spawned document work concurrently with <All> #855

Description

@taras

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions