Skip to content

Harden machine handler capture - #84

Merged
SandroMaglione merged 1 commit into
mainfrom
codex/harden-handler-capture-eligibility
Aug 10, 2026
Merged

Harden machine handler capture#84
SandroMaglione merged 1 commit into
mainfrom
codex/harden-handler-capture-eligibility

Conversation

@SandroMaglione

@SandroMaglione SandroMaglione commented Aug 10, 2026

Copy link
Copy Markdown
Member

Summary

  • Capture event dispatch maps, transition descriptors, and declared target arrays when handlers are registered so caller-owned mutation cannot make planner strategies diverge.
  • Make indexed execution eligibility fail closed: unknown state semantics now select the generic planner until explicitly supported.
  • Add public and internal regression coverage, plus concise Effect-internals testing, performance, and quality guidance for future agents.

Changeset

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

Validation

  • pnpm check
  • Relevant example checks, when examples changed (not applicable; no examples changed)
  • Reviewed the automated type-performance report, when the public TypeScript API or inference changed
  • Reviewed the automated runtime-performance report, when runtime behavior changed
  • pnpm perf:types
  • pnpm perf:runtime

Type instantiations are unchanged in every measured scenario. Runtime throughput and memory remain within the benchmark suite's expected run-to-run variability.

@github-actions

github-actions Bot commented Aug 10, 2026

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) 2,965 2,965 0 (0.0%)
Machine.make (3 states, 2 events) 8,715 8,715 0 (0.0%)
machine.handle (3 states, 2 transitions) 23,492 23,492 0 (0.0%)
machine.handle (depth 8) 117,075 117,075 0 (0.0%)
machine.handle (depth 12) 129,713 129,713 0 (0.0%)
machine.handle (depth 16) 145,055 145,055 0 (0.0%)
machine.handle (depth 24) 183,851 183,851 0 (0.0%)
machine.handle (wide depth 16) 214,189 214,189 0 (0.0%)
machine.handle (parallel/history/choice) 132,667 132,667 0 (0.0%)
machine.handle (4 successive calls) 128,674 128,674 0 (0.0%)
machine exact input/output/error/services 110,584 110,584 0 (0.0%)
execution adapter readiness 127,854 127,854 0 (0.0%)

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,910 2,910 0 (0.0%)
Machine.make (3 states, 2 events) 5,742 5,742 0 (0.0%)
machine.handle (3 states, 2 transitions) 14,777 14,777 0 (0.0%)
machine.handle (depth 8) 109,601 109,601 0 (0.0%)
machine.handle (depth 12) 121,039 121,039 0 (0.0%)
machine.handle (depth 16) 135,181 135,181 0 (0.0%)
machine.handle (depth 24) 171,577 171,577 0 (0.0%)
machine.handle (wide depth 16) 202,224 202,224 0 (0.0%)
machine.handle (parallel/history/choice) 114,331 114,331 0 (0.0%)
machine.handle (4 successive calls) 113,703 113,703 0 (0.0%)
machine exact input/output/error/services 100,670 100,670 0 (0.0%)
execution adapter readiness 102,092 102,092 0 (0.0%)
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.11s
Machine.make (3 states, 2 events) 0.16s 0.17s
machine.handle (3 states, 2 transitions) 0.23s 0.25s
machine.handle (depth 8) 0.53s 0.53s
machine.handle (depth 12) 0.58s 0.58s
machine.handle (depth 16) 0.61s 0.60s
machine.handle (depth 24) 0.71s 0.68s
machine.handle (wide depth 16) 0.79s 0.77s
machine.handle (parallel/history/choice) 0.66s 0.64s
machine.handle (4 successive calls) 0.61s 0.62s
machine exact input/output/error/services 0.55s 0.52s
execution adapter readiness 0.62s 0.58s

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

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Runtime performance

Median of 5 independent benchmark processes on INTEL(R) XEON(R) PLATINUM 8573C with Node v24.18.0.

Pull request baseline

Scenario Effect Machine XState 5 XState 6 alpha
Plan counter transitions 151,864 transitions/s 149,257 transitions/s 17,535 transitions/s
Drain burst with terminal fence 659,681 increments/s 421,360 increments/s 190,841 increments/s
Drain burst with a change observer 608,604 increments/s 417,902 increments/s 191,315 increments/s
Lookup and send to one child 531,677 increments/s 422,148 increments/s 184,848 increments/s
Start and stop a machine 207,814 machines/s 209,380 machines/s 164,069 machines/s
Start and stop a parent with one child 56,786 families/s 80,587 families/s 53,605 families/s
Plan transitions through a compound state 136,961 transitions/s
Plan transitions through parallel regions 107,919 transitions/s
Drain burst through a compound state 609,105 events/s 370,190 events/s 192,293 events/s
Drain burst through two parallel regions 560,575 events/s 292,401 events/s 153,541 events/s
Drain a compound-state burst with a change observer 558,591 events/s

Effect runtime reference points

Scenario Effect Machine Effect runtime primitives
Start and stop a raw generic process 33,792 processes/s
Start and stop a raw compiled process 88,292 processes/s
Start and interrupt a suspended fiber 193,874 fibers/s
Start and stop a queue worker 141,764 workers/s
Start and stop an actor shell 130,804 actors/s
Start and stop two actor shells 81,473 families/s
Update an owner-only mutable snapshot 302,023,558 updates/s
Update a synchronized snapshot 1,764,885 updates/s
Create, resolve, and await a terminal latch 1,713,655 latches/s
Memory profile Effect Machine XState 5 XState 6 alpha Effect runtime primitives
Idle machine 1.5 KiB 3.8 KiB 2.2 KiB
Raw generic managed process 12.1 KiB
Raw compiled process 2.6 KiB
Two independent idle machines 2.9 KiB 7.3 KiB 3.9 KiB
Idle parent with one child 4.8 KiB 5.5 KiB 4.0 KiB
Parent with observed child registry 9.0 KiB
Parent with observed invoked child snapshots 5.0 KiB
Suspended Effect fiber 0.7 KiB
Effect queue with waiting fiber 2.8 KiB
Effect mailbox actor shell 3.2 KiB
Two Effect actor shells 6.8 KiB

Effect Machine change from base

Metric Base Base variability PR PR variability Difference
Plan counter transitions 147,249 transitions/s 2.5% MAD 151,864 transitions/s 1.9% MAD +3.1%
Drain burst with terminal fence 618,266 increments/s 4.7% MAD 659,681 increments/s 0.9% MAD +6.7%
Drain burst with a change observer 570,577 increments/s 1.8% MAD 608,604 increments/s 0.6% MAD +6.7%
Lookup and send to one child 524,167 increments/s 1.0% MAD 531,677 increments/s 0.3% MAD +1.4%
Start and stop a machine 210,881 machines/s 0.8% MAD 207,814 machines/s 1.7% MAD -1.5%
Start and stop a parent with one child 58,817 families/s 2.7% MAD 56,786 families/s 1.0% MAD -3.5%
Plan transitions through a compound state 138,462 transitions/s 2.1% MAD 136,961 transitions/s 3.0% MAD -1.1%
Plan transitions through parallel regions 106,340 transitions/s 0.2% MAD 107,919 transitions/s 0.8% MAD +1.5%
Drain burst through a compound state 614,142 events/s 0.6% MAD 609,105 events/s 0.9% MAD -0.8%
Drain burst through two parallel regions 570,206 events/s 1.5% MAD 560,575 events/s 0.3% MAD -1.7%
Drain a compound-state burst with a change observer 575,259 events/s 1.8% MAD 558,591 events/s 1.3% MAD -2.9%
Idle machine heap per unit 1.5 KiB 0.1% MAD 1.5 KiB 0.0% MAD -0.2%
Raw generic managed process heap per unit 12.1 KiB 0.0% MAD 12.1 KiB 0.0% MAD +0.0%
Raw compiled process heap per unit 2.6 KiB 0.1% MAD 2.6 KiB 0.4% MAD +0.6%
Two independent idle machines heap per unit 2.9 KiB 0.0% MAD 2.9 KiB 0.2% MAD +0.2%
Idle parent with one child heap per unit 4.8 KiB 0.0% MAD 4.8 KiB 0.0% MAD -0.1%
Parent with observed child registry heap per unit 9.0 KiB 0.0% MAD 9.0 KiB 0.0% MAD +0.0%
Parent with observed invoked child snapshots heap per unit 5.0 KiB 0.2% MAD 5.0 KiB 0.0% MAD +0.1%

Effect runtime reference change from base

Metric Base Base variability PR PR variability Difference
Start and stop a raw generic process 33,105 processes/s 1.1% MAD 33,792 processes/s 0.6% MAD +2.1%
Start and stop a raw compiled process 87,773 processes/s 0.3% MAD 88,292 processes/s 0.7% MAD +0.6%
Start and interrupt a suspended fiber 191,718 fibers/s 0.6% MAD 193,874 fibers/s 0.5% MAD +1.1%
Start and stop a queue worker 140,371 workers/s 1.7% MAD 141,764 workers/s 0.8% MAD +1.0%
Start and stop an actor shell 129,870 actors/s 1.0% MAD 130,804 actors/s 0.6% MAD +0.7%
Start and stop two actor shells 75,614 families/s 2.2% MAD 81,473 families/s 1.2% MAD +7.7%
Update an owner-only mutable snapshot 302,114,804 updates/s 0.1% MAD 302,023,558 updates/s 0.0% MAD -0.0%
Update a synchronized snapshot 1,801,211 updates/s 1.3% MAD 1,764,885 updates/s 3.3% MAD -2.0%
Create, resolve, and await a terminal latch 1,729,571 latches/s 1.0% MAD 1,713,655 latches/s 1.4% MAD -0.9%
Versions and interpretation
  • Effect Machine: 0.3.0
  • XState 5: 5.32.5
  • XState 6 alpha: 6.0.0-alpha.31
  • Effect runtime primitives: 4.0.0-beta.107

Higher throughput is better; lower heap is better. Variability is the median absolute deviation across independent processes, relative to their median. Runtime measurements on shared GitHub-hosted hardware remain informational, so small differences should be confirmed across multiple workflow runs.

@SandroMaglione
SandroMaglione force-pushed the codex/harden-handler-capture-eligibility branch from ad3e4c3 to 748627e Compare August 10, 2026 13:54
@SandroMaglione
SandroMaglione merged commit b7ea7a5 into main Aug 10, 2026
10 checks passed
@SandroMaglione
SandroMaglione deleted the codex/harden-handler-capture-eligibility branch August 10, 2026 14:16
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