Repository navigation
feat(repo): test xstate's state matching as a conformance spec - #68
Merged
Merged
Conversation
…e spec with a data-last assertEvent
…spec with a data-last matchesState
…list of call forms
…tate-matching # Conflicts: # packages/xstate/tests/__fixtures__/eventAssertion.ts
…tate-matching # Conflicts: # packages/AGENTS.md
# Conflicts: # packages/AGENTS.md # packages/xstate/etc/xstate.api.md # packages/xstate/package.json # packages/xstate/stryker.config.ts # packages/xstate/tests/__fixtures__/eventAssertion.ts # packages/xstate/tests/event-assertions.conformance.test.ts # packages/xstate/tsconfig.tsgo.json
…state its edge-input changes
systemfsoftware-maker
marked this pull request as draft
October 8, 2026 20:28
# Conflicts: # packages/xstate/etc/xstate.api.md
systemfsoftware-maker
marked this pull request as ready for review
October 8, 2026 23:48
…fuse inlined deps The xstate package has no dependency on effect, so importing `dual` made tsdown inline part of effect into xstate's dist and write an inlinedDependencies field into its package.json. matchesState now has two overloads and checks its argument count in its own code. The inlinedDependencies field is gone, and xstate's build now sets deps.onlyBundle to an empty list, so any dependency the build would inline fails the build and gate:dist.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
@systemfsoftware/xstate's state-matching capability (matchesState,snapshot.matches) is now judged by a conformance spec over a model of active state paths instead of twelve hand-picked examples.matchesStategains a data-last form, and it now reads a state name inside a state value as a plain name even when the name holds a dot (conductor ruling, cycle 128, option a). Off main, with main merged up to #71.Behaviour change
Only a state value that is a whole string is read as a dotted path (
\.is a dot inside a name). For a stated.enested underb,snapshot.matches({ b: 'd.e' })is nowtrue; on main it isfalseand only{ b: 'd\\.e' }matched.snapshot.matches('b.d\\.e')staystrue.Three edge inputs also change, all stated in the changeset: a machine snapshot passed in place of a state value is no longer unwrapped (the published type never allowed it; no caller in the repo passes one); a region whose value is
undefinedanswersfalsewhere main threw aTypeError; an id ending in a lone\keeps the backslash where main produced the textundefined. A pre-PR differential sweep (40,000 generated cases against main's algorithm) found no other divergence.Code move
matchesState,pathToStateValueandtoStatePathmove fromsrc/utils.tsintosrc/stateMatching.ts. Internal callers (State.ts,StateMachine.ts,stateUtils.ts) import from there, which removes theState.ts/utils.tsimport cycle. Public names are unchanged;pathToStateValuenow takes a readonly path. The API report is regenerated.Packaging
@systemfsoftware/xstatehas no dependencies.matchesState's data-last form is written in our own code, asassertEvent's is: two overloads and an argument-count check, with noeffectimport insrc. An earlier head (b3a8fb08) imported Effect'sdual, so tsdown inlined part of effect into the dist and wroteinlinedDependencies: { effect: "4.0.1" }intopackage.json. Both are gone. xstate'stsdown.config.tsnow setsdeps: { onlyBundle: [] }, so the build, and with itgate:dist, fails if it would inline any dependency.Deleted tests → new coverage (CONST-T9)
packages/xstate/test/match.test.tsis deleted. Every row below is covered bytests/state-matching.conformance.test.ts, whose expected answers come fromtests/__fixtures__/stateValue.model.ts(imports onlyeffect): a parent matches a child when every leaf path of the parent is a prefix of a leaf path of the child.MatchItselfcommands: every generated tree, compound and parallel, matched against itself in each form and callMatchcommands whose parent paths are prefixes of the child's, judged by the modelfalse; the liveness step requires each call form to have answeredfalseparentForm/childFormdraw each argument as a value or as an escaped dotted id stringmatches()on a snapshotsnapshotcall form:checkStateInon a real snapshot, which callssnapshot.matchesfor non-#idvaluesSpec shape
Conformance.sequentialruns 64 generated sequences of 8 match commands per seed (1, 2, 3) through the data-first, data-last and snapshot forms. A liveness step checks that each call form answered both ways, that escaped-dot ids were passed, and that state values nested a leaf name holding a dot. Two planted matchers are rejected with the model-diverged judgement: one that splits ids on escaped dots, and one that reads nested leaf names as dotted paths.Enrolment
packages/xstate/stryker.config.ts):src/assert.ts,src/stateMatching.ts.lintscript addssrc/stateMatching.ts, the spec and its fixtures. 0 findings, no disable comments.tsconfig.tsgo.jsonaddssrc/stateMatching.ts.lint:tsgo:Checked 2 files out of 2 files. 0 errors, 0 warnings and 0 messages.src/stateMatching.ts.Evidence
78f1a112: xstate suite--sequence.shuffleseeds 1-6 each exit 0 (2440 passed, 29 skipped, 3 todo). The spec's 5 cases pass with the same names on seeds 1-3.78f1a112(scratch tree, deleted), each3 failed | 2 passedon seeds 1, 2 and 3:every→someacross regionstoStatePath78f1a112:pnpm --filter @systemfsoftware/xstate buildexits 0.dist/*.mjscomes from outsidesrc/; a grep fornode_modules,effect@and non-src/#regionmarkers finds no matches.package.jsonhas noinlinedDependencies.b3a8fb08with only the newtsdown.config.ts, the build exits 1:effect is located in node_modules but is not included in deps.onlyBundle option ... Imported by - .../src/stateMatching.ts.78f1a112:gate:dist, dprint, typecheck, lint andtest:scripts(23/23) exit 0.lint:tsgo:Checked 2 files out of 2 files. 0 errors, 0 warnings and 0 messages.b3a8fb08: the only change is thatmatchesStateis now two function overloads instead of aconstwith two call signatures. Against main, no export is removed or renamed.test/match.test.tsis dropped from the xstate config's inlineunguardedlist, which fix(repo): let a stryker sandbox load each package's own test config #69 added;test:scriptschecks that list againstunguarded-tests.json.src/assert.tsat the Release gate on main7c234dec(run 37853142378, xstate shard report):CompileError=5 Killed=16, 0 survived, 0 no coverage, thresholds 100/100/100. No assert.ts survivor needs a test.src/stateMatching.ts(parent/child swapped; escape handling dropped; nested-name ruling reverted) each turned the spec red.Remaining
packages/unguarded-tests.json: 190 -> 189 files.