Skip to content

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

  1. Language Server (sysml-lsp) — IDE support with live diagnostics, semantic hover, go-to-definition, intelligent completion, and workspace-wide symbol search
  2. Interactive REPL (sysml) — Exploratory modeling: define models incrementally, evaluate expressions, instantiate parts, inspect runtime state
  3. Execution Runtime — Instantiate parts, evaluate constraints, execute calc/analysis cases, simulate behavioral models
  4. 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:

source → lexer → parser → AST → symbol index → resolve → passes

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-safe
  • Conforms(a, b) bool — conformance checking
  • HasSpecializationCycle(sym) bool
  • members.go:
  • MembersOf(sym) — local + inherited members with masking
  • LookupMember(sym, name) — member lookup
  • Effective feature list per type (substrate for runtime instantiation)
  • multiplicity.go:
  • MultiplicityOf(sym) (Range, bool) — parse multiplicity bounds
  • Range{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=false for 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 Diagnostic carries the quickfix.Fix values (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.Valuenull, strings, instance references, collections (sequences/sets)
  • Instance: Typed object with one feature value per effective feature (Tier 1)
  • Instantiation: Materialize instance graph from part/item usage
  • 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 bodiesreturn expressions + mixed parameter declarations (✅ fully executable) - Constraint bodiesassert/assume with optional not negation (✅ fully executable) - Requirement bodiessubject/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:

  1. ActionExecutor — Petri-net token-flow execution
  2. Token-based control flow (initial → action → final, first/done keywords)
  3. Fork/Join for parallelism, Decision/Merge for branching
  4. Nested action invocation with attribute initialization
  5. Send statement for message passing
  6. ObjectFlow for pin-to-pin data routing
  7. Deadlock detection via progress tracking
  8. Golden trace recording with deterministic token ordering
  9. APIs: Step(), RunToCompletion(), Tokens(), SetBreakpoint(), SetTrace()

  10. StateExecutor — Event-driven state machine execution

  11. Initial/final state keywords (initial/final)
  12. Entry/exit/do behaviors (do runs while its state is active, one action per round, interleaved with the do behaviors of the states active alongside it)
  13. TimeEvent scheduling with priority queue
  14. ChangeEvent condition polling
  15. Guard evaluation for transitions
  16. Transition effect actions
  17. Hierarchical states with LCA-based entry/exit propagation
  18. Orthogonal regions with multi-region event broadcasting
  19. Choice + Junction pseudostates
  20. Golden trace recording for transitions/entry/exit
  21. APIs: ProcessNextEvent(), CurrentState(), EventQueue(), StateData(), SetTrace()
  22. Deferred events: an event no active transition handles is retained while a state deferring it is active, and delivered afterwards in arrival order
  23. CallEvent matches the operation named by the trigger (signal.go, state_executor.go; signal_test.go:TestCallEventMatchesOperationName)

  24. Context Integration — Public runtime APIs

  25. InvokeCalc(symbol, args) — Invoke calculation with arguments, return result
  26. EvaluateConstraint(symbol) — Evaluate constraint, return satisfaction boolean (assert/assume)
  27. EvaluateRequirement(symbol) — Evaluate requirement, return satisfaction boolean (require/subject/actor/assume/nested)
  28. ExecuteAction(symbol) — Run action to completion, return results
  29. ExecuteState(symbol) — Run state machine until final/suspended
  30. CreateActionExecutor(symbol) — Create executor for debugging
  31. CreateStateExecutor(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:

go build -o sysml-lsp ./cmd/sysml-lsp
./sysml-lsp  # stdio mode for editors

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: %calc parses 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: TestStdlibConformance loads 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:

go test -v -run TestStdlibConformance ./internal/core/libs

2. Golden AST Snapshots

  • Purpose: Verify AST structure matches expected output
  • Location: internal/core/parser/golden_test.go
  • Fixtures: testdata/parse/*.sysml and *.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:

{
    name: "unclosed_package",
    input: "package Foo {",
    wantError: true,
},


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: TestGolden passes, 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: TestExecutionConformance runs .sysml + .expected.json pairs
  • 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:

go test -v -run TestExecutionConformance ./internal/core/runtime

3. Golden Execution Traces

  • Purpose: Verify how execution proceeds (ordering, scheduling), not just final result
  • Location: internal/core/runtime/trace_test.go
  • Test: TestExecutionTrace compares 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.golden files 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:

go test -v -run TestRuntimeRobustness -timeout 60s ./internal/core/runtime


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:

  1. ✅ Add representative example to testdata/parse/*.sysml
  2. ✅ Run go test -run TestGolden -update to generate golden
  3. ✅ Verify TestStdlibConformance still passes (no regressions)
  4. ✅ 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):

  1. ✅ Add golden AST fixture to internal/core/parser/testdata/parse/ (if not already covered)
  2. ✅ Implement semantics in internal/core/runtime/ (executor or evaluator)
  3. ✅ Add conformance case: .sysml + .expected.json in internal/core/runtime/testdata/conformance/
  4. ✅ Add golden trace case: .trace.golden for ordering-sensitive features (fork/join, transitions)
  5. ✅ Add robustness test for failure modes (deadlock, unbound params, missing refs)
  6. ✅ Update docs/project/spec-compliance.md with semantic rule → implementation → test → status
  7. ✅ 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)