Repository navigation
Conversation
An atomic state targeting itself with internal=True ran its entry actions without its exit actions on StateChart. With enable_self_transition_entries=False (the StateMachine default), an internal transition to a descendant never entered the target, and an internal self-transition on a compound state exited it without re-entering it, leaving the configuration without the parent. The entry set now skips a target only when it is the transition domain, the one state the transition never exits. Document internal transitions on compound states and targetless transitions. Refs #692 Signed-off-by: Fernando Macedo <fgmacedo@gmail.com>
With internal transitions fixed, the attribute no longer changes any outcome: an external self-transition always runs exit and entry actions, and an internal one is decided by the source kind. Keep the attribute for compatibility, warn when a subclass sets it, and drop it from StateMachine and from the docs comparison table. Refs #692 Signed-off-by: Fernando Macedo <fgmacedo@gmail.com>
A nested atomic state targeting itself with internal=True re-entered every ancestor, and inside a parallel state it also re-entered the sibling regions, activating two states of the same region. Skip the entry set of a transition whose domain is its own target. Refs #692 Signed-off-by: Fernando Macedo <fgmacedo@gmail.com>
A multi-target transition whose first target is its atomic source was taken as an atomic internal self-transition, dropping the other targets; the no-op domain now requires a single target. The SCXML reader ignores type="internal" on an atomic source, which SCXML treats as external. Document which internal transitions exit their source. Refs #692 Signed-off-by: Fernando Macedo <fgmacedo@gmail.com>
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #696 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 53 53
Lines 5897 5903 +6
Branches 940 941 +1
=========================================
+ Hits 5897 5903 +6
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This was referenced Oct 15, 2026
This branch has not been deployed
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.



Why
Internal transitions (
internal=True) produced wrong traces or corrupt configurations onmain(bugs B3, B4, B7 of the UML/PSSM plan, Refs #692):StateChart,shopping.to.itself(internal=True)ranon_enter_shoppingwithouton_exit_shopping.StateMachine(enable_self_transition_entries=False),open.to(open.review, internal=True)left the configuration as['open'](target never entered, the next event raisedTransitionNotAllowed), andopen.to.itself(internal=True)exitedopenwithout re-entering it, leaving['draft']without its parent.False(default)" while theStateChartdefault isTrue, anddocs/transitions.mdsaidinternal=Truenever runs exit/entry.What changes
internal=True, targeting itself: no exit, no entry, only the effect, onStateChartandStateMachine, at any nesting depth (ancestors and sibling regions are left untouched).internal=True, targeting a descendant: the source stays active, the active child exits, the target and its default descendants enter, on both classes.internal=True, and a parallel source targeting a descendant: exited and re-entered (SCXML, W3C test533 unchanged). Docs point to a targetlessto(None)for an effect without leaving a compound state.type="internal"on an atomic source as external, as SCXML 3.13 specifies (onexitandonentryrun).enable_self_transition_entriesno longer changes any outcome: it is deprecated, setting it on a subclass emits aDeprecationWarningat class creation, andStateMachineno longer overrides it (maintainer decision).docs/transitions.md,docs/behaviour.md,docs/diagram.md, the upgrade guide and theTransitiondocstring describe the behavior above.Evidence
New regression tests run on both engines (
sm_runner) and both classes. Against themainengine and SCXML reader, with this branch's tests and docs:On this branch:
ruff check,ruff format --check, mypy and pyright pass through the pre-commit hooks on every commit. The W3C test files are untouched.Adversarial review by fresh subagents with no shared context, three rounds:
StateChart), and an internal transition to the source's own empty history state dropping the source. Fixed the first with a test; the second is pre-existing and belongs to the history work (below).type="internal"deviation on atomic sources, and doc sentences claiming the source is never exited. All fixed with tests; the SCXML mapping was decided by the maintainer.Not verified
contrib/diagram/extract.py) still treat everyinternal=Truetransition as an in-state action, soopen.to(open.escalated, internal=True)andopen.to.itself(internal=True)draw no edge. Pre-existing onmain, not changed here; a follow-up should mark as internal only an atomic single-target self-transition.HistoryStatewith no recorded history and no default transition leaves the configuration inconsistent (maindrops the source too). Left to the history work.docs/releases/upgrade_2x_to_3.mdstill says 2.x self-transitions skipped entry/exit; not checked against 2.x.