Skip to content

feat(runtime): execute terminate in actions - #335

Merged
HuiJun merged 30 commits into
developfrom
feature/terminate-action-execution
Sep 17, 2026
Merged

HuiJun merged 30 commits into
developfrom
feature/terminate-action-execution

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

What and why

terminate written in an action had two silent no-op paths: action stop terminate; lost its keyword at parse time and ran as an empty action, and lower.lowerBody dropped a terminate; / terminate c1; written in a nested action node's body. This PR makes terminate execute in actions.

Actions only. A terminate in a state's entry/do/exit body, a transition to a terminate action usage (accept Abort then stop; action stop terminate;), and terminate <expr> naming an occurrence instance (terminate this;) are not part of this change; they keep typed refusals ('terminate' in a body is not executable, ErrTerminateOccurrence) and are marked ❌ in docs/project/spec-compliance.md.

Parser, AST, lowering, export

  • ast.Usage.IsTerminate marks a terminate action usage; the parser records it instead of dropping the keyword, and no longer reads a keyword after an action node's name as an action reference. Dumped in ast/dump.go, carried by the AST codec (snapshot format bumped to 17 so a pre-change blob is refused as stale; embedded stdlib snapshot regenerated). The keyword is accepted on action usages only; part p terminate; is diagnosed.
  • lower.lowerBody includes *ast.TerminateStatement; a terminate action usage lowers to a one-statement body. lower.Effect carries the resolved target so the executor re-resolves nothing:
    type Effect struct {
        Kind       EffectKind
        Node       ast.Node
        Scope      *symbols.Scope
        Terminates TerminateTarget // Containing | Enclosing | Node | Occurrence | Unknown
        Target     ast.Node        // the action node, for TerminateNode
        TargetExpr ast.Node
    }
    TerminateContaining is a bare terminate;; TerminateEnclosing is a terminate action usage (it ends the flow it is a step of, not its own one-statement performance); TerminateNode names an action node; TerminateOccurrence is a feature or expression that is not an action node.
  • RDF mapping: a terminate action usage is written as sysml:TerminateActionUsage and read back as action <name> terminate;.

Executor (internal/core/runtime/action_terminate.go)

  • actionStmtHost.effect dispatches EffectTerminate to performances.terminate, which resolves the target performance (terminateTarget: the body's own frame, its parent, or the named node's frame found as f.node or in f.subactions walking up the frame chain) and either unwinds the body with a typed *terminated{perf} error when the body runs within the target, or ends the target in place (endOther) when it is a sibling flow.
  • stepToken catches the unwinding (endTerminatedFor) at the token whose step is the outermost within the target and ends it (endAround): every other token inFlowOf the performance is dropped lowest ID first (dropTokensIn) — paused bodyRuns ended, nested frames marked ended — and recorded as terminate <perf>: dropped token N@node, ...; the surviving token leaves the node (leaveTerminated) through endPerformance + completeNode, so the parent takes the node's succession with the outputs assigned so far and no inner succession fires. The root ends with StateCompleted and endPerformanceLife.
  • A statement-driven nested node (performNode) and a body-driven flow (usageWork.perform, runSubflow) treat a terminate of their own performance as completion.
  • New sentinels: ErrTerminateTarget (name is no action node of a flow around the statement, or nothing runs in it), ErrPerformanceEnded (target already ended), ErrTerminateOccurrence (the out-of-scope occurrence target). Calculations still refuse with ErrCalcSideEffect; an unsequenced terminate among an action's members is still ErrStatementOutsideFlow.

Docs

  • docs/project/spec-compliance.md: Actions map gains rows for the flow node, the named terminate action usage, the nil-target body statement, the named-node body statement, occurrence targets (❌) and state positions (❌); the Track E paragraph drops the action clause only.
  • docs/guide/06-behavior.md: a terminate paragraph with the action-only scope.
  • changes/unreleased/terminate-action-execution.added.md.

Known limitations

  • The training example 19. Terminate Actions/Terminate Actions Example-1.sysml still stops at no initial node found in action node performCriticalActivity: its body has no first/start. That is the flow-start rule, unchanged here; terminate is proven on fixtures that declare a start.
  • terminate <expr> on an occurrence, and every state-machine position, are typed refusals pending the state-machine half.

Specification basis

SysML v2 §7.17.10 (terminate action usages): a terminate with no target ends the immediately containing performance; a named target ends that action node's ongoing performance; the parent continues along the node's succession.

How it was verified

  • go build ./..., go vet ./..., gofmt -l . (empty), go test ./..., go test -race ./..., make lint: all clean.
  • go test -run TestGolden ./internal/core/parser — new fixture action_terminate.sysml (then terminate;, action stop terminate;, then action a2 terminate;, terminate c1; and terminate; in a body); no other golden moved. TestNegative gains part_terminate, attribute_terminate, state_terminate.
  • go test -run TestStdlibConformance ./internal/core/libs.
  • go test -run 'TestExecutionConformance|TestExecutionTrace' ./internal/core/runtime — nine new fixtures under testdata/conformance/action_terminate_*: flow node, nested body, named usage, named usage reached over two successions that synchronize (trace golden), fork dropping a sibling (trace golden), nested fork (trace golden), naming the node from its own body, naming the enclosing node, naming a sibling flow's node (trace golden).
  • go test -run TestRuntimeRobustness ./internal/core/runtimeterminate_of_an_ended_performance, terminate_of_an_unknown_name, terminate_of_a_non_action_feature, terminate_of_an_occurrence_expression, terminate_of_a_node_of_a_sibling_flow; calc_terminate_is_rejected unchanged.
  • OPENSYSML_REQUIRE_TRAINING_CORPUS=1 OPENSYSML_REQUIRE_PILOT_CORPORA=1 go test -count=1 ./internal/core/model -run 'TestTrainingExamples|TestPilotCorpora' — ok; training_examples_expected.txt unchanged.
  • OPENSYSML_REQUIRE_TRAINING_CORPUS=1 OPENSYSML_REQUIRE_PILOT_CORPORA=1 go test -count=1 ./internal/core/export -run TestCorpusRoundTrip — ok, no file moved.
  • make docs-counts, make docs-check, python3 scripts/changelog.py check — ok.

Checklist

  • make test and make lint pass locally
  • Tests added or updated for the change
  • Documentation extended where it already covers the surface (see CONTRIBUTING.md)
  • Changelog entry added as changes/unreleased/<slug>.<section>.md, not as an edit to CHANGELOG.md
  • baselines regenerated and make docs-counts run if a gate count moved (compliance rows need nothing: the census is counted at docs build)
  • No internal work-item labels (waves, slices, F4, K5) in the body, docs, or changelog

…dy terminates

`action stop terminate;` declares a terminate action usage: the parser
records it as `Usage.IsTerminate` instead of dropping the keyword, and no
longer reads a keyword after an action node's name as an action reference.
`lowerBody` includes `terminate` statements of a nested node's body, and
`lower.Effect` carries the resolved target: the containing performance for a
bare `terminate;`, the action node a name reaches, or the occurrence or unknown
name it stands for otherwise. The RDF mapping writes the usage as
TerminateActionUsage and reads it back.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

devin-ai-integration Bot and others added 2 commits September 16, 2026 02:33
A terminate node, a terminate action usage reached by a succession, and a
terminate statement of a nested action node's body end the performance they
are written in: later nodes do not run, every other token of that performance
is dropped in a recorded order, and a nested node's parent continues along the
node's succession with the outputs assigned so far. A named target ends the
ongoing performance of that action node of the flow around it. An ended
performance, a name that is no action node, and an occurrence target are typed
errors; calculations and state bodies refuse terminate as before.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
@devin-ai-integration
devin-ai-integration Bot marked this pull request as ready for review September 16, 2026 03:13
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 2 commits September 16, 2026 03:18
…action-execution

Co-Authored-By: jason.han <hanhuijun@gmail.com>

# Conflicts:
#	README.md
… a terminate

A terminate node several successions reach performs as the token their arrivals
collapse into; the step now takes that token's identity before it runs, so the
terminate unwinding out of it finds the token and ends the performance.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 4 commits September 16, 2026 03:44
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…s only

Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 3 commits September 16, 2026 04:33
…reading RDF

A declared usage and the terminate statement share the TerminateActionUsage
metaclass; the decoder wrote both as the statement, so a named usage came
back as a bare terminate; and its successions lost their target. A declared
usage states sysx:hasBody, a statement never does, so that tells them apart.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…action-execution

Co-Authored-By: jason.han <hanhuijun@gmail.com>

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
…action-execution

Co-Authored-By: jason.han <hanhuijun@gmail.com>

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 2 commits September 16, 2026 13:45
A repeated `terminate`, a typing, a modifier, a value or any other clause
written after the marker is diagnosed and skipped up to the body, where
the grammar's TerminateNode admits nothing but a body after the keyword.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
A merge can bring a second token to a node whose earlier performance is
still paused, so the frame the parent's subactions map holds is only the
latest one. Named termination now collects every performance of the node
from the tokens that run in it or hold it paused, ends them earliest
first, and ends a paused leaf performance through the token that holds
exactly that performance rather than the first token found at the node.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 2 commits September 16, 2026 13:59
…t names

A qualified name that reaches no action node of an enclosing flow is
resolved as a feature path like a simple name is, so an occurrence it
names is the occurrence refusal rather than an unknown target.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…action-execution

Co-Authored-By: jason.han <hanhuijun@gmail.com>

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
devin-ai-integration[bot]

This comment was marked as resolved.

… body

A terminate unwinding out of a flow that a node of an if branch or a loop
body owns left that flow's tokens (a forked waiter) in the executor, so the
action deadlocked once the terminated node's parent had completed. Both
sites that end a node by its body unwinding now drop what still runs in
the performance before completing the node.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

… a node names of itself

A terminate action usage's body may declare pins, which its lowering skipped,
so a flow into one was refused as undeclared. A `terminate <node>;` run inside
a performance of that node ended only that performance; it now ends every
ongoing performance of the node, the earliest begun first, as one run beside
them does.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

…nwinding

A terminate naming several ongoing performances of a node, run within one of
them, ended the later ones in place before unwinding out of its own, so their
successions and trace records came first. The unwinding now carries the
performances named after its own, which end once it has completed.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

…tion

The body of a terminate action usage was replaced by the terminate alone,
so statements written in it were dropped without report. They now lower as
a leaf node's and run before the terminate; a body stating a flow of its
own is refused as an invalid action flow at initialize.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 4 commits September 16, 2026 15:35
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ut sysx:hasBody

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…action-execution

Co-Authored-By: jason.han <hanhuijun@gmail.com>

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
…action-execution

Co-Authored-By: jason.han <hanhuijun@gmail.com>

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
#	internal/core/libs/stdlib.snapshot
devin-ai-integration[bot]

This comment was marked as resolved.

…ds the enclosing action

A `terminate;` (or `terminate stop;`) stated in the body of `action stop terminate`
ended only the usage's own performance, so the action it is a step of went on past it.
The unwinding terminate now goes on to the terminate the usage stands for, so the action
or node holding the usage ends as it does when the body runs through.

Restores the action terminate rows of spec-compliance.md that the merge from develop
dropped.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

A block node performed from a body statement that paused inside it is held
only by that body's paused frame; the node's parent keeps just the latest
performance and the token holds no frame of it. `terminate <node>` now finds
every such performance through the paused bodies of the tokens, ends it where
it stood (abandoning what paused inside it) and lets the body go on past the
node as completed; a snapshot captures those performances too.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 2 commits September 16, 2026 19:58
A leaf action node's body — a terminate action usage's included — lowered
only its send, assign, loop, if and terminate statements, so a performed
action written among them was dropped and the statements after it read
outputs that never came back. lowerBody now lowers the same members
BodyStatementMembers recognizes, performed actions included.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…action-execution

Co-Authored-By: jason.han <hanhuijun@gmail.com>

# Conflicts:
#	README.md
#	docs/project/spec-compliance.md
devin-ai-integration[bot]

This comment was marked as resolved.

…action-execution

Co-Authored-By: jason.han <hanhuijun@gmail.com>

# Conflicts:
#	internal/core/libs/snapshot_test.go
#	internal/core/libs/stdlib.snapshot
devin-ai-integration[bot]

This comment was marked as resolved.

…rformance around it

A terminate action usage ending its containing action or node returned the unwinding
before its own performance was completed, so the out pins its body assigned never
returned to enclosing features or reached their bindings. Both the flow-node and the
block-statement paths now end the usage's performance first, exactly once, then go on
to the enclosing termination.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@HuiJun
HuiJun merged commit a6b524d into develop Sep 17, 2026
12 checks passed
@HuiJun
HuiJun deleted the feature/terminate-action-execution branch September 17, 2026 01:56
HuiJun added a commit that referenced this pull request Sep 17, 2026
…fresh

* docs(roadmap): record E1, the fUML referee and the state-executor findings as landed on develop

The develop head is fcfb0a7 (#352), 64 pull requests past v0.8.0. E1 landed in
two halves (#335, #352) and the PSSM referee reads 46 pass / 17 fail, every
failure attributed and nine of them waiting on the region-order design
record (#342). The fUML referee for actions is complete (#319, #321, #334;
15 pass / 0 fail / 36 not-expressible / 4 differs-by-design), modeled
randomness sits beside A3 (#344), the MSI runs a setup wizard (#350), the
errata overlay covers the bundled library (#304), and the build-time test
figures are re-quoted at the head. The release paragraph names the open
release/0.8.1 patch cut from v0.8.0 and keeps the next cut from develop a
minor bump.

Co-Authored-By: jason.han <hanhuijun@gmail.com>

* docs(roadmap): count the PSSM failures the region-order choice point releases as eleven, Transition 017 apart

Co-Authored-By: jason.han <hanhuijun@gmail.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: jason.han <hanhuijun@gmail.com>
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