OptionalreadOnlyValues: unknownOptionalexecutionEnv: ExecutionEnvOptionalpolicy: RetryPolicyOptionaloptions: CallOptionsOptionaldescription: stringSee ScopeMethods.$detachAndForget.
Optionalinput: unknownSee ScopeMethods.$detachAndJoinLater.
Optionalinput: unknownFire a structured event to every attached recorder implementing
onEmit. Synchronous, in-order, pass-through — no buffering.
stageName, runtimeStageId,
subflowPath, pipelineId, timestamp to the event.RedactionPolicy.emitPatterns regexes are matched
against name — matched events have their payload replaced with
'[REDACTED]' before dispatch.onEmit does not propagate — it is
caught and routed to onError on remaining recorders, matching the
pattern used by other scope events.Consumers call this via the scope.$emit(name, payload) scope method;
the method routes here via createTypedScope.
Returns the readonly input values passed to this pipeline, cast to T.
Ordinary mutable records/arrays are copied and frozen, not frozen in place.
Opaque capabilities and explicitly frozen wrappers remain borrowed/live;
see protection/readonlyInput.ts · createFrozenArgs for the ownership rule.
Cached at construction time for zero-allocation repeated access.
const { applicantName, income } = scope.getArgs<{ applicantName: string; income: number }>();
RFC-003 D2: args are untracked BY DESIGN, so calling this (with actual
input present) marks the stage's commit with untrackedSources: ['args']
— telling causal-slice consumers the backward slice may be incomplete
here. An empty-args read carries no information and is not flagged.
The attempt this stage function runs in — scope.$attempt() (design
§3.1). Its signal aborts when the attempt must stop (its cap, the run's
deadline, a cancellation): pass it to fetch and SDK calls. Its
timeLeftMs() reads the run's deadline — time is an input the record
cannot replay, so with a deadline the read marks the stage's commit
untrackedSources: ['env'], as getEnv() does.
A stage the attempt runner does not run (no retry policy, no timeoutMs,
no deadline) is attempt 1 of 1, with the run's signal and Infinity left.
const t = scope.$attempt();
const res = await fetch(url, { signal: t.signal });
if (t.timeLeftMs() < 1_000) scope.summary = 'partial';
Returns the execution environment — read-only infrastructure values
that propagate through nested executors (like process.env for flowcharts).
Contains: signal (abort — the run's one effective signal), timeoutMs, traceId, operationId, traceparent,
deadline (epoch ms) — the run's env as admitted (attempt/correlation.ts · admitRunEnv: one own-field
snapshot; traceparent is the canonical header the run's correlation carries, never the received bytes), then
armed (runner/deadline.ts · armRun). Frozen at construction time. Inherited by subflows automatically.
const { signal, traceId } = scope.getEnv();
RFC-003 D2: env is untracked BY DESIGN, so calling this (with a
non-empty environment) marks the stage's commit with
untrackedSources: ['env'] — see getArgs.
Returns a compliance-friendly report of all redaction activity. Never includes actual values — only key names, field names, and patterns.
Returns all state keys without firing onRead. Used by TypedScope ownKeys/has traps. A listing hands the stage names and no value, so it never counts as a selected read.
Tracked read of shared state.
Read values are BORROWED — do not mutate them. Since the lazy buffer
(#13), reads before the stage's first write return references INTO
COMMITTED SHARED STATE, and reads after a write return references into
the stage's private transaction-buffer working copy (the eager engine
returned references into that working copy for ALL reads). Mutating a
returned value in place would corrupt state without a commit record —
write changes back via setValue/updateValue instead. TypedScope
consumers are safe automatically: the proxy routes every mutation
through setValue/updateValue/copy-on-write array ops.
There is deliberately NO dev-mode freeze guard here: deep-freezing a
buffer-served read would freeze the stage's own working copy and make a
legitimate read-then-deep-write throw, and freezing a committed-state
read mutates an object shared with every other consumer of the live
state. See src/lib/memory/README.md ("Read values are borrowed").
Recorder note: the onRead event below passes the SAME live reference
(no clone) unless field-level redaction scrubs a copy — recorders must
treat event values as read-only too.
Optionalkey: stringPath-aware adapter access. Segments stay separate from literal dotted keys in STATE;
the recorder event's key is only a dot-joined display label, so ['user','ssn'] and
a literal top-level 'user.ssn' produce the same event key (same for the writes below).
Optionalkey: stringRead state without firing onRead. Used by array proxy getCurrent() to avoid phantom reads on internal array operations (.length, .has, iteration, etc.). The initial property access fires one tracked onRead via getValue(); subsequent internal array operations use this method to stay silent. NOTE: Like getValue(), returns the raw value to the caller. Redaction applies only to recorder dispatch — it does not filter the returned value. This matches the existing getValue() contract where user code always receives raw data.
RFC-003 D2: a silent read of a key this stage never TRACKED-read marks
the stage's commit with untrackedSources: ['silent'] — a causal slice
built from onRead events would miss this dependency, and consumers must
be told. Silent reads shadowed by a tracked read of the same key (the
array-proxy pattern above) are not flagged: their edge is captured. A
whole-state silent read (no key) is always flagged.
Optionalkey: stringCheck key existence without firing onRead. Used by TypedScope has trap. Contract: returns false for keys never set OR keys set to undefined. This matches deleteValue() semantics (sets to undefined = deleted).
hasKey as a listing asks it, once per key (the TypedScope descriptor trap, under Object.keys, for…in and
a spread). The same answer and the same read row, but it hands the stage no value, so it never counts as a selected
read. 'k' in scope stays hasKey: a question about one key by name.
OptionalshouldRedact: booleanOptionaldescription: stringPath-aware writes share recording, redaction and lifetime policy with flat writes.
OptionalshouldRedact: booleanOptionaldescription: stringOptionaldescription: stringPath-aware merges use the same write funnel as replacements.
Optionaldescription: string
A call to an outside system, made inside this stage —
scope.$call(target, fn, policy?)(design §3.6, P2).fnruns on the attempt runner underpolicy(none = ONE attempt, still recorded: a single call is evidence too), and every attempt is anAttemptRecordof kind'call'on this stage's execution-tree node and ononAttempt— its timing, outcome, failure category and the retry-or-stop decision, its error served under the run's redaction rule.The call lives within this stage attempt: it is capped by the time the attempt has left (its cap, the run's deadline) and
fn'ssignalaborts when the attempt does; once the attempt is over nothing of the call lands and no further attempt of it starts (attempt/call.ts).targetis a NAME from code — never a value.A call that CHANGES an outside system declares it (P3):
options.touchesnames the system andoptions.idempotencyKeythe key every attempt is made under (t.idempotencyKey) — a value from the stage's input or state, never from model output. A touching call that may retry without a key is refused here, before anything is sent; each record says what its attempt did (effect), and a call that ends without an answer after any attempt left the effect unknown rejects withUnknownEffectError— reconcile by the key, do not retry blindly.