Date: 2026-09-20
Status: Discovery, design and implementation notes for editors/syson/
Scope: a future editors/syson/, the Java client under client/java/opensysml-client, the wire in api/proto/sysml.proto
SysON pinned at: v2026.9.2 (commit 4632d8999602f4d43da09dd0db12a75adb47dd30); the discovery below was conducted against v2026.9.0, whose ErrorPayload.message() accessor is removed in v2026.9.2
Eclipse SysON is a web modeling workbench for SysML v2 built on Sirius Web: a Spring Boot backend that holds each project as an EMF resource set persisted as JSON in PostgreSQL, and a React frontend that draws the explorer, diagrams, tables and forms. SysON edits models; it does not execute them. OpenSysML executes, verifies and analyzes them but has no graphical editor. The plugin this document designs lets a SysON user select an element and run, instantiate, execute or verify it on OpenSysML, see the result inside SysON, and optionally have OpenSysML check the textual notation SysON imports.
Section 2 answers the eight discovery questions against the pinned SysON checkout, each with the files that answer it. Section 3 is the architecture, section 4 the phased plan, section 5 the unknowns and risks. Every statement that was not read from a file or run on this machine is marked unverified.
Paths below are relative to the SysON checkout unless they begin with client/, internal/,
docs/ or api/, which are this repository.
SysON's backend is one Spring Boot application, and third-party code joins it by being on its classpath in a package the application scans. There is no service-loader layer and no plugin registry of its own:
// backend/application/syson-application/src/main/java/org/eclipse/syson/SysONApplication.java
@SpringBootApplication
@ComponentScan(basePackages = { "org.eclipse.syson", "org.eclipse.sirius.web", "org.eclipse.sirius.components" })
public class SysONApplication { ... }So a jar contributes by declaring @Service/@Configuration classes in a package under one of
those three roots, or by shipping a Spring Boot auto-configuration
(META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports) so its own
package name is free. Every SysON module does the former: SysMLv2DocumentExporter,
SysideSysMLTextImporter, SysONSysMLValidationService, InsertTextualSysMLv2EventHandler
are plain @Service beans implementing Sirius interfaces, and Sirius collects them by type.
The application itself is assembled by Maven, not at runtime:
backend/application/syson-application/pom.xml depends on org.eclipse.sirius:sirius-web-starter
plus one org.eclipse.syson artifact per feature (syson-sysml-metamodel, syson-sysml-import,
syson-sysml-export, syson-sysml-validation, syson-sysml-rest-api-services, the syson-*-view
modules, syson-frontend). Adding a plugin to a SysON build therefore means adding one
dependency to that POM (or to a downstream application POM that depends on syson-application),
which is how SysON's own features are added. There is no way to drop a jar into a running
SysON distribution; unverified: whether the SysON Docker image's classpath can be extended
with a loader.path (Spring Boot's PropertiesLauncher) without rebuilding — the image build
was not inspected.
The Sirius interfaces a plugin implements (all read in SysON's sources, so present in
sirius-web 2026.9.0):
| Contribution | Interface | SysON example |
|---|---|---|
| a GraphQL mutation | @QueryDataFetcher(type="Mutation", field=…) + IDataFetcherWithFieldCoordinates, schema in src/main/resources/schema/*.graphqls |
syson-sysml-import/.../datafetchers/MutationInsertTextualSysMLv2DataFetcher.java, schema/syson-import.graphqls |
| its handler on the editing context | org.eclipse.sirius.components.collaborative.api.IEditingContextEventHandler over an IInput record |
syson-sysml-import/.../services/InsertTextualSysMLv2EventHandler.java |
| explorer context-menu entries | ITreeItemPaletteCustomizer returning SingleClickTreeItemTools |
syson-tree-explorer-view/.../menu/context/SysONExplorerTreeItemContextMenuEntryProvider.java |
| a document exporter | org.eclipse.sirius.web.application.document.services.api.IDocumentExporter |
syson-sysml-export/.../SysMLv2DocumentExporter.java |
| a textual loader | org.eclipse.sirius.web.application.document.services.api.IExternalResourceLoaderService |
syson-sysml-import/.../upload/SysMLExternalResourceLoaderService.java |
| validation | org.eclipse.sirius.components.core.api.IValidationService |
syson-sysml-validation/.../SysONSysMLValidationService.java |
| element lookup | org.eclipse.sirius.components.core.api.IObjectSearchService, IIdentityService |
everywhere |
Versions and JDK. The root pom.xml sets <java.version>21</java.version>,
<sirius.web.version>2026.9.0</sirius.web.version>, <syson.version>2026.9.0</syson.version>
and inherits from spring-boot-starter-parent 4.1.0; CI (.github/workflows/build.yml) builds
on Temurin 21. The OpenSysML Java client is compiled for release 17
(client/java/opensysml-client/pom.xml, docs/reference/java-api.md) and its only compile
dependency is protobuf, so it runs unchanged on the SysON JDK.
The Maven coordinates a plugin depends on, all at 2026.9.0:
org.eclipse.sirius:sirius-web-starter— pulls the Sirius Web application, collaborative, core and EMF modules with the interfaces above;org.eclipse.syson:syson-sysml-metamodel— the generated SysML v2 EMF metamodel (org.eclipse.syson.sysml.Elementand friends);org.eclipse.syson:syson-sysml-metamodel-services—SysMLElementSerializer,ElementUtil;org.eclipse.syson:syson-sysml-exportandsyson-sysml-import— only if the plugin callsSysMLv2DocumentExporterorISysMLTextImporterdirectly rather than through Sirius;org.eclipse.syson:syson-tree-explorer-view— only to reuse the explorer palette constants.
Where they come from. Neither Sirius Web nor SysON publishes to Maven Central. SysON's
settings.xml names GitHub Packages repositories for eclipse-sirius/sirius-web,
eclipse-sirius/sirius-emf-json and eclipse-syson/syson, and GitHub Packages requires an
authenticated request even for public packages (GET …/sirius-web-starter-2026.9.0.pom answered
401 on this machine, see §2.8). A plugin build therefore needs a GitHub token in Maven
settings, as SysON's own CI has.
The frontend is two npm workspaces (frontend/syson, frontend/syson-components, root
package.json with Turbo). SysON does not have a frontend plugin mechanism of its own either; it
uses Sirius Web's ExtensionRegistry. frontend/syson/src/index.tsx builds one registry, adds
its contributions, and renders <SiriusWebApplication extensionRegistry={…}>:
sysONExtensionRegistry.addComponent(footerExtensionPoint, { identifier: `syson_${footerExtensionPoint.identifier}`, Component: SysONFooter });
root.render(<SiriusWebApplication httpOrigin={httpOrigin} wsOrigin={wsOrigin} theme={sysonTheme}
extensionRegistryMergeStrategy={new SysONExtensionRegistryMergeStrategy()} extensionRegistry={sysONExtensionRegistry}>
<DiagramRepresentationConfiguration nodeTypeRegistry={sysONNodeTypeRegistry} />
</SiriusWebApplication>);frontend/syson-components/src/extensions/registry/SysONExtensionRegistry.tsx is the catalogue
of extension points SysON already uses, and the two the plugin needs are there:
- A context-menu action on explorer items. The backend adds a palette entry with an id (the
SingleClickTreeItemTool("newObjectsFromText", …)inSysONExplorerTreeItemContextMenuEntryProvider), and the frontend overrides how that id is rendered throughtreeItemContextMenuEntryOverrideExtensionPointfrom@eclipse-sirius/sirius-components-trees, with aTreeItemContextMenuOverrideContributionwhosecanHandle(entry)matches the id and whosecomponentis aforwardRefMenuItem(InsertTextualSysMLv2MenuContribution.tsx). It receiveseditingContextId,treeId,item,readOnly,expandItemandonClose, and may open a MUI dialog — exactly the shape a "Run with OpenSysML…" entry wants. Diagram-side, the same registry haspaletteToolOverrideExtensionPoint(RotateNodeToolOverriddenContribution.tsx) for a tool on a node anddiagramToolbarActionExtensionPoint(SysONDiagramPanelMenu.tsx) for a toolbar button. - A results panel. Nothing in SysON registers a workbench view, so this point is
unverified against SysON code; Sirius Web's
sirius-web-applicationexports aworkbenchViewContributionExtensionPointin its own sources (not read here). The safe design for phase 2 is what SysON does for import reports: a dialog opened by the menu entry (NewObjectAsTextDocumentReport.tsxrenders themessagesof the mutation payload), with the workbench view as the later upgrade.
Build and packaging. The frontend is one Vite bundle: frontend/syson's build script is
vite build && tsc, CI copies frontend/syson/dist/* into
backend/application/syson-frontend/src/main/resources/static, and that jar serves it. There is
no runtime loading of a second bundle, so a plugin's React code has to be compiled into the SysON
frontend. Two ways, both requiring a SysON build: publish an npm package (SysON publishes its own
to npm.pkg.github.com, publishConfig in frontend/syson/package.json) and add it plus one
addComponent/putData call to a fork of index.tsx; or vendor the components into a fork of
frontend/syson-components. Either way the artifact a SysON deployment consumes is a rebuilt
syson-frontend jar (or a rebuilt image). This is the single largest cost of the plugin and is
called out in §5.
The exporter is org.eclipse.syson.sysml.export.SysMLv2DocumentExporter
(backend/application/syson-sysml-export), an IDocumentExporter:
public boolean canHandle(Resource resource, String mediaType) {
// MediaType.TEXT_HTML, and the resource's first root is an Element
}
public Optional<byte[]> getBytes(Resource resource, String mediaType) {
// installs a MembershipCacheAdapter, then
new SysMLElementSerializer(SYSML_TEXTUAL_FORMAT_OPTIONS, status::add).doSwitch(element)
}It serializes one document (EMF Resource) at a time, from its first root element down.
The serializer itself, org.eclipse.syson.sysml.metamodel.services.textual.SysMLElementSerializer
in backend/services/syson-sysml-metamodel-services, is an EMF switch (3,258 lines, 82 case
methods) that can start at any Element, so a plugin can export a subtree by calling it on the
selected element directly — the exporter only wraps it for whole documents. Names are
deresolved to the shortest unambiguous form by a FileNameDeresolver.
Over HTTP the exporter is reached through Sirius Web's document download,
GET /api/editingcontexts/{editingContextId}/documents/{documentId} with Accept: text/html
(doc/content/modules/developer-guide/examples/download_sysml_file.py; the recipe notes that
text/html is "the media type currently used by SysON to select the textual SysML exporter").
The explorer's Download entry on a document is the same call.
Known gaps, from the Status.warning messages the serializer emits: BodyExpression,
CollectExpression, ConditionalExpression, ExtentExpression, FunctionOperationExpression,
IndexExpression, MetadataAccessExpression, SelectExpression, NamedArgumentList, some
TransitionFeatureMembership kinds, a SuccessionAsUsage with an invalid number of ends, and a
generic "{0} are not yet handled" for anything else the switch does not cover. Unresolved
proxies produce "Found one proxy". The warnings are only logged (Status.log(LOGGER)) — the
download response carries no report, so a plugin that wants the gaps must call the serializer
itself with its own Consumer<Status>. Cross-document references are written as the deresolved
name, so a project of several documents exports to several files that must be parsed together,
which Connection.parseSources does.
SysON does not parse SysML v2 in Java and uses no ANTLR grammar. It embeds the
SysIDE CLI as a JavaScript bundle,
backend/application/syson-sysml-import/src/main/resources/syside-cli.js (version: "0.9.0" in
the bundle; CHANGELOG.adoc records the move to 0.9.0), and runs it as a child process with
Node:
// backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/SysmlToAst.java
final String[] args = { "node", sysIdeInputPath.toString(), "dump", sysmlInputPath.toString() };with a 60-second timeout. The JSON AST it prints is turned into EMF by
org.eclipse.syson.sysml.ASTTransformer (ASTTransformer.convertToElements). The SysML v2
specification revision SysIDE 0.9.0 implements was not checked (unverified); it is a
Langium grammar, which is why a SysON deployment needs node on the path.
Two entry points wrap it, and both are where a pre-import check sits:
- Uploading a
.sysml/.kermlfile goes through Sirius Web'suploadDocumentmutation toSysMLExternalResourceLoaderService(IExternalResourceLoaderService.canHandleon the extension, thenSysmlToAst.convertand the transformer), which returns aLoadingReport. - "New objects from text" on an element is SysON's own mutation
insertTextualSysMLv2(schema/syson-import.graphqls) handled byInsertTextualSysMLv2EventHandler, which callsISysMLTextImporter.importSysMLText(editingContext, parent, text, messages); the one implementation isSysideSysMLTextImporter, and themessageslist is what the frontend dialog shows.
Both are ordinary Spring beans found by type. A plugin cannot wrap them from outside, but it can
replace them: ISysMLTextImporter is a single-bean dependency, so a @Primary bean (or a
@ConditionalOnMissingBean arrangement in a fork) that first runs OpenSysML's ParseSources
over the text and prepends its diagnostics as Messages before delegating to the SysIDE importer
gives the check on the "new objects from text" path with no SysON change. The upload path is a
list of IExternalResourceLoaderServices that Sirius iterates in bean order, so the same trick
needs the plugin's loader to be ordered first and to fall through when it declines; unverified
how Sirius orders them.
SysON serves a partial implementation, and says so:
doc/content/modules/developer-guide/pages/api/api.adoc opens with "The SysML v2 API isn't
fully available yet". The implementation is Sirius Web's generic REST layer under
/api/rest/ (api-details.adoc; Swagger at /swagger-ui/index.html) with SysON delegates in
backend/services/syson-sysml-rest-api-services:
SysONObjectRestService(IObjectRestServiceDelegate) — element lookup, and the project's elements excluding standard-library resources and implied relationships;SysONProjectDataVersioningRestService(IProjectDataVersioningRestServiceDelegate) — commits and branches delegated to Sirius;changessynthesized from the element list, each change idUUID.nameUUIDFromBytes(commitId + elementId);SysMLv2JsonSerializerregistered bySysMLv2SerializerConfig— JSON shape@id(=Element.getElementId()),@type(the metaclass name), then attributes, then references as{"@id": …}; arrays of references omit standard-library elements and virtual links, by comment "to avoid huge responses".
Paths follow the standard (/api/rest/projects, /projects/{p}/commits,
/projects/{p}/commits/{c}/elements[/{e}], /projects/{p}/branches, per the cookbook scripts
under doc/content/modules/developer-guide/examples/). Each project has exactly one branch and
one commit, and "creating additional commits is not functional" (api-details.adoc) — the
commit id is stable while the content changes underneath it, so there is no history to diff
against. Authentication: none; the application property file only sets CORS
(sirius.components.cors.* in application-dev.properties) and no Spring Security filter chain
exists in the SysON sources. Unverified: whether the hosted demo or a Sirius Web profile adds
one.
Normative UUIDs — yes, and they match OpenSysML. SysON's bundled libraries
(backend/application/syson-application-configuration/src/main/resources/{kerml,sysml}.libraries/*.json)
carry two ids per element: the Sirius object id (random, copied per project by
StandardLibrariesLoader's SysONCopier) and the SysML elementId attribute, which is what the
REST API serves as @id. For every element sampled, elementId equals what
internal/semantic/identity/normative.go (identity.ElementID) derives — the RFC 4122 v5 scheme
of the OMG pilot (uuid5(NAMESPACE_URL, prefix + package), then uuid5(package, qualified name)):
| element | SysON elementId |
OpenSysML identity.ElementID |
|---|---|---|
Base |
cdd5d1e3-fe4b-52bd-8a01-51a53f22ba47 |
same |
Base::Anything |
d5b4e7df-e644-5f2f-b95e-cf6f1f6c076d |
same |
Base::things |
3176ab6a-8d7b-5e14-b263-57bd30f77f78 |
same |
Base::DataValue |
eeef8c48-5018-5f4f-b1d7-a199b05d86ed |
same |
ScalarValues::Real |
14c0aa22-5489-59b5-b438-ded26e83ba31 |
same |
ISQBase::MassValue |
9cd0e404-efee-50e5-a59b-681065bd188c |
same |
The cookbook's "IDs are randomized and may differ between runs" (api-cookbook.adoc) is about
Sirius object ids and user elements, not library elementIds. User elements get a random
elementId when created in SysON, and the identity table's SourceDeclared (an @ElementId
annotation) is how OpenSysML would carry one through text (§2.6).
On the wire an OpenSysML symbol is identified by its fully qualified name:
api/proto/sysml.proto SymbolInfo.id is documented "Unique identifier (fully qualified name)",
internal/frontend/grpc/convert.go sets Id: idx.GetFQN(sym), and Symbol.id in the Java
client is that string. A SysON element has three handles: the Sirius object id (IIdentityService.getId,
what tree items and GraphQL use), Element.getElementId() (the SysML id, what REST serves)
and Element.getQualifiedName() (derived, ::-separated). The SysML metamodel's
qualifiedName and Symbol.id are the same notion, so the mapping is:
- SysON → OpenSysML: export the document(s) to text (§2.3), parse them as one model, and
address the selected element by
element.getQualifiedName(). Anonymous elements have no qualified name; both sides derive one from position (OpenSysML'sRoot/FQNfor unnamed symbols, SysON'sElementUtil), and the two derivations are not guaranteed to agree — unverified, and a test of the plan in §4.1. - OpenSysML → SysON: a diagnostic or verdict names a qualified name; the plugin resolves it
in the editing context's resource set (walk
Namespace.getMember()by segment, or keep theMap<String, Element>it built while exporting) and then asksIIdentityService.getId(element)for the id the frontend needs to select or decorate it. - Stable identity across edits: the plugin can write
@ElementId { id = "<elementId>" }annotations while exporting, which OpenSysML reads intoidentity.Info.DeclaredID(internal/semantic/identity/identity.go), so results keyed by UUID survive a rename. The REPL's-sync-diff/-sync-apply(docs/reference/cli.md) already speaks the SysML v2 API keyed by effective id, which is what phase 4 builds on.
The text OpenSysML sees also has to contain the standard library. It does, from its own bundle
(internal/workspace/libs), and because both sides mint the same normative ids (§2.5) a library
reference in a result maps to the SysON library element without a lookup table.
Sirius Web's validation is pull-based: IValidationService.validate(IEditingContext) returns
the diagnostics the Validation view shows, and SysON's SysONSysMLValidationService runs an
EMF Diagnostician over every non-library resource with the EValidator.Registry
(SysMLValidatorRegistrationConfiguration registers SysONSysMLValidator and its rules). Two
ways for a plugin to surface OpenSysML diagnostics:
- As validation results — a second
IValidationServicebean (unverified that Sirius collects several rather than requiring one; if one, the plugin's service wraps SysON's) that returns the last OpenSysML run's diagnostics for the editing context as EMFDiagnostics whosedataholds the EObject, mapped from the qualified name (§2.6). Sirius renders validation entries per object, and the diagram layer decorates nodes whose semantic element has a diagnostic — unverified for SysON's own node descriptions; the explorer does not decorate. - As mutation messages — the
IPayloadof the run mutation carriesmessageswithMessageLevel, which the frontend shows in the dialog (the import path does exactly this). Immediate, but not attached to elements.
Phase 2 uses both: messages for the run that just happened, a validation service holding the run's diagnostics until the next run so the Validation view and diagrams show them. Neither is a persistent marker; the diagnostics live in the plugin's memory per editing context.
Bundled examples. SysON ships one example project template, Batmobile
(SysMLv2ProjectTemplatesProvider.BATMOBILE_TEMPLATE_NAME), loaded from
backend/application/syson-application-configuration/src/main/resources/templates/Batmobile.json
by SysONDefaultResourceProvider.loadBatmobileResource; the same directory holds
Batmobile.sysml, the textual form. The other two templates ("SysMLv2" and "SysMLv2 library")
are empty. There are no further bundled example projects; the textual fixtures are the twenty
.sysml files under backend/application/syson-sysml-import/src/test/resources/ASTTransformerTest/
and the two under integration-tests-playwright/playwright/resources/.
Building SysON on this machine did not work, so the exports SysON would produce were not
regenerated and the checks below ran on the textual files in the repository. The machine has
Temurin-compatible OpenJDK 21.0.12, Maven 3.6.3 and Node 24.19.0. The build steps CI uses
(.github/workflows/build.yml) are:
npm ci && npm run build # needs .npmrc token for npm.pkg.github.com
cp -R frontend/syson/dist/* backend/application/syson-frontend/src/main/resources/static
mvn -U -B -e clean verify --settings settings.xml # needs USERNAME/PASSWORD for maven.pkg.github.comTwo things stop them without credentials: @eclipse-sirius/* npm packages and
org.eclipse.sirius:* Maven artifacts are on GitHub Packages, which answered 401 to an
anonymous GET of sirius-web-starter-2026.9.0.pom and of @eclipse-sirius/sirius-components-core;
and Maven Central rate-limited this machine (429 on spring-boot-starter-parent-4.1.0.pom,
after which mvn -pl backend/application/syson-frontend validate --settings settings.xml
stalled on that download and was stopped after five minutes). A SysON build is feasible with a
GitHub token in ~/.m2/settings.xml and .npmrc, and is the first task of phase 1.
Checks. OpenSysML was built with make build (bin/sysml, bin/sysml-lsp, bin/sysml-grpc
at v0.8.1-1275-g301cba525), and every file above was checked with bin/sysml -validate <file>
(-validate is the REPL's check-and-exit flag; the exit code is 0 clean, 2 on an error).
| file | result |
|---|---|
templates/Batmobile.sysml |
1 error: 254:16 unresolved reference: Dont_Panic_Batmobile — the view batmobileParts exposes Dont_Panic_Batmobile::** but the package is Batmobile; a stale name in the template |
ASTTransformerTest/convertAliasTest/alias.sysml |
clean |
convertAllocationTest/allocation.sysml |
1 error Must have at least two related elements (an allocation with a single part source;), 1 warning (source duplicates an inherited member) — the fixture is a deliberately minimal allocation |
convertAssignmentTest/assignment{1,2,3}.sysml |
clean |
convertBooleanTest/boolean.sysml |
2 errors: 26:42 Initialized feature must be variable (attribute init1 … := 0), 53:2 Only a variable feature can be constant (constant attribute ro;) — the fixture exists to exercise every boolean flag of the metamodel, not to be well-formed |
convertFeatureTypingTest/featureTyping.sysml |
clean |
convertImportTest/import.sysml |
clean |
convertInheritanceTest/inheritance.sysml |
clean |
convertNamespaceImportTest/{model,namespace}.sysml |
clean, each alone |
convertNamespaceImportValueTest/model.sysml |
2 errors alone (its imports live in the sibling file); clean when the directory is loaded as one model |
convertNamespaceImportValueTest/namespace.sysml |
clean |
convertProjectWithErrors/proxyResolutionError.sysml |
1 error unresolved reference: FakeType — by design, the fixture tests SysON's error path |
convertRedefinesTest/redefines.sysml |
2 syntax errors at 3:36: attribute packetSecondaryHeader' redefines … has a stray ' — the fixture is not valid notation |
convertSubclassificationTest/subclassification.sysml |
clean |
convertVisibilityTest/visibility.sysml |
clean |
isUniqueFeature/model.sysml |
clean |
playwright/resources/SysMLv2WithGeneralView.sysml |
clean |
playwright/resources/SysMLv2WithInterconnectionView.sysml |
clean |
Fourteen of twenty-one files check clean; every finding on the other seven is either a
deliberate error fixture or a real defect in the fixture (the Batmobile package name, the stray
quote), none is an OpenSysML parse failure on notation SysON accepts. What this does not
establish, because SysON was not built, is how OpenSysML fares on text SysON's serializer
produces from a project edited graphically — the Status gaps in §2.3 predict warnings on
models with the listed expression kinds. That is the first measurement of phase 1.
The textual export SysON produces from a project edited graphically — SysON's
SysMLElementSerializer, seen on the Batmobile template — used to drop pieces of
the model rather than serializing them, and OpenSysML reported each as a syntax
error where the gap landed. Four of those gaps are fixed on the Open-MBEE fork
of SysON (branch integration/textual-export-fixes, combining the fork's pull
requests 1–3 with fix/succession-implicit-target) and are pending upstream; a
SysON built from that branch exports:
- Feature-chained connector ends —
interface bat2eng : PowerInterface connect battery.powerPort to batmobileEngine.enginePort;(previouslyconnection bat2eng : PowerInterface connect to ;). - A succession to an anonymous decision node —
then decide;(previouslythen;followed bydecide ;). - A succession with an explicit
firstsource and an implicit target —first start then startBatmobile;followed byaction startBatmobile;(previously the target action was inlined as the invalidfirst start then action startBatmobile;, which OpenSysML rejected withexpected ';' or '{' after initial node). - A satisfy with no subject —
assert satisfy 'system components';(previously a danglingassert satisfy 'system components' by;).
With those in place a Batmobile export parses far enough that instantiate,
validateInstance, verifyRequirement and verifySatisfaction answer
ok: true. What still goes wrong is in the serializer, not the plugin or the
parser:
- A succession to a non-action target is dropped.
then timeslice charginginpart bm1exports astimeslice chargingwith the warningUnable to export a SuccessionAsUsage (…) with an implicit target and no following action; the implicit-target fix only inlinesActionUsagetargets. - Redefinition through a feature chain is dropped.
attribute :>> battery.capacity = 40000 [SI::'watt hour'];exports asattribute = 40000 [SI::'watt hour'];, an anonymous attribute with no redefinition — valid text that no longer says what it meant. view defexports aspart def.SysMLElementSerializerhas nocaseViewDefinition, so aview deffalls tocasePartDefinitionand exports aspart def 'Part list' { … }with itsfilter @SysML::PartUsage;dropped; theview batmobileParts : 'Part list'usage then fails OpenSysML'sA view must be typed by one view definitioncheck. Similarlyexpose Dont_Panic_Batmobile::**;exports asexpose Batmobile;.- Unresolved library proxies are reported as
Found one proxy kermllibrary:///…warnings; the export itself is unaffected.
What the Batmobile run reports after the fork fixes, attributed:
- Batmobile model.
actor driver : Batman;andstakeholder pm : ProductManagement;are typed byitem defs, which OpenSysML rejects as kind mismatches (actors and stakeholders are part usages).'Drive Batmobile'execution stops at the decision guard becausescanEnvironment.statusis never given a value. - OpenSysML runtime. The inherited
resultis an output parameter, andActivateRocketBooster :> 'Activate rocket booster'runs to completion. Only areturndeclared in a non-function owner is refused, matching the validator.
Verdicts on standard-library constraints (ShapeItems, Geometry) that
validateInstance reports carry no SysON element: the library is not part of
the project, so there is nothing to select.
editors/syson/
README.md
pom.xml
syson-api-stubs/ compile-only Sirius Web and SysON API classes
backend/ org.openmbee:opensysml-syson
src/main/java/org/openmbee/opensysml/syson/
OpenSysMLAutoConfiguration.java
run/RunWithOpenSysMLService.java
export/ProjectTextExporter.java
identity/ElementIndex.java
run/RunWithOpenSysMLInput.java
run/MutationRunWithOpenSysMLDataFetcher.java
run/RunResultStore.java
menu/OpenSysMLTreeItemPaletteCustomizer.java
validation/OpenSysMLValidationService.java
src/main/resources/schema/opensysml.graphqls
src/test/java/...
frontend/ @openmbee/opensysml-syson
src/extension/RunWithOpenSysMLMenuContribution.tsx
src/dialog/RunWithOpenSysMLDialog.tsx
src/results/RunResultsPanel.tsx
src/registry/opensysmlExtensionRegistry.ts
distribution/ (not in this phase)
The backend module depends on org.openmbee:opensysml at the version
client/java/pom.xml declares (the Java client,
§2.1 for why it is JDK-compatible), org.eclipse.sirius:sirius-web-starter,
org.eclipse.syson:syson-sysml-metamodel and syson-sysml-metamodel-services, all 2026.9.0,
the last three provided because the SysON application already ships them. It carries no
sysml-grpc binary: the client starts one from OPENSYSML_GRPC_BINARY, or downloads the pinned
release, or connects to OPENSYSML_SERVICE (ConnectionOptions, docs/reference/java-api.md),
and the configuration bean exposes the same three choices as opensysml.* Spring properties. A
deployment with one SysON JVM gets one child sysml-grpc per classloader, which is one.
- The user right-clicks an element in the explorer.
OpenSysMLTreeItemPaletteCustomizer(anITreeItemPaletteCustomizerwhosecanHandlematches SysON's explorer description id and anElementthat is not from a library) appendsSingleClickTreeItemTool("runWithOpenSysML", "Run with OpenSysML…"). - The frontend's
TreeItemContextMenuOverrideContributionfor that id renders the menu item; choosing it opensRunWithOpenSysMLDialog, which offers the operations the selected element's metaclass admits —instantiatefor a definition or usage,executeAction/exploreActionfor anActionDefinition/ActionUsage,executeState/exploreStatefor a state,verifyConstraint,verifyRequirement,verifySatisfaction,evaluateCalc,runAnalysisfor the corresponding kinds, andvalidatefor anything — and their arguments (initial values, step budget, schedule —ExecutionOptionshasscheduleandperformer). - The dialog sends
mutation runWithOpenSysML(input: { id, editingContextId, objectId, operation, arguments }).MutationRunWithOpenSysMLDataFetcherconverts it to theRunWithOpenSysMLInputrecord and dispatches it to the editing context, which serializes it with every other edit of that project (Sirius runs one input at a time per editing context). RunWithOpenSysMLEventHandler:- resolves
objectIdwithIObjectSearchServiceto anElement; ProjectTextExporterwalks the resource set, skips library resources (ElementUtil.isStandardLibraryResource) and referenced libraries, serializes each remaining resource withSysMLElementSerializerand aConsumer<Status>that collects the gaps of §2.3 as warnings, and records for each namedElementits qualified name → (element, Sirius id) in anElementIndex;connection.parseSources(documents)— oneModel, its parse cache keyed by content so an unchanged project is not re-parsed; the model'sdiagnostics()become the first messages;- the operation:
model.instantiate(qn),model.executeAction(qn, options), …, each a one-line call onModel; - maps the result: a
Diagnosticwith aSpanis located by re-reading the exporter's line map (it knows which element it wrote at which line), one without a span by the qualified name in its message when there is one; anInstantiation's root and reachable instances, anActionRun/StateRun(outputs, trace), anExploration, aVerification, aValue, are rendered asmessagesplus a JSONresultfield of the payload, and stored inRunResultStorefor the editing context; - emits
RunWithOpenSysMLSuccessPayload(id, messages, result)and aChangeDescriptionof kindNOTHING— a run does not change the model — unless the user asked to record the minted@ElementIds (§2.6), which is aSEMANTIC_CHANGE.
- resolves
- The dialog renders the payload: messages by level, the verdict, the instance tree or trace as
a table.
OpenSysMLValidationService.validate(editingContext)returns the stored diagnostics as EMFDiagnostics whosedatais theElementfromElementIndex, so the Validation view and any diagram decoration show them until the next run replaces them.
| OpenSysML | SysON |
|---|---|
Diagnostic{severity, message, span} |
Message(text, MessageLevel) in the payload; EMF Diagnostic{severity, source="opensysml", data=[element]} in the validation service |
Symbol.id (qualified name) |
ElementIndex.element(qn) → Element; IIdentityService.getId(element) for the frontend |
Instantiation.root/reachable, Value.InstanceReference |
a tree in the dialog; each instance's type qualified name links to the SysON element |
Verification (verdict, verifications, instances, diagnostics) |
verdict as a message; a decided false is a message, an undecided verdict's error() is an error |
ActionRun/StateRun outputs and trace, Exploration runs and choice points |
tables in the dialog; step subjects link by qualified name |
| library element in any of the above | the library Element by normative elementId, no export needed (§2.5) |
Nothing is written into the SysON model by phases 1–3 except, on request, @ElementId
annotations; results are transient per editing context.
Deliverable: editors/syson/backend builds against SysON 2026.9.0, and a SysON built with it
answers a runWithOpenSysML GraphQL mutation for every operation in §3.2 step 2, tested with
Spring tests over SysON's own fixtures.
- Obtain a GitHub token for Maven and npm and build SysON
v2026.9.0unmodified (§2.8); record the exact commands and time ineditors/syson/README.md. - Export the Batmobile template and a project of each
ASTTransformerTestfixture throughSysMLElementSerializer, check each export withbin/sysml -validate, and record the results beside §2.8's table — this measures the exporter gaps and the anonymous-name agreement (§2.6) before any code depends on them. OpenSysMLConfiguration,ProjectTextExporter,ElementIndex, the input/handler/fetcher andRunResultStore; the GraphQL schema; the palette customizer (the entry appears with the default rendering, a no-op click, until phase 2).- Tests: a Spring Boot test in the module that boots SysON's test application with the plugin
jar, creates a project from the Batmobile template, runs
instantiateon a part andverifyRequirementon a requirement, and asserts the payload.
Deliverable: the menu entry, the dialog, and results in the Validation view.
editors/syson/frontendas an npm package with the override contribution and the dialog; thedistribution/fork adds the two dependency lines and merges the registry.OpenSysMLValidationService; verify which SysON diagram descriptions decorate elements with diagnostics (§2.7 unverified) and, if none, add the decoration to the dialog only.- A workbench results view replaces the dialog's result section if
workbenchViewContributionExtensionPointproves usable (§2.2 unverified).
Deliverable: OpenSysML diagnostics on text a user pastes or uploads, before SysIDE converts it.
CheckingSysMLTextImporteras@Primary ISysMLTextImporter:connection.parse(text)(with the rest of the project as context throughparseSources), prepend diagnostics as messages, then delegate; anopensysml.import.block-on-errorproperty decides whether an error stops the import.- The same in front of
SysMLExternalResourceLoaderServicefor uploads, once the ordering ofIExternalResourceLoaderServices is established (§2.4 unverified); otherwise a fork of that class indistribution/. - Compare the two parsers on the fixtures of §2.8 and record where they disagree.
Deliverable: OpenSysML reads and writes a SysON project through /api/rest/ instead of text,
keyed by elementId.
- Point
sysml -sync-diff <syson-url>/api/rest/projects/<p>at a SysON project (internal/translate/interop/reposync); record what the partial API (§2.5: one commit, arrays without library elements,@typenaming) breaks in the diff and fix it on the OpenSysML side where it is a client assumption, or document it as a SysON gap. - Mint
@ElementIds for user elements on export so text and repository identity agree. - Track SysON's API completion (commits, branches) on its
mainand re-run; the release pinned here does not support it.
Unverified, in the order they matter:
- How OpenSysML fares on text produced by
SysMLElementSerializerfrom graphically edited projects — no SysON export was produced on this machine (§2.8). - Whether the qualified names SysON derives for anonymous elements agree with OpenSysML's, on which result mapping for unnamed elements depends (§2.6).
- Whether
workbenchViewContributionExtensionPointinsirius-web-application2026.9.0is usable for a results view (§2.2); Sirius Web's own sources were not read. - Whether any SysON diagram description decorates nodes from
IValidationServiceresults (§2.7). - The order Sirius Web calls
IExternalResourceLoaderServicebeans in (§2.4). - Whether a SysON Docker image's classpath can be extended without a rebuild (§2.1).
- Which SysML v2 specification revision SysIDE 0.9.0 implements (§2.4).
- Whether any SysON deployment profile adds authentication in front of
/api/rest/(§2.5).
Risks:
- Every deployment is a fork. Backend jars need a POM line, the frontend needs a rebuilt
bundle; there is no drop-in path. The
distribution/module contains the fork, but each SysON release means re-applying it and re-pinningsirius.web.version; SysON releases roughly monthly (the tag isv2026.9.0;CHANGELOG.adoclists a version per month). - Text is the interchange. Until phase 4 the plugin round-trips the model through the
serializer, whose known gaps (§2.3) are in expressions — exactly what constraints, calculations
and requirements are made of. A model using
ConditionalExpressionorSelectExpressionexports with a warning and a hole, and OpenSysML verifies the hole. - GitHub Packages credentials are needed to build anything at all, including CI for
editors/syson/; a token must be provisioned for the OpenSysML repository's CI. - Two parsers. SysIDE (SysON) and OpenSysML will disagree on some notation; phase 3 makes the disagreement visible to the user, which is the point, but it needs a policy for whose verdict blocks an import.
- Node on the backend host is already required by SysON for SysIDE; the plugin adds a
sysml-grpcbinary. Both are process spawns from the JVM, which some deployments forbid; the client'sOPENSYSML_SERVICEmode (a service run elsewhere) is the answer, and the plugin's configuration must make it the documented production mode. - Long-running RPCs on the editing-context thread. Sirius serializes inputs per editing
context, so an
exploreActionthat takes a minute blocks that project's edits for a minute. The handler should run the RPC off the dispatcher thread and complete the payload asynchronously, or enforce the request timeout fromConnectionOptions; which the Sirius input contract permits is to be confirmed in phase 1.
Where the code departs from §3–§4, and why.
- Beans are registered by Spring Boot auto-configuration, not by package scan. The jar's
classes live under
org.openmbee.opensysml.sysonand are listed inMETA-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports, so a deployment adds the jar and nothing else; putting them underorg.eclipse.syson(§2.1) would have made a foreign jar claim SysON's namespace. - Compile-only stubs stand in for the SysON and Sirius Web artifacts by default. Both are
published only to GitHub Packages, which needs a token even to read.
syson-api-stubs/holds the exact classes, signatures and constants the backend uses, taken from the2026.9.0sources; the defaultstubsMaven profile compiles and tests against them, and-Psyson-artifactsswaps in the realprovideddependencies throughsettings.xml. The frontend does the same with ambient type declarations for@eclipse-sirius/sirius-components-coreand-trees, runtime doubles used only by its tests, andnpm run install:syson/build:sysonfor the real packages. The mandatory CI job runs the stub path;syson-artifacts.ymlruns the real one on demand with aread:packagestoken. - The palette customizer runs last. Sirius applies
ITreeItemPaletteCustomizers in bean order and SysON's own customizer rebuilds the palette instead of extending it, so the OpenSysML customizer is orderedLOWEST_PRECEDENCEand appends to whatever it receives. - The frontend appends to SysON's contribution rather than merging registries. SysON's
registry already
putDatas attreeItem#contextMenuEntryOverride, andputDatareplaces;addOpenSysMLContributions(registry)reads the existing entry and re-puts it with the OpenSysML contribution appended. - Budgets are schedule strings. The Java client exposes no separate budget fields;
opensysml.explore.runsandopensysml.explore.depthare encoded into the exploration schedule (explore:runs=N,depth=D) that the explore operations send. - Results stay in the dialog. The workbench view extension point (§4.2 step 3) was not
exercised; the dialog renders outcomes, traces, verdicts, instances and diagnostics, and a
diagnostic click selects its element. Diagnostics also reach the Validation view through
OpenSysMLValidationService, which serves the last run per editing context. - Documents render through the same export.
RENDER_DOCUMENTandRUN_DOCUMENT_QUERYreuse the exported project andModel.renderDocument/Model.runDocumentQuery; the result carries the rendered Markdown or HTML (the two forms the service renders) with the file name a rendered set gives the document (RenderDocumentResponse.file_name, planned by the service'sDocumentFiles, so links between downloaded documents hold), or the query's columns and rows mapped back to project elements. Bindings reuse the dialog's name/expression inputs: a qualified name the export or the service resolves binds the element, anything else is evaluated and bound as a literal. The frontend shows HTML in a sandboxediframeand downloads the content through an object URL; PDF stays CLI-only. - A project's copy of a bundled library is not exported. SysON resolves
DocumentQueries::*only when the project holds the library's text, but exporting that copy would shadow the service's own and the document check (part def :> DocumentQueries::Document) compares against the service's symbol.ServiceLibrariesasks a freshly parsed empty model whether it already resolves alibrary package's name and what it declares at any depth;ProjectTextExporterdrops a copy declaring nothing beyond that with an informational message, and exports one that adds declarations with a warning that it shadows the service's library. - Diagnostic selection: diagnostics naming a qualified name select that element; span-only diagnostics select the root element of the exported document containing the line. The serializer emits no source map.
- Deferred: building SysON itself and the Batmobile export survey (§4.1 steps 1–2), the
Spring Boot test over SysON's test application (§4.1 step 4, replaced by unit tests over a
stubbed export plus integration tests against a real
sysml-grpc), and phases 3–4.