Skip to content

Redesign actor event semantics - #114

Merged
SandroMaglione merged 1 commit into
mainfrom
codex/xstate-v6-event-semantics
Aug 15, 2026
Merged

Redesign actor event semantics#114
SandroMaglione merged 1 commit into
mainfrom
codex/xstate-v6-event-semantics

Conversation

@SandroMaglione

Copy link
Copy Markdown
Member

Summary

  • separate actor inputs, raised events, and outward ephemeral emissions with Machine.emittedEvents and hot MachineRef.emissions streams
  • add typed self and optional parent actor references, explicit sendTo(parent, event), and composition-checked parentEvents
  • integrate emission streams with AtomMachine, migrate structural context names and examples, and preserve typed decode failures across generic and compiled runtimes

Changeset

  • Added or updated for a library or package-metadata change
  • Not required because this PR does not change src/ or package.json

A minor changeset documents the breaking API migration with a public example.

Validation

  • pnpm check
  • Relevant example checks, when examples changed
  • Automated type-performance measurement passed or was not required
  • Automated runtime- and memory-performance measurement passed or was not required

Also ran pnpm perf:types, pnpm perf:runtime, and the CI-equivalent five-process base/head runtime comparison locally. The head benchmark harness completed against the previous API and the regression guard passed.

@github-actions

Copy link
Copy Markdown
Contributor

Type performance

Measured with TypeScript 6.0.3 and skipLibCheck=true.

Scenario Base PR Difference
Effect only 55 55 0 (0.0%)
Import effect-machine 55 55 0 (0.0%)
Machine.defineStates (3 states) 3,039 3,039 0 (0.0%)
Machine.make (3 states, 2 events) 10,528 13,199 +2,671 (+25.4%)
machine.handle (3 states, 2 transitions) 27,434 30,310 +2,876 (+10.5%)
Machine.invoke (state-dependent Effect) 66,191 70,064 +3,873 (+5.9%)
machine.handle (depth 24) 184,846 187,523 +2,677 (+1.4%)
machine.handle (wide depth 16) 216,944 219,925 +2,981 (+1.4%)
machine.handle (parallel/history/choice) 124,766 127,412 +2,646 (+2.1%)
machine.handle (4 successive calls) 132,322 135,030 +2,708 (+2.0%)
machine exact input/output/error/services 105,688 109,263 +3,575 (+3.4%)
execution adapter readiness 118,404 121,040 +2,636 (+2.2%)

Marginal instantiations are measured against the matching setup without that API call:

Scenario Base PR Difference
Import effect-machine 0 0 0
Machine.defineStates (3 states) 2,984 2,984 0 (0.0%)
Machine.make (3 states, 2 events) 7,481 10,152 +2,671 (+35.7%)
machine.handle (3 states, 2 transitions) 16,906 17,111 +205 (+1.2%)
Machine.invoke (state-dependent Effect) 57,829 59,031 +1,202 (+2.1%)
machine.handle (depth 24) 167,448 167,454 +6 (+0.0%)
machine.handle (wide depth 16) 200,607 200,917 +310 (+0.2%)
machine.handle (parallel/history/choice) 101,798 101,758 -40 (-0.0%)
machine.handle (4 successive calls) 113,757 113,779 +22 (+0.0%)
machine exact input/output/error/services 92,952 92,732 -220 (-0.2%)
execution adapter readiness 87,161 86,962 -199 (-0.2%)
Check times (informational)
Scenario Base PR
Effect only 0.03s 0.03s
Import effect-machine 0.03s 0.03s
Machine.defineStates (3 states) 0.10s 0.10s
Machine.make (3 states, 2 events) 0.16s 0.19s
machine.handle (3 states, 2 transitions) 0.25s 0.26s
Machine.invoke (state-dependent Effect) 0.40s 0.38s
machine.handle (depth 24) 0.71s 0.70s
machine.handle (wide depth 16) 0.72s 0.75s
machine.handle (parallel/history/choice) 0.58s 0.57s
machine.handle (4 successive calls) 0.60s 0.59s
machine exact input/output/error/services 0.51s 0.51s
execution adapter readiness 0.56s 0.53s

Type instantiations are the comparison metric. Check time varies with runner load and is informational only.

@github-actions

Copy link
Copy Markdown
Contributor

Runtime performance

Median of 5 independent benchmark processes on AMD EPYC 9V74 80-Core Processor with Node v24.19.0.

Pull request baseline

Scenario Effect Machine
Plan counter transitions 120,698 transitions/s
Drain burst with terminal fence 381,441 increments/s
Drain burst with a change observer 355,705 increments/s
Lookup and send to one child 324,648 increments/s
Start and stop a machine 149,254 machines/s
Start and stop a parent with one child 42,744 families/s
Plan transitions through a compound state 108,166 transitions/s
Plan transitions through parallel regions 87,518 transitions/s
Drain burst through a compound state 346,391 events/s
Drain burst through two parallel regions 326,895 events/s
Drain a compound-state burst with a change observer 325,574 events/s

Process runtime reference points

Scenario Effect Machine
Start and stop a raw generic process 26,699 processes/s
Start and stop a raw compiled process 67,513 processes/s
Memory profile Effect Machine
Idle machine 1.7 KiB
Raw generic managed process 13.6 KiB
Raw compiled process 2.9 KiB
Two independent idle machines 3.3 KiB
Idle parent with one child 5.2 KiB
Parent with observed child registry 9.4 KiB
Parent with observed invoked child snapshots 5.6 KiB

Effect Machine change from base

Metric Base Base variability PR PR variability Difference
Plan counter transitions 118,540 transitions/s 1.4% MAD 120,698 transitions/s 0.6% MAD +1.8%
Drain burst with terminal fence 384,472 increments/s 0.9% MAD 381,441 increments/s 1.0% MAD -0.8%
Drain burst with a change observer 357,457 increments/s 1.4% MAD 355,705 increments/s 0.9% MAD -0.5%
Lookup and send to one child 325,760 increments/s 2.4% MAD 324,648 increments/s 0.7% MAD -0.3%
Start and stop a machine 166,694 machines/s 0.9% MAD 149,254 machines/s 0.1% MAD -10.5%
Start and stop a parent with one child 44,458 families/s 0.8% MAD 42,744 families/s 1.2% MAD -3.9%
Plan transitions through a compound state 108,167 transitions/s 1.3% MAD 108,166 transitions/s 0.7% MAD -0.0%
Plan transitions through parallel regions 85,640 transitions/s 0.9% MAD 87,518 transitions/s 0.3% MAD +2.2%
Drain burst through a compound state 350,506 events/s 1.4% MAD 346,391 events/s 1.1% MAD -1.2%
Drain burst through two parallel regions 328,180 events/s 2.9% MAD 326,895 events/s 0.2% MAD -0.4%
Drain a compound-state burst with a change observer 326,775 events/s 1.3% MAD 325,574 events/s 0.3% MAD -0.4%
Idle machine heap per unit 1.5 KiB 0.1% MAD 1.7 KiB 0.6% MAD +9.1%
Raw generic managed process heap per unit 12.7 KiB 0.0% MAD 13.6 KiB 0.0% MAD +7.6%
Raw compiled process heap per unit 2.8 KiB 0.3% MAD 2.9 KiB 0.3% MAD +4.6%
Two independent idle machines heap per unit 3.0 KiB 0.1% MAD 3.3 KiB 0.0% MAD +9.2%
Idle parent with one child heap per unit 4.9 KiB 0.0% MAD 5.2 KiB 0.0% MAD +7.2%
Parent with observed child registry heap per unit 9.1 KiB 0.0% MAD 9.4 KiB 0.0% MAD +3.8%
Parent with observed invoked child snapshots heap per unit 5.3 KiB 0.0% MAD 5.6 KiB 0.1% MAD +5.7%

Process runtime reference change from base

Metric Base Base variability PR PR variability Difference
Start and stop a raw generic process 26,958 processes/s 1.8% MAD 26,699 processes/s 2.5% MAD -1.0%
Start and stop a raw compiled process 69,823 processes/s 1.2% MAD 67,513 processes/s 1.4% MAD -3.3%

Regression guard

No large, noise-adjusted throughput or heap regressions detected.

Versions and interpretation
  • Effect Machine: 0.9.0

Higher throughput is better; lower heap is better. Variability is the median absolute deviation across independent processes, relative to their median. Small differences on shared GitHub-hosted hardware remain informational; the required guard rejects only large changes beyond the measured noise allowance.

@SandroMaglione
SandroMaglione merged commit 62e2281 into main Aug 15, 2026
13 checks passed
@SandroMaglione
SandroMaglione deleted the codex/xstate-v6-event-semantics branch August 15, 2026 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant