Skip to content

Compose authored document targets with <Call> #416

Description

@taras

Story

As an Executable.md author, I want to call a statically authored document target from another place in Markdown, so repeatable tasks can compose without duplicating their source or starting another workflow run.

Hierarchical targets from #412 are named tasks or entry points, not functions. A call uses the same target-selection contract: the document preamble, direct content of ancestors along the selected path, and the selected subtree execute; sibling subtrees do not.

Example

<!-- release.md -->
---
props:
  type: object
  properties:
    version: { type: string }
  required: [version]
---

# Release

Prepare release infrastructure.

## Build

<Build version={props.version} />

## Publish

<Publish version={props.version} />
<!-- nightly.md -->
# Nightly release

<Call
  target="./release.md#Build"
  props={{ version: props.version }}
/>

The called task's rendered output appears at the <Call> position. release.md's root preparation and Build subtree execute; its Publish sibling does not.

Target resolution

target is a document reference using #412's target representation and exact-one matching rules:

  • ./release.md#Build calls a target in another authored document;
  • #Verification calls a target in the current document;
  • ./release.md calls the complete document; and
  • an expression is permitted, but its result must be a valid reference resolving to exactly one authored target before any called content executes.

A relative document path resolves relative to the authored document containing <Call>. Inspection and execution use the same source loader and target resolver as the CLI and shared API; <Call> does not implement a second selector grammar.

Under a workflow run, the called source belongs to the same immutable definition object as the root document. It is executable control-plane source, not a path read through API.Files and not a mutable file in the run's Workspace. A workflow cannot write generated.md and then call it as trusted source. Explicitly pinned cross-object definition dependencies may be designed separately.

Inputs

<Call> is self-closing in the initial contract and has two control props:

<Call target={reference} props={arguments} />

props defaults to an empty object. The called document validates and defaults that exact object through its root props contract before executing its preamble or selected content. The called task receives no arbitrary eval bindings from the caller.

The explicit object keeps task arguments separate from <Call>'s own control props and permits forwarding an existing configuration:

<Call target="./release.md#Build" props={props.release} />

Children are rejected. Content projection into a called document is a separate language feature and is not implied by task composition.

Execution and scope

<Call> is structural composition inside the caller's current document execution:

  • it does not recursively invoke execute();
  • it does not allocate another run ID or create a child workflow run;
  • it uses the caller's current Workspace, journal, providers and surrounding error middleware;
  • it creates a fresh binding and lifetime boundary for the called task;
  • its rendered output is inserted inline at the call site; and
  • a failure propagates at the call site under the caller's ordinary lexical error handling.

When the call returns, its Workspace mutations, journaled results and rendered output remain. Its eval bindings, contextual middleware, services, tasks and other live resources are torn down. Ongoing effects never escape the call's structured scope.

<Call> has no special as contract. Existing structural composition such as <Capture> can capture its rendered output.

Identity, replay and repeated calls

Every call site creates its own structural expansion frame. Selected callee segments preserve their authored source positions and expand beneath that frame. Replaying the same call reconstructs the same structural path and lets nested durable effects claim their recorded results without making the selected definition part of the <Call> element's own expansion identity.

<Call> appends no wrapper journal effect. Durable effects inside the called target retain their existing descriptions, results and replay behavior.

Two sequential calls to the same target are two invocations. Each runs the target, including its ancestor setup, and receives distinct nested effect identities:

<Call target="./release.md#Build" props={props.first} />
<Call target="./release.md#Build" props={props.second} />

There is no implicit memoization or run-once behavior. An active direct or indirect call cycle fails when it attempts to re-enter an already-active called target; completed sequential calls remain valid.

Security boundary

<Call> composes statically authored definition source. It is not a general data-to-code operation. Workspace content, Agent output and other generated strings do not become callable merely because they contain XMD.

Dynamic XMD evaluation through a future md eval block is separate work. That feature requires its own admission, filtered-source retention and replay contract. The deferred Markdown testing use case can exercise workflows through <Call> without requiring dynamic evaluation.

Acceptance

  • <Call target="./document.md#Task" props={...} /> uses Run document sections as hierarchical workflow targets #412's exact target selection and ancestor-setup semantics.
  • A missing fragment calls the whole authored document; a fragment-only reference addresses the current document.
  • Relative paths resolve from the containing authored document.
  • Target expressions are allowed but must resolve to one authored target before called content executes.
  • Root props are validated and defaulted before any called setup or body effect.
  • Caller eval bindings do not leak into the called task; explicit props do.
  • The caller's Workspace, journal, providers and surrounding error handling remain active.
  • Output renders inline at the call site, with ordinary partial-output and failure behavior.
  • Each call has an isolated lifetime; live resources and contextual state are torn down before it returns.
  • Each call site has stable, distinct expansion identity, including when the same target is called repeatedly.
  • Replay expands the immutable called source and restores nested durable effects without a wrapper effect.
  • Direct and indirect active call cycles fail; later sequential calls remain valid.
  • Children are rejected in the initial contract.
  • Workflow mode refuses source outside the immutable workflow definition object and never loads called definitions through API.Files or the mutable Workspace.
  • xmd run and xmd workflow use the same language-level call, target, input, output and scope semantics.
  • Existing whole-document and CLI target execution remain unchanged.
  • Architecture and the executable MDX and workflow specifications describe the shipped contract in present tense.

Out of scope

  • md eval or arbitrary generated-XMD evaluation;
  • starting independently addressable child workflow runs;
  • content projection through <Call> children;
  • multi-match target fan-out;
  • cross-repository or cross-object definition dependencies;
  • passive Markdown inclusion or macro expansion; and
  • a Markdown-specific workflow testing harness.

Dependencies and related work

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions