Start a flowchart with its first stage; returns a fluent builder.
Like flowChart(), but the ROOT stage is a SELECTOR — it runs first and
its branches attach directly to it (no separate seed stage). Returns a
SelectorFnList; declare branches then call .end() to get the builder
back for any subsequent stages.
Evaluates rules in order (first-match). Returns a branded DecisionResult.
Evaluates ALL rules (not first-match). Returns a branded SelectionResult.
Navigate the execution snapshot tree by a slash-separated subflow path.
List all available subflow paths in a snapshot.
Detect what kind of schema an unknown value is.
Returns true if the input can be used for runtime validation (has .safeParse()/.parse(), or is a Zod schema).
Returns true if the input is a Zod schema (v3 or v4). Convenience wrapper — prefer detectSchema() when you need the full kind.
Validate data against a schema. Returns a result — does not throw.
Validate data against a schema. Throws InputValidationError on failure.
Extract structured error info from any thrown value.
Format a StructuredErrorInfo back to a human-readable string. Use this at rendering boundaries (narrative output, log lines). Includes field-level details when issues are present.
Run fn under policy (none = one attempt, still recorded) and resolve with its answer, or reject with the LAST
attempt's own error. fn gets the attempt's AttemptContext: pass signal to the work, read timeLeftMs(),
and report an SDK's own retries with observed().
The category of an attempt's failure. outcome is how the attempt ended (default 'failed'): it decides
'timeout' (the attempt's own timer) and 'unknown' (the run's signal) before the error is read.
A RetryPolicy.backoffMs function for an exponential schedule: the wait after failed attempt n is drawn under the
ceiling min(maxDelayMs, baseDelayMs × multiplier^(n − 1)) by the declared jitter — the schedule is capped BEFORE
the draw, so it keeps its spread. The policy's maxBackoffMs still caps every wait after it.
The retry rule table for policy — six decide() rules, then RETRY_DEFAULT. The ceiling is the
whole number of attempts the policy allows, never fewer than one (attemptCeiling): a ceiling of NaN or
Infinity cannot make rule 4 unreachable.
A RetryPolicy.backoffMs function that honours a wait the outside system stated: when readStatedWaitMs(error)
answers a finite number >= 0, that is the wait; otherwise inner (a backoff function, or a fixed number of
milliseconds) decides. A reader that throws or answers anything else states nothing — a malformed Retry-After
falls back to the schedule rather than to a wrong wait. The runner still caps the stated wait at maxBackoffMs and
stops the series when it would outlast the run's deadline.
Disable dev-mode diagnostics across the whole library (default state).
Enable dev-mode diagnostics across the whole library.
Returns whether dev-mode diagnostics are currently enabled.
True iff the recorder implements at least one emit-channel event method.
See hasRecorderMethods for the ownership-detection rules (own or
class-prototype methods count; Object.prototype pollution does not).
True iff the recorder implements at least one control-flow event method.
See hasRecorderMethods.
True iff the recorder implements at least one data-flow event method.
Used by executor.attachCombinedRecorder to decide whether to hook into
the scope data-flow channel.
Discriminator for the union payload types on CombinedRecorder's shared
methods (onError, onPause, onResume). Returns true if the event
was emitted from the control-flow channel (FlowRecorder).
The attempt's own time cap ran out before it answered.
Each instance gets a unique auto-increment ID (debug-1, debug-2, ...),
so multiple recorders with different verbosity coexist.
Thrown when runtime input fails schema validation. Extends Error for compatibility, but carries structured .issues.
A run with sink: { onFailure: 'fail' } lost an entry: the record the app asked to be kept is not whole.
cause is the adapter's own error.
A logical runner or native executor already owns an unfinished execution.
A traceparent the library will not read. The header itself is never in the message or on the error.
A side-effecting target's last attempt ended with no answer: whether the effect happened is unknown.
Options bag accepted by every attach*Recorder call.
The attempt a function is running in — a stage's (scope.$attempt(), design §3.1), or a call's (the argument
scope.$call and attempt() hand their function, P2). Never serialised: it holds a live signal.
What a retry-or-stop decision reads — the inputs retryRules evaluates, first match wins (§3.1). The runner
computes each one only when a rule reaches it, so retryOn and backoffMs run exactly when a retry needs
them.
How a standalone attempt series is named, timed and reported.
One attempt — the unit of time and failure evidence. Frozen; survives structuredClone.
One attempt, named — series (the stage execution's runtimeStageId) plus the 1-based attempt. The
identity AttemptRecord carries until the engine stamps foottrace's attempt id (design §3.4).
A series at a glance — stamped once, when the series ends (the Lens badge, §3.10). last is how it ended:
the last record's next / reason (and category, retryOnThrew), or stop / aborted when the run's
signal cut a wait (no attempt followed, so none is recorded).
The schedule exponentialBackoff builds a RetryPolicy.backoffMs function from.
A built chart, as addParallelForEach's branch factory returns it.
What the caller knows that the chart does not declare. Keys are ids as the run names them.
A stage on the slowest path, with what it declares and how many times the path runs it.
Explicit capture-only adapter seam; callbacks must be synchronous, pure and may not mutate answers.
Absent/undefined fields are not supplied; {} is a valid empty observation. Malformed/refused capture is
disclosed on the native saved note, never substituted for the business answer or thrown error.
Actual adapter-observed output identities; capture never inspects arbitrary business answers.
What scope.$call(target, fn, policy, options) declares beyond its policy (P3): the outside system the call
touches, and the key it is made under. A call that touches a system and may retry must carry a key; the key is a
VALUE the stage computed from its input or its state — never from model output — and is checked like a stage's.
Explicit host-owned durability capability. A normal RecordSink.flush is not this promise.
Adapter assertion about the preceding evidence and end entry, not about this receipt's delivery.
Exact root segment and evidence entry acknowledged by a storage adapter.
OpenAPI generation options.
One captured component occurrence: private cells plus joined terminal control. WHY: mounted components have no public run wrapper to own cleanup. entry.ordinal is a native occurrence coordinate, never application identity.
Synchronous component wiring, captured before its stages or mappers run. WHY: mounts have no public run wrapper to bind their private integration state.
A runner that can expose its internal flowChart for subflow composition.
Snapshot format for composite recorders — wraps child snapshots, each copied by the one
bundle copier (recorder/snapshot.ts), so a child keeps description/preferredOperation/meta.
The Block 9 observability surface (amendment A4) — pure getter.
Event delivered to EmitRecorder.onEmit.
Pluggable observer for consumer-emitted structured events.
A recorder threw in a scope-channel hook (operation says which kind:
a read, a write, or a commit hook). Delivered to every recorder on the
scope channel, inline and deferred alike (
ExecutionEnv — infrastructure values that propagate through nested executors.
Event passed to FlowRecorder.onAttempt — one attempt, at its end (10.1): a
stage's own under its retry policy, a timeoutMs or a run deadline
(record.kind === 'stage'), or — during the stage — a call it made
('call', scope.$call) or an outside attempt it reported ('observed',
P2). stageName / stageId / traversalContext are the stage's in every
case; filter on record.kind to count one kind.
Event passed to FlowRecorder.onBreak.
Serializable checkpoint — everything needed to resume a paused flowchart, and nothing a resume never reads.
Options object for FlowChartExecutor — preferred over positional params.
Event passed to FlowRecorder.onDecision.
Event passed to FlowRecorder.onError.
Event passed to FlowRecorder.onFork.
Event passed to FlowRecorder.onLoop.
Event passed to FlowRecorder.onNext.
FlowRecorder — Pluggable observer for control flow events.
Event passed to FlowRecorder.onRunStart / onRunEnd.
Event passed to FlowRecorder.onRunFailed — the TERMINAL failure boundary
for a top-level run. Mirror of onRunEnd for the error path: it fires
once, during traversal, when the run throws a non-pause error, before
the exception propagates. Carries the structured error (field-level
issues, codes) rather than a flattened string.
Event passed to FlowRecorder.onSelected.
Event passed to FlowRecorder.onStageExecuted.
Event passed to FlowRecorder.onStageRetry — one failed attempt at a stage that carries a declared import('../types.js').RetryPolicy, and which WILL be attempted again.
Event passed to FlowRecorder.onSubflow.
Event passed to FlowRecorder.onSubflowRegistered (dynamic subflow attachment).
Event passed to FlowRecorder.onThrottled — a fork child failed and the
executor's throttlingErrorChecker said its error is throttling (9.39.0).
What a stage's idempotency-key rule is handed — once per EXECUTION, before attempt 1 (StageTouches).
What an interrupt() asks for. Rides the checkpoint as pauseData.
A write, emit, diagnostic, detach or call an attempt tried AFTER it had ended — timed out, aborted or failed —
dropped at the scope and recorded on its stage (StageSnapshot.lateWrites, the first 100 —
StageContext · MAX_LATE_WRITES). A name, never the value.
Per-listener accounting (A2/A4).
One stage execution whose retry series retried, gave up or timed out, at a glance.
A single entry in the subflow manifest tree.
MCP tool description — shape required by the Model Context Protocol spec.
Pluggable formatter for narrative output. Turns structured event context objects into the text lines that make up the narrative array.
A descriptor for a separately copied immutable native prefix, not a closure claim about a live snapshot. Outcome and closure are independent: a failed execution remains failed. This grants no semantic cause, remote cancellation or authenticity authority; required channel coverage is assessed separately.
Value-free native function bookkeeping for one root-log leg. An unverified terminal observation is immutable; neither zero pending invocations nor storage acknowledgement establishes closed capture or remote cancellation. Version 1 has no closed state; a future closure attestation requires a separately reviewed version.
Engine observation of ONE root-log segment. Never a durable or whole-workflow completeness claim.
The same immutable recorded prefix is served in memory and bound into its saved history. No live state.
A native occurrence coordinate only; never application identity or authorization.
Opaque captured parent-runtime identity supplied separately to native mapper callbacks.
A new discriminant prevents strict v1 readers from accepting new completeness claims.
An attempt an outside system ran on its own, as an adapter reports it (AttemptContext.observed). Only these
fields are read — each once; anything else on the object is dropped.
The Block 9 observability surface — snapshot.observerStats. The A4
dispatcher stats plus the terminal-flush stranding count from Block 8.
Present on RuntimeSnapshot only when a deferred observer was attached.
Runtime fan-out config — one branch per item, branch COUNT decided at runtime from the payload. Design: docs/design/execution-control.md (D2).
A parsed traceparent: the W3C trace-id, the parent-id (the caller's unit of work), and its trace-flags.
Handler for a pausable stage — has two phases: execute and resume.
The paused EXECUTION (9.37.0): the run that paused and the paused stage's runtimeStageId in it. Recorded on the checkpoint (and on each queued sibling's pending record) so a resume can LINK to it.
A pause a PARALLEL SIBLING of the paused stage raised in the same fan-out —
two children of one fork (or two selected branches of one selector) that
both paused (9.28.0). Only one pause is asked at a time: the checkpoint's
own (pausedStageId), then each of these in turn, with the fan-out's join
running only once every child is done.
Marker recorded in StageSnapshot.stageReads under readTracking: 'summary'.
A producer acknowledgement, not engine inspection of a recorder's implementation.
Snapshot of a single recorder's collected data.
What executor.flushRecord() resolves with — the drainObservers shape.
The executor's sink option in its long form. The short form is the adapter itself.
getSnapshot().sink — what the run handed its sink, and what the sink did not take (10.1, P6).
The redaction a pause carries to the resumed run — NAMES only, never a value
(the checkpoint stays real): the keys marked secret for the rest of the run
(a per-call setValue(key, value, true), a key the policy redacted whole, a
subflow mapper's taint) and the fields a mapper handed to a new key. Resume
seeds the resumed run's rule with them, so a copy made before the pause is
still masked after it.
Declarative key/path redaction for recorded state and boundary records.
State redaction report. Never includes values; emit and diagnostic selectors/activity are not represented here.
The paused execution a resume continues — TraversalContext.resumedFrom.
Both ids are the PAUSED run's: runId is that run's, runtimeStageId the
execution of the stage that paused (as its onPause event stamped it).
The role of one physical call inside a logical pass.
The attempt a function is running in — a stage's (scope.$attempt(), design §3.1), or a call's (the argument
scope.$call and attempt() hand their function, P2). Never serialised: it holds a live signal.
The explicit chart decision, after reconciliation; never rewrites a physical call's outcome.
Logical identity of an explicit retry workflow. Physical attempt identities remain unchanged.
Operational state stays real; normal state retention scrubs selected keys and every error is already served.
Declarative per-stage retry policy — the policy the attempt runner runs (AttemptPolicy in the design is
this one type, under this one name).
The run's correlation — which outside trace and unit of work this run belongs to (design §3.4). Resolved ONCE
per leg (run(), and each resume()) from the env by attempt/correlation.ts · resolveCorrelation, frozen, and
handed by reference to RuntimeSnapshot.correlation, every flow event's TraversalContext.correlation, and
every scope and emit event's correlation. Absent — no key anywhere — when the env gives none.
The five observability dials as an executor takes them — every field optional, an absent
one is its default. FlowChartExecutorOptions extends this interface.
FlowChart enriched with d3-style run methods and self-describing outputs.
Options for FlowChartExecutor.run().
Result from RunContext.run() — owns state and output.
Owner-issued key. Neither serialization nor a structural copy creates a valid key.
Captures one execution; methods refuse after its executor begins another native leg.
Pluggable observer for scope operations.
Native execution identity and actual stage-function invocations. No values, timings or outcomes.
One native source-book segment. A storage receipt does not authenticate this observation.
A stage that changes something outside the run — a charge, an email, a write to another service (design §3.3,
P3). Declared with .touches(system, { idempotencyKey }) or a touches option; a POLICY-like field on the node,
like retry and tags. The built spec carries system only.
Event payload for StructureRecorder.onDeciderComplete. Fires
when a decider / selector sub-builder closes via .end(). By the
time this fires every branch has been announced and the
defaultBranch (if set via .setDefault()) is final. Lets
recorders treat the decider as no-further-mutation and snapshot
its branch list as final.
Structured representation of any error caught during stage execution.
Event payload for StructureRecorder.onEdgeAdded. Fires whenever
the builder wires an outgoing edge from one node to another. Both
endpoints have already been announced via onStageAdded by the
time this fires.
Event payload for StructureRecorder.onLoopEdgeAdded. Fires when
.loopTo(target) installs a back-edge. Distinct from
onEdgeAdded so consumers can theme back-edges differently and
because runtime onLoop carries iteration: number which has no
build-time meaning — keeping the events separated avoids forcing
an iteration?: number lie into the structural shape.
Pluggable observer for BUILD-TIME chart construction events.
Event payload for StructureRecorder.onStageAdded. Fires for every
spec node added to the chart, including the seed (added via
flowChart() factory) and subflow mount nodes. Excludes synthetic
loop-reference nodes (those carry isLoopReference: true on the
spec; consumers see them via the separate onLoopEdgeAdded event
instead of as fresh stages).
Event payload for StructureRecorder.onStageTagged. Fires when names
land on a stage AFTER its onStageAdded already fired — the
.tag(...) cursor door (flowChart(...).addFunction(...).tag('a')).
Names declared in the same call as the stage (options.tags,
SubflowMountOptions.tags) ride StructureStageAddedEvent.tags and
do NOT fire this: one declaration, one event. tags is the COMPLETE
list on the stage after the door — the builder refuses a second
declaration on the same stage, so it is also the only one.
Event payload for StructureRecorder.onSubflowMounted. Fires once
per subflow mount (addSubFlowChart / addSubFlowChartNext and
their lazy variants).
The result of navigating to a subtree within a snapshot.
Safe context for the failure that escaped a native run; never the exception itself.
What timeBudget(chart) reports. JSON-safe and frozen.
What formatTraceparent writes from. flags (2 lowercase hex) is the trace-flags byte, '00' when absent;
sampled, when given, sets or clears its bit 0.
Traversal context attached to every FlowRecorder event. Created by the traverser during DFS, passed to recorders as read-only data. Enables recorders to build trees, group by subflow, and correlate events without maintaining their own stacks or post-processing.
A stage the check cannot bound, and why.
What an UnknownEffectError names.
A single validation issue — one field, one problem.
Marker recorded in StageSnapshot.stageWrites (and the commit observer's
mutations payload) under writeTracking: 'summary' (#13c-A). Same fields
and cost contract as ReadSummaryMarker; distinct brand so consumers
can tell which dial produced an entry.
The failure category of an attempt that did not succeed — ONE fixed set of seven, classified by
errorCategory (attempt/category.ts), so every recorder breaks failures down the same way (§3.9).
Evidence, never policy: the default retryOn retries every category.
What an attempt did to the outside system a touching target names (design §3.3, P3) — present only on the records
of a target that declared touches:
'applied' — the attempt answered (ok);'none' — it failed, and its error itself declares effect: 'none' (the adapter's word: nothing was sent);'unknown' — anything else: a timeout, a cancellation, a pause, or a failure the adapter did not declare
harmless. Nobody knows whether the effect happened, so the series never reports it as failed or ok: a last
attempt of unknown effect fails with UnknownEffectError — reconcile, do not retry blindly.An attempt's error as the run SERVES it — StructuredErrorInfo under the run's redaction rule
(RedactionRule.retainStageError), without raw: a record must survive structuredClone and must never
hand out the thrown value itself.
Who ran the attempt: the engine, for a stage's own policy ('stage'); the attempt runner, for a call a stage made
through scope.$call or for the standalone attempt() ('call'); an outside system on its own — an SDK's
internal retry — reported through AttemptContext.observed ('observed', P2).
What follows an attempt: another one, the value ('done'), or the attempt's own error ('stop').
How one attempt ended. 'timeout' is the attempt's OWN timer (never the outside system failing — law 4);
'paused' is a pause (control flow, never retried). 'aborted' is the run's signal ending the attempt: the
attempt threw the abort's own reason. An attempt that threw its own error is 'failed', with its category,
even when the run aborted around it — the abort is then the series' stop reason (reason: 'aborted'), never a
rewrite of what the attempt did.
Attempt counts per outcome — the shape AttemptSummary uses, without the series' end.
Why the series went on or ended — the matched rule's label in retryRules (attempt/decision.ts), or the
default's ('retryable').
A RetryPolicy.backoffMs function: the wait in milliseconds after failed attempt attempt (1-based).
How much of an exponential wait is random: none of it, half of it, or all of it.
Persistence result only. An acknowledged failed/paused prefix is not a completed business workflow.
How an event payload is materialized into the envelope (RFC-001 §5). See the module header for the full per-policy contract.
A recorder that MAY observe any combination of supported event streams.
Native component leg terminal, including failed cancellation. WHY: paused cleanup may preserve adapter resources; it must not imply remote closure. SAME and FRESH resumes open new cells/subscriptions and need explicit admitted-data restore.
The fallback branch of a decide() — taken when NO rule matched.
Legacy observations remain readable; only v2 contains independently collected ownership.
Full return type of FlowChartExecutor.run() and resume().
Multiple operators on the same key are ANDed (e.g., { gt: 5, lt: 10 } means 5 < value < 10).
Component-local ordinary recorder using the canonical served-event/error boundary. WHY: mid-run consumers need preceding events synchronously, never one beat behind. Absent/inline delivery is admitted; deferred, invalid or accessor delivery refuses with ERR_COMPONENT_RECORDER_DELIVERY rather than being silently overridden.
contract/types.ts — Schema types for the FlowChart contract layer.
Delivery tier for an attached observer (RFC-001).
Result shape of executor.drainObservers() — see DrainResult.
How the ring treats a push when it is at capacity (RFC-001 §5).
Returned by run()/resume() when execution pauses.
Policy for how tracked reads are recorded into StageSnapshot.stageReads.
RecorderOperation — the three standard operations on auto-collected traversal data.
What a lost entry does to the run: 'report' counts it and goes on; 'fail' fails the run.
When the adapter's flush() runs: after each run's closing entry ('auto'), or when the app asks.
The executor's sink option: an adapter, or RecordSinkOptions.
How a tracked operation's value is retained in the per-stage snapshot view.
The kind of schema detected.
Factory that creates a scope instance for each stage.
The function signature for stage handlers.
streamCallback injected for streaming stages.Reads the wait an error states (a parsed Retry-After), in milliseconds, or undefined when it states none.
Edge kinds the builder produces at structural time. Matches the footprintjs control-flow vocabulary used at runtime.
Why a traceparent was refused — a NAME, so a caller can branch on it without reading a message.
Clean stage function type for TypedScope users. No breakPipeline parameter — use scope.$break() instead.
Why a stage keeps a path unbounded: no per-attempt cap ('no-timeout'); a function backoff with no declared
maxBackoffMs ('backoff' — its waits may depend on an error that does not exist at build); a loop with no
assumed passes ('loop', named by its target); a lazy subflow whose chart is unknown at build ('lazy-subflow');
a parallelForEach whose branch chart is built at run time ('items').
Union result type.
Policy for how tracked writes are recorded into StageSnapshot.stageWrites
(#13c-A) — the sibling of ReadTrackingMode.
The longest idempotency key.
RecorderOperation — the three standard operations on auto-collected traversal data.
What an attempt that ended outcome (with error, when it failed) did to the outside system.
Write a version-00 traceparent — for an outbound call that continues this run's trace. Both ids must be W3C
ids and flags a byte of 2 lowercase hex (refused by name otherwise, like a parse); sampled, when given,
sets or clears bit 0 of the flags. formatTraceparent(parseTraceparent(h)) is h for any valid version-00 h.
Why value is not an idempotency key, or undefined when it is one.
Parse a W3C traceparent header strictly. Returns a frozen ParsedTraceparent; throws a
TraceparentError naming the first rule the header breaks.
The slowest the chart can take, computed from its declared retry policies — and every stage that makes a path unbounded, by name. See the module header for the rules.
Pause the run and ask for an answer.
The main door: build, run, decide, retry, pause.
The common import paths: