sysmlpy — Project Status¶
Current version: v0.97.1 (2026-10-16)
In Flight (on main, unreleased)¶
(Nothing staged.)
Recent Highlights (0.97.1)¶
- Boxes action-flow view: v2-native glyphs — chamfered octagons + the solid start diamond; named succession statements bind their keyword control ends; flow/succession edge dedupe.
Recent Highlights (v0.97.0)¶
- CI integrations (GitHub Actions / GitLab / pre-commit, new
sysmlpy cisubcommand): parse gate blocking, semantics advisory (--semantic strictopt-in) — seedocs/ci-integration.md. Dogfooded on this repo: the gate on the bundled library found and fixed two visitor crashes on named multiplicity bounds ([nCauses],[wallNumber]).
Recent Highlights (v0.96.4)¶
doc_ids=True— source-carried explicit ids:doc /* @id: … */inside an element's braces fixes its interchange@id(L3 identity, survives any edit); duplicate claims raise at export. Placement contract pinned by tests.
Recent Highlights (v0.96.3)¶
- Stable element identities (L2 + L3): content-addressed
interchange
@ids (to_interchange(stable_ids=True), CLI--stable-ids) survive insertion/reordering elsewhere; a registry sidecar (reconcile_ids/--reconcile-with model.json) keeps ids across ancestor renames and re-nesting — re-export with the previous file as registry and name-matched elements never lose identity. - Documented in full:
docs/stable-identities.md(L0-L3 levels, options, decisions); runnable demoexamples/element_identity.py; README "Element Identity: Stable Interchange IDs". - Gates: interchange 59/59, fast suite 654 passed / 1 skipped.
Recent Highlights (v0.96.2)¶
- Grammar upgraded to OMG release 2026-08: vendored grammar is now daltskin/sysml-v2-grammar v2026.08.1 (upstream #11 expression cascade
-
16 required import visibility). Bare
import X;is a¶SysMLSyntaxError. - Non-normative forms removed per the upstream review: bare
qualified identification and connector-end trailing multiplicity.
Normative forms (
connect [1] a to b;,subject : Q::N;) round-trip. - OMG XPect conformance: 123 passed / 3 skipped; grammar round-trip 168/168; fast suite 595 passed; KerML batteries 5/5.
Recent Highlights (v0.96.1)¶
- doc comments survive everywhere:
doc /* ... */on packages and on any usage/definition kind (previously dropped when shared with sibling body items; only Requirement kept it) is captured as.docon the API object and re-emitted bydump()/classtree(). - interface ends captured:
end <name>;andend <name> ::> part.port;in interface bodies parse intoInterface.ends/Interface.iface_connections(previously grammar-layer only), and::>targets survive the dump round-trip. - Round-trip fix: interface usages no longer dump a spurious
connectkeyword (syntax error on re-parse). - Flows verified end-to-end:
flow a to b;→Flowchild → dump → Interconnection View edge.
Recent Highlights (v0.96.0)¶
- KerML parser: OMG-KEBNF-generated ANTLR4 grammar + visitor-dict
layer (
sysmlpy.kerml); 130/130.kermlcorpus files parse. - LibrarySymbolIndex now parses
.kermllibrary files (2,986 symbols, was 1,604 regex-scraped). - Zero correctness failures on the OMG release-repo corpus sweep (404 files); Annex A SimpleVehicleModel and all interface-connect forms parse.
- Community PRs merged: port-def rendering fix (#10), boundary ports (#13), interface-usage connection edges (#12), binding connector rendering (#11) in the Interconnection View.
Completed¶
Public API Classes¶
These classes are fully implemented, have programmatic construction, dump() serialization, and test coverage.
| Class | SysML Keyword(s) | Notes |
|---|---|---|
Package |
package |
Name, shortname, children, load_from_grammar |
Model |
(root) | Wraps packages; load from string |
Part |
part / part def |
Typed by, children, load_from_grammar |
Item |
item / item def |
Typed by, children |
Attribute |
attribute / attribute def |
set_value/get_value with pint units |
Port |
port / port def |
In/out/inout directed features, add attribute |
Action |
action / action def |
Add in/out parameters, typed by, specializes |
Reference |
ref |
Simple, typed, and redefinition (ref :>> name : Type;) |
Requirement |
requirement def / requirement |
Subject, actor, doc, constraint, assume constraint |
UseCase |
use case def / use case |
Subject, actor, include |
Interface |
interface def / interface |
Add end, add connection |
Message |
message |
From, to, of-type |
State |
state def / state |
Transitions, entry/do/exit actions, .parent property |
Transition |
transition |
Source, target, guard, trigger, effect |
Constraint |
constraint def / constraint |
Assert constraint, derivation forms |
AnalysisCase |
analysis def / analysis |
Subject, objective, result expression |
VerificationCase |
verification def / verification |
Parse and load from grammar |
Concern |
concern def / concern |
Parse and load from grammar |
View |
view def / view |
Parse and load from grammar |
Viewpoint |
viewpoint def / viewpoint |
Parse and load from grammar |
Individual |
individual def / individual |
Parse and load from grammar |
Metadata |
metadata def / metadata |
Parse and load from grammar |
Rendering |
rendering def / rendering |
Parse and load from grammar |
Allocation |
allocation def / allocation |
Parse and load from grammar |
Flow |
flow def / flow |
Parse and load from grammar |
Connection |
connection def / connection |
Parse and load from grammar |
Calculation |
calc def / calc |
Parse and load from grammar |
Enumeration |
enum def / enum |
Parse and load from grammar |
Relationship Methods (on all Usage/Definition classes)¶
| Method | Syntax |
|---|---|
set_typed_by() |
: TypeName |
set_specializes() |
:> SuperDef (definitions) |
set_subsets() |
:> superset (usages) |
set_redefines() |
:>> original |
add_child() |
fluent child builder |
Parser¶
- CLI —
sysmlpyconsole script with subcommands (v0.61.0):parse(repr/dump/grammar-JSON),analyze(semantic analysis with CI exit codes: 0 clean, 1 findings at--fail-onthreshold, 2 parse error; text or--format json),view --view NAME(all 11 views,--focus,--element,--style,--direction,--format,-o),format(aliasfmt, multiple files),trace(requirement traceability & verification coverage:--format text|markdown|json,--fail-on uncovered,-o; exit 0 clean, 1 uncovered, 2 parse error).repl(v0.93.x — interactive session: declarations accumulate with member-granularity merge;%eval/%set/%calc/%check/%valuesover the evaluator,%sim/%send/%stepover the simulator,%view,%list/%show/%dump/%load/%save/%reset). Legacy flat invocation (sysmlpy FILE --dump) preserved with original exit codes. - Requirement traceability —
sysmlpy.traceability(extract_traceability()→TraceabilityReportwith per-requirement traces, coverage queries, text/markdown/JSON output;as_traceability_matrix_view()in markdown/html/plantuml). Satisfy / verify / verification / subject relationships parse and round-trip (v0.62.0). - JSON interchange —
sysmlpy.interchange(to_interchange()/from_interchange()): JSON-LD-style partition documents (flat@graph,@id/@typeelements, deterministic uuid5 ids) with lossless import back to a live model (v0.63.0). - Expression evaluator —
sysmlpy.evaluator(collect_values()/evaluate_expression()/evaluate_calculation()/check_constraints()): pint-bound evaluation of attribute defaults, calc results and constraint bodies, with what-if bindings;sysmlpy evalCLI (v0.64.0). - LSP server —
sysmlpy.lsp: dependency-free LSP 3.17 subset (diagnostics, documentSymbol, hover, definition, completion; FULL text sync) over stdio;sysmlpy-lspconsole script +python -m sysmlpy.lsp; VS Code extension scaffold ineditors/vscode/; Neovim recipes indocs/LSP.md(v0.65.0). - Spreadsheet bridge —
sysmlpy.spreadsheet: CSV export of the tabular views (--format csv), XLSX workbook export (sysmlpy xlsx, optional openpyxl extra), and spreadsheet value import into evaluator bindings (eval --set-file,import_values_csv()) (v0.66.0). - Official SysML v2 notation fidelity — edge encodings aligned to the OMG pilot's PlantUML generator (thick plain connection lines, heaviest binding lines, distinct redefinition arrow, send/accept action edges, variant/objective/metadata edges); connection endpoints now parse (was a parse stub); stv renders composite states as nested blocks; gv renders connector edges (v0.67.0).
- Boxes-backed interconnection + action flow views —
as_interconnection_view_boxes()(parts as boxes with boundary ports,connectionusages as port-to-port Z-routed edges) andas_action_flow_view_boxes()(actions as boxes, parameters as in/out ports, flows as port-to-port edges, nested actions as composite children, successions as dashed..>edges) againstdiagramboxes(renamed fromboxes, its v0.4.0); braille + SVG render helpers for both (v0.68.0). Control nodes render in both: PlantUML afv uses hexagons (deployment syntax has no diamond), boxes afv uses true decision diamonds + start/done dots via diagramboxes 0.5.0, whose sugiyama port routing is obstacle-aware (v0.68.0+). The PlantUML interconnection view follows official iv notation (enclosure nesting, typed labels, flows/connections as the only edges — flows port-to-port; v0.68.0+), and the package view follows official package notation:package "Name" { members }enclosure, leaf definition boxes, typed member labels (v0.68.0+). State-machine simulation (v0.69.0):sysmlpy sim FILEruns astate defmachine Cameo-style — triggers fire transitions, guards evaluate against model attribute values (pint-aware) with--set/set_valuewhat-if overrides, completion transitions run to completion, effects are logged, and an interactive TUI shows the live state with the transitions available from it (--run "T1; T2"scripts sessions). Guards also appear in boxes-view edge labels (the shorthand-whenextraction fix); transitiondoeffects parse through to the simulator — references round-trip, send/assignment declarations surface as text (send Alert to logger,x := 5) — and composite regions simulate with qualified names: entering a composite lands in its initial substate, region transitions run inside it, and composite-level transitions apply from every substate. For color-vision accessibility,set_stereotype_palette("okabe-ito")switches thestyle="color"views to the Okabe-Ito palette (bwremains the default). Validator depth (Goal 9) starts with state-machine well-formedness:UNRESOLVED_TRANSITION_ENDPOINT(error),NO_INITIAL_STATEandUNREACHABLE_STATE(warnings) run insideanalyze()on the simulator's expanded machine descriptor. Batch 2 (v0.69.1) addsUNRESOLVED_TRIGGER_PAYLOAD(bare and guardedacceptpayloads resolve or error) andREQUIREMENT_UNCOVERED(requirement usages with no satisfy/verify traces; defs exempt). Batch 3 (v0.70.0) addsUNRESOLVED_TRACE_TARGET/TRACE_TARGET_NOT_REQUIREMENTonsatisfytargets — dangling edges no longer materialize phantom requirements in coverage reports un-flagged. Batch 4 (v0.71.0) addsUNRESOLVED_VERIFY_TARGETandCONNECTOR_DIRECTION_MISMATCH; batch 5 (v0.72.0) addsUNRESOLVED_SATISFY_PARTand fixes nested satisfies silently reading as uncovered; batch 6 (v0.73.0) addsUNRESOLVED_CONNECTOR_ENDandSATISFY_SUBJECT_TYPE_MISMATCHand completes Goal 9 at 29 rule codes. Goal 8 (semantic model diff) starts at v0.74.0:sysmlpy.diffelement-level diff with text / Markdown / JSON rendering and a CI-friendlysysmlpy diffCLI. Goal 10 opens at v0.75.0:*//unit-dimension derivation (UNIT_DIMENSION_DERIVATION_MISMATCH, 30 rule codes) and SLL error parity (sll_onlystays in SLL prediction). v0.76.0 adds connector-end type compatibility (CONNECTOR_END_TYPE_MISMATCH, 31 rule codes) and lexer-based import extraction for dependency scanning (bare imports fixed, comment/string false positives gone). v0.77.0 closes Goal 10 with CayleyStore hardening + query parity, verified against a live podman Cayley v0.7 server (delete/clear/get/put/len/query fixes, label-namespaced subjects, NetworkX-identical glob query results). - Relationship legends opt-in —
include_legend=Falseby default everywhere; the built-in legends restated standard notation and are noise. Monochromebwremains the default style for color-vision accessibility (v0.68.0). - ANTLR4 parser — default parser, using OMG grammar v2026.03.0
load(),loads(),parse(),load_grammar()(public API)load_antlr(),load_grammar_antlr()(explicit ANTLR4 path)- Full ANTLR4 visitor (
antlr_visitor.py, ~11K lines) converting parse tree to internal dict representation - Supports comments, documentation blocks, and annotating elements
- Supports Case, AnalysisCase, VerificationCase, and TradeStudy definitions
- State machine support: entry/do/exit actions, accept/send/perform/assign nodes, transitions with guards
- Non-raising
parse(text)variant: returns(Model, [])on success,(None, [errors])on syntax error
Grammar Round-Trip Coverage (parse → dump)¶
168 / 168 tests passing (100%) as of v0.90.0.
The suite grew from 96 to 165 cases (full-model round-trips drawn from the OMG spec corpus, including ActionTest / ControlNodeTest / DecisionTest action-body successions; v0.87.0 added view filter/expose members, guarded entry transitions, typed-nameless connections, and typed prefixed-usage round-trips; v0.88.0 added usage-body imports, satisfy valueparts, and accept-member name/typing/multiplicity round-trips; v0.88.1 added nested-definition and typed-transition round-trips). Earlier corpus-level fixes: implicit-package wrap vs trailing line comments, MetadataFeature / TextualRepresentation annotating elements, missing succession-member classes, WHEN triggers, and malformed interface ends.
All categories pass, including the 14 control flow node tests (IfNode, WhileLoopNode, ForLoopNode, ControlNode, SendNode, AcceptNode, TerminateNode).
| Category | Pass | Total |
|---|---|---|
| Packages | 3 | 3 |
| Part definitions | 1 | 1 |
| Generalization / Subsetting / Redefinition | 3 | 3 |
| Enumerations | 2 | 2 |
| Parts | 2 | 2 |
| Items | 1 | 1 |
| Connections | 1 | 1 |
| Ports | 2 | 2 |
| Interfaces | 2 | 2 |
| Binding connectors | 2 | 2 |
| Flow connections | 3 | 3 |
| Actions | 5 | 5 |
| States | 6 | 6 |
| Expressions | 4 | 4 |
| Calculations | 3 | 3 |
| Constraints | 7 | 7 |
| Requirements | 4 | 4 |
| Analysis | 3 | 3 |
| Control flow | 14 | 14 |
| Lifecycle metadata | 9 | 9 |
| Corpus-driven full models | +17 | +17 |
| Total | 96 | 96 |
Grammar Resilience (v0.27.0)¶
All 68+ raise NotImplementedError stubs in grammar/classes.py replaced with
graceful handling. The parser no longer crashes on any edge-case input.
Stubs fully implemented (__init__, dump(), get_definition()):
- PortionKind — stores kind field (snapshot/timeslice/individual)
- PrefixMetadataMember — stores memberElement, dumps as @<name>
- LifeClassMembership — stores memberElement, dumps as lifeClass <name>
Missing classes added:
- DefinitionBody, DefinitionBodyItem
- FeatureSpecializationPart, SubclassificationPart
Catch-all unknown branches → print warning instead of crashing (68 sites):
- DefinitionElement, InterfaceBodyItem, OccurrenceUsageElement,
NonOccurrenceUsageElement, PrimaryExpression, PackageBody,
RelationshipBody, ConnectorPart, FeatureSpecialization, and more.
Expression chain classes — gracefully handle None child / non-empty operands:
- ConditionalExpression, NullCoalescingExpression, ImpliesExpression,
OrExpression, XorExpression, ClassificationExpression,
ExponentiationExpression, UnaryExpression, ExtentExpression
Semantic Analysis Engine (v0.17.0 → v0.20.0)¶
| Feature | Status | Details |
|---|---|---|
| Symbol table | ✅ Complete | Hierarchical scopes with parent chain lookup |
| Import resolution | ✅ Complete | Namespace (::*), membership, recursive (::*::**) |
| Import visibility | ✅ Complete | private/public/protected enforcement |
| Library symbol index | ✅ Complete | 88 .kerml/.sysml files, ~1,417 symbols |
| Inheritance resolution | ✅ Complete | Supertype chain traversal for subsetting/redefinition |
| OCL constraints | ✅ 8 checks | See table below |
Implemented OCL Well-Formness Checks¶
| Code | Rule | Description |
|---|---|---|
UNDEFINED_SYMBOL |
— | Reference to non-existent type or feature |
DUPLICATE_NAME |
Namespace.duplicate_names | Two members with same name in a scope |
CYCLIC_SPECIALIZATION |
Type.no_cyclic_specialization | Type specializing itself (directly or indirectly) |
INCOMPATIBLE_SUBSETTING |
Feature.subsetting_compatible | Subsetting ref to undefined feature |
INCOMPATIBLE_REDEFINITION |
Feature.redefinition_compatible | Redefinition ref to undefined feature |
INCOMPATIBLE_PART_DEFINITION |
Part.definition_compatible | Part typed by non-PartDefinition |
INCOMPATIBLE_PORT_DEFINITION |
Port.definition_compatible | Port typed by non-PortDefinition |
INCOMPATIBLE_FEATURE_CHAIN |
Feature.chaining_compatible | Chained features with incompatible types |
INVALID_MULTIPLICITY_BOUNDS |
Multiplicity.bounds_valid | Lower bound > upper bound |
UNRESOLVED_IMPORT |
— | Import target does not exist |
PlantUML View Generation (v0.25.2 → v0.27.0)¶
| Function | SysML v2 View | Output | Notes |
|---|---|---|---|
as_general_view() |
General View (GV) | PlantUML | All element types, full filtering |
as_package_view() |
Package View | PlantUML | Package structure + import arrows |
as_action_flow_view() |
ActionFlowView (AFV) | PlantUML | Actions + flow connections |
as_interconnection_view() |
InterconnectionView (IV) | PlantUML | Parts, ports, connections |
as_state_transition_view() |
StateTransitionView (STV) | PlantUML | States + transitions |
as_tabular_view() |
Tabular View (GridView) | PlantUML / MD / HTML | Configurable columns |
as_data_value_tabular_view() |
Data Value Tabular View | PlantUML / MD / HTML | Attribute values + units |
as_relationship_matrix_view() |
Relationship Matrix View | PlantUML / MD / HTML | Cross-element relationship matrix |
All views support: focus, elements, show_external, direction, B&W/color toggle, custom_style, and legend.
Nested Requirement Children (v0.36.1)¶
Requirement.load_from_grammar now populates self.children with nested requirement usages and requirement def definitions encountered in the body. Previously the body walk stubbed out DefinitionBodyItem with pass, so nested requirements were parsed by the grammar but dropped from the public object tree. Deep nesting recurses; .parent links are set on each child. Non-requirement nested elements are skipped for now. Grammar-object round-trip is unaffected.
Boxes-backed State-Machine Visualizer (v0.36.0)¶
Optional renderer that produces a boxes Diagram with native UML state shapes (rounded «state» boxes, filled-circle initial pseudostate, bullseye final state, orthogonal port-to-port routing). Alternative to as_state_transition_view() when you don't want a PlantUML/Java runtime.
| Function | Returns | Notes |
|---|---|---|
as_state_transition_view_boxes(model, focus=None) |
boxes.Diagram |
Build an in-memory diagram you can introspect / extend |
render_state_transition_view(model, focus=None, routing=...) |
braille terminal string | Quick terminal preview |
render_state_transition_view_svg(model, focus=..., routing=..., scale=...) |
SVG string | Vector output for embedding in docs |
Handles entry; then X;, do/exit actions as state attributes, guarded transitions (if guard), shorthand accept X then Y;, the reserved done final-state target, dotted feature-chain targets (S2.S3), nested composite states, and parallel region composition. Lazy-loaded so import sysmlpy works without boxes installed. See docs/boxes_view.md for the full pseudostate landscape and worked examples.
Storage Backends¶
| Backend | Dependencies | Persistence | Use Case |
|---|---|---|---|
InMemoryStore |
None | Volatile | Testing, small models |
NetworkXStore |
networkx | Volatile | Graph analysis, centrality, cycles |
KuzuStore |
kuzu | Disk (optional) | Embedded graph DB, Cypher queries |
CayleyStore |
requests | Server-managed | Remote graph DB, multi-tenant |
Test Coverage¶
Counts from pytest --collect-only at v0.90.0 (1635 total).
| Test file | Tests | Status |
|---|---|---|
tests/grammar_test.py |
165 | ✅ All pass (100%) |
tests/semantic_test.py |
179 | ✅ All pass |
tests/plantuml_test.py |
151 | ✅ All pass (incl. official-notation tests) |
tests/store_test.py |
121 | Pass (optional deps skipped if not installed) |
tests/class_test.py |
84 | ✅ All pass |
tests/traceability_test.py |
46 | ✅ All pass |
tests/interchange_test.py |
38 | ✅ All pass |
tests/evaluator_test.py |
44 | ✅ All pass |
tests/lsp_test.py |
37 | ✅ All pass (batch 4: 36 more in tests/lsp_batch4_test.py) |
tests/spreadsheet_test.py |
37 | ✅ 35 pass, 2 skip (no openpyxl) |
tests/navigate_test.py |
42 | ✅ All pass |
tests/cli_test.py |
39 | ✅ All pass |
tests/repl_test.py |
28 | ✅ Interactive REPL: session merge, %commands, loop, CLI |
tests/validator_test.py |
84 | ✅ All pass |
tests/repr_test.py |
34 | ✅ All pass |
tests/kuzu_store_test.py |
32 | Pass (skipped if kuzu not installed) |
tests/import_test.py |
31 | ✅ All pass |
tests/cayley_store_test.py |
39 | Pass (skipped if cayley not installed) |
tests/boxes_view_test.py |
54 | Pass (skipped if diagramboxes not installed) |
tests/project_test.py |
21 | ✅ All pass |
tests/redefined_name_test.py |
14 | ✅ All pass (100%) |
tests/dfa_cache_test.py |
14 | ✅ All pass |
tests/resolve_test.py |
12 | ✅ All pass |
tests/reference_parse_test.py |
12 | ✅ All pass |
tests/constraint_text_test.py |
12 | ✅ All pass |
tests/two_stage_parse_test.py |
12 | ✅ All pass |
tests/main_test.py |
7 | ✅ All pass |
tests/partial_test.py |
7 | ✅ All pass |
tests/palette_test.py |
6 | ✅ All pass |
tests/conformance_test.py |
123 | ✅ All pass (100%) |
| Total | 1635 | 1510 fast + 123 conformance pass (2 skipped: optional deps) |
Documentation¶
README.md— installation, usage examples, view rendering docsAGENTS.md— AI agent onboarding guidedocs/index.md— project overviewdocs/quickstart.md— step-by-step usage guideTUTORIAL.md— comprehensive guide with class mapping tablesdocs/PROJECT_SUMMARY.md— work summary for future agents/team membersdocs/plantuml-reference-analysis.md— PlantUML generator assessmentdocs/plantuml-examples/— rendered diagram examples
Completed Since v0.27.0¶
Dead-Code Removal + Lint Hygiene (v0.90.1)¶
- ~120 dead lines deleted — silently-shadowed duplicate methods
(
Action._get_definition,_evaluate_glued, two_get_childstubs), unused imports across 10 modules, the never-readsll_errorslocal, dead locals inAction.dump/_descend/to_networkx_store. - Bare
except:→except Exception:in the example scripts; stalerequirements.txtrewritten in sync withpyproject.toml. - No behavior change: fast suite 1510 passed / 2 skipped and conformance 123/123 before and after; functional smoke over every touched module.
View Rendering Member Specializations (v0.90.0)¶
render eng : Engine;/render eng2 : Engine :>> eng;/render eng3[2];inside a view body no longer drop typing, redefinition and multiplicity on dump (the last liveantlr_visitor.pyTODO);_build_fsp_from_ctx()builds theFeatureSpecializationPartdict with proper group ordering.
Parallel Regions in the Simulator (v0.89.0)¶
state def C parallel { ... }simulates: entering the composite activates all regions at their defaults, regions fire their own transitions independently (events dispatched per region in declaration order), run-to-completion applies per region, and transitions out of the composite exit all regions.sim.stateis a tuple of active leaves while regions are co-active.- Nested parallel composites (a
parallelregion inside aparallelregion) activate and cascade on exit. - Fixed: composites whose children were all composites were demoted to plain leaves (preventing these shapes from expanding at all).
Dead-Code Removal + Nested-Definition Fidelity (v0.88.1)¶
- 410 dead lines deleted —
_visit_nested_usageand 11 more zero-reference visitor helpers; the remainingspecialization: Nonenode-declaration slots verified unreachable (grammar rejects typed declarations there). - Nested definitions round-trip —
part p { state def X; }and 16 sibling kinds were silently dropped even at the grammar-dump level; the nested dispatcher now covers all package-level kinds. - Nested usages in state bodies surface —
state s1 { action a1 : A; }now yields an Action child with name + typed_by_name; nested states keep their children. - Typed transitions —
transition t1 : T first e1;keeps: T(round-trips, typed_by_name on the Transition object). - Satisfy typing — members carry typed_by_name; semantic finding: ors is the requirement reference, not the member name (anonymous by design).
Control-Flow Member Fidelity + Usage-Body Imports (v0.88.0)¶
- Usage-body imports —
part p1 { private import Q::*; }(and public/protected/recursive forms) round-trip; the visitor now dispatchesimportRulein every body andDefinitionBodyItemhandles the Import relationship. satisfyvalueparts —satisfy s1 : R = 3;keeps= 3(the emitter now uses the shared_visit_value_parthelper; the expression sat one level below where it probed).acceptmembers —accept msg : M;keeps name + typing (wasaccept ;); multiplicities render in re-parsable source order (: M[1]).- Metadata usages navigable —
metadata m1 : MD;/@m1 : MD;/aboutforms surface as Metadata children with name + typed_by_name (find('m1')works). - Nested rendering usages —
part w { rendering r2 : E; }creates a Rendering child with name + typed_by_name.
Round-Trip Fidelity Close-Out + View Body Members (v0.87.0)¶
- View
filter/exposemembers — the last known silent-drop TODOs in the visitor:view v { filter @e1; expose e1; }now parses into grammar classes and dumps back losslessly (wasview v ;). Grammar classesElementFilterMember/Expose/MembershipExpose/NamespaceExposeimplemented. - Guarded entry transitions —
entry a1; if x > 0 then s2;round-trips (the visitor handler probed nonexistent ctx attributes and the dict shape crashed the grammar class). - Typing round-trip on prefixed usages —
view v : Engine,viewpoint,concern,allocation,rendering,connection,individualusages keep their typing on dump (visitor emitters previously hardcodedspecialization: None). typed_by_namefor all dispatch kinds — viewpoint, concern, allocation, rendering, metadata, individual, constraint, calculation, connection, flow, satisfy, dependency now populate typing names (post-pass overPackage.load_from_grammarchildren; extraction made idempotent).ConnectionUsage.dump()canonicalization — bareconnect a to b;no longer rendersconnection connect\n…; typed-nameless connections keep theconnectionkeyword + typing.RootNamespace— KerML roots load members; root-levelElementFilterMemberwarns + skips (noUnboundLocalError).
Storage Query Parity Across Backends (v0.86.0)¶
- NetworkXStore gains
siblings/hub_elements/shortest_path_between_named; KuzuStore gainsall_paths/ in-out degree centrality /descendants_depth_limited/neighborhood/impact_analysis(client-side, mirroring Cayley) plus hub direction validation and deterministic tie-breaking. TestBackendParity: 14 analytics queries must return identical results on NetworkX, Kùzu and a live Cayley server — the three graph stores now expose the same query surface. Goal 10 fully closed.
Interchange & Evaluator: Conditional Expressions, Calc Parameters, JSON-LD Context (v0.85.0)¶
- Conditional expressions:
if <cond> ? <then> else <else>evaluate in attribute defaults, constraints, and calc bodies; boolean condition enforced, nested else-chains, embedded ternaries via fragment substitution, unit suffixes on branches; dump respaces glued ternary text so models round-trip. - Calc invocation: positional
inparameter binding with declared defaults, recursion supported (depth 32), constraints can call calcs with what-if bindings. - JSON-LD context: :func:
build_jsonld_contextproduces explicit term definitions (<vocab>#<name>); vocabulary IRI configurable (no normative OMG context file exists to bundle); import normalizes IRI-keyed properties.
Performance: Persistent DFA Cache + Profiling Harness (v0.84.0)¶
sysmlpy.dfa_cache— warmed parser/lexer ATN+DFA graphs pickled to~/.cache/sysmlpy/, reinstated on cold start (3.7x faster first parse on the benchmark model);SYSSMLPY_DFA_CACHEenv +set_dfa_cache()control; cache failures never break parsing.benchmarks/bench_parse.py(cold/cached/warm timing) andbenchmarks/profile_parse.py(cProfile harness; parse dominates ~80 %, no visitor hotspot).
LSP: Incremental Sync, Position Tracking, Workspace Symbols, .-Completion (v0.83.0)¶
- Incremental text sync (ranged
didChangeedits; full-text changes still accepted); UTF-16 position math with clamping. - Position-tracked semantic diagnostics via source-order pairing (duplicate names map to their own declaration).
workspace/symbolover open documents + workspace root*.sysmlscan (cached, capped)..-member completion resolved through type names, surviving transiently unparsable documents via the last good parse.
Semantic Diff: Renames, Grammar Fields, State Machines, Trace Gate (v0.82.0)¶
- Rename detection: removed+added pairs with equal kind and structural
signature match as
renamed(unique candidate only; ambiguous stays removed+added);ElementChange.old_name/old_qualified_name. - Grammar-level signature fields:
value,multiplicity,direction,abstract(canonical-dump heuristics); requirementtracesedges. diff_state_machines()— state-machine diff via the simulator'sMachineDescriptor.sysmlpy diff --threshold FRACchange-rate CI gate;ModelDiff.change_rate/elements_old/elements_new.
State-Machine Simulation: Assignment Effects & History (v0.81.0)¶
do x := <expr>effects are evaluated (evaluate_expression) and applied viaStateSimulator.set_value, so later guards see the new value; applied pairs surface onStepRecord.assignments.- History pseudostates (
state h : HistoryUsage;,h : HistoryUsage;, or bareh;/history;references) are recognized byboxes_view._collect_state_machine(new per-regionpseudostates), excluded from the state list, and transitions targeting them resume the region's last active substate (default entry when unvisited). Deep history viadeep_history=True/sysmlpy sim --deep-history.
Action Control-Flow Node Classes (v0.28.0–v0.29.0)¶
All 14 control flow grammar classes are now ported to grammar/classes.py:
IfNode,WhileLoopNode,ForLoopNode,ControlNode,InitialNode,InitialNodeMember,SendNode,AcceptNode,TerminateNode,ActionTargetSuccession,ActionTargetSuccessionMember,GuardedSuccession,GuardedSuccessionMember,SourceSuccession,SourceSuccessionMember
Mutation API Stabilization (v0.30.2)¶
All private underscore-prefixed mutation methods given public aliases:
- _set_child() → add_child(); _set_name() → set_name(); _set_typed_by() → set_typed_by()
- _set_specializes() → set_specializes(); _set_subsets() → set_subsets()
- _set_redefines() → set_redefines(); _get_child() → get_child()
- Private names kept as backward-compatible aliases.
Model Navigation Enhancements (v0.30.2–v0.31.0)¶
find_one()— single-match find withLookupErroron ambiguity__iter__,__len__,__contains__— container protocol on all model elements__str__— returns SysML text (delegates todump())find()usessysml_type=keyword (legacytype=still works with deprecation)- Typed property accessors (
model.parts,model.actions, etc.)
Semantic Analysis Enhancements (v0.30.2)¶
AnalysisResultclass with.errors,.warnings,.raise_on_errors(),bool()analyze(model, strict=True)raises immediately on errorsSysMLSyntaxErrorexported from package root- Non-raising
parse()variant:model, errors = parse(text)
Jupyter Integration (v0.30.2)¶
_repr_html_()on all model elements — collapsible HTML tree in notebooks
Grammar Round-Trip (v0.31.0)¶
- All 97 grammar tests pass (100%) — control flow, successions, lifecycle metadata complete. New tests/redefined_name_test.py (8 tests) for re-declaration name resolution and
References(:>,references) handling. - No deferred tests remaining
Known Issues¶
| Location | Description |
|---|---|
antlr_visitor.py |
~~Top-level attribute multiplicity not captured~~ Resolved — bounds since v0.40.0, ordered/nonunique flags since v0.59.0 (note: nonunique ordered canonicalizes to ordered nonunique on dump; grammatically identical). |
definition.py (RootNamespace) |
~~load_package_body() raises NotImplementedError for AliasMember and Import nodes~~ Resolved in v0.58.0 — node types were already dispatched; the real gap (imports/aliases moved to the end of the package body on public-API dump) is fixed. KerML NamespaceBodyElement roots load their members since v0.87.0. |
usage.py |
~~Type relationships (: TypeName) not preserved in load_from_grammar()~~ Fixed in v0.57.0 — _extract_specialization_info() hoisted to base Usage; typed_by_name now populates on all usage kinds, including the prefixed kinds (view, viewpoint, concern, allocation, rendering, constraint, connection) whose dispatch branches skipped it — wired via a post-pass in v0.87.0. typedby object resolution is a follow-up. |
semantic.py |
*// dimension derivation (mass * speed → ForceValue inference) not yet implemented (future); +/- dimension equality and operand-category checks are complete (v0.55.0). |
antlr_parser.py |
SLL error wording may differ from LL wording (missing '}' at '<EOF>' vs extraneous input '<EOF>' ...); source position always matches (v0.56.0). |
Remaining Work¶
High Priority¶
| Feature | Description |
|---|---|
| ~Typed-by preservation~ | Done in v0.57.0, extended in v0.87.0 — _typed_by_name / typed_by_name preserved for all usage kinds loaded from grammar. v0.57.0 covered Part, Attribute, Item, Port, Action, Interface, UseCase, Requirement, State, behavior children; v0.87.0 wired the prefixed kinds (view, viewpoint, concern, allocation, rendering, constraint, connection, individual, metadata, satisfy) whose dispatch branches assigned grammar manually, and made the visitor emit specialization for view/viewpoint/concern/allocation/rendering/connection/individual so view v : Engine round-trips its typing. Resolving typedby to the definition object via a model pass remains a follow-up. |
| ~Fix top-level attribute multiplicity~ | Verified fixed (v0.40.0) + flags bug fixed in v0.59.0 — bounds ([N], [N..M], [*]) already survived since v0.40.0 (docs were stale); the real remaining bug was ordered/nonunique hardcoded False in the visitor extractors plus a MultiplicityPart.dump() XOR-guard that dropped ordered. All fixed. |
| ~AliasMember / Import handling~ | Done in v0.58.0 — nodes were already parsed and held on Package.imports (definition.py); the gap was _ensure_body() reordering imports/aliases to the end of the body on dump. Source-order interleaving now preserved in both Model and Package rebuild paths. |
Medium Priority¶
| Feature | Description |
|---|---|
| ~Feature chain type resolution~ | Done in v0.60.0 — ReferenceCollector tags reference kind (typing/subsetting/redefinition/subclassification); chain check applies only to genuine feature chains (fixes false INCOMPATIBLE_FEATURE_CHAIN on every qualified type name like ScalarValues::Real). Dotted expression chains (wheels.hub.mass) resolve through the declared type of each feature, following :> inheritance; members of an enclosing usage's declared type (part myCar : Car { attribute x :> engine::power; }) resolve for ::, ., and single-member references (_resolve_through_context); inherited chain features advance to their declared type in the compatibility check. 17 tests. |
| ~Connection multiplicity ends~ | Verified fixed (v0.87.0) — connect c1[0..1] to c2[1]; parses and the multiplicity ends survive the round-trip (round-trip test test_bare_connect_dump_canonical). |
~Nested :>> redefines in return~ |
Verified working (v0.87.0) — return attribute X : Type { :>> feature = expr; } parses and round-trips. |
| ~Connector end compatibility~ | Done in v0.76.0 — full type-assignability check in _check_connector_ends_compatible() flags CONNECTOR_END_TYPE_MISMATCH (warning). |
Low Priority¶
| Feature | Description |
|---|---|
| Grammar auto-update pipeline | Automated refresh from the OMG KEBNF spec when new releases drop |
| Full OCL constraint library | Machine-readable OCL constraints extendable without code changes |
| Parse library files (not regex) | Replace LibrarySymbolIndex._extract_from_file() with actual parsing |
Conformance Test Suite¶
Source: SysML-v2-Pilot-Implementation-2026-03 (org.omg.sysml.xpect.tests)
Library: 88 files bundled at src/sysmlpy/library/ (kernel/ systems/ domain/)
Test files: 123 .sysml files under tests/sysmlv2/, each with a .error sidecar
Run with: poetry run pytest -m conformance
Current results (2026-05-27)¶
123 / 123 passing (100%)
| Category | Files | Pass | Fail | Pass % |
|---|---|---|---|---|
simpletests/ |
37 | 37 | 0 | 100% |
validation/valid/ |
34 | 34 | 0 | 100% |
validation/invalid/ |
47 | 47 | 0 | 100% |
expression/ |
4 | 4 | 0 | 100% |
linking/ |
1 | 1 | 0 | 100% |
| Total | 123 | 123 | 0 | 100% |
Summary Counts¶
| Category | Count |
|---|---|
| Public API classes (complete) | 28 |
Grammar classes with get_definition() |
358 of 358 (100%, reflection-audited) |
| Grammar classes with graceful fallback | All 358 (no more NotImplementedError crashes) |
| Unit + grammar + integration tests | 809 passing |
| Grammar round-trip tests passing | 143 / 143 (100%) |
| Helper-property tests | 8 / 8 |
| Grammar-side References dispatch | ✅ |
| PlantUML rendering tests | 108 passing |
| Conformance tests (2026-03 XPect suite) | 123 / 123 (100%) |
| Semantic analysis tests | 153 passing |
| Storage backend tests | 97 passing (optional deps skipped if missing) |
| Bundled standard library files | 88 (kernel .kerml + systems .sysml + domain .sysml) |
| Library symbols indexed | ~1,604 (incl. library function declarations, v0.54.0) |
| PlantUML view functions | 10 (GV, PV, AFV, IV, STV, SV, CV, Tabular, DataValue, RelMatrix) |