Skip to content

Replace Machine.invoke with fluent state invocations - #148

Merged
SandroMaglione merged 1 commit into
mainfrom
codex/fluent-invocations
Aug 19, 2026
Merged

Replace Machine.invoke with fluent state invocations#148
SandroMaglione merged 1 commit into
mainfrom
codex/fluent-invocations

Conversation

@SandroMaglione

Copy link
Copy Markdown
Member

Summary

  • Replace Machine.invoke and object invocation configs with state-local invoke: (from) => ... chains for Effects, Streams, timers, reusable logic, and child machines.
  • Enforce source-specific typestate: every reachable completion, failure, and element channel is handled exactly once, snapshot handling remains optional, and owner state, protocols, source channels, failures, and services stay inferred through the chain.
  • Normalize completed chains into the existing invocation runtime model, preserve state-scoped lifecycle semantics, and migrate tests, fixtures, benchmarks, documentation, and all affected examples.

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 (platformer, playground, and pokemon)
  • Automated type-performance measurement passed
  • Automated runtime- and memory-performance measurement 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.states (3 states) 3,039 3,039 0 (0.0%)
Machine.make (3 states, 2 events) 10,759 10,759 0 (0.0%)
machine.handle (3 states, 2 transitions) 29,625 28,192 -1,433 (-4.8%)
fluent transition (10 named branches) 107,405 106,006 -1,399 (-1.3%)
fluent invocation (state-dependent Effect) 92,583 92,756 +173 (+0.2%)
machine.handle (depth 24) 196,139 192,588 -3,551 (-1.8%)
machine.handle (wide depth 16) 227,285 220,950 -6,335 (-2.8%)
machine.handle (parallel/history/choice) 130,795 128,389 -2,406 (-1.8%)
machine definition (3 independent implementations) 129,848 127,342 -2,506 (-1.9%)
machine exact input/output/error/services 114,991 113,853 -1,138 (-1.0%)
execution adapter readiness 124,898 118,896 -6,002 (-4.8%)

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

Scenario Base PR Difference
Import effect-machine 0 0 0
Machine.states (3 states) 2,984 2,984 0 (0.0%)
Machine.make (3 states, 2 events) 7,712 7,712 0 (0.0%)
machine.handle (3 states, 2 transitions) 18,866 17,433 -1,433 (-7.6%)
fluent transition (10 named branches) 97,156 95,757 -1,399 (-1.4%)
fluent invocation (state-dependent Effect) 83,318 83,491 +173 (+0.2%)
machine.handle (depth 24) 175,443 171,892 -3,551 (-2.0%)
machine.handle (wide depth 16) 207,796 201,461 -6,335 (-3.0%)
machine.handle (parallel/history/choice) 110,532 108,126 -2,406 (-2.2%)
machine definition (3 independent implementations) 113,879 111,373 -2,506 (-2.2%)
machine exact input/output/error/services 100,480 99,342 -1,138 (-1.1%)
execution adapter readiness 93,457 89,084 -4,373 (-4.7%)
Check times (informational)
Scenario Base PR
Effect only 0.03s 0.03s
Import effect-machine 0.03s 0.04s
Machine.states (3 states) 0.12s 0.11s
Machine.make (3 states, 2 events) 0.18s 0.17s
machine.handle (3 states, 2 transitions) 0.29s 0.28s
fluent transition (10 named branches) 0.59s 0.54s
fluent invocation (state-dependent Effect) 0.52s 0.55s
machine.handle (depth 24) 0.88s 0.85s
machine.handle (wide depth 16) 0.91s 0.90s
machine.handle (parallel/history/choice) 0.68s 0.67s
machine definition (3 independent implementations) 0.68s 0.69s
machine exact input/output/error/services 0.64s 0.61s
execution adapter readiness 0.65s 0.60s

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 110,421 transitions/s
Drain burst with terminal fence 327,618 increments/s
Drain burst with a change observer 307,787 increments/s
Lookup and send to one child 282,433 increments/s
Start and stop a machine 138,677 machines/s
Start and stop a parent with one child 40,376 families/s
Plan transitions through a compound state 97,521 transitions/s
Plan transitions through parallel regions 80,667 transitions/s
Drain burst through a compound state 278,381 events/s
Drain burst through two parallel regions 284,365 events/s
Drain a compound-state burst with a change observer 263,879 events/s

Process runtime reference points

Scenario Effect Machine
Start and stop a raw generic process 24,918 processes/s
Start and stop a raw compiled process 64,541 processes/s
Memory profile Effect Machine
Idle machine 1.8 KiB
Raw generic managed process 13.9 KiB
Raw compiled process 3.2 KiB
Two independent idle machines 3.4 KiB
Idle parent with one child 5.8 KiB
Parent with observed child registry 10.0 KiB
Parent with observed invoked child snapshots 6.3 KiB

Effect Machine change from base

Metric Base Base variability PR PR variability Difference
Plan counter transitions 112,746 transitions/s 1.2% MAD 110,421 transitions/s 2.3% MAD -2.1%
Drain burst with terminal fence 328,057 increments/s 0.3% MAD 327,618 increments/s 0.1% MAD -0.1%
Drain burst with a change observer 309,133 increments/s 0.9% MAD 307,787 increments/s 0.8% MAD -0.4%
Lookup and send to one child 283,564 increments/s 0.9% MAD 282,433 increments/s 0.9% MAD -0.4%
Start and stop a machine 138,102 machines/s 1.3% MAD 138,677 machines/s 0.8% MAD +0.4%
Start and stop a parent with one child 41,058 families/s 0.4% MAD 40,376 families/s 0.8% MAD -1.7%
Plan transitions through a compound state 99,569 transitions/s 0.3% MAD 97,521 transitions/s 0.9% MAD -2.1%
Plan transitions through parallel regions 80,909 transitions/s 1.1% MAD 80,667 transitions/s 1.6% MAD -0.3%
Drain burst through a compound state 280,461 events/s 1.5% MAD 278,381 events/s 1.1% MAD -0.7%
Drain burst through two parallel regions 289,416 events/s 0.3% MAD 284,365 events/s 1.3% MAD -1.7%
Drain a compound-state burst with a change observer 264,516 events/s 0.8% MAD 263,879 events/s 0.5% MAD -0.2%
Idle machine heap per unit 1.8 KiB 0.2% MAD 1.8 KiB 0.1% MAD -0.3%
Raw generic managed process heap per unit 13.9 KiB 0.0% MAD 13.9 KiB 0.0% MAD +0.0%
Raw compiled process heap per unit 3.2 KiB 0.6% MAD 3.2 KiB 0.4% MAD -0.0%
Two independent idle machines heap per unit 3.4 KiB 0.1% MAD 3.4 KiB 0.1% MAD +0.5%
Idle parent with one child heap per unit 5.8 KiB 0.0% MAD 5.8 KiB 0.0% MAD -0.0%
Parent with observed child registry heap per unit 10.0 KiB 0.0% MAD 10.0 KiB 0.0% MAD +0.0%
Parent with observed invoked child snapshots heap per unit 6.3 KiB 0.1% MAD 6.3 KiB 0.0% MAD +0.1%

Process runtime reference change from base

Metric Base Base variability PR PR variability Difference
Start and stop a raw generic process 25,708 processes/s 1.8% MAD 24,918 processes/s 2.1% MAD -3.1%
Start and stop a raw compiled process 64,922 processes/s 0.8% MAD 64,541 processes/s 1.2% MAD -0.6%

Regression guard

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

Versions and interpretation
  • Effect Machine: 0.17.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 d6d19c1 into main Aug 19, 2026
13 checks passed
@SandroMaglione
SandroMaglione deleted the codex/fluent-invocations branch August 19, 2026 13:37
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