SysML v2 Execution Environment — Architecture¶
Module: github.com/Open-MBEE/OpenSysML
Language: Go 1.23+
Overview¶
A complete, production-grade SysML v2 implementation delivering the integrated tooling experience systems engineers expect from modern language ecosystems (Python, Rust, Go).
Core Components¶
- Language Server (
sysml-lsp) — IDE support with live diagnostics, semantic hover, go-to-definition, intelligent completion, and workspace-wide symbol search - Interactive REPL (
sysml) — Exploratory modeling: define models incrementally, evaluate expressions, instantiate parts, inspect runtime state - Execution Runtime — Instantiate parts, evaluate constraints, execute calc/analysis cases, simulate behavioral models
- Toolchain — Workspace management, dependency resolution, incremental compilation, bundled stdlib, persistent caches
Design Principles¶
- Performance: Sub-millisecond parsing, single static binary, no JVM/Eclipse runtime
- Completeness: SysML v2 textual notation support (95/95 stdlib files parse clean: 94 vendored OMG files and 1 OpenSysML extension)
- Executable models: Not just validation—runtime that instantiates, evaluates, simulates
- Incremental & lazy: Parse immediately, resolve semantics on-demand (gopls/rust-analyzer precedent)
- Immutable AST: All semantic state lives in side tables keyed by node/symbol
Architecture Layers¶
┌─────────────────────────────────────────────────────────┐
│ Frontends: LSP Server │ Interactive REPL │
├─────────────────────────────────────────────────────────┤
│ Workspace: Multi-file projects, dependency management │
├─────────────────────────────────────────────────────────┤
│ Semantic Engine: Types, resolution, validation │
├─────────────────────────────────────────────────────────┤
│ Execution Runtime: Expressions, instances, behaviors │
├─────────────────────────────────────────────────────────┤
│ Parser/Lexer: Hand-written recursive descent │
├─────────────────────────────────────────────────────────┤
│ AST: Syntax-only, immutable (semantics in side tables) │
└─────────────────────────────────────────────────────────┘
Module Structure¶
github.com/Open-MBEE/OpenSysML
├── cmd/
│ ├── sysml-lsp/ # LSP server binary
│ ├── sysml-grpc/ # gRPC server binary
│ └── sysml/ # Interactive REPL binary
├── internal/core/
│ ├── source/ # Source files, spans, line indexing
│ ├── lexer/ # Hand-written scanner (~200 keywords)
│ ├── parser/ # Recursive-descent parser
│ ├── ast/ # Syntax tree nodes (immutable)
│ ├── symbols/ # Symbol tables, scope trees
│ ├── resolve/ # Name resolution (lazy, memoized)
│ ├── semantics/ # Type system, conformance, multiplicity
│ ├── passes/ # Validation passes (syntax → constraints)
│ ├── lower/ # AST → execution IR (ActionGraph/StateGraph)
│ ├── runtime/ # Execution engine (eval, instances, builtins)
│ ├── model/ # Workspace, document management
│ └── libs/ # Standard library bundling & caching
├── internal/lsp/ # LSP protocol implementation
├── internal/repl/ # REPL loop implementation
├── internal/grpc/ # gRPC service implementation
├── python/ # Python client bindings (opensysml)
├── api/proto/ # Protobuf service definitions
├── testdata/ # Test fixtures (.sysml, .kerml)
├── examples/ # Example models and demos
└── docs/ # Documentation
Core Pipeline¶
Static Analysis Path:
1. Source & Lexer (internal/core/source, internal/core/lexer)¶
- SourceFile: Input file (.sysml or .kerml) with byte content
- Lexer: Hand-written scanner producing tokens with full position tracking
- Trivia: Comments and whitespace tracked as leading/trailing trivia
- Keywords: ~200 SysML keywords (case-sensitive, pre-registered)
2. Parser (internal/core/parser)¶
- Hand-written recursive descent (chosen over ANTLR4/yacc/JNI bridge)
- Rationale: Zero overhead, full error recovery, sub-ms parses for keystroke-latency feedback
- Entry:
parser.New(source).ParseFile() → *ast.RootNamespace - Always produces tree: ErrorNodes on bad input, parsing never fails
- Grammar source: OMG pilot Xtext grammars (SysML.xtext + KerMLExpressions)
3. AST (internal/core/ast)¶
Key architectural rule: AST is syntax-only, immutable after parse
- Node interface:
{Span() source.Span; LeadingTrivia()/TrailingTrivia() []Trivia} - NodeBase: Embedded by all nodes
- No semantic info in AST: All derived data lives in side tables keyed by node/symbol
- Expression AST: Full SysML v2 expression grammar (literals, operators, feature refs, invocations, collections, lambdas)
- Behavioral AST: Action control-flow nodes (InitialNode, FinalNode, ForkNode, JoinNode, MergeNode, DecisionNode, ActionExecutionNode), succession edges with guards
4. Symbols & Resolution (internal/core/symbols, internal/core/resolve)¶
- Symbol:
{Name, Kind, Decl ast.Node, Visibility, Scope, OwnerScope} - Scope:
{Parent(), Node(), Children(), LookupLocal(name), MemberNames()} - Index:
DocumentRoot(name) *Scope— global qualified-name index - Resolver: Lazy name resolution, memoized,
ResolveQualified(scope, *ast.QualifiedName) (*Symbol, bool) - Deduplication: Short+primary names alias same
*Symbol— dedupe by pointer when walking
5. Semantic Model (internal/core/semantics)¶
Runtime's primary substrate. Built via NewModel(*resolve.Resolver). All results memoized in side tables.
model.go:DirectSupertypes(sym)— resolved generalization edges (specializes/subsets/redefines/typing)AllSupertypes(sym)— transitive, cycle-safeConforms(a, b) bool— conformance checkingHasSpecializationCycle(sym) boolmembers.go:MembersOf(sym)— local + inherited members with maskingLookupMember(sym, name)— member lookup- Effective feature list per type (substrate for runtime instantiation)
multiplicity.go:MultiplicityOf(sym) (Range, bool)— parse multiplicity boundsRange{Lower, Upper Bound};Bound{Value int64, Infinite bool, Known bool}eval.go:Eval(n ast.Node) (Value, bool)— constant-folder (seed of runtime)Value{Kind ValueKind, Int, Real, Bool}— int/real/bool/infinity only- Returns
ok=falsefor feature refs, strings, null, invocations, collections - Runtime Tier 3 extends this to full evaluator
6. Validation Passes (internal/core/passes)¶
Pluggable validation tiers:
- PassLevel:
{LevelSyntax, LevelNameResolution, LevelType, LevelConstraint} - Pass:
{Level() PassLevel; Run(ctx, name, root) []Diagnostic} - Context: Exposes
Resolver()+Model()(both lazy, memoized) - DefaultRegistry: SyntaxPass, NameResolutionPass, TypeCheckPass, ConstraintPass
- Tiered execution: Higher tiers skipped if lower tier errors
- Quick fixes: A
Diagnosticcarries thequickfix.Fixvalues (internal/core/quickfix) the layer reporting it attached, so an editor offers edits without parsing messages
6a. Highlighting (internal/core/highlight)¶
- Semantic tokens:
Tokens(content, root, scope, Resolution)— keywords, comments and literals from the lexer; declared names from the symbol table; reference segments from the resolver - Ordered and disjoint: the result is sorted by offset with overlaps dropped, semantics winning, so a consumer encodes it directly
- Vocabulary: LSP token types and modifiers (
Classes(),Modifiers()give legend order)
7. Workspace (internal/core/model)¶
- Single source of truth: Owns document set + global index + diagnostic cache
- Document:
{source, AST, scope, version} - One Workspace per session (LSP/REPL)
Execution Runtime Architecture¶
Package: internal/core/runtime
Not a Pass: Execution is stateful/iterative/value-producing (different shape than diagnostic-emitting pass)
Tier 1 — Feature Flattening ✅¶
Harden MembersOf into stable, ordered effective-feature list per type:
- Own + inherited − redefined/masked
- Each entry: type + multiplicity + default-value expression
- Schema for instance materialization
Tier 2 — Instance Model ✅¶
- Value: Extends
semantics.Value→null, strings, instance references, collections (sequences/sets) - Instance: Typed object with one feature value per effective feature (Tier 1)
- Instantiation: Materialize instance graph from
part/itemusage - Recursively instantiate composite features
- Multiplicity governs feature value cardinality
- Lazy feature value materialization
Tier 3 — Expression Evaluator ✅¶
Full evaluator with user-defined calc invocation, constraint evaluation, and requirement evaluation:
- Feature access x.y.z resolved against instance feature values
- KerML operator library (->select, ->collect, size, string ops)
- Calc invocation: Resolve calc symbol → extract params/return → bind args to parameters → evaluate return expression
- Constraint evaluation: Extract assert/assume members → evaluate boolean expressions → check satisfaction (with optional not negation)
- Requirement evaluation: Extract subject/assume/require/actor members → validate bindings → evaluate conditions
- Scoped evaluation: EvalContext.scope for name resolution, frame stack for parameter bindings
- Membership unwrapping: Runtime automatically unwraps AST Membership nodes when extracting members
- Unlocks: Constraint checking against concrete values, calc execution, requirement validation, runtime behavioral verification
Tier 4 — Behavioral AST ✅¶
Parse + model all behavioral bodies with unified fallback grammar:
- Calc bodies — return expressions + mixed parameter declarations (✅ fully executable)
- Constraint bodies — assert/assume with optional not negation (✅ fully executable)
- Requirement bodies — subject/assume/require/actor declarations (✅ fully executable)
- Action bodies — Control flow nodes (initial/final/fork/join/merge/decision) + action execution nodes + succession edges (✅ parsed, executor infrastructure complete)
- State bodies — Entry/do/exit behaviors, substates, transitions with triggers/guards/effects (✅ parsed, executor infrastructure complete)
- Unified Grammar: Body parsers use graceful fallback to general member grammar (no terminal keyword whitelists)
- Status: All parsers complete. Calc/constraint/requirement fully executable. Action/state executors complete with control flow keywords, nested invocation, send statement.
Tier 5 — Behavioral Interpreter ✅ Complete¶
Package: internal/core/runtime
Status: Complete. Conformance gate: every case passing (calc/constraint/requirement/satisfy/action/state all functional); count in the measured counts.
Spec Alignment: The governing reference is the SysML v2 metamodel or the bundled KerML semantic library (internal/core/libs/stdlib/); UML 2.5.1 is a fallback only where the SysML v2 notation has no production for a concept and the KerML library no performance for it (state-body fork/join, history, entry/exit points, regions). Token flow is succession-ordered: a succession is a KerML HappensBefore link (Occurrences.kerml), which orders occurrences in time and carries no values — a SuccessionFlow is the form that carries a payload (KerML.kerml: Succession specializes Connector, SuccessionFlow specializes Succession, Flow). State machine execution is Occurrences::Occurrence::isRunToCompletion over its runToCompletionScope ("determines whether transition performances might happen during state entry performances within the run to completion scope"), with event dispatch isDispatch / dispatchScope. See SPEC_COMPLIANCE.md for detailed compliance mapping (~98% faithful implementation).
Architecture:
- ActionExecutor — Petri-net token-flow execution
- Token-based control flow (initial → action → final, first/done keywords)
- Fork/Join for parallelism, Decision/Merge for branching
- Nested action invocation with attribute initialization
- Send statement for message passing
- ObjectFlow for pin-to-pin data routing
- Deadlock detection via progress tracking
- Golden trace recording with deterministic token ordering
-
APIs:
Step(),RunToCompletion(),Tokens(),SetBreakpoint(),SetTrace() -
StateExecutor — Event-driven state machine execution
- Initial/final state keywords (initial/final)
- Entry/exit/do behaviors (
doruns while its state is active, one action per round, interleaved with the do behaviors of the states active alongside it) - TimeEvent scheduling with priority queue
- ChangeEvent condition polling
- Guard evaluation for transitions
- Transition effect actions
- Hierarchical states with LCA-based entry/exit propagation
- Orthogonal regions with multi-region event broadcasting
- Choice + Junction pseudostates
- Golden trace recording for transitions/entry/exit
- APIs:
ProcessNextEvent(),CurrentState(),EventQueue(),StateData(),SetTrace() - Deferred events: an event no active transition handles is retained while a state deferring it is active, and delivered afterwards in arrival order
-
CallEvent matches the operation named by the trigger (
signal.go,state_executor.go;signal_test.go:TestCallEventMatchesOperationName) -
Context Integration — Public runtime APIs
InvokeCalc(symbol, args)— Invoke calculation with arguments, return resultEvaluateConstraint(symbol)— Evaluate constraint, return satisfaction boolean (assert/assume)EvaluateRequirement(symbol)— Evaluate requirement, return satisfaction boolean (require/subject/actor/assume/nested)ExecuteAction(symbol)— Run action to completion, return resultsExecuteState(symbol)— Run state machine until final/suspendedCreateActionExecutor(symbol)— Create executor for debuggingCreateStateExecutor(symbol)— Create executor for debugging
Implementation:
- context.go (460 lines) — Public Execute/Invoke/Evaluate APIs, step budget enforcement
- action_executor.go (729 lines) — Token-flow engine with nested actions, send statement
- state_executor.go (1149 lines) — Event-driven state machine with do behaviors
- executor_common.go — Token, Event, EventQueue, ExecutionState
- trace.go (154 lines) — Deterministic execution trace recorder
- eval.go — Expression evaluation (binary/unary operators, literals, feature references, qualified names, type coercion)
- Lowering to execution IR lives in internal/core/lower/ (ToActionGraph, ToStateGraph)
Testing:
- Golden ASTs: internal/core/parser/testdata/parse/ — count in the measured counts
- Negative tests: internal/core/parser/negative_test.go — count in the measured counts
- Unit tests: action_executor_test.go, state_executor_test.go (action, state)
- Conformance gate: .sysml + .expected.json pairs, all passing - conformance_test.go — counts and per-category breakdown in the measured counts
- Golden traces: .trace.golden files - trace_test.go — count in the measured counts
- Robustness: failure-mode cases (deadlock, unbound params, missing features, dangling transitions, sourceless accept, step budget, pseudostate dead ends and cycles, history and defer misuse, send/accept misrouting, calc arity/recursion, perform reference failures) - robustness_test.go
- Coverage: All behavioral types fully functional. Action: 14/14 features ✅. State: 13/13 features ✅. Calc: 8/8 ✅. Constraint: 5/5 ✅. Requirement: 5/5 ✅. Evaluation: 7/7 ✅.
Measured Compliance: See SPEC_COMPLIANCE.md for semantic rule → implementation → test case mapping with status (✅ faithful / ⚠️ approximate / ❌ not yet implemented).
Tier 6 — Analysis & Verification Drivers ⏳ (Future)¶
- Analysis case: subject → calc chain → result values
- Verification case: evaluate requirements → pass/fail
- Entry points: REPL/LSP commands (
%run,%verify)
LSP Server¶
Package: internal/lsp
Binary: cmd/sysml-lsp
Status: ✅ Complete (stdio protocol, 10 LSP features, tested end to end)
Features¶
Lifecycle:
- initialize — Advertise server capabilities, record the session's folders
- initialized — Scan those folders and index every .sysml/.kerml file they
hold, so cross-file names resolve without the editor opening each file
- shutdown / exit — Graceful termination
Document Synchronization:
- textDocument/didOpen — Track opened documents (the buffer becomes authoritative)
- textDocument/didChange — Incremental updates (UTF-8 byte offsets)
- textDocument/didClose — Revert to the file's on-disk content; the document
stays indexed, since other documents resolve names through it, but its markers
are withdrawn — only open documents carry diagnostics
- textDocument/didSave — Refresh diagnostics for every open document
- workspace/didChangeWatchedFiles — Reindex files created, edited or deleted
outside the editor; a deletion leaves an open buffer alone
- workspace/didChangeWorkspaceFolders — Walk a folder added mid-session and
unindex what a removed one contributed, open buffers aside
Diagnostics: - Publish on document open/change; the edited document immediately, the other open ones on a coalesced sweep once the edit burst settles, since each sweep re-analyzes them - Withdrawn (empty set) for a document the workspace no longer holds - Syntax errors (parser) - Semantic errors (name resolution, type checking, validation passes) - Real-time feedback
Hover (textDocument/hover): - Symbol info: name, kind, type, multiplicity - Definition source location - Documentation comments (future)
Go-to-Definition (textDocument/definition): - Navigate to symbol declaration - Follows qualified name chains - Cross-document navigation
Find References (textDocument/references): - Find all usages of symbol, in every workspace document, at whichever segment of a qualified name denotes it - Include declaration option, reported in the declaring document
Completion (textDocument/completion):
- Trigger characters: :, .
- Symbol-based suggestions
- Future: keyword completion, snippet support
Semantic Tokens (textDocument/semanticTokens/full, /range):
- Legend advertised at initialize; tokens classified by internal/core/highlight
- Keywords, comments and literals from the token stream; names from the symbol
table and the resolver, with declaration/definition/readonly/abstract modifiers
- Encoded relative to the previous token, split per line; no delta support
Code Actions (textDocument/codeAction):
- Quick fixes only, from the quickfix.Fix values parser and resolver
diagnostics carry — spelling of an unresolved name, importing the namespace
declaring it, inserting a semicolon the parser located exactly
Document Symbols (textDocument/documentSymbol): - Outline view (packages, parts, attributes, actions, states) - Hierarchical structure - Navigate within file
Workspace Symbols (workspace/symbol): - Global symbol search - Fuzzy matching - Aggregates across every indexed document, opened or not
Implementation¶
Architecture:
- server.go — Server lifecycle, stdio transport
- base.go — Stub handlers for unimplemented LSP methods
- handler.go — Custom didChange with pointer-valued Range (full vs incremental edits)
- sync.go — Document synchronization (didOpen/didChange/didClose/didSave)
- files.go — Folder scan and watched-file events (the on-disk half of the workspace)
- lifecycle.go — Initialize capabilities advertisement
- diagnostics.go — Error publishing
- hover.go, completion.go, definition.go, references.go, symbols.go — Feature implementations
- posmap.go — UTF-8 offset ↔ LSP line/character conversion
- semantictokens.go, codeaction.go — Semantic tokens and quick fixes
- walk.go — Reference lookup over resolve.References
Testing: - Tests covering every feature — count in the measured counts - Integration tests with mock clients - Incremental sync edge cases (astral plane characters, multi-change, offset-zero insertion)
Usage:
Editor Setup: - VS Code: Generic LSP Client extension + workspace settings - Neovim: nvim-lspconfig custom server - Emacs: lsp-mode manual server registration
See the guide for VS Code configuration.
REPL Integration¶
Package: internal/repl
Binary: cmd/sysml
Commands¶
Document management:
- %help — Show help
- %list — List current session declarations
- %clear — Reset session
- %load <file> — Load .sysml file
Runtime execution:
- %instantiate <name> — Create instance from part def
- %eval <expr> — Evaluate expression (feature refs + literals)
- %features <name> — Show an object's features and their values
- %instances — List all created instances
Behavioral execution:
- %calc <name> [args...] — Invoke calculation with literal arguments (e.g., %calc add 10 20)
- %constraint <name> — Evaluate constraint, check assert/assume satisfaction
- %requirement <name> — Evaluate requirement, validate subject/require/actor conditions
- %satisfy [name] — Evaluate satisfaction assertions, with the requirement's subject bound to the object by names
Action debugging:
- %action <name> [<object>] — Start debugging action execution, optionally performed by an instantiated object
- %step — Advance all tokens one step
- %continue — Run action to completion, or to the first breakpoint hit
- %tokens — Show active tokens with location + data
- %break <nodeName> — Set breakpoint on node; %continue stops when a token reaches it
State machine debugging:
- %state <name> [<object>] — Start debugging state machine, optionally performed by an instantiated object
- %events — Show event queue length
- %current — Show current state, stack, stateData, time
- %advance <time> — Advance simulation time by <time> units, processing every event due
- %stop — Stop debugging session
Implementation¶
- Session: Manages document + runtime context + instances + debugging sessions
- getOrCreateRuntime(): Lazy init, builds index from current document
- Runtime commands wire to:
runtime.Context.Instantiate(),runtime.Context.Eval(),runtime.Context.InvokeCalc()runtime.Context.EvaluateConstraint(),runtime.Context.EvaluateRequirement()runtime.Context.ExecuteAction(),runtime.Context.ExecuteState()runtime.Context.CreateActionExecutor(),runtime.Context.CreateStateExecutor()- Argument parsing:
%calcparses literal args via wrapper parsing (part { attribute arg = <expr>; }) + Membership unwrapping - Debugging sessions: Session tracks active ActionExecutor/StateExecutor for step-by-step control
Technology Choices¶
Why Go?¶
- Goroutines: Concurrent reindex/query handling
- Single binary: Cross-platform, no JVM/runtime dependencies
- LSP track record: gopls demonstrates Go's suitability for language servers
- Performance: Fast compilation, efficient memory model
Why Hand-Written Parser?¶
Alternatives rejected: ANTLR4-Go, goyacc, JNI/gRPC bridge to pilot
Rationale: - Zero runtime overhead - Full control over error recovery - Sub-millisecond parses (keystroke-latency diagnostics) - Trade-off accepted: Manual grammar translation from Xtext
Incremental & Lazy Analysis¶
Precedents: gopls, rust-analyzer
- Parse immediately (syntax errors visible instantly)
- Defer name resolution / type checking until requested
- Memoize all semantic queries
- Result: Interactive performance even on large workspaces
Development Status¶
| Component | Status |
|---|---|
| Lexer/Parser (structural + behavioral) | ✅ Operational (95/95 stdlib clean - see conformance gate) |
| Symbol resolution & type system | ✅ Complete |
| Validation passes (syntax → constraints) | ✅ Complete |
| Expression evaluator & instance model (Tiers 1-3) | ✅ Complete |
| Workspace/reindex/file watching | ✅ Complete |
| Behavioral parser (all behavioral bodies) | ✅ Complete |
| Calc invocation & constraint evaluation | ✅ Complete |
| Action execution engine (Tier 5) | ✅ Complete |
| State machine runtime (Tier 5) | ✅ Complete |
| REPL debugging commands | ✅ Complete |
| REPL implementation | ✅ Complete |
| Standard library bundling | ✅ Complete |
| LSP server implementation | ✅ Complete |
Parser coverage: 95/95 bundled library files parse cleanly — the 94 official SysML v2 standard library files and the non-normative OpenSysML Libraries/OpenSysMLMathFunctions.kerml extension. Conformance verified by stdlib_conformance_test.go. Grammar reference available at OMG Xtext grammar.
Testing Strategy¶
Parser Test Contract¶
New grammar features require a four-layer test contract to ensure correctness and prevent regressions:
1. Conformance Gate¶
- Purpose: Ensure stdlib continues to parse cleanly
- Location:
internal/core/libs/stdlib_conformance_test.go - Test:
TestStdlibConformanceloads all 95 bundled library files - Acceptance: 95/95 files parse without errors
- Allowlist:
testdata/stdlib_known_failures.txt(currently empty) - Failure mode: Regression breaks previously-working stdlib files
Usage:
2. Golden AST Snapshots¶
- Purpose: Verify AST structure matches expected output
- Location:
internal/core/parser/golden_test.go - Fixtures:
testdata/parse/*.sysmland*.kerml(one representative file per construct) - Goldens:
testdata/parse/*.golden(AST dumps) - Acceptance: Parse output matches golden file
- Update flag:
go test -run TestGolden -update(regenerate goldens after intentional changes)
Coverage: - Package/namespace declarations - Part/attribute definitions and usages - Connections and relationships - Requirements and constraints - State machines - Calculations - Enumerations - Imports and aliases - Metadata annotations
3. Round-Trip Serialization¶
- Status: Explicitly deferred (no faithful SysML printer exists)
- Rationale:
ast.Dump()is debug-only, not spec-compliant - Future work: If SysML printer added, verify
parse(print(parse(input))) == parse(input)
4. Negative Test Suite¶
- Purpose: Verify parser rejects malformed input gracefully
- Location:
internal/core/parser/negative_test.go - Test:
TestNegative, one subtest per malformed input - Acceptance: Each case produces diagnostics (doesn't panic)
- Coverage: Unclosed blocks, unexpected tokens, invalid syntax, incomplete behavioral members
Example:
Behavioral Test Contract¶
New behavioral features (actions, states, calc, constraints, requirements) require a four-layer test contract to ensure execution correctness:
1. Golden AST Fixtures¶
- Purpose: Lock in parse structure before execution changes
- Location:
internal/core/parser/testdata/parse/(behavioral fixtures) - Coverage: the behavioral fixtures (action, calc, constraint, requirement, state) among the whole set
- Acceptance:
TestGoldenpasses, AST dumps match expectations - Update flag:
go test -run TestGolden -update
Behavioral fixtures:
- action_control_flow.sysml, action_if_branch_body.sysml, action_mixed_params.sysml, action_send_port.sysml
- state.sysml, state_full.sysml, state_transition_variants.sysml, state_call_trigger.sysml, state_def_region_pseudostate.sysml, state_defer.sysml, state_fork_join.sysml, state_history_entry_exit.sysml, state_timed_triggers.sysml
- calc.sysml, calc_defaults_and_invocation.sysml, calc_return.sysml, calc_return_parameter.sysml
- constraint_assert_assume.sysml
- requirement.sysml, requirement_members.sysml
2. Execution Conformance Gate¶
- Purpose: Verify behavioral execution produces expected outcomes
- Location:
internal/core/runtime/conformance_test.go - Test:
TestExecutionConformanceruns.sysml+.expected.jsonpairs - Schema:
internal/core/runtime/testdata/conformance/README.md(outcome format for each behavioral type) - Allowlist:
known_failures.txt(currently empty — all cases pass) - Acceptance: Expected outputs/satisfaction match actual execution results
Coverage (by fixture prefix, all passing; counts in the measured counts):
- Calc: parameter binding, return values, defaults, inherited parameters, unary operators, type coercion, qualified names, body-local usages, statement bodies, nested and from-constraint invocation
- Action: token flow, outputs, nested invocation, send/accept, port communication, perform reference and shorthand, accept...then, flows, loops and decisions
- State: simple, do behavior, concurrent do, transition effect, choice/junction/fork-join pseudostates, orthogonal regions and region pseudostates, shallow/deep history, entry/exit points, deferred/undeferred events, call and timed triggers, signal discrimination/unmatched, self signal
- Requirement: require/subject/actor/assume satisfaction, nested
- Instance: derived feature values, constraint binding, inherited constraints, nested usage bodies
- Unit and quantity evaluation
- Constraint: assert/assume satisfaction, negation
- Satisfy assertions, variations, redefinitions, variants, feature chains, string operations, nested behaviors, element filters, the ball-and-chain model, and one each of attribute, connector, cubesat and view
Usage:
3. Golden Execution Traces¶
- Purpose: Verify how execution proceeds (ordering, scheduling), not just final result
- Location:
internal/core/runtime/trace_test.go - Test:
TestExecutionTracecompares executor traces against.trace.golden - Determinism: Token sorting by ID, fixed event queue tie-breaking
- Acceptance: Trace output matches golden file
- Update flag:
go test -run TestExecutionTrace -update-traces - Coverage:
.trace.goldenfiles for action, calc, state, constraint, accept and string execution
Trace format:
- Action: step N: token T1@node1, token T2@node2 (sorted)
- State: entry: StateName [hasEntryAction], transition: From -> To [event], exit: StateName [hasExitAction]
4. Runtime Robustness Tests¶
- Purpose: Verify malformed/pathological behaviors fail gracefully (typed errors, no panics/hangs)
- Location:
internal/core/runtime/robustness_test.go - Test:
TestRuntimeRobustness, one subtest per failure mode - Acceptance: All return typed errors, never panic, timeout guard (60s) prevents hangs
Failure modes:
- Deadlocked action (join starvation)
- Decision with no satisfied guard
- State machine with dangling transition
- Sourceless accept...then at top level
- Calc with unbound parameter, surplus or unknown-named arguments, no result, non-calc target, direct or mutual recursion
- Constraint referencing missing feature
- Step budget exceeded
- Fork/join misuse (branches sharing a region, join with one incoming branch)
- Region pseudostate with no satisfied guard, or a cycle
- Non-numeric time trigger
- Send that reaches only its addressee, accept of an unsent type, send through an unconnected port
- History outside a composite state, or without a record or default
- Defer of a non-deferrable trigger
- Non-terminating do behavior
- Call of an unhandled operation, call argument of the wrong type
- perform of a missing action, perform reference cycle
Usage:
Behavioral Semantics Map¶
See: SPEC_COMPLIANCE.md for semantic rule → implementation → test case → status mapping.
Every behavioral feature must have: - Semantic rule reference: the SysML v2 metamodel or the bundled KerML semantic library, and UML 2.5.1 only where neither has the concept - Implementation location (file:function) - Test case(s) exercising the feature - Status: ✅ Faithful / ⚠️ Approximate / ❌ Not Yet Implemented / 🚧 Known Failure
Current coverage: ~98% faithful implementation. Calc/constraint/requirement fully functional. Action/state executor infrastructure complete (fork/join/decision, TimeEvent/ChangeEvent, guards, hierarchy, orthogonal regions all tested); every conformance case passes. Fork/join, shallow/deep history, entry/exit points and deferred events are implemented and reachable from source text — see docs/project/spec-compliance.md and docs/reference/grammar/README.md.
Unit & Integration Tests¶
- Unit tests: Per-package test coverage (lexer, parser, semantics, runtime)
- Integration tests: End-to-end REPL/runtime scenarios
- Test fixtures:
testdata/*.sysml,testdata/*.kerml - Golden files: Expected parse/resolve/diagnostic outputs
- Verification:
go test ./...(all tests pass),go build ./...(clean build)
Contributing New Grammar Features¶
When adding parser support for new SysML v2 constructs:
- ✅ Add representative example to
testdata/parse/*.sysml - ✅ Run
go test -run TestGolden -updateto generate golden - ✅ Verify
TestStdlibConformancestill passes (no regressions) - ✅ Add negative test case if construct has error conditions
Contributing New Behavioral Features¶
When adding execution support for behavioral constructs (actions, states, calc, constraints, requirements):
- ✅ Add golden AST fixture to
internal/core/parser/testdata/parse/(if not already covered) - ✅ Implement semantics in
internal/core/runtime/(executor or evaluator) - ✅ Add conformance case:
.sysml+.expected.jsonininternal/core/runtime/testdata/conformance/ - ✅ Add golden trace case:
.trace.goldenfor ordering-sensitive features (fork/join, transitions) - ✅ Add robustness test for failure modes (deadlock, unbound params, missing refs)
- ✅ Update
docs/project/spec-compliance.mdwith semantic rule → implementation → test → status - ✅ Verify all tests pass:
go test ./internal/core/parser/ ./internal/core/runtime/
See CONTRIBUTING.md for full contribution guidelines.
References¶
- OMG SysML v2.1 Beta 1 Spec: https://www.omg.org/spec/SysML/2.0 (2026-05 release)
- Pilot Implementation: SysML-v2-Pilot-Implementation 2026-05
- Pilot Xtext Grammar:
SysML.xtext+KerMLExpressions(OMG reference implementation) - Metamodel: OMG SysML v2 metamodel (semantic foundation)
- Precedents: gopls (Go LSP), rust-analyzer (Rust LSP), IPython/Jupyter (REPL design)