Expand description
The dispatcher — node registration, subscription, wave engine.
Mirrors ~/src/graphrefly-ts/src/__experiments__/handle-core/core.ts
(the Phase 13.6 brainstorm prototype, ~370 lines, 22 invariant tests).
§Scope (M1 dispatcher + Slice A+B parity, closed 2026-05-05)
- State + derived + dynamic node registration.
- Subscribe / unsubscribe with push-on-subscribe (R1.2.3).
- RAII
Subscriptionwith Drop-based deregister (§10.12). - DIRTY → DATA / RESOLVED ordering (R1.3.1.b two-phase push).
- Equals-substitution (R1.3.2): identity is zero-FFI; custom crosses boundary.
- First-run gate (R2.5.3) — fn does not fire until every dep has a handle.
- Diamond resolution — one fn fire per wave even with shared upstream.
set_deps()atomic dep mutation with cycle detection + Phase 13.8 Q1 terminal-rejection policy (R3.3.1).- PAUSE / RESUME with lockId set + replay buffer (R1.2.6, R2.6, §10.2).
- INVALIDATE broadcast + cascade with R1.4 idempotency.
- COMPLETE / ERROR cascade + Lock 2.B auto-cascade gating (ERROR dominates COMPLETE; first error wins).
- TEARDOWN auto-precedes COMPLETE (R2.6.4 / Lock 6.F) +
has_received_teardownidempotency. - Meta TEARDOWN ordering (R1.3.9.d) — companions tear down before parent.
- Resubscribable terminal lifecycle (R2.2.7, R2.5.3) — late subscribe to a resubscribable terminal node resets lifecycle, except after TEARDOWN (per F3 audit guard: TEARDOWN is permanent).
§Module split (Slice C-1, 2026-05-05)
Wave-engine internals (drain loop, fire selection, emission commit, sink
dispatch) live in [crate::batch]. The split is purely organizational —
the methods are still on Core. See batch.rs for the wave-engine
entry points (run_wave, drain_and_flush, commit_emission,
queue_notify, deliver_data_to_consumer).
§Out of scope (later slices / milestones)
- Deactivation cleanup (RAM nodes clear cache when sink count → 0) — M2.
See migration-status.md for the
milestone tracker and porting-deferred.md
for surfaced concerns deferred to evidence-driven slices.
§Re-entrance discipline (Slice A close, M1: fully lock-released)
- Wave-end sink fires drop the state lock first. A subscriber’s sink
that calls back into
Core::emit/pause/resume/invalidate/complete/error/teardownre-acquires the lock cleanly and runs a nested wave (s.in_tickis cleared before the deferred-fire phase). BindingBoundary::invoke_fnfires lock-released. The wave engine acquires + drops the state lock per fn-fire iteration around theinvoke_fncallback. User fns may re-enterCore::emit/pause/ etc. and run a nested wave.BindingBoundary::custom_equalsfires lock-released.commit_emissionbrackets the equals check around a lock release; custom equals oracles may re-enter Core safely.- Subscribe-time handshake also fires lock-released.
Core::subscribeacquires theCore::wave_ownerre-entrant mutex first (cross-thread serialization), installs the sink under the state lock, drops the state lock, then fires the per-tier handshake ([Start]/[Data(cache)]?/[Complete]?/[Error(h)]?/[Teardown]?per R1.3.5.a) lock-released. A handshake-time sink callback may re-enter Core (emit/complete/error/subscribe); same-thread re-entry passes throughwave_ownertransparently. Cross-thread emits block onwave_owneruntil the subscribe path drops it, preserving R1.3.5.a happens-after ordering.
Structs§
- Core
- The handle-protocol Core dispatcher.
- Node
Opts - Per-kind opts for
Core::register. Cross-kind config knobs live here; per-kind specifics (deps, fn_or_op) live onNodeRegistration. - Node
Registration - Unified node-registration descriptor (D030, Slice D).
- Operator
Opts - Registration options for
Core::register_operator. - Resume
Report - Reported back from
Core::resumewhen the final lock releases. - Subscription
- RAII subscription handle.
- Weak
Core - Weak handle to a
Core— does not contribute to strong refcount.
Enums§
- Equals
Mode - Equality mode for a node’s outgoing emissions.
- Node
FnOr Op - Closure-form fn id OR typed operator discriminant — the two dispatch
paths a node can use. State / passthrough nodes pass
NonetoCore::register(no fn at all). - Node
Kind - Node kind discriminant — derived metadata computed from
[
NodeRecord]’s field shape (D030 unification, Slice D). - Operator
Op - Built-in operator discriminant. Selects the per-operator dispatch path
in
fire_operator(crates/graphrefly-core/src/batch.rs). Each variant carries the binding-side closure ids (and seed handle for stateful folders) needed for the wave-execution path; Core stores no user values itself per the handle-protocol cleaving plane. - Pausable
Mode - Pause behavior mode (canonical-spec §2.6 — three modes shipped in TS; Slice F audit, 2026-05-07 — closed the Rust port gap).
- Pause
Error - Errors returnable by
Core::pauseandCore::resume. - Register
Error - Errors returnable by
Core::registerand its sugar wrappers (Core::register_state,Core::register_producer,Core::register_derived,Core::register_dynamic,Core::register_operator). - SetDeps
Error - Errors returnable by
Core::set_deps. - SetPausable
Mode Error - Errors returnable by
Core::set_pausable_mode. - Terminal
Kind - Terminal-lifecycle state — once set on a node, the node will not emit further DATA; per-dep slots on consumers also use this to track which upstreams have terminated (R1.3.4 / Lock 2.B).
- UpError
- Errors returnable by
Core::up(canonical R1.4.1).
Type Aliases§
- Sink
- A subscriber callback.
Send + Syncso the Core can fire it from any thread;Fn(notFnMut) so multiple references coexist — capture mutable state inMutex<T>or atomics on the binding side.