Releases: Open-MBEE/OpenSysML
Release list
v0.1.2: QOL release
0.1.2 — 2026-08-20
A measurement release. Two advisory harnesses now ask the OMG pilot implementation and the
pinned OMG grammars where this implementation differs from them, and most of the behavior below
is what they found — chiefly in KerML, which the pilot's own KerML validator now judges. A
.kerml file is read as KerML rather than as SysML written with other keywords, so a KerML
model 0.1.1 rejected may analyze clean here; notation accepted beyond the grammars now warns
where it used to be silent. Nothing was renamed and no interface changed.
The pilot implementation judges every corpus, KerML included
cmd/pilot-diffcompares our diagnostics against the pinned OMG pilot implementation
(2026-05, jupyter-sysml-kernel 0.60.1) file by file, over 338 files in seven roots: the OMG
training corpus, the pilot's own SysML example and validation corpora, its KerML examples, our
testdata, our examples and our probes. 221 files agree exactly. It is advisory — a harness that
finds work, not a gate — so nothing in CI depends on it, and its committed baseline
(docs/project/pilot-differential-baseline.json) makes a rerun a diff rather than a reading.
On the 100-file training corpus both implementations report nothing at all.- The KerML corpus is judged by the pilot's own KerML validator, not by our reading of the
KerML specification:scripts/pilot-kerml-validator/ValidateKerML.javaregisters the pilot's
KerMLStandaloneSetup, loadssysml.libraryand the corpus into one EMFResourceSet, and
asks the pilot'sIResourceValidatorwithCheckMode.ALL. It contributes no rule of its own,
so a disagreement it prints is the reference's verdict. Over the pilot's 58 KerML examples, the
diagnostics only we reported fell from 439 to 150 as the KerML work below landed — the syntax
class from 360 to 140, and the genuine name-resolution class from 123 to 7 — and 33 of the 58
files now agree exactly. - Six diagnostics the pilot reports and we do not are a defect in the pilot, not a gap here:
EMF's unpaired-bidirectional-reference check firing on the pilot's own
Type::ownedDisjoining/Disjoining::owningTypeopposite, reproducible from three lines
(classifier A; classifier B disjoint from A;) in a fresh resource set. It is recorded with
its reproducer rather than absorbed into our own numbers. cmd/grammar-coveragemeasures the pinned OMG grammars against every corpus we hold:
483 of 727 productions and 802 of 807 notational forms have input-presence evidence. The
number is deliberately an over-approximation — presence of an input a production admits, never
parser-execution coverage or compliance — so the page's useful reading is the five forms with
no evidence anywhere, each adjudicated: the%remainder operator and prefix metadata on a
namespace are implemented but exercised by nothing, and the nameddisjoining,conjugation
andredefinitionrelationship declarations are not implemented.
A .kerml file is analyzed as KerML
- A KerML declaration specializes the library type its keyword implies, so the features of
that library type are inherited:classreachesOccurrences::Occurrence,structreaches
Objects::Object, and so on throughassoc,behavior,function,interaction,
metaclass,datatype,classifierandtype. No library member was inherited before, so
portion focusedState : Camera subsets timeSlices;reported an unresolved reference to a
feature the library declares. A declared generalization suppresses the implicit base only when
it already reaches it, sostruct MyWheel specializes Wheelstill reachesObjects::Object,
and a supertype restored from the index cache keeps those edges. A barefeaturestill gets
no base: the SysML attribute base is not KerML's. - SysML's definition-and-usage checks no longer fire on KerML declarations.
class Person specializes Objectwas reported as "only a definition may specialize; found a usage" — a
distinction KerML does not draw. A.kermlspecialization or typing now requires only that
its target be a type, from an explicit list of what a type is rather than a guess about what
isn't, sometaclass AtomMetadata specializes Metaobjectis accepted while a non-type target
is still reported. The SysML files keep the kind checks they had. - A union conforms through its unioning types —
classifier MyWheel unions MyWheel1, MyWheel2— without unioning becoming a generalization. - A declaration's header sees its own body. Names in a
featured by,crossesor
subsetting clause resolve against the members and imports of the body of the same declaration
before the enclosing scope, and stay reachable afterwards by qualified name and feature chain.
Resolution of a member inherited through an implicit base no longer recurses. - An unknown subsetting target is tolerated rather than reported as a KerML type error about
something else.
KerML notation the reference accepts now parses
featured by, an n-ary connector end list, and a typed or redefining succession parse:
class Owner { member feature inCart : Product featured by Account; },
connector c : A (a, b, c);andsuccession s : Link [1] first paint then dry;— whose own
[1]belongs to the succession rather than to its first end — along with thesuccession redefines s : …spelling. A missingby, a missing target, a missingthenand a trailing
comma are still reported.at,while,mergeanddecideare names in a.kermlfile, where they are not KerML
keywords, asabout,bindand the other SysML-only words already were. The remaining KerML
feature-prefix forms —abstract var feature x [0..*];— are still not parsed and are recorded
as such.
Imports and visibility
- A
publicimport is re-exported to importers of the importing namespace, transitively;
before, re-export stopped after one namespace. A root-level import is visible from a nested
package, an imported name may prefix a qualified name, and an import cycle terminates instead
of recursing. Aprotectedimport is reachable through a specialization of the importing
namespace and from nowhere else. - An import with no visibility indicator warns. The grammar requires one, so
import Lib::*;
now reports[syntax/import-visibility] import without a visibility indicator: SysML v2 requires public, private or protected before 'import'. It is a warning at the syntax tier and
analysis continues through it;exposeis exempt, its grammar supplying protected visibility
implicitly.
SysML notation the parser was refusing
- A connector, interface or flow written with shorthand ends may have a body:
connect x.p to r.p { ... },interface b1.p to b2.p { ... }andflow s1.x to s2.x { ... }
parse with their members. An unclosed body, an interface withouttoand an unterminated flow
body are still reported. - An accept node is an action statement, so
then action engineStopped accept engineOff : EngineOff;parses and executes. An accept in a loop or branch body remains unsupported by
lowering and is reported when reached, rather than accepted and silently skipped. connectrequires its ends and reads a leading multiplicity on each of them:
connect [1] a to [1] b.connect;,connect { ... }and a missing target are reported where
some were accepted and misrepresented.- Eleven words that no grammar production reserves are names again,
onandvaramong
them, sostate on;,part on : On;andattribute var : ScalarValues::Integer;parse as
declarations namedonandvar. Each word remains a keyword in the position its grammar
gives it, sovar a : Integer;without a kind is still reported. - A modifier before a kind prefix keeps the kind, which was dropped from the tree and from
every diagnostic that read it.
Notation accepted beyond the grammars now says so
- A construct we accept that no pinned production admits warns at the syntax tier, from the
conformance audit:namespace,region,choice,junction, an entry/exit/history point,
defer, theinitial/final/decisionnode spellings, andtransition <source> to <target>;.namespace P { }in a.sysmlfile reports "namespaceis KerML notation: the
SysML v2 grammar has no namespace declaration, so writepackagehere or move the declaration
to a .kerml file";featured byin a.sysmlfile reports the same for the featuring clause.
The same notation in a.kermlfile is silent, because there it is standard. These are
warnings — the models that use this notation still parse, and no higher tier is gated — and the
REPL warns for its own buffer, which it reads as SysML.
Analysis corrections
- An alias is followed through every type relationship — specialization, typing, subsetting,
redefinition, multiplicity and invocation inference — sopart def AvionicsLRU :> Box, where
BoxaliasesRectangularCuboid, no longer reports "part cannot specialize alias (kind
mismatch)" and inherits what the aliased definition declares. An alias cycle terminates. - An invocation of an aliased action or function is type-checked against its parameters
instead of going unchecked. - A declaration with a short name is listed once among a document's members, not twice.
- A part typing check that read an unrelated declaration is gone, with the diagnostics it
produced on valid models.
Diagrams in the VS Code extension
SysML: Open Diagramrenders the open model in a panel and keeps it current as the file
changes, over three new LSP requests —opensysml/render,opensysml/viewsand
`opensysml/ren...
v0.1.1: Fix Release
A fix release. Every change corrects something 0.1.0 got wrong about a valid model, so a model
0.1.0 rejected or misread may analyze differently here. Nothing was renamed and no interface
changed.
Install: go install github.com/Open-MBEE/OpenSysML/cmd/sysml@v0.1.1, or take a platform archive
below. The Python client opensysml is released separately.
The OMG training corpus reports no errors
- Every definition body inherits the features of the library definition its kind implies. Only
behavior definitions had an implicit base, sosnapshot sale = start;inside apart def
reportedunresolved reference: starteven thoughItems::Itemdeclaresstartanddoneand
Parts::Partredefines both. Two corpus files previously recorded as "bugs in the OMG files" —
Time Slice and Snapshot Example and Individuals and Time Slices — were never buggy; the
corpus baseline now lists no files at 100/100 clean. - A member that reuses an inherited feature's name is now reported instead of shadowing
silently:part def C { part start; }conflicts withParts::Part::start. Redefine it to keep
the name —part start :>> Parts::Part::start;. - A qualified redefinition of an inherited library feature is accepted.
snapshot start :>> Parts::Part::start;was rejected as "not an inherited member" when the
supertype came back from the index cache. Redefining a feature the owner does not inherit is
still reported. - A definition may specialize a definition of a comparable kind.
individual item def Alice :> Personwas refused becausePersonis apart def; specialization now follows the definition
taxonomy. Disjoint kinds — a part definition and an attribute definition — are still refused. - A transition may leave the entry action of the state that declares it.
entry action begin { } transition begin then off;reported the action as "not a state or pseudostate"; it now
designates the machine's initial state and executes as such, and is rendered in a view that
exposes it. Only that bare completion shape designates a start — a triggered or guarded
transition out of an entry action, a transition into one, and a name reaching another state's
entry action or a junction are reported rather than accepted with the trigger, guard or effect
dropped. - A metadata usage ends at its own
;or body.@M part def Car;was read as an annotation
plus an undiagnosed declaration;#M part def Car;is the prefix spelling. A metadata usage
member also names a type, so@Securty;reports an unresolved reference instead of going
unchecked, and@M;now parses in a namespace, a body and a state body. - A value part accepts every operator the grammar allows —
= expr,:= expr,
default expr,default = expr,default := expr— wherever a usage, parameter, result or
subject binds a value. - The REPL prints a syntax warning once, not once per deferred analysis.
A rendering read at a terminal
sysml -renderwrites the text form at a terminal and the machine-readable form of the kind
rendered — Mermaid for a diagram, Markdown for a table — into a file or a pipe.
sysml m.sysml -render Views::tableshowed a Markdown table on screen.> table.md,| tool,
-o table.mdand-render-formare unchanged.- The text form is ASCII. The rendering header and connection edges used an em dash, which an
ASCII-only terminal showed as a replacement character. - A text table is written to fit the terminal, wrapping an over-wide cell rather than
truncating it or overflowing the window, and narrowing no column below 8 characters. A table
written to a file or a pipe keeps every column as wide as its widest cell, so a saved artifact
does not depend on the window it came from.
RDF conversion (experimental)
RDF stays experimental: expressions are still carried as source text, ownership is collapsed rather
than materialized as membership elements, and no round trip against a triplestore has been run yet.
Two changes land here.
- Element IRIs now carry a reversible encoded id instead of the raw qualified name.
Demo::Vehicleis written asurn:sysmlv2:element:Demo__Vehicle:::becomes__,_itself
escapes to_5f, and any other byte outside[A-Za-z0-9-]becomes_plus two hex digits, so
the id is unique, reversible and inside the[a-zA-Z0-9_-]+character set that a SysML v2 API
service requires of an element id. Element identity is read from thesysml:qualifiedName
literal rather than reconstructed from the IRI, so a.ttlwritten by 0.1.0 still reads back —
but a graph re-exported from 0.1.1 has different subject IRIs, and anything matching those IRIs
by string needs updating. - A reference to an element the graph does not describe is reported with a typed diagnostic
instead of crashing the converter. - A metadata usage member is refused with a typed diagnostic rather than written as an
annotation on a different element.
Documentation and internal quality gates
docs/is published as a documentation site built with MkDocs, with the build run in strict
mode in CI so a broken link or a missing page fails the check rather than shipping.- The generated SysML OWL ontology table is checked into the tree, with a domain/range gate
over the exported graphs, derived from the Open-MBEE SysML v2 RDF ontology's 411 property
declarations. It ships with a committed inventory of the graph shapes that do not yet conform,
so the remaining work is recorded rather than hidden. - Static analysis runs on every build through SonarCloud, scoped to hand-written source with
coverage wired in. The one vulnerability it reported is fixed: the ontology table generator no
longer resolvesgitthroughPATH, reading the checkout's Git metadata directly instead
(loose refs,packed-refs, detached HEAD, worktrees, and SHA-1 or SHA-256 object ids). The
generated table is byte-identical, so no recorded commit id changed.
v0.1.0: OpenSysML Release
ATTENTION: We have renamed the project to OpenSysML.
OpenSysML 0.1.0
First release under the OpenSysML name, and the release where a model does more than parse: an
object runs the behavior its type exhibits, an SMT solver decides what its conditions permit, a
view renders, and a model can be edited through the source it was parsed from.
Install
brew install Open-MBEE/tap/opensysml # brings z3 along, so solving works out of the box
go install github.com/Open-MBEE/OpenSysML/cmd/sysml@latest
pip install opensysml # Python client, released separatelyOr download opensysml-<os>-<arch>.tar.gz (.zip on Windows) below: sysml, sysml-lsp and
sysml-grpc for macOS, Linux and Windows on amd64/arm64.
What's new
An object runs its behavior. Materializing an object starts the state machines and actions its
type exhibits or performs — each object with its own execution, event queue and feature values,
each entry/do/exit/effect body reading and writing that object's values, and send … to reaching
the addressed object. Startup and quiescence are defined, and a machine that never settles reports
a budget error instead of hanging. %invoke <object> <op> p=… performs an operation of the
object's type; %state <object> points %step, %advance, %current, %events and %features
at that object's own machine.
A solver decides (experimental). %check answers whether a constraint, requirement or
satisfaction assertion can hold, with an assignment on sat; %explain reduces an unsat to a
minimal core, printing each conflicting condition with its declaring element and
file:line:col; %solve synthesises a witness while keeping fixed what already is; %configure
answers which variant selections an assertion permits; %optimize improves the objectives an
analysis def states, lexicographically, verifying every optimum by asking whether anything does
better. A solver is optional — discovered on PATH or named by OPENSYSML_SMT — and what a
backend can do is probed as capabilities, so a missing feature is a typed refusal, never a
fabricated verdict. z3 covers everything; cvc5 covers everything but %optimize.
A view renders. %render <name> [text|mermaid|markdown] and sysml -render <view> produce a
containment tree, an interconnection diagram, a state machine, an action flow or a table. State and
action renderings read the same lowered graphs the runtime executes, so a diagram cannot drift from
what runs, and rendering is a read: no object is materialized, and an %action/%state session
keeps stepping across it.
A model is edited in place. ApplyEdits, and model.edit() in Python, sets a feature's value
or renames a declaration by rewriting the bytes of the parsed source, so comments and the text as
typed survive; the result is re-parsed and re-analyzed before it is returned, and a request's edits
apply all or none.
%print writes the session's model — or one element — back as SysML notation at the prompt,
through the same writer %save uses.
RDF (experimental) now covers behavior: action and state bodies round-trip byte-identically
through Turtle, and 102 of 120 example models convert, up from 71, with the rest refused by
construct rather than partly written.
Semantics and notation fixes. A nested value body governs over an inherited value (KerML
§7.3.4.5); require/assume bodies resolve and type-check to any depth, in their own scope;
binding connectors propagate in both directions with typed conflict and cycle errors; bind
accepts qualified and chained ends, and a requirement body takes a prefixed connector; fork,
join, merge and decision register the names they declare; feature-chain resolution is linear
in chain length; and an element reached through two scope trees is exposed and rendered once.
Upgrading
The rename is a clean break, with no compatibility aliases:
- Go module:
github.com/Open-MBEE/OpenSysML(the old path resolves only forv0.0.x). - Python:
pip install opensysml(first release under the new name is 0.3.0, carrying on from
pysysml0.2.0),import opensysml,OPENSYSML_*variables,OpenSysMLError,
opensysml-generate,~/.opensysml.pysysmlgets one final0.2.1that only reports the
rename; pinpysysml==0.2.0to stay on the pre-rename client while migrating. - Env vars are
OPENSYSML_*;SYSTEMICA_*is not read. - VS Code: extension
opensysml-sysml, settingsopensysml.server.*/opensysml.trace.server—
re-set them under the new keys. - Models importing
SystemicaMathFunctions::*must importOpenSysMLMathFunctions::*. A bare
exp,ln,logoratan2without that import is now a typed error naming it, instead of
being reported unresolved and evaluated anyway. .ttlwritten before this release carriesurn:systemica:sysml:properties and is refused
rather than read with those properties dropped — re-export from the notation source.%slotsis now%features, and the "slot" vocabulary is gone everywhere: on the wire
Instance.feature_values/FeatureValue(field 3 and the nameslotsstay reserved), and
in Pythonfeatures,raw_features,get_feature,FeatureValueErrorand
feature_value/optional_feature_value/list_feature_value. The client requires the service's
feature_valuescapability, so a pre-0.1.0sysml-grpcis named rather than answering with an
object that appears to hold nothing. Diagnostic text changed with the vocabulary (feature value …,do action steps); exit statuses are unchanged, so a script matching the old text needs
updating.
Known limitations
- Constraint solving is experimental and needs an external solver:
%check,%explain,%solve
and%configurewant z3 or cvc5,%optimizewants z3 (optimization is a z3 extension). A
condition with no SMT-LIB form refuses the whole query rather than dropping the condition. - RDF conversion is experimental: expressions are not emitted as triples, end-binding heads depend
onsysx:sourceText, the vocabulary may change without a compatibility path, and no round trip
through a running triplestore has been demonstrated. - An edit sets a value or renames a declaration; creating or deleting an element, and renaming a
referenced one, are refused. - Only an action member is executable through
%invoke; acalcorconstraintoperation is
evaluated as an expression and says so. - A package-owned binding connector does not propagate values; one declared in a materialized type
or usage body does. - A rendering is tool-defined output — SysML v2 §10.2 leaves rendering to the tool — so
%render
output is OpenSysML's own artifact, not a standard interchange form. - A
thatinside a nestedaction,constraintor transition-guard body binds to the innermost
enclosing usage; an unqualified standard library name requires an import (conformant, won't-do);
and a port that accepts TCP but never answers gRPC costs the Python client about 9 s.
Quality gates
gofmt, go build, go vet, go test -race ./..., staticcheck and gosec green; stdlib
conformance clean; the OMG training corpus at its pinned 98/100 baseline; the SMT path gated with
z3, with cvc5 and with no solver, including a differential gate requiring solver verdicts to agree
with the evaluator; Python client suite green. Rule-by-rule status: docs/project/spec-compliance.md.
Full detail: CHANGELOG.md § 0.1.0.
v0.0.9: Optimization release
Systemica 0.0.9
Behavior, tooling and performance release. 52 pull requests since 0.0.8. Nothing is removed
and no model that worked before needs editing; two Python names are renamed with the old
spellings kept as deprecating aliases. Ships with pysysml 0.2.0.
Highlights
- The VS Code extension can start the language server.
sysml-lspaccepts--stdio
and implements theshutdown/exitlifecycle, so the shipped client no longer
crash-loops the shipped server. If 0.0.8's extension never worked for you, this is why. - State machines behave correctly around composite states. A transition out of a
composite state fires while a substate is active, and a transition between sibling regions
exits only its source rather than the whole composite state. a then band transition endpoints resolve like the names they are — in the scope
they were written in, with a diagnostic when an endpoint names a vertex of another machine
or afirst/thenmarker, instead of silently dropping the edge.- The Python client starts ~31× faster: 509 ms → 16.5 ms, and a cold model load on the
service dropped from ~110 ms to under 1 ms, so short-lived scripts and per-call
calculations are practical. - A valueless feature of a value type reads as
<unset>on every surface, instead of an
empty object.
Language and semantics
- Messages route by port direction, conjugation and the performing part; a state machine
inside a part reaches that part. - Blocks own their token flow:
foriterates every collection it is given, outputs the
block's own flow assigns are counted, and aforover a non-collection is reported. - Library evaluation: string operators and
StringFunctions,VectorFunctionsand
ComplexFunctions,@/@@classification, a queryable exposed set for views, library
feature values by name (TrigFunctions::pi, deg/rad) andincludingAtinsertion. - Enumeration literals are values, in the runtime and across the API.
- Multi-valued defaults are honoured when they conform and reported when they do not; a
default with no declared multiplicity is held to the assumed1..1. - Calc bodies with an implicit result are type-checked, and calc recursion runs under a
budget rather than exhausting the stack. - The subject of a check or evaluation is chosen deterministically and named in verdicts,
labels and over the wire, including a nested one. - A member chain from
that(= that.a) resolves; a root-levelprivate import X::*;
serves its own document — the spelling OMG's training files use. - Parser: keyword-named parameters and loop variables, modifier-driven usage kinds, a
classifier specializing any definition, and KerML classifier/datatype classification.
Tools
- REPL: unclosed submissions, load diagnostics, object-resolved subjects,
%view,
ranked suggestions, quoted qualified names and a pinned%evalcontext. A piped session
whose command could not materialize a slot exits 2, so scripts detect it. - CLI: materialization diagnostics are reported instead of swallowed, and an undecided
check is reported under one prefix. - gRPC: quantities cross in both directions with magnitude, written unit and reduced
term preserved; enumeration literals cross as literals; unreduced, zero-scale or
reduction-less units are rejected;Value.unsetis a new arm the service sends and
refuses to accept.
pysysml 0.2.0
- Startup 509 ms → ~17 ms, with the same bounded
START_TIMEOUTandConnectionError. pysysml.UNSETfor a slot holding no value — falsy, distinct fromNone.- Quantities can be sent, not only read.
pysysml.eval→pysysml.evaluate,pysysml.RuntimeError→pysysml.ExecutionError;
the old names still resolve with aDeprecationWarningand are out of__all__.UnpinnedReleaseErrorfor a release no digest is pinned for, subclassing
ChecksumMismatchError; only it may be answered from a cached binary.pysysml.__version__reports the declaration shipped beside the module; the generated
protobuf stubs ship type annotations, somypyis clean.- Owns only services it spawned, authenticates them by pid, and pins release digests.
Quality gates
Measured on the released commit; the per-figure home is
docs/project/spec-compliance.md.
| Gate | Result |
|---|---|
| Tests and subtests | 4,447 — 4,440 pass, 7 self-skip |
| Execution conformance | 297 cases, all passing |
| Golden execution traces | 98 |
| Golden AST fixtures / negative parser subtests | 86 / 129 |
| Robustness | 165 runtime cases, 8 gRPC cases |
| gRPC conformance | 14 cases |
| Standard library parse | 95/95 files clean |
| OMG training corpus | 98/100 files clean (2 files / 4 errors, pinned OMG source bugs) |
| Python suite | 514 passed, 17 skipped |
cmd/sysml-grpc now has a process-lifecycle gate; it was at 0% coverage in 0.0.8 despite
being a published artifact.
Known limitations
- A port that accepts TCP but never answers gRPC fails after ~9 s of wall clock rather than
the nominal 2.5 sSTART_TIMEOUT. Bounded, and it raises a clearConnectionError. thatinside a nestedaction/constraint/transition-guard body binds to the innermost
usage, sothat.knaming a member of the enclosing part is unresolved. This follows the
spec text as written; say so if you need the outward binding.- Unqualified standard-library names still require an import (
private import ScalarValues::*;). This is spec-conformant — only public top-level elements of a root
namespace are globally visible ([SysML, 7.2] over [KerML, 8.2.3.5]) — and is recorded as
won't-do rather than open. - RDF/Turtle interchange remains partial: expressions are not emitted as triples, and some
example models do not convert. Treat the RDF path as experimental. - macOS binaries are unsigned and un-notarized; the VS Code extension is not on the
marketplace. - The release tag pipeline does not run the OMG corpus gate — it was run locally for this
release.
v0.0.8: Shakeout release
Systemica v0.0.8
sysml 0.0.8 · sysml-lsp 0.0.8 · sysml-grpc 0.0.8 · pysysml 0.2.0
This release is about trust in the answers. v0.0.7 made the answers reachable from
a script, a prompt, an editor and Python; this one fixes nine ways a reachable
answer could still be the wrong one — a violating model reported ✓ passed with
exit 0, a released sysml-grpc reporting version dev, a Python client serving a
cached binary from a different release, an RDF export refusing every model with a
constraint. The editor surfaces the LSP advertised but stubbed are implemented, a
dimensional mistake is caught at validation time, and the documentation is a
handbook instead of a page per feature.
Highlights
A check answers about the object, not the declaration
A constraint or requirement declared on a definition is now evaluated against the
object that carries it, so %constraint, %requirement and -constraint on an
instantiated model read the object's values rather than the declaration's
defaults. A model violating its own mass budget used to be answered ✓ passed
with exit 0 — the failure mode that matters most, since it is silent:
$ sysml rover.sysml -instantiate Rover::rover -constraint Rover::RoverDef::MassBudget ; echo "status=$?"
✓ Created instance of Rover::rover
ID: 1
✗ Constraint Rover::RoverDef::MassBudget failed (on Rover::rover ID: 1)
Assertion evaluated to false: chassis.mass <= maxMass
status=1%eval reads the same subject, so a check and an %eval of a feature in one
session can no longer answer about different objects (a nested redefined feature
is still the exception, below); where several objects carry the feature it refuses
to choose rather than picking one. And a condition whose evaluation could
not be carried out is worded as undecided (? … could not be evaluated) and names
why, where it used to print a failure while exiting 2.
Dimensional mistakes are caught before evaluation
A comparison or sum of quantities whose dimensions are both statically determined
and incommensurable is reported at validation time, as a type-tier warning,
from the stdlib QuantityDimension power factors:
$ sysml model.sysml -validate
model.sysml:6:9: warning: operator '<' combines incommensurable quantities: MassValue (dimension M) and m (dimension L)
mass < 1000.0 [m]
^~~~~~~~~~~~~~~~~Evaluation keeps its hard error, and a warning changes no exit status — so this
catches the mistake early without failing a build that used to pass. A dimension a
declaration does not determine stays unknown and is not reported: there are no new
warnings on examples/, the bundled library or the OMG corpus.
Editors get highlighting and quick fixes
textDocument/semanticTokens/full and /range are implemented over a new
internal/core/highlight package, so highlighting comes from the parse rather
than from a TextMate approximation. textDocument/codeAction answers quick fixes
carried as structured edits from the layer that reported the diagnostic — a
located missing semicolon, a near-miss spelling, an importable namespace. Token
deltas are not implemented and are not advertised.
RDF export stops refusing constraints
The members that state a condition — a constraint body's conditions, a
requirement's assumptions and required conditions, a subject, a result — have a
mapping, so converting a model with a constraint no longer aborts. Conditions are
carried as sysx:condition notation, as every expression-valued position in this
mapping is. Turtle written back as SysML also spells the notation now: an
unrestricted name gets its quotes, so a model with a quoted name re-parses — every
example model that converts and validates on its own — 44 of the 71 — now
round-trips through Turtle and back and validates again.
Python — pysysml 0.2.0
Modelgainedinstantiate,execute_actionandexecute_state, so every
call taking a model hash is reachable on the model it is about.- A cached
~/.pysysml/bin/sysml-grpcrecords the release and the repository
it was downloaded from beside it, and a cache from another release is replaced
rather than served — the failure that silently disabled newer RPCs. A failed
integrity check is its ownpysysml.ChecksumMismatchError— exported from the
package, like every other exception a documented failure raises — and is never
answered from the cache; a download that fails on the network keeps the working
binary. - A service already listening is asked what it is and compared against the release
and capabilities asked for, raisingStaleServiceErrornaming the remedy
instead of aMissingCapabilityErroron the first newer call. It is stopped only
when this client started it and no other client holds it. sysml-grpc -versionreports the metadata the linker sets, where a released
binary saidversion dev / commit unknown.
The command line reads standard input
A lone - names standard input wherever a model path is taken, -convert
included, and is reported as <stdin>; it is read even when stdin is /dev/null,
and stays distinct from a file named -. sysml-lsp parses its command line with
the flag package, so -version works and an unreadable flag is a usage error
rather than protocol mode.
Documentation is a handbook
The pages are organized by what a reader is doing rather than by the feature that
landed: a numbered guide (install → first model → command line → REPL → checking →
behavior → saving and RDF → editors → Python → troubleshooting) under
docs/guide/, looked-up material under docs/reference/, design under
docs/internals/, status under docs/project/. QUICKSTART.md and
RDF_INTEROP.md are split into the chapters they already were, the guide content
stranded in examples/*.md and python/README.md is folded in, and the paths the
v0.0.7 README linked leave pointers behind. scripts/check-doc-links.py gates
every relative link and heading anchor in CI.
Release publishing keeps the notes
Release assets are published with ghr -replace. -delete is an alias of
-recreate: it deleted the release and its tag ref and created an empty one, so
re-running the workflow for a tag wiped hand-written release notes, the title and
the prerelease/latest flags. The Homebrew tap now updates itself from a scheduled
workflow in Open-MBEE/homebrew-tap, reading the release's SHA256SUMS.txt, with
scripts/render-homebrew-formula.sh as the manual fallback.
Verification
| Gate | Result |
|---|---|
gofmt -l . |
clean |
go build ./... / go vet ./... |
clean |
make lint (staticcheck, gosec) |
pass |
go test -race -count=1 ./... |
3,687 tests and subtests — 3,682 pass, 5 self-skip, 0 fail |
TestStdlibConformance |
95/95 bundled library files clean |
| OMG training corpus | 98/100 files clean (2 files / 4 errors, pinned OMG source bugs) |
pytest python/tests/ |
369 pass, 26 skip (integration, no service listening) |
scripts/check-doc-links.py |
0 broken links |
Known limitations
- Converting a model whose behavior is stated as action or state nodes to RDF
still reports the node and aborts (initial nodes,perform,send,
terminate, loop nodes, state regions): 71 of the 120 models underexamples/
convert. - A nested feature redefined on an instantiated object is not yet the subject of a
check or an%eval, so those answer about the declaration — and print no
(on …)— while%slotsshows the instantiated value. - A
calcbody written withoutreturnis not expression-type-checked, so no
static dimensional warning is reported inside it. - Submitting any declaration to the REPL ends an active
%actionor%state
debugging session. semanticTokens/full/deltais not implemented and is not advertised.
Install
# Homebrew
brew install Open-MBEE/tap/systemica
# or a release archive
curl -fLO https://github.com/Open-MBEE/Systemica/releases/download/v0.0.8/systemica-linux-amd64.tar.gz
curl -fLO https://github.com/Open-MBEE/Systemica/releases/download/v0.0.8/SHA256SUMS.txt
sha256sum -c --ignore-missing SHA256SUMS.txt && tar xzf systemica-linux-amd64.tar.gz
# Python client
pip install pysysml==0.2.0The full list of changes is in CHANGELOG.md.
v0.0.7: QOL release
Systemica v0.0.7
sysml 0.0.7 · sysml-lsp 0.0.7 · sysml-grpc 0.0.7 · pysysml 0.1.1
This release is about the surfaces. The engine's answers did not change in ways a
model author would notice; what changed is that the answers are now reachable,
discoverable and trustworthy from a script, a prompt, an editor and Python. It
also carries the element-filter, Query-RPC and loading-performance work that
v0.0.6 shipped without a changelog entry of its own.
Highlights
The command line can fail
sysml is usable in CI and in a Makefile for the first time:
- Exit status means something on every path —
0when the requested
operation succeeded and every requested check held,1when a check answered
false,2when nothing was decided (a model that did not analyse, an
expression that could not be evaluated, an unreadable file, a misused flag). A
check is gated on analysis, so no verdict is ever reported about a model nobody
could read. - Streams are separated — findings and diagnostics on stderr, requested
output on stdout, under onesysml:prefix.-his stdout and exit 0; an
unknown flag stays stderr and exit 2. - Checks run without a prompt —
-validate,-constraint,-requirement,
-satisfy,-instantiate,-calc,-action,-state -advance,-json. One
evaluation stands behind both the flag and the prompt's%constraint/
%requirement/%satisfy. - A project loads as a project —
sysml <dir>,sysml 'src/*.sysml'and
%load <dir>expand, sort and deduplicate their inputs and submit them as one
submission, so resolution no longer depends on load order; diagnostics keep
each file's own line numbers.
$ sysml examples/combined-behavioral-demo.sysml -validate ; echo "status=$?"
✓ package VehicleModel
status=0Breaking: conversion is spelled sysml model.sysml -convert ttl. The model is
a positional argument as in every other mode and -convert names the target
format; -convert <file> and -to <format> are gone (-to reports its
replacement), and the output path no longer picks the format, so -o /dev/null
or a FIFO needs nothing extra.
The REPL stops losing your session
Re-typing a namespace merges into the one already in the buffer instead of
replacing its body. An instance and an active %action / %state session
survive a submission that did not change what they depend on — declaring an
unrelated part def B; no longer discards the instance of A — and anything
genuinely invalidated is dropped with a notice naming the submission that ended
it. The library is discoverable from the prompt: %search, %builtins, Tab
completion over meta commands, symbols and paths, and history kept outside the
temporary directory.
Diagnostics point at the fix
An unresolved name carries the nearest spelling on every surface — command
line, prompt and editor — and candidates are ranked by how a reader would reach
them rather than by edit distance: the budget scales with the typed name's
length, a name in scope beats one reachable only by a path, your own declaration
beats a bundled library one, and a candidate too far out is dropped even when it
is the only one. Whel beside your own Wheel offers Wheel alone.
$ sysml model.sysml -validate
sysml: model.sysml:3:21: unresolved reference: Integer — did you mean ScalarValues::Integer?The base library is not implicitly visible, so a bare Integer is an error with
its qualified spelling offered; every shipped examples/*.sysml and
examples/*.kerml imports what it uses and analyses cleanly.
Element filters are evaluated
filter <expr>; in a package, definition or usage body, import P::*[@T], and a
filter at a document's root all gate the names beside them. A condition is a
boolean predicate over one candidate with the candidate as the implicit self
(KerML 8.2.4), judged against a symbol and the metadata annotating it, with
conformance through supertypes — so @Safety matches a metadata type
specializing Safety. A condition outside the evaluated subset is reported as
such rather than silently selecting nothing, and completion applies the filters
in force where the name is being completed.
Runtime
- An action flow ends at a node with no succession, so an action whose last
node is a plain nested action reachesCompletedinstead of failing. - A performance holds its values in one feature space its tokens share, so a
fork's concurrent branches no longer overwrite each other's assignments —
which write decides a feature both branches assign is step order, stated in
docs/SPEC_COMPLIANCE.md. - A fifth runaway bound,
SYSML_MAX_ELEMENTS(default 1 000 000), bounds the
collection elements one evaluation holds, so1..10000000is refused before it
conjures ~1 GB.%budgetprints the five bounds and the variable that raises
each. - An evaluation outside a body (a guard, a change condition, a default, an
argument, a constraint check) runs in a scope of its own, so a revisited
decision re-reads its calc usage over current values instead of returning the
first evaluation's result.
Editors
A first-party VS Code extension lives in editors/vscode: TextMate highlighting
for .sysml and .kerml, comment/bracket configuration, and an LSP client that
finds sysml-lsp from systemica.server.path, a workspace's bin/sysml-lsp, or
PATH. It is built and side-loaded from this repository (make vscode-package)
and is published to no marketplace. Completion is typed and context-aware, and
sysml-lsp now serves a session over one reader — it used to race two decoders
over its own stdio and die with corrupted framing within seconds of typing.
Python — pysysml 0.1.1
Model.eval()evaluates in the model's context;pysysml.eval()still works
without one.- Instance expansion is complete and typed: nested parts, connector ends and
slots carry their kind, so a value is abool/int/float/strrather than
a string, and a DataFrame column has a usable dtype. - Verification raises on a wrong-kind subject (
WrongKindError) instead of
answering an undecided verdict, andconnect("host:port")is accepted. - Conversion APIs, a
QueryRPC accepting the SysML v2 API & Services query
model's JSON payloads verbatim, and typed model generation are available.
Performance
Loading a large model is linear where it was quadratic (three lookups scanned a
namespace's members once per member), and ParseFile hits its cache on the
source it read, so re-loading unchanged content costs ~0.5 ms instead of ~35 ms.
docs/PERFORMANCE.md records the measurements.
Removed
internal/core/deps — the sysml.toml manifest, lockfile, git fetcher and
resolver — is deleted. Nothing imported it: no manifest was ever looked for by
the command line, the prompt or the server, and the README claim it backed is
gone.
Verification
| Gate | Result |
|---|---|
gofmt -l . |
clean |
go build ./... / go vet ./... |
clean |
make lint (staticcheck, gosec) |
pass |
go test -race -count=1 ./... |
3,585 tests and subtests — 3,580 pass, 5 self-skip, 0 fail |
TestStdlibConformance |
95/95 bundled library files clean |
| OMG training corpus | 98/100 files clean (2 files / 4 errors, pinned OMG source bugs) |
pytest python/tests/ |
pass |
Known limitations
- A surviving REPL debugger session keeps the executor it was started with rather
than being re-lowered. @Safety;as a standalone semicolon-terminated annotation member does not
parse;@Safety(no semicolon),@Safety part x;andmetadata Safety;do.- The LSP advertises no code actions, and
SemanticTokensFullis still a stub —
highlighting comes from the TextMate grammar. Modelhas noinstantiate/execute_action/execute_stateconvenience
method; theConnectionmethods behind them do exist.sysml -validate -treats-as a filename (no stdin), and a piped
expression is parsed as a model rather than evaluated.
Install
# Homebrew
brew install Open-MBEE/tap/systemica
# or a release archive
curl -fLO https://github.com/Open-MBEE/Systemica/releases/download/v0.0.7/systemica-linux-amd64.tar.gz
curl -fLO https://github.com/Open-MBEE/Systemica/releases/download/v0.0.7/SHA256SUMS.txt
sha256sum -c --ignore-missing SHA256SUMS.txt && tar xzf systemica-linux-amd64.tar.gz
# Python client
pip install pysysml==0.1.1The full list of changes is in CHANGELOG.md.
v0.0.6: Calc release
0.0.6 — 2026-08-13
The executable-model release: a calc body is now a computation rather than a
single expression, collections and sequences are evaluable, specialization,
redefinition and variation resolve to one slot each, and the standard
behavioural and structural notation the public SysML v2 models are written in
parses and runs. Five of the eight Open-MBEE SysML v2 models now load with zero
diagnostics, and a full lunar-descent model — curvilinear equations of motion,
a fixed-step RK4 integrator, and an in-model trajectory history — reproduces a
Python reference to ~1e-11.
Language and semantics
- A
calcbody is computational: statements, local declarations,if/else,
whileandforloops, andreturn. A calculation's declared result is
answered after its steps have run, so a body may compute towards it instead of
being one expression; a succession among a calculation's members is not a step. - A calc usage's
outfeatures are readable — every one of them, not only a
designated result — and an evaluation of a multi-output calc with no result
says so, naming the outputs and pointing at the usage, rather than answering
one of them arbitrarily. - An
outfeature assigned by a body statement is bound, including accumulation
into it across a loop, so a trajectory history exists in-model. An output given
both an initializer and a body assignment is a typed error, and a value the
bodyreturns no longer leaks into an output the same body assigned. - A nested calc usage binds its inputs in the environment reading it, so an
argument naming a feature of the enclosing body resolves to that body's value
rather than to the declaration's. - Inputs bind once per activation (KerML 7.4.9): two outputs of one usage can
never come from different input bindings, so readingp.a, assigning to the
usage's input, then readingp.banswers one consistent state instead of
silently mixing two. A usage declared in an action body or among a state
machine's members binds from the values the behaviour has reached. - Sequence indexing (
s#(i)), ranges (1..n, evaluated as the ordered integer
sequence the library declares), and the KerML collection operations —
including,excluding,size,isEmpty,head,tail,select,
collect,sum,min,maxand the rest — are evaluable, with a sequence
expression's results flattened as the library specifies. [*]means0..*, not*..*, so an unbounded part slot instantiates instead
of erroring; a feature chain reaches through a multi-valued feature
(sum(subsystem.volume)), which is what every roll-up model exists to
demonstrate; and a subset of a redefined collection is populated under either
name.- Specialization and redefinition merge: inherited members are merged through
redefinition rather than discarded, nested values of a redefined part survive,
and a renaming redefinition shares one slot whichever of its names carries
the value — base, intermediate or leaf, at any depth. Supplying a value under
two names for the same feature is a typed error, not a silent pick. - A struct-typed attribute valued by a nested body (
attribute :>> material { attribute :>> v = 3.0; }) materializes; a body and= expron the same
feature is a typed error. - Variation points resolve and a selected variant materialises — once, and per
owning instance, so two objects of one type may select differently. A variation
point that inherits its variation-ness is recognised, a variation offers only
its real variants as choices, a selection naming a non-variant is rejected, and
avariantoutside a variation keeps its value and is reported. - Visibility: a protected import is reachable from a specialization, an
unqualified reference no longer reaches a privately imported name, and an
exposeoutside a view usage is reported. viewandviewpointmembers have their own usage kinds.
Notation
- Standard behavioural notation parses and executes: named flows
(flow f from a.out to b.in),accept at/accept when/accept :> e
(named, short-named and nameless),send … viaandsend … to self,
then doneandthen loop … until,if/elsein an action flow,
exhibit statebodies, transition triggers with payload parameters visible to
the guard and effect, performed transition effects, entry-succession initial
states and pending signals. - Structural and interface notation: conjugated ports (
~P, read from the
feature typing, with aport-conjugationwarning on like-typed interface ends
andattribute x : ~Prejected),endusages including the default
end;interface end,require/assumeby qualified reference
(require Pkg::req { }), andsnapshot/timesliceoccurrence portions. The
standard view library ships with the binary. - The compact transition form with a statement effect
(transition active to fin do perform Bump;,do assign c := c + 1;) no
longer demands a second semicolon;;;is now an error rather than silently
accepted; and the compacttransition <src> to <tgt>spelling is recorded as a
Systemica extension rather than presented as OMG notation. - An anonymous
entry action { … }/do action { … }/exit action { … }
body lowers and executes instead of failing at runtime, and a named or empty
action body parameter runs as the loop body.
Runtime and tooling
- A fifth runaway bound,
SYSML_MAX_ELEMENTS(default 1 000 000), bounds the
collection elements one evaluation holds rather than the work a run does: an
element is a 104-byteValueliving as long as the collection holding it, so
the default holds ~104 MB, in the band the other defaults were sized against.
Every materialising path is charged — a range, a sequence literal,->collect
and the other collection operations — and exceeding it is
ErrElementLimitExceedednaming the variable, not the step limit:1..10000000
used to conjure ~1 GB before the step budget reported it. - Activations are scoped, so what a run holds is what it is using: an action
node's body ends the activation it ran in, and an evaluation outside a body — a
decision or transition guard, a change condition or duration, an inline node
expression, an attribute or slot default, an action argument, a constraint
check — runs in a scope of its own. A decision revisited after its body
assigned reads its usage again over those values instead of the first
evaluation's result. %budgetprints the five bounds a session runs on with the variable that
raises each, and a literal expression that spends one is answered with that
failure instead of "no declarations loaded".- Connector usages materialise, including an untyped one
(interface iface connect a.p to b.q;, previously an<unknown>slot), and a
connection's ends are bound by reference to the features it connects rather
than to fresh copies, so a connection identifies what is connected. A selected
variation-interface connection is realised at runtime and routed for the object
that selected it, and connector cycles are guarded. - Features are read through the occurrence a part usage denotes, an unselected
variation read as the base of a feature chain is reported, and a REPL session's
own declaration answers a name the library also declares. - Runtime conformance fixtures fail on an undeclared parser diagnostic, closing
the gate hole that let a fixture execute while emitting a parse error.
Python bindings and sysml-grpc
Instantiatereturns every instance reachable from the root, so a Python caller
expands a composite slot (inst.engine.power) instead of holding a bare instance
id, and a slot the service could not evaluate is reported inSlotValue.error
rather than as a null value. On the client, slot values convert to Python
scalars, lists and nestedInstances, with the raw protobuf still reachable
throughget_slot()/raw_slots; attribute and item access raise
AttributeError/KeyError/SlotErrorrather than returningNone.python -m pysysml.generateemits a Python class per SysML definition —
properties that carry the static type and perform the runtime delegation, so an
editor completesinst.massand a type checker rejectsinst.mas.GetSymbol
reports the type facts this needs (type_infowith primitive reduction,
multiplicity, all specialization edges),pysysmlshipspy.typed, and
emission is deterministic so the output can be committed.GetServerInforeports the service's build version and the capabilities it
supports by name, so a client can require a capability instead of comparing
version strings. Typed generation requires thetype_factscapability and
fails naming the service in use, where it came from and how to replace it:
against the v0.0.5sysml-grpc, which predatestype_info, every generated
feature was typedobject, indistinguishable from one that is genuinely
untyped.- A generated module records the model source hash and the generator's emission
schema, andpysysml.generate --checkregenerates in memory and exits non-zero
when the committed module is missing or would change, writing nothing — a stale
module was previously found at attribute access, or never. TypedObject.from_instancerejects an instance of another definition, naming
both types, instead of failing later with a confusingTypeMismatchErroron the
first slot read;unchecked(instance)is the explicit escape hatch.Model.findaccepts a symbol's own id, the gRPC instance graph terminates on
recursive parts, and set-valued and unmaterialized slots serialize correctly.
pysysml on PyPI is versioned and released independently of t...
v0.0.5: Quantities, satisfaction verdicts and RDF round trip
Highlights
Requirements and constraints actually decide something. A requirement or constraint condition is evaluated against the features of the element stating it — its own attributes, the ones it inherits from its definition, and the values a usage rebinds (attribute :>> maxVerticalSpeed = 1.5;). This was the first known limitation listed for 0.0.4. assert satisfy <requirement> by <part>; now has a verdict of its own: the requirement's subject is bound to an object of the part named by by, and %satisfy evaluates the satisfaction assertions a model states — every one, or the ones a named element states. Assertions can be negated (assert not constraint { … }), a violated condition reports which condition failed, and a requirement with no condition has no verdict rather than passing vacuously.
Quantities and units. attribute maxVerticalSpeed = 1.5 [m/s]; is evaluated as a real quantity: units reduce to a scale factor over base units through the Quantities and Units library's own unitConversion, so commensurable units convert before a comparison or a sum — 1.5 [m/s] <= 5.4 [km/h] is true, exactly, at its boundary — and a composed unit is kept (10 [m] / 2 [s] is 5 [m/s], 4 [m] / 2 [m] is 2). Comparing incommensurable units is an error rather than a comparison of bare magnitudes. Units may be written unqualified at the prompt, a name a declaration shadows is reported with the shadowing declaration and the way out (… shadowing the measurement unit SI::metre — write SI::m to name the unit), %calc accepts quantity arguments in every argument form, and a quantity renders like the bare Real it measures (v = -15.20 [m/s]) instead of full float precision.
Math functions. The KerML function library's scalar numerics are evaluable (sqrt, abs, floor, round, max, min, isZero, isUnit, and the trigonometric family), as is exponentiation (**, ^). exp, ln, log(x, base) and atan2(y, x) have no OMG signature, so they live in a new non-normative extension library, SystemicaMathFunctions.kerml (import SystemicaMathFunctions::*;) — the vendored OMG files are unchanged and the stdlib parse gate is 95/95 clean. A result that is not a finite value of the declared type is reported where it is evaluated instead of folding to a NaN, an infinity or a wrapped integer.
Action and state execution. while, loop, for … in … and if … else … lower to real decision and merge nodes and execute, so an iterating body reaches its final node with the values it computed rather than deadlocking. A then written as a body member is a real succession edge, like the standalone form. An attribute default written in an action or state body is evaluated in the scope that declares it, so a unit or type an enclosing package imports resolves there.
Saving and RDF interop. %save <file> writes the session out — notation (.sysml) or RDF Turtle (.ttl), chosen by the extension, written atomically. A session that does not fully parse still saves as notation, with the syntax errors reported as warnings, so work is never trapped in the REPL. sysml -convert converts between notation and Turtle in both directions, round-tripping packages, definitions, usages, features, imports, connectors, successions and satisfy assertions; what the mapping normalizes and what it refuses is documented in docs/RDF_INTEROP.md.
Runaway bounds you can raise. The evaluation, action-step, event and do-activity budgets are each configurable (SYSML_MAX_STEPS, SYSML_MAX_ACTION_STEPS, SYSML_MAX_EVENTS, SYSML_MAX_DO_STEPS), defaults raised to 10M/1M/1M/5M, and every limit error names the variable that raises it. The evaluation budget bounds one run rather than a whole session, so a long REPL session no longer exhausts its allowance and starts failing everything.
Editor and tooling correctness. A relationship written with a keyword and one written with its symbol are the same relationship end to end (specializes/:>, subsets/:>, redefines/:>>, references/::>), so hover, go-to-definition, completion and the index agree whichever spelling a model uses; a feature taking its effective name from what it redefines is read under that name too. Removing a document unwinds what its wildcard re-exports contributed and reuses its index slot, so an editing session's memory no longer climbs with the number of reindexes. A parser try-parse that gives up now rewinds properly, fixing a condition beginning with a feature named constraint.
Installation
Homebrew (macOS and Linux, avoids the Gatekeeper quarantine a browser download sets):
brew install Open-MBEE/tap/systemicaOr download the bundle for your platform from the assets below and verify it:
curl -fLO https://github.com/Open-MBEE/Systemica/releases/download/v0.0.5/systemica-linux-amd64.tar.gz
curl -fLO https://github.com/Open-MBEE/Systemica/releases/download/v0.0.5/SHA256SUMS.txt
sha256sum -c SHA256SUMS.txt --ignore-missing
tar xzf systemica-linux-amd64.tar.gz && ./sysml --versionAssets
Per platform (linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64):
sysml-<os>-<arch>andsysml-lsp-<os>-<arch>archives (.tar.gz,.zipon Windows);systemica-<os>-<arch>bundle archives holding both binaries under their plain names, which is the layout Homebrew and a PATH install expect;sysml-grpc-<os>-<arch>, raw with a.sha256sidecar — new in this release. This is what the Python client downloads, verifies, caches under~/.pysysml/binand starts, so a Python caller needs no Go toolchain. v0.0.4 described these assets but did not carry them; 0.0.5 is the first release that does;SHA256SUMS.txtover every archive and everysysml-grpcbinary.
Python client
pysysml is released independently, by its own pysysml-v* tag, because it resolves a sysml-grpc binary at runtime from whichever release the caller names rather than from a release matching its own version. Until that tag is pushed, install it from source:
pip install "git+https://github.com/Open-MBEE/Systemica.git#subdirectory=python"Python 3.10+. See docs/RELEASING.md.
Known limitations
- RDF/Turtle conversion has no mapping for a member that states a condition or a step, so a model containing one is reported rather than converted:
requireandassumemembers, a constraint body's condition,subject, a computedreturn,assign,if/while/loop/for, substates, transitions andentry/do/exit(cannot convert the *ast.RequireMember at <file>:<line>). A requirement stating a condition, and any state machine or action body with statements, must be saved as.sysml. Full list in docs/RDF_INTEROP.md. - The REPL's prompt evaluates in the last namespace the session declared. After typing a second package, the first package's members and the units its imports brought in are reached by qualified name only (
1.0 [SI::m], not1.0 [m]). - Re-typing a declaration whose name the session already holds replaces the earlier snippet rather than merging into it, so adding a member to a package by re-typing the package drops the members left out of the new text.
- A multi-valued feature that is both typed and given a default takes the typed instantiation; the default is not merged into it (as in 0.0.4).
- An attribute declared with a type but no value (
attribute diameter : Real;) instantiates as an object of that type rather than an unset value, so%slotsshowsdiameter = Instance(ID: n)with(no features)under it. - The macOS and Windows binaries are unsigned, so a browser download is quarantined by Gatekeeper or flagged by SmartScreen. Install with Homebrew or
curl; see docs/MACOS_DISTRIBUTION.md for the workarounds and what signing would take.
Verification
Measured on the commit this release is cut from: gofmt clean, go build ./... and go vet ./... clean, make lint (staticcheck + gosec) passing, go test -race -count=1 ./... → 2,465 tests and subtests, 2,460 pass / 5 self-skip / 0 fail, stdlib conformance 95/95 clean, OMG training corpus 98/100 clean (the two remaining files hold four pinned OMG source errors), Python client 92 passed / 18 skipped.
v0.0.4: Package-scoped model support
- First tagged release of Systemica
- Language & Semantics: Hand-written SysML v2 lexer/parser, lazy name resolution, type system, tiered validation, 98/100 OMG corpus compliance
- Execution: Instantiation with derived defaults, constraint/requirement evaluation, action execution (fork/join/decision/merge, send/accept), state machine execution (transitions, guards, entry/do/exit, hierarchy, orthogonal regions, pseudostates, triggers)
- REPL (sysml): Full declaration/instantiation/inspection commands, action & state debugging, output modes (quiet/debug/trace), scoped diagnostics
- Tooling: LSP server (sysml-lsp), gRPC bindings with Python support, multi-platform release archives
- Known Limitations: 2 edge cases documented
v0.0.3 - Semantic Fidelity & Textual Notation Improvements
Major improvements to SysML v2 specification compliance. Highlights: - Implicit parameter redefinition (by position) - Reference subsetting member contribution (perform, references) - Complete textual notation for history/entry/exit/defer - If/else branch namespaces - Flow payload declaration - Homebrew distribution for macOS - 51 execution conformance tests + 5 gRPC tests - 950+ tests passing See release notes for complete details.