A SysML v2 model that runs its own verification
This is the output of one command, run on 2026-08-21 against a SysML v2 model file:
analysis SatcomTerminalPAS::pasStudy [phased-array-systems]: 62 metrics
PASS REQ-MARGIN link_margin_db >= 3.0 actual=22.14 margin=+19.14 [must]
PASS REQ-EIRP eirp_dbw >= 40.0 actual=46.04 margin=+6.04 [must]
PASS REQ-SLL sll_db <= -20.0 actual=-25.42 margin=+5.42 [must]
PASS REQ-POWER prime_power_w <= 450.0 actual=368.64 margin=+81.36 [must]
PASS REQ-COST total_cost_usd <= 60000 actual=25600 margin=+34400 [must]
passed: True
The command is sysml2kit verify. It read a model of a 28 GHz satcom
terminal, found an analysis case bound to the
phased-array-systems
engine, executed a 16 by 16 Taylor-tapered array study, and checked five
requirements against the 62 metrics that came back. Every threshold, every
verify link, and every satisfy relationship lives in the model. The engine
knows nothing about SysML. The kit knows nothing about antennas.
Last month I mapped the stack I am building for agent-driven RF design and named its biggest gap: no agent yet takes a requirement and decides to move between layers, or allocates simulation fidelity against a budget. This post is about the substrate half of that gap. Requirements now carry machine-checkable thresholds, analyses now execute, and verdicts now flow back into the model with provenance. The allocation loop where something decides which fidelity to buy remains future work, and I will say more about that at the end.
The missing layer was systems engineering
The stack post described nine repositories that go from requirements to photolithography masks, and a reader could fairly ask where the systems engineering lived. The honest answer was: in flat YAML files and two incompatible requirement dialects. My trade-study tool spelled a requirement as a metric key, an operator, and a value. My benchmark harness spelled it as a metric and a single bound. Nothing modeled parts, ports, allocations, or the question a review board actually asks, which is “show me the requirement, the analysis that verified it, and the number.”
SysML v2 is the standards answer to that question, and it became practical
for tool builders in a way v1 never was: the language has a normative
textual syntax and a REST API specification, both formal OMG standards
since September 2025. So I built
sysml2kit, an Apache-2.0 Python
toolkit, and sysml2kit-rf-library,
the RF vocabulary as model content. The kit stays domain-general. Part
definitions like PhasedArrayAntenna and requirement definitions like
EirpRequirement are SysML packages in the library, loadable by name, and
the two dialects I mentioned are now both generated from one model through
small adapters that shipped in phased-array-systems 0.13.0 and the aedl
benchmark harness this month.
How an analysis becomes executable
The piece I could not find anywhere is a convention for binding a SysML v2 analysis case to code that runs. The specification defines analysis cases structurally and explicitly leaves evaluation strategy to tools. I surveyed what exists as of August 2026: the official API cookbook has no analysis recipe and last saw a commit in March 2025, Sensmetry’s commercial Automator evaluates expressions inside the model rather than dispatching external tools, pyMBE has been dormant since October 2024, and the closest academic work (PySysML2 from a 2024 Springer paper, and arXiv:2606.29006 from June) published no runnable tooling. If a portable convention exists, I could not find it, and I would genuinely like to be corrected.
So here is mine. An analysis case carries a metadata annotation:
analysis pasStudy : LinkBudgetAnalysis {
subject terminal;
objective { doc /* Evaluate the terminal study with phased-array-systems. */ }
}
metadata verificationBinding about pasStudy {
engine = "phased-array-systems";
configRef = "satcom_terminal_pas.yaml";
}
Two design decisions matter more than the syntax. First, engine is a
name, and names resolve against a registry populated from the
sysml2kit.engines entry-point group plus whatever the caller registers
explicitly. A model file can never name an importable code path, because
models are data that agents and strangers will hand you, and data should
not get to choose what executes. Installing phased-array-systems 0.13.0
registers its engine; uninstalling it makes the binding fail loudly with
the list of engines that are available. Second, the config payload lives
in a separate YAML file next to the model, containment-checked so it
cannot reference paths outside the model’s directory. The runner never
interprets the payload. Each engine owns its own schema, which is what let
the phased-array engine reuse its existing study-config format unchanged.

The verification loop. Every box is released code and every arrow is a call verified in source; the loop closes when verdicts are written back into the model that requested them.
The runner does the obvious thing from there. It executes each bound
analysis (engine exceptions are captured per analysis, never raised),
looks up which requirements each analysis verifies through the model’s
verify links, compares each requirement’s threshold against the returned
metric, and reports pass, fail, or unknown with a margin. Unknown means
the metric was absent or was not a number, and unknown on a must-severity
requirement fails the run, because a check that silently checked nothing
is worse than a failure. With --write-back, every checked metric is
recorded as an attribute on its analysis with a provenance string naming
the engine, its version, and the timestamp, and every requirement gains a
verdict annotation. Running it twice replaces the results instead of
piling them up.
What is real and what is not
A verification tool that overstates its own fidelity would be a bad joke, so this section is the part I most want to get right. sysml2kit implements a pragmatic profile of about 20 element kinds, pinned to the 2026-05 spec release, with an opaque passthrough that preserves any element it does not model. The three representations do not carry equal information, and the differences are measured, tested, and documented rather than smoothed over:
| representation | carries everything in the profile? | the part that is not |
|---|---|---|
| interchange JSON | yes, this is the lossless form | nothing known; round trip is property-tested |
| textual notation (.sysml) | names, docs, typing, multiplicity, values with units, satisfy links | verify, derive, and allocate edges vanish in parsing, an upstream defect described below |
| OMG pilot server | element kinds, names, requirement text | ownership, verify and derive kinds, multiplicity; the server also mints its own element ids |
Two findings behind that table are worth reporting on their own, because each cost me a day and would cost you one too.
The parser I build on,
sysmlpy 0.36.2, has a public loader
that destroys part of its own parse tree: the ANTLR visitor captures
satisfy statements, docs, and ports inside part bodies, and the
wrapper-object layer then rebuilds usage bodies lossily and drops them.
sysml2kit works around this by walking the raw visitor output directly,
which recovered short names, cross-package typing, attribute values with
units, and satisfy traceability from text. Two constructs never reach the
visitor output at all, so no downstream workaround exists: dependency
statements and the endpoints of allocate X to Y. I filed both upstream
and then submitted fixes
(#6,
#7); the dependency fix also
repairs three of that project’s own conformance-suite failures. Until
they merge, my test suite pins the losses, so an upstream release that
fixes them will show up as a failing test telling me to wire the fidelity
through.
The second finding came from running a real Systems Modeling API server instead of mocking one. A docker compose file in the repo starts the OMG pilot implementation, and a weekly job pushes a model at it and pulls it back. The first push failed five different ways: the pilot types relationship endpoints, feature typing, and requirement text as lists where my records carried scalars, it has no type for two of my relationship kinds, it drops multiplicity, it ignores the ownership key I send, and it assigns its own element ids regardless of what the client provides. The client now adapts records on the way out and tolerates the differences on the way back, and the table above is honest about what that costs. Mocked tests would have told me none of this.
There is also a syntax oracle: a weekly job feeds every .sysml file my
writer emits to the pilot implementation’s parser, running out of process
through a community-built jar, pinned by checksum. The oracle taught me
one more lesson in humility, since the pilot crashes internally on unit
literals like 5.0 [kg] when its standard library is not loaded, and I
had to teach the job to distinguish that crash from a genuine rejection of
my output.
Where this goes
The narrow claim: as of 2026-08-21 there is a released, pip-installable path from a SysML v2 requirement to an executed analysis and back, with the thresholds in the model, the engine behind an entry point, and the results carrying provenance. The claim I am explicitly not making: that anything here decides what to run. The satcom example binds one analysis to one engine at one fidelity. The interesting version of this problem has a scan-loss requirement deciding between an analytic pattern model and a full-wave solve, with an agent spending a compute budget where the margin is thinnest. That loop needs exactly the substrate this post described, and building it on top is the next project. A full-wave engine wrapping openEMS through antenna-cad is the natural first candidate.
Update, later the same day: sysml2kit 0.4.0 shipped a deterministic first version of that loop. Sibling bindings labeled with fidelity and cost form a ladder, and an escalate policy spends a wall-clock budget on the requirements with the thinnest margins. The 0.4.0 post demonstrates it catching a scanned-gain failure the cheap rung missed. Agent-chosen allocation is still open.
If you work in MBSE tooling and know of prior art for the binding convention, or you maintain a SysML v2 tool and the fidelity tables above misstate what your side does, tell me and I will correct the record.
Try it
pip install "sysml2kit[parse,verify]" sysml2kit-rf-library phased-array-systems
MODEL=$(python -c 'import sysml2kit_rf_library as m; print(m.models_dir())')
sysml2kit verify "$MODEL/interchange/satcom_terminal_pas.json" --report run.json
sysml2kit show "$MODEL/interchange/satcom_terminal_pas.json" --traceability
Every version number, upstream issue, and fidelity claim above was checked against source, PyPI, or a live run on 2026-08-21. The packages are pre-alpha; pin exact versions if you build on them.
These are independent projects I build on my own time. The views are my own and do not represent any current or former employer.
Frequently asked questions
What is sysml2kit?
An Apache-2.0 Python toolkit for building, querying, validating, and executing SysML v2 models. Version 0.3.0, current when this post was written, covers an object model for a documented subset of the language, a textual-notation writer, Systems Modeling API JSON interchange, traceability queries, validation rules, diffs, mermaid diagrams, an MCP server with nine tools, an HTTP client for the standard API, and a verification runner. It is on PyPI as sysml2kit.
How does a SysML v2 model bind to an executable analysis?
A metadata annotation named verificationBinding on an analysis case names an engine and a config file. Engines are resolved by name from the sysml2kit.engines entry-point group, so installing a package like phased-array-systems 0.13.0 makes its engine available. The model file itself never names importable code paths, because models are data and should not be able to run arbitrary code.
Can I run the demo without the RF stack?
You need three pip installs: sysml2kit with the parse and verify extras, sysml2kit-rf-library for the example model, and phased-array-systems for the engine. The whole loop runs in a few seconds on a laptop. The engine executes an analytic 16 by 16 array study at 28 GHz, so no full-wave solver or GPU is involved.
Does this conform to the full SysML v2 specification?
No, and the documentation says so. sysml2kit implements a pragmatic profile of roughly 20 element kinds with an opaque passthrough for everything else, pinned to the 2026-05 spec release. Writer output is checked against the OMG pilot implementation's parser out of process, and fidelity tables in SPEC.md state exactly what survives textual notation, interchange JSON, and a round trip through the pilot server.
Is the requirement-driven fidelity allocation problem solved?
Partly. This post describes the substrate: requirements with machine-checkable thresholds, verify links to analyses, engines that execute, and results recorded back with provenance. Release 0.4.0, published later the same day, added a deterministic first version of the allocation loop: fidelity ladders with an escalate policy that spends a wall-clock budget on the requirements with the thinnest margins. The version where an agent chooses what to spend remains future work.
More in AI agents
- MCP transport is a lifecycle decision · 2026-08-27
- Data provenance for agentic AI workflows · 2026-08-17
- An agent designed a maritime search radar and beat my reference on cost · 2026-08-13