Skip to content

Expand SCXML parallel conformance - #37

Merged
JovaniPink merged 1 commit into
masterfrom
scxml-assign-more-parallel-tests
Aug 18, 2026
Merged

Expand SCXML parallel conformance#37
JovaniPink merged 1 commit into
masterfrom
scxml-assign-more-parallel-tests

Conversation

@JovaniPink

@JovaniPink JovaniPink commented Aug 17, 2026

Copy link
Copy Markdown
Owner

Summary

  • Support the fixture-proven SCXML integer-data subset: non-negative literal initialization, same-variable + 1 assignment, and standalone strict integer equality guards.
  • Correct atomic self-transition handling so one active parallel branch exits and re-enters without cycling the parallel parent or sibling branches.
  • Enable upstream more-parallel/test10 and test10b, retaining byte-identical Apache-2.0 fixtures under the repository's existing provenance boundary.
  • Document the expanded but deliberately narrow import surface and update the configured SCXML counts to 56 cases, including all 15 more-parallel cases.

Why

more-parallel/test10 and test10b were intentionally disabled because the importer did not initialize SCXML data, convert <assign>, or evaluate their integer guards. Once that data flow was present, the fixtures exposed a second issue: the transition-domain calculation treated an atomic self-transition as cycling the containing parallel state, which executed parent and sibling exit/entry behavior and produced the wrong counter sequence.

The change implements only the syntax and transition behavior evidenced by these two fixtures. It does not add a JavaScript evaluator or claim broader SCXML datamodel support.

User and developer impact

  • The two previously excluded parallel conformance cases now load and execute through the public Machine.transition(...) path.
  • Imported charts can use the narrowly documented integer initialization, increment, and equality forms.
  • Unsupported expressions fail during import with InvalidConfigError instead of being ignored or evaluated.
  • There are no new runtime dependencies and no change to the public Machine(config, ...) boundary.

Implementation

Fail-closed SCXML expression subset

  • Accept one top-level datamodel="ecmascript" block containing simple identifiers initialized from non-negative integer literals.
  • Accept <assign location="x" expr="x + 1"/> only when the expression increments that same declared location by exactly one.
  • Accept one standalone declared-variable comparison such as x === 2; the existing Boolean-only parser remains unchanged.
  • Reject undeclared variables, property/index locations, alternate arithmetic, comparison composition, unsupported datamodels, and non-integer assignment state.
  • Convert raise and assign executable content in document order.

Atomic self-transition boundary

  • Detect an atomic transition whose effective target is its source.
  • Exit and re-enter only that atomic source, preserving the containing parallel state and sibling configuration.
  • Cover the native engine behavior independently from the SCXML fixtures so the transition-domain rule cannot regress invisibly.

Fixture provenance

  • Vendor test10.scxml, test10.json, test10b.scxml, and test10b.json from SCXML Test Framework commit b46a10a1c3a3b1ca5c5cb4bb44ddb5c785611f41.
  • Confirm all four files are byte-identical to that upstream commit.
  • Keep the existing Apache-2.0 license and tests/fixtures/scxml/PROVENANCE.md ownership boundary.

Scope

  • SCXML XML conversion and its focused expression validation.
  • Atomic self-transition entry/exit behavior.
  • Two vendored fixture pairs, focused regression coverage, inventory enablement, and current documentation.

Non-scope

  • General ECMAScript or Python expression evaluation.
  • Property access, function calls, event access, arbitrary arithmetic, or mutable object paths.
  • Broader SCXML datamodel or executable-content conformance.
  • Dependency, package-version, release, deployment, or publication changes.
  • Complete W3C SCXML conformance claims.

Evidence and validation

Local validation on commit 730f1396baf7ab3ac9082f82fb137e20d2475ddf:

  • Primary suite: 428 passed.
  • SCXML suite: 57 passed — 56 configured cases plus the fixture inventory/provenance guard.
  • Ruff formatting: passed across 68 files.
  • Ruff lint: passed.
  • MyPy: passed across 22 source files.
  • poetry check --lock: passed.
  • Source distribution and wheel build: passed.
  • Installed-wheel smoke validation: passed for xstate 0.7.0.
  • GitHub Actions: Python 3.13, Python 3.14, SCXML smoke, and code-quality checks passed on the exact PR head.

Risks and mitigations

  • Run-to-completion core: algorithm.py is correctness-critical, and the atomic self-transition rule affects native machines as well as imported SCXML. A dedicated native parallel regression test plus the complete primary and SCXML suites cover the entry/exit boundary.
  • Expression-surface creep: accepting more syntax could accidentally become an evaluator. Full-match regular expressions, declared-location checks, integer type checks, and explicit rejection cases keep the grammar closed.
  • Fixture drift: the inventory guard, pinned provenance commit, retained license, and byte comparison constrain the vendored source.
  • Documentation drift: README, product/comparison docs, concept documentation, packaging comments, and AGENTS.md are updated together with the enabled-case counts.

Diff-size justification

The PR changes 15 files with 530 additions and 58 deletions. Of those additions, 194 lines are unmodified upstream fixture bytes. The remaining size is concentrated in the fail-closed parser boundary, focused acceptance/rejection tests, one native transition regression, and documentation required to state the new support limits accurately. The runtime change remains localized to src/xstate/scxml.py and a 16-line transition-domain adjustment in src/xstate/algorithm.py.

Rollback

Revert commit 730f1396baf7ab3ac9082f82fb137e20d2475ddf. That removes the parser and transition-domain changes, disables the two cases, removes the four vendored fixture files, and restores the prior documented 54-case boundary. No data migration, dependency rollback, or external cleanup is required.

Review focus

  1. Confirm the accepted grammar is no broader than the two fixtures require.
  2. Verify unsupported expressions and locations fail at import time with clear InvalidConfigError messages.
  3. Confirm atomic self-transitions execute only source exit/entry actions while preserving the parallel parent and siblings.
  4. Verify executable-content ordering and context snapshot behavior remain run-to-completion safe.
  5. Confirm fixture bytes, provenance, license, and configured-suite counts are consistent.

Unresolved decisions

None required for this PR. Additional assignment forms, data types, nested locations, and broader executable content remain explicitly deferred to separately evidenced work.

Merge order and release boundary

This is a standalone PR against master with no prerequisite or follow-up merge ordering. It is intentionally draft for review and validation only; merging does not publish PyPI artifacts or deploy any runtime.

@JovaniPink
JovaniPink marked this pull request as ready for review August 17, 2026 23:19
@JovaniPink
JovaniPink merged commit 502da52 into master Aug 18, 2026
4 checks passed
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.

2 participants