Recursion carries identity through ancestry. It does not create authority. Every Resource an instance may consume or produce must be declared at the recursive contract and delegated across each child edge that needs it.
Declare authority before closing the cycle
A direct or mutually recursive contract may declare directional Resource requirements alongside its input, output, error, and event alphabets:
const Self = Statechart.contract({
name: "WorkNode",
input: WorkInput,
output: Schema.String,
error: Schema.Never,
recursive: { maximumDepth: 2 },
resources: [Pool.Input],
});
Pool.Input says every WorkNode instance may need authority supplied by an ancestor provider.
Pool.Output would say the recursive population may create durable material owned by that
provider. Direction remains explicit; a shared Resource tag does not make input and output
interchangeable.
Finite contract + recursive delegation + leaf ownership
Carry authority down without widening it.
Follow one provider projection from an ancestor, through three separately identified WorkNode instances, to the leaf that acquires it.
1 · Declare the requirement
The recursive contract names authority once.
Pool.Input enters the early WorkNode contract. Closure retains one finite symbolic requirement rather than trying to expand every path the recursive definition could produce.
requiredawaiting Birthawaiting Birth2 · Delegate every edge
Each admitted Birth carries the same bounded authority forward.
The ancestor provider delegates a grant to depth zero. Every recursive child edge preserves the declared projection, while each child remains a separately identified owner with its own obligations.
Pool.InputPool.InputPool.Input3 · Use at the leaf
The deepest worker acquires from the original provider.
The leaf requests one key through Pool.Input. The provider implementation runs at its owning ancestor, while the acquisition and eventual release remain attributed to the leaf's full ancestry.
Pool.InputPool.InputPool.InputKeep recursive requirements finite
Closing the chart compares its actual directional Resource requirements with the early contract.
The resulting declaration retains one symbolic Recursive requirement. It does not expand
WorkNode → WorkNode → WorkNode into a depth-sized list or hash a guessed runtime population into
the chart.
Every recursive child mount carries that same finite requirement. At runtime, each admitted Birth receives the concrete delegation required for its own incarnation. The definition stays finite; the committed ancestry records how many edges were actually crossed.
Choose ambient propagation or a named slot
Use resources when the recursive chart has one unambiguous requirement that should propagate
through its ordinary recursive edge. Use slots when the chart needs a named provider position
that callers and descendants must bind explicitly:
const PoolSlot = Pool.slot("PoolSlot");
const Self = Statechart.contract({
name: "SlottedWorkNode",
input: WorkInput,
output: Schema.String,
error: Schema.Never,
recursive: { maximumDepth: 2 },
slots: [{ slot: PoolSlot, resources: [Pool.Input] }],
});
The returned handle exposes the exact slot as Self.PoolSlot. The first parent binds that slot to
an actual provider site. Every recursive child edge then forwards the child's slot from the current
instance's slot:
spawn(Descendants, childInputs).bind(Self.PoolSlot, Self.PoolSlot);
That repeated spelling is useful evidence. It shows where authority crosses an ownership edge and gives attenuation a precise place to narrow it. Omitting the binding is not interpreted as an empty or ambient capability.
Acquire where the work is owned
The leaf invokes ordinary work with the Resource input:
invoke(UsePool, () => ({ pool: Pool.acquire("shared") })).using(Self.PoolSlot);
The provider implementation still runs at the ancestor that owns its capacity and release policy. The grant belongs to the leaf instance that requested it, carries that instance's full owner ancestry, and is recovered and released under the normal Resource lifecycle. Recursive depth does not merge acquisition ledgers or move settlement into the root chart.
Return output material to its provider
A recursive contract may instead declare resources: [Evidence.Output]. A leaf Activity can return
new Evidence.Output material; the engine routes it through the recursive child edges to the
ancestor provider's create implementation. The provider chooses the durable key and owns later
settlement.
The provider receives the creating leaf's owner ancestry. That preserves who produced confidential or external material without copying the material itself into State facets, child output, or ancestry frames.
Make attenuation monotone at every edge
Recursive propagation repeats the ordinary Resource law; it does not relax it. Each child may receive the same authority or a narrower projection. It may not recover fields, operations, capacity, or visibility removed by an earlier edge.
For a recursive agent, that can mean the root receives a broad document store, each planning child receives a project-scoped view, and each execution leaf receives only the exact document handles it needs. The lineage can grow while authority monotonically shrinks.
Refuse missing and extra declarations at closure
If the implementation requires Pool.Input but the early contract omits it, Self.make(...)
refuses with ContractResourcesMustMatchClosure. Declaring an unused input or output
fails the same exact comparison. The recursive contract cannot advertise less or more authority
than the closed chart actually requires.
Test both the definition and the runtime boundary:
- Assert the closed chart retains one finite recursive requirement.
- Omit and add a directional requirement and verify closure refuses both mismatches.
- Run to the deepest modeled leaf and verify acquisition reaches the intended provider.
- Verify the provider observes the leaf's full owner ancestry.
- Recover the run and prove the same grants and obligations resume without widening authority.
- Apply attenuation at an intermediate edge and prove a descendant cannot regain removed access.
Next, Component Definitions and Placements returns from separately running recursive instances to reusable authored topology inside one chart.