Skip to content

feat(engine): run state machines and actions on the clock (advance), with a clocked hero scene - #1777

Merged
HuiJun merged 4 commits into
developfrom
feat/engine-state-advance
Oct 30, 2026
Merged

HuiJun merged 4 commits into
developfrom
feat/engine-state-advance

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

What and why

ExecuteStateRequest.advance and ExecuteActionRequest.advance are in the proto and honoured by sysml-grpc, but the in-browser engine's JSON mirrors (JExecuteStateRequest, JExecuteActionRequest) lacked the field, so sysml-engine always ran a machine to quiescence and an action to completion. The Node client already sends advance; only the engine ignored it. The homepage hero needed a run that stops mid-way to show a clocked state machine, which is what advance is for.

Engine

  • The advance-aware runs move out of the gRPC package into the runtime, beside StateOutcomePerformedBy: (*Context).StateOutcomeAdvancedBy, ActionOutcomeAdvancedBy and ExecuteActionAdvancedBy (with runtime.ErrNotCompleted, formerly the grpc package's). run_options.go delegates to them, so the service and the engine share one implementation and the two request mirrors cannot drift on this field.
  • JExecuteStateRequest.advance and JExecuteActionRequest.advance are read by the engine with the service's validation (finite, non-negative, otherwise InvalidArgument).
  • seed, draws and clock_step stay out of the engine's mirrors: they did not fall out of this change (the engine serves no exploring schedule and has no draw policy plumbing), so they are left for their own change.
  • Nothing in the response was added: the service does not report the next clock due time, so the page does not show one either rather than computing it.

Hero (editors/vscode/src/landing, overrides/home.html)

  • A third tab, "Mission phases on the clock" (scene phases), renders #state:MissionSequence::MissionPhases from examples/runtime-showcase/mission-sequence.sysml. Run calls ExecuteState on the engine with performerSymbolId: MissionSequence::mission, advance: 400000, trace: true, then replays the trace the engine recorded: each state lit as its entry record comes, the token travelling each fired transition, the status line reading the clock from the records (t = 9,000 s · transition: prelaunch -> translunarCoast (event: time)), ending with the engine's answer: the states visited, finalTime, and whether the last state visited is the machine's final state.
  • The variant toggle "Advance to 800,000 s" runs the same program with advance: 800000: the path continues through transEarthCoast and recovered to done, which the trace puts at t = 702,000 s.
  • LandingModel gains a machine shape for a state view (LandingMachine: the states by name, lifted out of the state def box; LandingState keeps kind and initial) beside the part-shaped composition, so a state is not bent into a part. TraceRecord carries the record's time; JourneyRun the run's finalTime. SceneProgram gains {kind: "clock", symbol, performer, advance} and a SceneVariant may carry its own program (programOf); the shell exposes program and summarizes a machine as "7 states, 7 transitions".
  • The Debug panel is reused: each record line is prefixed with its clock instant; for a clocked scene the Send bar, the seed controls and the "Waiting events" line are hidden (data-osml-debug-events), and a scene-specific hint shows. The .is-live lighting now also applies to a machine's state boxes (osml-state).
  • Snapshot editors/vscode/src/landing/phases.json is pinned by tests/wasm/landing_phases_test.go (TestLandingMissionPhasesModel), which also checks the view's states and transition labels, the mission's instantiation, and the two clocked runs the scene offers.
  • Caption, editor hint and changelog fragment added.

Landing order with #1778 (feat/execute-action-trace-events, which adds trace/events to ExecuteAction): this PR lands first. A dry merge of #1778's head onto this branch conflicts in internal/exec/runtime/context.go, internal/frontend/engine/{engine,json}.go and internal/frontend/grpc/{run_options,service}.go; the resolution is mechanical and was built and tested locally (tests/wasm, tests/grpc, internal/frontend/grpc green on the merged tree): thread #1778's events []runtime.Message through ExecuteActionAdvancedBy/ActionOutcomeAdvancedBy/startActionAdvancedBy (calling DeliverEvents before Advance), keep both new JSON fields, and have runAction pick ExecuteActionWithEvents or ExecuteActionAdvancedBy by advance. #1778 should merge develop after this lands and apply that resolution; neither branch is rebased.

How it was verified

  • tests/wasm/engine_advance_test.go: MissionSequence::MissionPhases performed by MissionSequence::mission with advance: 400000 answers statesVisited prelaunch, translunarCoast, lunarOrbit, onSurface, finalTime 400000 and transition records at t=9000, 279000, 379000; advance: 800000 reaches recovered then done at t=702000; without advance the machine runs through its clock to done at t=702000; every answer is byte-equal to the service's. An action run for 2 s stops short of its wait at t=5 and says so, run for 10 s completes; advance: -1 is refused as InvalidArgument on both methods.
  • tests/wasm/landing_phases_test.go pins the scene's snapshot and the two clocked runs; editors/vscode unit tests cover machineModel, stateIdOf, runClock (performer/advance/trace on the wire, finalTime kept, a failed run's partial trace), programOf and the three-scene chooser.
  • The existing gRPC conformance fixture execute_state_exhibited_performance_performer ("advance": 20) and the internal/frontend/grpc run-option tests stay green through the refactor.
  • CLI cross-check: ./bin/sysml -quiet -instantiate MissionSequence::mission -state MissionSequence::mission -advance 400000 examples/runtime-showcase/mission-sequence.sysml ends in onSurface waiting on t=457000; -advance 800000 reports "State machine completed"; the engine answers the same states and instants.
  • Local gates: go build ./..., go vet ./..., gofmt -l . (empty), go test ./... all packages ok (four heavy packages — cmd/sysml, internal/exec/runtime, internal/exec/smt, tests/codegen — hit the 10-minute timeout only while the browser run and the Node tests shared the machine, and passed re-run alone); staticcheck clean on the touched packages; cd editors/vscode && npm run build && npm run typecheck && npm test (449 pass, 0 fail); cd client/node && rm build/wasm/sysml-wasm.wasm && npm run build && npm test (471 pass, 0 fail, 1 skipped as on develop).
  • Browser-verified on the built MkDocs site against the real wasm module (cache disabled): first paint from the snapshot (8 boxes, 7 transitions); Run ends with ExecuteState ran MissionPhases on mission for 400,000 s in 31 ms: prelaunch → translunarCoast → lunarOrbit → onSurface. The clock stopped at t = 400,000 s with the machine still in onSurface.; with the variant on, … → transEarthCoast → recovered → done. The clock stopped at t = 800,000 s; the machine completed when a transition reached done at t = 702,000 s.; the Debug panel steps the records with their instants; editing after 9000 [s] to 5000 redraws and the first transition reads t = 5,000 s; the stack and mass scenes still run; Download PNG saves mission-phases.png; no console errors beyond the theme's two api.github.com 403s.
400,000 s — stops in onSurface 800,000 s — reaches done
400,000 s end state 800,000 s end state
First paint from the snapshot Debug panel with clock instants
snapshot debugger

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) — the hero's caption and hints are the documentation of the scene; the wire contract already documents advance
  • 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 gate count moved
  • No internal work-item labels (waves, slices, F4, K5) in the body, docs, or changelog

Link to Devin session: https://nasa-jpl-demo.devinenterprise.com/sessions/5da68ef994984c60bf6057a840223710
Open in Devin Desktop: https://nasa-jpl-demo.devinenterprise.com/desktop/session/5da68ef994984c60bf6057a840223710?variant=devin
Requested by: @HuiJun

Move the advance-aware state and action runs from the gRPC service into
the runtime (StateOutcomeAdvancedBy, ActionOutcomeAdvancedBy,
ExecuteActionAdvancedBy) so sysml-grpc and sysml-engine share one
implementation, and read advance from the engine's JSON request mirrors
with the service's validation.

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

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

devin-ai-integration Bot and others added 2 commits October 30, 2026 18:06
The hero gains a "Mission phases on the clock" tab that renders
#state:MissionSequence::MissionPhases and runs it on the engine with
advance 400000 (or 800000 through a variant), replaying the recorded
trace with each record's clock instant. LandingModel gains a machine
shape for state views beside the part-shaped composition, and a Go test
pins the scene's snapshot.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
… the clocked scene

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@devin-ai-integration
devin-ai-integration Bot marked this pull request as ready for review October 30, 2026 18:35
devin-ai-integration[bot]

This comment was marked as resolved.

…gger when a variant changes the program

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@HuiJun
HuiJun merged commit 1e34d9d into develop Oct 30, 2026
30 checks passed
@HuiJun
HuiJun deleted the feat/engine-state-advance branch October 30, 2026 19:48
devin-ai-integration Bot added a commit that referenced this pull request Oct 30, 2026
…at they are equal

Co-Authored-By: jason.han <hanhuijun@gmail.com>
HuiJun added a commit that referenced this pull request Oct 31, 2026
* docs(perf): account for every merge up to develop cf16528 in the 0.12.0 record

The export rows are re-probed a second time at d01e4f4 (#1774: bytes
and allocations equal to 5e0322e's on every row), the runtime, gRPC
and REPL rows re-run six interleaved rounds at 1e34d9d against
cc1d477 (#1777: nothing moved, bytes and allocations equal, the
tests/perf binary's peak resident set within spread), the whole-binary
element-ID probe repeated, and the candidate named as develop cf16528
with the SHA each row set was measured at.

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

* docs(perf): say what InstantiateWarmModel's #1777 figures are, not that they are equal

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

* docs(perf): re-check the load rows at develop 9a4ffcd and name #1784's cost

#1784 (Usage::mayTimeVary over the effective isComposite) is on the
semantics path every load runs: six interleaved rounds against
cc1d477 show the load rows +1.5 % bytes and +4.7–4.8 % allocations per
element loaded (ImportValues/nested up to +13 % allocations), the
runtime, instantiation and parser rows unchanged, sec/op unresolved; the
record names the merge as the cause, not fixed here, and the candidate
as develop 9a4ffcd.

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

* docs(perf): re-probe the export rows at develop e022cbc after #1783

The RDF export fix touches internal/translate/export only, so the
record re-runs the export rows (WriteAPIJSON, ConvertAPIJSON/32 and
/128, the element-ID query, the Turtle export) six interleaved rounds
at e022cbc against d01e4f4, names develop e022cbc as the
candidate and keeps the #1784 load findings as reported.

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>
richetl pushed a commit to richetl/OpenSysML that referenced this pull request Nov 3, 2026
….0 record

The export rows are re-probed a second time at d01e4f4 (Open-MBEE#1774: bytes
and allocations equal to 5e0322e's on every row), the runtime, gRPC
and REPL rows re-run six interleaved rounds at 1e34d9d against
cc1d477 (Open-MBEE#1777: nothing moved, bytes and allocations equal, the
tests/perf binary's peak resident set within spread), the whole-binary
element-ID probe repeated, and the candidate named as develop cf16528
with the SHA each row set was measured at.

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