Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,25 @@ jobs:
- install-solvers
- download-corpora

# The metamodel table is generated from the pilot's SysML.ecore at the pin;
# a pin bump without regenerating it fails here.
- restore_cache:
keys:
- pilot-metamodel-v1-{{ checksum "scripts/download-pilot-metamodel.sh" }}-{{ checksum "scripts/pilot-pin.sh" }}

- run:
name: Download the pilot metamodel
command: ./scripts/download-pilot-metamodel.sh

- save_cache:
key: pilot-metamodel-v1-{{ checksum "scripts/download-pilot-metamodel.sh" }}-{{ checksum "scripts/pilot-pin.sh" }}
paths:
- build/pilot-metamodel

- run:
name: Check the metamodel table matches the pinned SysML.ecore
command: make ontology-table-check

# The corpora gate alone, so its per-root verdict lines are legible in the
# log and a skip cannot hide behind a green suite.
- run:
Expand Down
12 changes: 11 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: all build build-sysml build-prod build-wasm-prod build-lsp build-grpc build-engine build-core build-syntax build-sysml-wasm build-release-wasm build-wasm build-wasm-wasip1 build-wasm-js wasm-check static-check windows-versioninfo-check man man-check install-tree pgo-profile conformance conformance-pkg conformance-rust conformance-julia conformance-matlab test test-shard coverage lint clean install help fuml-expected python-test python-coverage scripts-coverage node-coverage python-install proto proto-buf python-proto proto-ts proto-rust proto-lint proto-breaking vscode-grammar vscode-build vscode-package docs docs-install docs-serve docs-engine-assets docs-counts docs-check changelog-check changelog-render self-model
.PHONY: all build build-sysml build-prod build-wasm-prod build-lsp build-grpc build-engine build-core build-syntax build-sysml-wasm build-release-wasm build-wasm build-wasm-wasip1 build-wasm-js wasm-check static-check windows-versioninfo-check man man-check install-tree pgo-profile conformance conformance-pkg conformance-rust conformance-julia conformance-matlab test test-shard coverage lint clean install help ontology-table ontology-table-check fuml-expected python-test python-coverage scripts-coverage node-coverage python-install proto proto-buf python-proto proto-ts proto-rust proto-lint proto-breaking vscode-grammar vscode-build vscode-package docs docs-install docs-serve docs-engine-assets docs-counts docs-check changelog-check changelog-render self-model

# Version information
# Only release tags describe a build; the moving `nightly` tag is not a version.
Expand Down Expand Up @@ -322,6 +322,16 @@ stdlib-snapshot-check: ## Verify the committed library snapshot matches the bund
go run -C $(TOOLS_DIR) ./gen/snapshot -check
@echo "✓ stdlib.snapshot is current"

ontology-table: ## Regenerate the metamodel table from the pilot SysML.ecore that scripts/pilot-pin.sh pins
./scripts/download-pilot-metamodel.sh
go run -C $(TOOLS_DIR) ./gen/ontology
@echo "✓ internal/translate/rdf/ontology/table.go regenerated"

ontology-table-check: ## Verify the committed metamodel table matches the pinned pilot SysML.ecore, as CI does
./scripts/download-pilot-metamodel.sh
go run -C $(TOOLS_DIR) ./gen/ontology -check
@echo "✓ internal/translate/rdf/ontology/table.go is current"

fuml-expected: ## Regenerate docs/project/fuml-referee-expected.json from the pinned fUML reference implementation (needs a JDK)
./scripts/fuml-expected.sh
@echo "✓ fuml-referee-expected.json regenerated"
Expand Down
1 change: 1 addition & 0 deletions changes/unreleased/ontology-bounded-many.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **Ecore upper bounds above one are multi-valued in the ontology table.** `MultiplicityRange::bound` and `Flow::flowEnd` (upper bound 2) were marked single-valued; they are now `Many`, so api-json writes them as arrays.
1 change: 1 addition & 0 deletions changes/unreleased/ontology-pilot-metamodel.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **The metamodel table is generated from the OMG SysML v2 metamodel version 20250201, read from the pinned pilot implementation's `SysML.ecore`, and records each property's ordering, derivation, redefinitions, subsettings and opposite.** It was pinned to the 202407 rendering of `Open-MBEE/sysmlv2-rdf-ontology`, which has not moved since. The table now declares `FlowUsage`, `PayloadFeature`, `FlowEnd`, `TerminateActionUsage` and the other metaclasses added since, so a type owns them through a `sysml:FeatureMembership` and lists them in `sysml:ownedFeature`, as it owns any feature; `nonunique` is written `sysml:isUnique false`, since the metamodel no longer declares `isNonunique`. Graphs written by earlier releases still read. `ontology.Property` gains `Ordered`, `Derived`, `Redefines`, `Subsets` and `Opposite`, `ontology.Class` gains `Abstract`, and the ontology gate reports a subject typed by an abstract metaclass. CI fails when the table drifts from the pinned metamodel (`make ontology-table-check`).
29 changes: 18 additions & 11 deletions docs/project/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -718,11 +718,13 @@ the inventory in `tests/export/testdata/ontology-known-violations.txt`. The `typ
`function` and `targetFeature` keys stay listed for the fixtures' unresolvable names and body
parameters above; the parameters go when D1/D2 make expression bodies elements of the graph.

The abstract-metaclass half is not mechanizable from the ontology: `SysML.owl` records no ecore
abstractness (see D8), so every metaclass the encoder writes (`kinds.go` and the constants in
`rdf_out.go`/`rdf_expr.go`) was checked by hand against the abstract classes of the pilot's
`SysML.ecore` and `kerml.ecore` — `ConnectorAsUsage`, `ControlNode`, `Element`, `Expose`,
`Import`, `InstantiationExpression`, `LoopActionUsage`, `Relationship`. Two were written:
The abstract-metaclass half was checked by hand while the table came from `SysML.owl`, which
records no ecore abstractness: every metaclass the encoder writes (`kinds.go` and the constants in
`rdf_out.go`/`rdf_expr.go`) against the abstract classes of the pilot's `SysML.ecore` and
`kerml.ecore` — `ConnectorAsUsage`, `ControlNode`, `Element`, `Expose`, `Import`,
`InstantiationExpression`, `LoopActionUsage`, `Relationship`. Generated from the ecore (D8), the
table now records them, and the same gate reports a subject typed by one as `abstract-class`; no
golden graph has one. Two were written:
`Import`, now `NamespaceImport` / `MembershipImport` and, for an `expose`, `NamespaceExpose` /
`MembershipExpose` in place of an `sysx:isExpose` flag, and `ConnectorAsUsage` for a KerML
`connector`, now `Connector`. The decoder still accepts both abstract classes from older graphs.
Expand All @@ -733,7 +735,9 @@ still delivers every `type`, `referent` and `targetFeature` of its fixture.

[`Open-MBEE/sysmlv2-rdf-ontology`](https://github.com/Open-MBEE/sysmlv2-rdf-ontology) renders the
OMG metamodel (version 202407, from `SysML.ecore`) as OML and OWL: `SysML.owl`, 172 classes, 348
object properties, 63 datatype properties, with `rdfs:domain`/`rdfs:range` on each. It uses the
object properties, 63 datatype properties, with `rdfs:domain`/`rdfs:range` on each, and has not
been regenerated since. The current metamodel, version 20250201 as the pinned pilot's `SysML.ecore`
declares it, has 175 classes, 351 object properties and 64 datatype properties. It uses the
*same* namespace we do and its class IRIs are the plain metaclass names we already emit; the
difference is the properties, each qualified by the metaclass that defines it
(`sysml:Element_declaredName`, `sysml:Element_owner` with range `OwningMembership`), and a
Expand All @@ -747,18 +751,21 @@ structural decisions (`internal/translate/export/rdf_out.go`), so the profile is
layer: property name → defining metaclass.

**Done:** the table and the gate. `internal/translate/rdf/ontology` holds the term table generated
from `SysML.owl` by `tools/gen/ontology` from a local checkout (version `202407`,
upstream commit in the generated header): 411 properties spanning only **336 distinct unqualified
names — 59 names are declared by more than one metaclass** (`type`, `value`, `source`, `target`,
by `tools/gen/ontology` from the pilot's `SysML.ecore` at the release `scripts/pilot-pin.sh` pins
(version `20250201`, pilot tag and commit in the generated header), naming each property by the
OWL rendering's `<DefiningClass>_<name>` IRI and recording its multiplicity, ordering, derivation,
redefinitions, subsettings and opposite; `make ontology-table-check` keeps it equal to the pin in
CI. It holds 415 properties spanning only **342 distinct unqualified
names — 58 names are declared by more than one metaclass** (`type`, `value`, `source`, `target`,
…), so the unqualified convention is genuinely lossy in the other direction and a profile encoder
has to pick by the subject's metaclass (`LookupProperty` returns every declaration;
`AmbiguousNames` reports the set). The gate is `TestGoldenGraphsMatchOntology`, whose inventory is
also the profile's work list, sorted into five causes: properties the metamodel declares on a
relationship or membership element that we collapse into the element (`value` → `FeatureValue`,
the multiplicity bounds → `MultiplicityRange`, `isNegated` → `Invariant`, a transition's ends →
`Connector`) — the same collapse D3.3 undid for ownership and D1/D2 will undo for expressions and
ends; names as literals (D7); metaclass names the 202407 rendering does not have (`FlowUsage`,
which it calls `FlowConnectionUsage`, and `TerminateActionUsage`); the metaclasses of our own
ends; names as literals (D7); metaclass names the metamodel does not have (`ClassUsage`,
`IndividualDefinition`, `IndividualUsage`); the metaclasses of our own
`sysx:` namespace; and the properties we write into the SysML namespace that no metaclass
declares, each either a relationship the metamodel reifies as an element (`specializes`,
`subsets`, `redefines`, `references`, `aliasedElement`, `via`) or a notation flag with no
Expand Down
2 changes: 1 addition & 1 deletion docs/project/spec-compliance.md
Original file line number Diff line number Diff line change
Expand Up @@ -2735,7 +2735,7 @@ a whole model or that its vocabulary is settled — see
| Ownership as the abstract syntax states it: `sysml:owner` and `sysml:owningRelatedElement` as element references, and a materialized `OwningMembership` — or `FeatureMembership` where a type owns a feature — between owner and member, each with its own IRI, `sysml:elementId` and member/owner wiring | `export/rdf_out.go` `owningMembership`/`relationshipOwnership`, `rdf/ids.go` `OwningMembershipID` (the member's id plus `_om`, which no element id can be) | `export/ownership_graph_test.go:TestEveryElementCarriesItsElementID`, `:TestOnlyTheOutermostElementIsARoot`, `:TestMembersAreReachedThroughTheirMembership`, `:TestAFeatureIsOwnedThroughAFeatureMembership`, `:TestARelationshipOwnsItsMemberDirectly`, `:TestVisibilityIsStatedByTheMembership`, `:TestOwnershipComesBackFromTheMembershipsAlone`, `:TestWithoutAnyOwnershipPropertyTheTreeFlattens`, `:TestCompactOwnershipGraphStillConverts`, `:TestMembershipWithoutItsEndsIsReported` | ✅ Faithful for containment — one root per document, and the tree comes back from the memberships alone with `sysx:sourceText` and `sysml:owningNamespace` stripped. The compact `sysml:owningNamespace` shape earlier releases wrote is still written and still read; a membership missing an end is a typed error naming `sysml:memberElement`. Not claimed: a graph read back through a live Flexo API |
| Expression node identity: an id in `[a-zA-Z0-9_-]+` and `sysml:elementId`, so a node is addressable by id like an element | `rdf/ids.go` `ExpressionNodeID` (the owner's id, `_p`, and the encoded position, which no element or membership id can be), `rdf/vocab.go` `ExpressionIRI`, `export/rdf_expr.go` `expressionNode` | `rdf/ids_test.go:TestExpressionNodeIDRoundTripsAndCannotCollide`, `export/export_test.go:TestFixtureElementIDsRoundTrip`, `export/w6g4_rdf_expr_test.go:TestExpressionIdentityIsPerPosition`, `:TestExpressionTreesKeepTheRoundTripExact` | ✅ Faithful for addressing — the id is deterministic, reversible and accepted where an element id is; the `.` an earlier id held was refused by `requireValidId` before the store was read. A node is still not a model element: no `sysml:qualifiedName`, no ownership, and it is reached only from the position holding it |
| Reference-valued properties as element references, as the API defines them: `sysml:type`, `importedNamespace`, `importedMembership` (the imported element's owning membership, KerML §8.3.2.5 `MembershipImport::importedMembership`), `sourceFeature`/`targetFeature`, `referent`, `function`, and every declaration-head relationship link the element the name resolves to — an element of the graph by its id, a standard library element by its normative id whether or not the library is in the graph — and stay a literal only for a name that resolves to nothing the model declares or a body parameter, which is no element | `export/rdf_out.go` `reference`, `importedMembership`; `export/rdf_identity.go` `subjectForNode` (normative ids through `identity.LibraryCatalog`); `export/names.go` resolver-checked shortest spelling; `export/rdf_in.go` `referenceName`, `libraryElement` (reads an IRI or a legacy literal) | `tests/export/reference_iris_test.go` `TestReferencePropertiesLinkElements` (a library type, a current-document type, an inherited `start`, a feature chain, a membership import's owning membership, a metadata definition by short name, an unresolvable name kept as a literal), `TestLegacyReferenceGraphsStillRead`; `tests/export/ontology_gate_test.go` `TestGoldenGraphsMatchOntology` (the `literal-for-object-property` inventory shrank to the fixtures' unresolvable names) | ✅ Faithful (a graph from a release that wrote the name as a literal reads back and gains the links on its next hop) |
| Every metaclass written is concrete in the metamodel, as every element the SysML v2 API returns is: an import is `sysml:NamespaceImport` or `sysml:MembershipImport`, never abstract `sysml:Import`; a KerML `connector` is `sysml:Connector`, never abstract `sysml:ConnectorAsUsage`; and no other abstract class of the pilot's `SysML.ecore`/`kerml.ecore` (`ControlNode`, `Element`, `Expose`, `InstantiationExpression`, `LoopActionUsage`, `Relationship`) is written | `export/rdf_out.go` `encodeMember` (`mNamespaceImport`/`mMembershipImport`); `export/kinds.go` `usageMetaclass` (`Connector`), `legacyConnectorAsUsage`; `export/rdf_in.go` `declarationHead` (accepts `sysml:Import` and `sysml:ConnectorAsUsage` from older graphs) | `tests/export/reference_iris_test.go` `TestReferencePropertiesLinkElements`, `TestLegacyReferenceGraphsStillRead` (abstract `sysml:Import`), `TestLegacyConnectorAsUsageStillReads`; golden `imports.golden.ttl` | ✅ Faithful (audited by hand against the ecore models' abstract classes, which the pinned `SysML.owl` does not record) |
| Every metaclass written is concrete in the metamodel, as every element the SysML v2 API returns is: an import is `sysml:NamespaceImport` or `sysml:MembershipImport`, never abstract `sysml:Import`; a KerML `connector` is `sysml:Connector`, never abstract `sysml:ConnectorAsUsage`; and no other abstract class of the pilot's `SysML.ecore`/`kerml.ecore` (`ControlNode`, `Element`, `Expose`, `InstantiationExpression`, `LoopActionUsage`, `Relationship`) is written | `export/rdf_out.go` `encodeMember` (`mNamespaceImport`/`mMembershipImport`); `export/kinds.go` `usageMetaclass` (`Connector`), `legacyConnectorAsUsage`; `export/rdf_in.go` `declarationHead` (accepts `sysml:Import` and `sysml:ConnectorAsUsage` from older graphs) | `tests/export/reference_iris_test.go` `TestReferencePropertiesLinkElements`, `TestLegacyReferenceGraphsStillRead` (abstract `sysml:Import`), `TestLegacyConnectorAsUsageStillReads`; golden `imports.golden.ttl` | ✅ Faithful (`TestGoldenGraphsMatchOntology` reports a subject typed by a metaclass the pinned `SysML.ecore` declares abstract) |
| Ownership cycle in an input graph | refused: every element's `owningNamespace` chain must reach a root, or no root owns the element and printing would emit an empty document | `export_test.go:TestOwnershipCycleIsUnsupported` | ⛔ Rejected rather than emitting an empty file (deliberate). Ownership in the abstract syntax is a tree — an element has at most one owning relationship, and the graph is what a notation document nests — so a cycle is not an unrepresentable model but an inconsistent graph. The alternative, writing the elements a root does reach, would silently drop the cycle's members; the refusal names the elements in it |
| Lexical `//` and `/* */` trivia across the RDF hop | no element owns trivia; `doc`/`comment` are declarations and do convert | `export_test.go:TestCommentsThroughRDF` | ⛔ Not carried through `.ttl` (a direct `.sysml` save keeps it). Trivia belongs to no element in the abstract syntax, so there is no subject to hang it on; carrying it as a per-element `sysx:` blob would make the graph a source archive rather than a model. The surfaces say so: `sysml -convert` and `%save` print the experimental note, and the guide points at the notation save for round trips that must keep comments |
| Blank nodes, RDF collections, bare literal shorthands | rejected by `rdf.ParseTurtle` | `rdf_test.go:TestParseTurtleRejects` | ⛔ Not supported (deliberate; see [the mapping](../reference/rdf-mapping.md)). This is a subset of Turtle, not all of it: an element of this graph is addressed by an IRI derived from its qualified name, and a blank node has no such name, so a blank-node subject could not be read back as an element or written twice to the same address. Collections and bare literal shorthands are unused by anything this mapping writes. A parse error names the line rather than dropping the statement, so a foreign graph using them fails loudly |
Expand Down
Loading
Loading