CHANGELOG¶
0.97.1 (2026-10-16)¶
- Boxes action-flow view: v2-native glyphs (with diagramboxes
0.15.x-0.17.x): action usages/definitions render as chamfered
octagons and succession starts as the solid v2 start diamond;
named succession statements (
succession s1 first start then b;) bind their keyword ends (start/done/terminate) to the control primitives, parented to the anchor action's owner box. Duplicate flow/succession edges from wrapper re-collection are deduped (_edge_once). Example:examples/v2_action_flow_poc.py(validated 10/10 against the Intro deck's providePower figure). - Fixes the stale
__version__string (shipped 0.96.4 inside the 0.97.0 wheel).
0.97.0 (2026-09-30)¶
- CI integrations: GitHub Actions, GitLab CI, pre-commit (new
sysmlpy cisubcommand). One engine (src/sysmlpy/ci_check.py) behind every integration, one exit-code contract: 0 clean, 1 findings at/above the failure level (parse failures always block), 2 operational error. - Parse gate (blocking, always on): every
.sysml/.kermlfile must parse (.kermlvia the KerML grammar). Syntax errors carryfile:line:col. - Semantic gate (advisory by default,
--semantic strictopt-in): findings are reported and labeled, but never fail the check by default — the bundled standard library is symbol-indexed, not model-loaded, so re-export facades (ISQ) and unit usages (SI::*,kg,['m/s²']) are known false-positive classes (28/71 valid-corpus files). Promote per-codebase once clean; theknown_limitationflag in JSON output identifies the classes. - GitHub reusable workflow
.github/workflows/sysml-check.yml(uses: mycr0ft/sysmlpy/.github/workflows/sysml-check.yml@vX.Y.Z) with DFA-cache restore for fast re-runs; also dogfoods on this repo's pushes/PRs (gating the bundled library + showcase fixtures). - GitLab template
.gitlab/sysml-check.gitlab-ci.yml(include: project: mycr0ft/sysmlpy, file: .gitlab/sysml-check.gitlab-ci.yml, extend.sysml-check). - pre-commit hooks
.pre-commit-hooks.yaml(sysmlpy-ci,sysmlpy-parse) — commit-time checks on changed files. - Docs:
docs/ci-integration.md(full options + promote-to-strict path); README CI section. Tests:tests/ci_check_test.py(25). -
Visitor fix: named multiplicity bounds (
[nCauses],[wallNumber]) — two of the three_make_boundcopies inantlr_visitor.pycalledint()on the bound text and crashed (invalid literal for int()) on non-numeric bounds; both now emit aFeatureReferenceExpressionlike the third copy. Found by dogfooding the new CI gate on the bundled library:CausationConnections.sysmlandShapeItems.sysmlnow parse (previously crashed at load). -
qn_registry()(P3 Vee-join side table). Everyto_interchange(stable_ids=True)document now carries"#qn_registry": deduped declared-QN path → stable@id(Identification nodes; doc-carried explicit ids included), readable with the new public helper of the same name — downstream consumers (pyoslc STEP domain) join SysML elements by qualified name without re-deriving hash chains. Empty for position-id exports. The registry is stable across export→import→export, survives JSON round-trips, and reconcile_ids adoption remaps it alongside the graph refs. Pinned bytests/qn_registry_test.py(11).
v0.96.4 (2026-09-30)¶
doc_ids=Trueonto_interchange(Phase 2b, L3 explicit identity) —doc /* @id: … */comments inside an element's own braces fix its interchange@id(impliesstable_ids): never re-minted over, surviving any edit (ancestor renames, re-nesting, cross-file moves) because the carrier rides the source text. Duplicate claims of one id raise at export. Placement contract (inside braces vs sibling-level bubbling) pinned by tests; end-to-end verified export → dump → re-parse → export. Scope record:docs/stable-identities.md§4 Phase 2b; demo:examples/element_identity.pysection 6.reconcile_ids/to_interchange(reconcile_with=...)carry forward from v0.96.3 (L3 registry reconciliation).
v0.96.3 (2026-09-28)¶
- Stable element identities across edits (L2 + L3 idempotency).
to_interchange(stable_ids=True)/ CLIsysmlpy export --stable-ids: content-addressed@ids — named elements hash their declared qualified-name path + metaclass type, unnamed elements hash the nearest named anchor + structural path. Inserting/reordering unrelated elements elsewhere leaves untouched ids unchanged. Default (stable_ids=False) stays byte-identical to the v0.63.0 position scheme.reconcile_ids(document, registry)/ CLIsysmlpy export --reconcile-with model.json -o model.json: the previous export doubles as an id registry — name-matched elements adopt registry ids, so ids survive ancestor renames and re-nesting (injective (declaredName, @type) matching keeps documents collision-free; nested{"@id"}refs are remapped so reconciled documents import cleanly; the CLI one-liner is an idempotent fixed point after the first export).- New public exports:
reconcile_ids,STABLE_NAMESPACE,doc_ids=True(v0.96.4: explicitdoc /* @id: … */ids honored at export). - New demo:
examples/element_identity.py— all identity layers (parse uuid4, position uuid5, content uuid5, registry reconciliation) runnable end to end. - New docs:
docs/stable-identities.md(idempotency levels L0-L3, options analysis, design decision: declared-path not resolved-QN, Phase 1 + Phase 2 implementation records); README section "Element Identity: Stable Interchange IDs". - All gates green: interchange 59/59 (11 stable-id + 10 reconcile tests), fast suite 654 passed/1 skipped. (Grammar 168/168, XPect conformance 123/3 optional-dep skips, KerML 5/5 — unchanged from v0.96.2, untouched by this release.)
v0.96.2 (2026-09-28)¶
- Grammar upgraded to OMG release 2026-08 — the vendored ANTLR4
grammar is now daltskin/sysml-v2-grammar v2026.08.1 (upstream
14b0d7a), replacing the 2026.05.0 base + local patches. Includes upstream #11's flat precedence-correctownedExpressioncascade and upstream #16's required import visibility (bareimport X;is now aSysMLSyntaxError; useprivate/public/protected). - Non-normative forms removed (resolved against the OMG KEBNF and
the pilot KerMLExpressions.xtext with daltskin): bare qualified
identification (
subject SystemGateway::System_Driver;— use the typed formsubject : SystemGateway::System_Driver;) and connector-end trailing multiplicity (connect a[1] to b;— use the normative leading formconnect [1] a to b;). - Visitor: structured expression chains re-emitted from the flat
left-recursive parse tree (revived v0.52.0 splice machinery);
symbol-form
&/|operators structured; multi-segmentFeatureChainMember(one context, many name segments) fully captured so feature-chain type resolution reports the whole chain. ConnectorEndpreserves the normative leading cross-multiplicity and dumps it before the declared name.-
All gates green: grammar 168/168, fast suite 595 passed/1 skipped, OMG XPect conformance 123 passed/3 skipped (optional-dep skips), KerML batteries 5/5.
-
(empty — Phase 2b released in v0.96.4, registry reconciliation in v0.96.3)
v0.96.1 (2026-09-25)¶
- doc comments survive everywhere —
doc /* ... */on a package, on any part/item/port/action/... usage or definition, and inside interface bodies is captured as.docon the API object (previously silently dropped whenever the comment shared its body with siblings; only Requirement carried doc).dump()/classtree()re-emits the doc alongside the other body items, and re-parsing the dumped text keeps.docintact. New exampleexamples/doc_ends_flows.py - 11 tests (doc_ends_flows 5/5).
- interface ends captured —
end <name>;andend <name> ::> part.port;members of an interface body now parse intoInterface.ends(name/type/multiplicity) andInterface.iface_connections(the::>targets as feature-path strings); previously the ends stayed in the grammar layer only and.endswas always empty.end ::>targets are re-emitted through the References/OwnedReferenceSubsetting/OwnedFeatureChain specialization chain, so they survive a dump round-trip (and re-parsing rebuildsiface_connections). - fix: round-trip of interface usages emitted a spurious
connectkeyword —InterfaceUsageDeclarationmarked ANYpart1as the CONNECT form, but the visitor also emits an emptypart1for plaininterface x : T { ... }; the resultinginterface x: T connect {...}dump re-parsed with a syntax error. Theconnectkeyword is now only set when the binary part actually carries the CONNECT-form ends (grammar tests 165/165). -
flows — verified end-to-end (already working):
flow a to b;parses into aFlowchild of the owning part,dump()re-emits it, and the Interconnection View renders the flow edge. -
perf: DFA cache supersede -
save_dfa_cache()refused to update an existing cache file, so DFA states learned after the file was written (short-name<K>forms,[1..*] nonuniquemultiplicity chains, STRING short-names) were re-built by every fresh process: the OMG library files SI / ISQSpaceTime / MeasurementReferences / ShapeItems and Annex A SimpleVehicleModel took 60-200 s per parse. A strictly-warmer in-process cache now atomically replaces the file; after one absorbing run per construct family, all six slowest OMG files parse in 0.2-8 s per fresh CLI process. 2 new supersede tests (dfa_cache 16/16). - New loaders for package-less snippets:
loads_wrapped()/load_wrapped()wrap bare top-level SysML (OMGSimple Tests/ training-snippet style, e.g.part def Camera { ... }with no enclosing package) in a synthetic package and load normally. Content already carrying a top-levelpackage/standard library packagepasses through untouched.Model.load's documented strictness ("Base Model must be encapsulated by a package") is unchanged. The corpus sweep helper (parse_one.py) now uses the wrapped loader, eliminating the 3 package-less rejections from the OMG release-repo sweep (DecisionTest, ControlNodeTest, Camera).
v0.96.0 (2026-09-23)¶
KerML parsing, OMG-corpus hardening, and community contributions (external PRs #10/#11/#12/#13).
- New: KerML parser (
src/sysmlpy/kerml/) - ANTLR4 grammar generated from the OMG KerML textual-notation KEBNF (Systems-Modeling/SysML-v2-Release, 2026-08 checkout) via the daltskin generator in KerML-only mode, plusparse/parse_file/parse_to_dictand a SysML-shaped visitor-dict layer. 7 documented corpus corrections (generator dead-rule reverts, KEBNF-vs-example fixes) insrc/sysmlpy/kerml/README.md. Corpus: 130/130 .kerml files parse and extract non-empty structure (OMG examples 58, OMG kernel libraries 36, bundled kernel 36). KerML imports carry no visibility keyword (unlike SysML). Tests:tests/kerml_test.py(3 pytest tests over standalone batteries). - LibrarySymbolIndex parses .kerml files (was regex-scraping):
.kermlfiles now go throughparse_to_dict;.sysmlfiles keep the line-regex walk (shared as the parse-failure fallback). Library symbols 1,604 to 2,986; all 94 OMG.kermlfiles yield symbols. 3 regex-only names were false positives (keyword-collision infeature all ...and a doc-expression line). - fix:
connect [N]with leading multiplicity through interface ends -_visit_multiplicityreads throughownedCrossMultiplicity().ownedMultiplicity(). Found by categorizing the OMG corpus-sweep failures; the Annex A SimpleVehicleModel now parses fully (241 elements). - fix: Port definitions lost
is_definition(external PR #10) -Usage.load_from_grammar's__init__()reset cleared the flag; port defs rendered as<<port>>instead of<<port def>>. - IV: boundary ports (external PR #13) - ports render as PlantUML
native
portsyntax on the hosting part's boundary, declared and inherited. - IV: interface-usage connections (external PR #12) - interface
usages render as connection edges; top-level
connect-form and body-declaredend ... ::> part.portends, nested interfaces scoped to their containing part. - IV: binding connectors (external PR #11) -
bind p = A.p;is preserved in the public tree (was silently dropped) and renders as a thick binding edge between the referenced features. - fix: UseCase missing re-declaration chain fields - loading any
model with a use case crashed with
AttributeError(_specializes_namesuninitialized).
Corpus status: the OMG release-repo sweep (404 files) now has zero correctness failures; what remains is 5 slow-but-finite large library files (42-94 s, perf tuning candidate) and 3 package-less snippet examples rejected by the loader's intentional package-wrapper guard.
Markdown tables column-align in monospace (sysmlpy.mdtables).
Every markdown table sysmlpy emits now lines up pipe-for-pipe when viewed in a monospace font:
- new
src/sysmlpy/mdtables.py:pretty_markdown_tables(text)post-processes arbitrary markdown (re-pads every pipe table that has a GFM separator; everything else passes through untouched) andformat_table(header, rows, align)builds aligned tables directly. Widths follow the East Asian Width convention (Wide/ Fullwidth = 2, combining marks = 0, Ambiguous - including the satisfy check mark - = 1) and count escaped characters (\|) as the single character they render as; GFM alignment colons are preserved through re-padding (:--left,--:right,:-:center). - wired into every markdown emitter: all PlantUML grid views
(tabular, data-value, relationship matrix) via
_format_table_rows_markdown, the traceability report (to_markdown) andas_traceability_matrix_view. - fixed a latent matrix-structure bug the alignment work
surfaced:
as_relationship_matrix_viewdata rows carried the row-label element name as the first cell but the header lacked the label column, so every row had one cell more than the header and GFM renderers silently dropped the last column. The header now carries the label column; the matrix CSV gains it too (header and rows now have equal field counts).
Honest limits (docstring): alignment cannot hold for cells whose rendered width differs from the measured one - emoji and other beyond-BMP glyphs (ambiguous across terminal fonts) or cells with embedded newlines (which GFM tables cannot carry anyway).
Test updates: new tests/mdtables_test.py (10 tests); alignment-
sensitive exact-shape asserts in plantuml/traceability/cli/
spreadsheet tests updated to width-tolerant regexes; matrix CSV
test now asserts the label column and equal field counts.
v0.95.0 (2026-09-18)¶
Interactive REPL (sysmlpy repl), runtime-showcase fixtures, and four
silent-drop fixes found by them.
Inspired by Open-MBEE's OpenSysML REPL, built on machinery sysmlpy
already had. See docs/COMPARISON.md for the three-way contrast
(sysmlpy vs OpenSysML vs gosysml) that motivated the round.
- New tool:
sysmlpy repl(src/sysmlpy/repl.py, 28 tests intests/repl_test.py). Declarations accumulate into a session model with member-granularity merge (re-declared member replaces the prior one, other members kept,note:line reports the replacement — the semanticsipython_magic.pyhas carried since v0.64.0, now shared). Multi-line submissions continue on...>while brackets stay open; a non-declaration line falls back to expression evaluation. 19%commands:%list/%show/%dump,%eval/%set/ %bindings/%calc/%check/%values(evaluator, pint-aware),%sim/%send/%step/%state(simulator session),%view(all 8 views),%load/%save/%reset/%quit,%help; readline completion over commands and element names; UUID names filtered from qualified-name display. Session core is I/O-injectable and headless-testable (same pattern assim.run_tui). - Visitor:
exhibit stateno longer silently dropped. The grammar ruleexhibitStateUsagehad zero visitor handling — both spellings (exhibit state m { ... }andexhibit state m : M;) vanished from the visitor dict, the public tree,Model.dump(), the boxes view, and the simulator. Now emitted as a StateUsage dict withexhibit: True;StateUsagegrammar class carries/dumps theexhibitkeyword. Spacecraft-comms' dump went from lossy 1,177 chars to faithful 5,334. - Public-API:
state defin part/item def bodies no longer dropped (no dispatch arm in the Usage body walk) andperform action m : Mno longer dropped (no_load_behavior_childbranch; PerformActionUsageDeclaration keeps name/typing in.children, now extracted —perform action mission : LunarMission { in budget = Flight::budget; }round-trips and surfaces as an Action child). - Evaluator: KerML math built-ins
ln,exp,log,log2,log10added (pint-aware, dimensionless results) — motivated by the showcase's rocket equation; positional calc-arg binding unchanged. - New fixtures: the five OpenSysML runtime-showcase models copied
to
tests/fixtures/runtime_showcase/(Apache-2.0, attribution README; unchanged model text) withtests/runtime_showcase_test.py(18 tests). The evaluator reproduces OpenSysML's published showcase digits exactly (stage delta-v 3037.6966629706967 / 6432.955324716369 m/s, margin 70.65198768706614) and both showcase state machines build and drive. - Docs:
README.mdcommand table + REPL example;STATUS.mdCLI section + test map;docs/COMPARISON.md.
Test totals at this release: 1,368 fast + 123 conformance passing (8 skipped for optional deps not installed).
v0.94.0 (2026-09-16)¶
Relationship Matrix View: verify cells — the Vee's V side.
verify <req>; relationships now land as V cells, closing the
requirements loop in the cross-checking grid: satisfy (check) shows
that a part claims to meet a requirement, verify (V) shows that a
verification case demonstrates it.
- new
_extract_verifiesrecovers verify references from both placements: surfaced VerifyRequirementUsage wrappers (read directly, like satisfy) and members nested anywhere in a VerificationCase/Requirement grammar's definition dict (objectives included), recovered via an iterativeget_definition()walk; qualified references (verify P::req;) resolve to the last segment, so usage-level and definition-level verifications both reach the requirement's axis name - vocabulary gains
"verify": "V"; verify wrappers join satisfy/ allocation/connection usages in the cells-not-axes exclusion - 2 new tests (verify-in-definition-objective and qualified-ref-in-usage), 13/13 matrix tests, 158/158 plantuml_test.py