ReadonlycommitCount of consumers gating Phase F.4 commit-log refresh.
ReadonlycommitCount of registered commitMetadataDerived nodes.
ReadonlycommitCount of live subscribeCommits(observer) registrations, deduped by
observer identity — the same function subscribed twice is one live
registration, on either engine (#279).
Optional ReadonlydeoptsCount of V8 deoptimisations attributed to the engine since
construction, when the host backend can surface it; undefined
otherwise. The canonical TS engine does not wire --trace-deopt
into runtime state and reports undefined. See
packages/bench/report/engine-status-deopts/SUMMARY.md for the
external-capture path.
ReadonlyderivedsCount of user-registered derived nodes — plain derived(...),
liveDerived(...) (devtools), and commitMetadataDerived(...).
Excludes the engine-owned commitLog derived (whose presence is
an engine-implementation detail; counting it would tear the
"user-registered nodes" semantics this counter advertises).
ReadonlyentriesSize of the engine's id → entry registry. Includes the
engine-owned commitLog derived; for user-only node counts use
inputs + deriveds.
Optional ReadonlygcCount of GC pause events attributed to the engine since
construction, when the host backend can surface it; undefined
otherwise. The canonical TS engine reports undefined for the
same host-dependence reason as EngineTelemetry.deopts.
The WASM backend SHOULD report 0 (linear-memory bump-pointer
allocation has no GC pauses).
ReadonlyinputsCount of user-registered input nodes — excludes the engine-owned
commitLog derived. Maintained as a running counter; bumps on
graph.input(...) and decrements on dispose(graph, inputNode)
via @causl/causl-wasm-ts/internal.
ReadonlylastGraphTime of the most recently published commit (0 at engine
genesis; advances by exactly 1 per successful commit(...)).
Failed commits leave this byte-identical to its pre-commit value.
Optional ReadonlymigrationAuto-adapt migration payback counter (#1072).
Only populated by createCausl({ backend: 'auto' })'s wrapper —
undefined on the canonical TS engine and on the (synchronous)
Phase-1 WASM backend. Semantics:
undefined — the wrapper has not yet migrated (still on the
TS backend).N > 0 — the wrapper migrated commitCount-threshold
commits ago and is still amortising the migration cost.
Decrements by 1 on every successful commit.0 — payback complete; the migration has earned back its
one-time cost per the post-#1006 break-even math (~640
commits at 0.7 ms/commit projected savings vs. ~450 ms
migration cost). The wrapper emits this as the "payback"
telemetry event by transitioning from a positive integer to
zero; adopters who subscribed to Graph.subscribeCommits
poll stats.migrationPaybackCommits to detect the transition.The field is at the tail of the object (append-only) per the
cross-backend telemetry contract — adopters that diff two
snapshots can branch on before.migrationPaybackCommits !== after.migrationPaybackCommits without breaking on TS-engine
snapshots where both sides are undefined.
ReadonlyretainedCurrent length of the bounded commit-history ring.
ReadonlysubscribersNumber of distinct nodes with at least one live subscriber.
ReadonlysubscribersTotal count of live subscribe(node, observer) registrations.
ReadonlytransientCount of live subscribe(...) / subscribeMany(...) registrations
with options.transient === true (#766 transient auto-dispose).
A non-zero value between commit boundaries indicates transient
observers waiting for their first non-initial Phase G fire.
Per-node version counter (#1242, SPEC §15.1).
Any handle reachable through the engine's public surface — input, derived, or commit-metadata derived.
The number of commits in which node.id has appeared in
commit.changedNodes over this engine's lifetime; 0 if the
node has never changed (or has never been seen).
Returns the monotonically-increasing counter for how many commits
the given node has appeared in Commit.changedNodes. The counter
advances by exactly 1 each time the node's value changed in a
commit (per SPEC §15.1 / #1129 semantics: change =
!Object.is(prevValue, nextValue)); it advances by 0 on a
commit where the node's value did not change (no-op commit,
equality-cutoff path, sibling-shape isolation). The initial value
(before any commit changes the node) is implementation-defined;
the canonical TS engine returns 0 for a never-changed node,
including nodes the engine has never seen.
The accessor is the load-bearing memoisation surface for adopters
who can no longer rely on read() reference identity (H1 hazard,
fixed in PR #1245). Memoise on engine.stats().nodeVersion(node)
and downstream caches invalidate iff the node's value actually
changed — a no-op commit will not bump the counter and so will
not invalidate the cache.
Cross-backend contract: nodeVersion(node) MUST be byte-identical
across the canonical TS engine and the Phase-1 WasmBackend wrapper
for the same commit sequence, because nodeVersion is a pure
derivation of Commit.changedNodes, which is already pinned
byte-identically by the determinism gate (#1059 / PR #1107).
Disposed-node semantics (#1164 generational NodeId): a disposed node's counter is reset. If the slot is reused with a new generation, the new node starts at counter 0.
Engine-wide retained-state telemetry surface — the cross-backend snapshot shape produced by Graph.stats.
Remarks
Each field is a snapshot count of an engine-internal collection or scalar at the moment the producing
stats()call was made. The shape is intentionally flat — one counter per audit-relevant collection — so devtools consumers and bench leak gates can diff two records by field rather than walk a tree. All counts are non-negative integers; thelastCommitTimefield is a GraphTime.The fields map onto graph-internal state as follows:
Node-cardinality counters (#696):
inputs— count of user-registered input nodes (excludes the engine-ownedcommitLogderived).deriveds— count of user-registered derived nodes (plainderived(...),liveDerived(...), andcommitMetadataDerived(...); excludes the engine-ownedcommitLogderived).Subscription counters (#757 first cut + #696 transient breakdown):
subscribersTotal— size of the flat per-nodesubscriptionsSet (one entry per livesubscribe(node, observer)registration; each member of asubscribeMany(...)group contributes one entry).subscribersByNodeKeys— size of the per-node subscriber index Map (i.e., the number of distinct nodes with at least one live per-node subscription).transientSubscribers— count of livesubscribe(...)/subscribeMany(...)registrations whoseoptions.transient === trueslot is set. A transient observer auto-disposes after its first Phase G fire (#766); this counter is the number of such registrations not yet fired (and therefore still pinned insubscriptions). On a steady-state graph this is almost always 0; a non-zero residual between commit boundaries means a transient registration is waiting for its first non-initial fire. The count is a WRAPPER fact on every engine, and #279 slice S3 is why this says so.{ transient: true }does not cross the FFI — theBackendEngine.subscribeseam carries no options, so the facade hosts the one-shot itself — and the Rust core'stransientSubscribersslot is structurally0, which is the true count of what the ENGINE owns because no transient registration was ever made with it. That0was read as an engine defect for two waves. It is the count of a thing that lives in TypeScript, and the facade takes it.commitObservers— size of thesubscribeCommitslistener Set (one entry per livesubscribeCommits(observer)registration; commit-bus observers are NOT counted insubscribersTotal). Underrust-ssota registration crosses to the engine's own Set instead, and the facade reports the union of the two — keyed by function identity on both sides, so one function subscribed twice is one live observer whichever engine is running (#279).Engine-internal index counters:
commitMetadataDeriveds— size of the registeredcommitMetadataDerivedid Set (#452 Phase F.5 seed set).commitLogConsumerCount— current value of the consumer counter that gates Phase F.4 commit-log refresh undercommitHistoryCap > 0(#715).entries— size of the canonical id → entry Map (registered nodes including the engine-ownedcommitLogderived; for user-only counts useinputs + deriveds).Commit-history counters (#696):
lastCommitTime— current GraphTime; equals 0 at engine genesis and advances by exactly 1 per successfully published commit. A failed commit leaves this byte-identical to its pre-commit value (atomicity rollback). Useful as the "did anything commit between snapshots?" predicate (after.lastCommitTime !== before.lastCommitTime).retainedCommits— current length of the boundedcommitHistoryring (capped bycommitHistoryCap; 0 when the engine was constructed withcommitHistoryCap: 0, the post-#778 default).Optional engine-status counters (audit-flagged, host-dependent):
deopts— count of V8 deoptimisations attributed to the engine since construction.undefinedwhen the host backend cannot surface this counter at runtime (the canonical TS engine does not wire--trace-deoptinto its retained state; the bench harness captures this externally — seepackages/bench/report/engine-status-deopts/SUMMARY.md). Future host-supported paths (e.g. a worker-side--trace-deoptadapter) may populate the field; the contract guarantees the field name, not its presence on every backend.gcPauses— count of GC pause events attributed to the engine since construction.undefinedon the canonical TS engine for the same host-dependence reason asdeopts. The WASM backend does not run inside a JS GC and SHOULD reportgcPauses: 0(linear-memory allocation is bump-pointer; "pauses" is not a Rust-side concept).Example: Diffing two snapshots
Example: Cross-backend parity assertion