Skip to content

feat(migrate): write every SysML v1 diagram as a v2 view exposing what it shows - #524

Merged
HuiJun merged 34 commits into
developfrom
feature/migrate-diagrams-to-views
Sep 22, 2026
Merged

HuiJun merged 34 commits into
developfrom
feature/migrate-diagrams-to-views

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Description

What and why

Every diagram of a SysML v1 model was skipped by the migrator as "tool-private xmi:Extension content" (1,347 report rows in the proof model). A diagram is a v2 view: what it shows is its expose list and its kind selects a render from the standard Views library. This PR reads the diagrams a tool serializes and writes each as a view usage.

Reader (internal/translate/xmi/sysmlv1/diagram.go). A uml:Diagram (local(xmi:type) == "Diagram", whatever the tool) becomes a tool-neutral record

type Diagram struct {
    ID, Name, Kind, UMLKind, OwnerID string
    Owner, Holder *Element   // ownerOfDiagram, and the element whose extension holds the diagram
    Shown         []ElementRef // usedElements, in serialization order, duplicates dropped, dangling ids kept with Element == nil
    Extender      string
}

Kind/UMLKind come from the DiagramRepresentationObject's type/umlType when one is serialized: the representation object is found by how it is held (diagramRepresentation) or tagged, else by a umlType, never by a plain type (which names a UML type on other descendants), and the shown ids are read from that object's subtree only; a bare diagram keeps an empty kind. Recognized diagrams leave the skipped-extension bookkeeping (no double reporting) while every other typed element of the extension is still recorded as skipped; ElementValue nodes a diagram's extension holds are adopted as before. External hrefs and proxies resolve like any other reference.

Migrator (internal/translate/migrate/diagrams.go). One view per diagram, in the body of the v2 element written for ownerOfDiagram (a package, or a classifier's def — a part def, state def, metadata def for a user stereotype…); a diagram whose owner has no v2 body (an enumeration, an opaque behavior, a property…) is written in the nearest ancestor that has one and reported approximated with the reason; a diagram owned by a behavior that is an operation's method is written in the operation's body, where that behavior is written. A diagram with no owner, or an owner id that resolves to nothing, is placed by the element whose extension holds it, or at the document's top level when the extension is held by the root model or by the document itself (the top level is a host of its own, never a root package); a holder under nothing written is unmapped with the reason. The view exposes every shown element the document writes through the existing qualified-name machinery (no new resolver); shown elements that are unwritten or dangling are dropped and counted in the note, and a diagram showing nothing writable is an empty view (which validates). Renderings are chosen by the diagram-type family the tool type and UML umlType name (first matching row wins):

words in the kind render
table, matrix Views::asElementTable
internal block, parametric, composite structure, interconnection Views::asInterconnectionDiagram
block definition, class, package, object, component, deployment, profile, structure Views::asTreeDiagram
anything else (activity, state machine, sequence, use case, user-defined kinds, no representation) Views::asTextualNotation

render Views::… is written $::Views::… when a member named Views on the way out of scope would hide the library, and a shown primitive is exposed as $::ScalarValues::… when a member named ScalarValues would.

Names follow the existing quoting rules; a view whose name collides with another member of its host (a state, an action, a «View» usage with the same name as its diagram — Cameo's default) is suffixed with the existing sibling-name disambiguation ('Overview 2') and the report says so. Anonymous diagrams are named diagram.

«Expose» to a diagram. #514 wrote an «Expose» dependency whose supplier is a diagram as unmapped ("notation only"). The seam is closed: it now writes expose <the diagram's view, qualified from the client>;. #514 has landed on develop, which this branch merges (ordinary merges), so the diff is the diagram work.

Consequences in shared code.

  • expose and view member references are written with :: member qualification (memberRef), not the feature-chain . that ref uses for expressions; the former mixed the two, which was a syntax error once a diagram exposed a nested feature.
  • A parser change (internal/syntax/parser/defusage.go): a view's reference members (render, satisfy, expose) accept a globally qualified target $::Views::asTreeDiagram, which the grammar allows and which the migrator writes when a member named Views hides the library.
  • Members a behavior's body has no place for are now reported unmapped with the reason instead of silently missing from the report: each behavior kind names the roles its body writes (behaviorWritesMember) and unwrittenMembers reports the rest once — a constraint, attribute, operation or nested behavior of an OpaqueBehavior, FunctionBehavior or Interaction, a port of an Activity or StateMachine (34 rows in the proof model that were absent before). A diagram showing such a member does not expose it.
  • An anonymous element written under an invented name is now always noted as approximated (168 classes and 26 ports in the proof model were written as unnamed without a note).
  • An anonymous association that is written as an actor usage is exposable through that usage rather than emitted a second time as a connection def.
  • A method behavior's members are qualified under the operation whose body holds them, and an association end renamed for a collision is declared, reported and referenced under one name (endNames), so expose never spells a path that is not written. A diagram view hosted by an operation body is numbered past the method's members too (viewName checks bodyMethod(host)), since they are written beside it.
  • A uml:ElementValue a tool keeps in an xmi:Extension is adopted as an operand at any nesting depth, not only one wrapper down; a serialized diagram's own content is never read as an operand.
  • A ** tree over three or more operands is written (a ** b) ** c, since the v2 grammar groups ** to the right while every operator tree is folded left.

Specification basis

SysML v2 Language Specification, Views and Viewpoints: a ViewUsage whose Expose imports name what it shows and whose ViewRenderingMembership names a rendering of the standard Views library (asTreeDiagram, asInterconnectionDiagram, asElementTable, asTextualNotation, internal/workspace/libs/stdlib/Systems Library/Views.sysml). The $:: global qualification is KerML's GlobalQualifiedName. No row of docs/project/spec-compliance.md moves.

How it was verified

  • Fixtures under tests/migrate/testdata/xmi/: diagrams.xmi (a BDD, an IBD, an activity diagram, a table, a state machine diagram; shown elements that are unwritten, dangling, imported from a library, owned by another package, nested features; diagrams owned by an activity and by an opaque behavior that are operation methods) and malformed_diagrams.xmi (missing owner, unknown owner, dangling ids, empty contents, a bare diagram with no representation, an anonymous diagram, an owner with no v2 body, a profile-owned diagram with no writable host). Their notation and report goldens are pinned by TestGoldenConstructFixtures, which also asserts each notation validates with 0 errors. report_views goldens regenerated for the «Expose»→diagram seam.
  • Reader unit tests (diagram_test.go): valid diagram, no owner, no representation, kind from a type-only representation object, a type on a non-representation descendant ignored, a diagram without a representation showing nothing, nested diagrams read separately, duplicate and dangling ids, diagrams in archive entries resolving across documents.
  • Migrator unit tests (diagrams_internal_test.go): package and classifier hosts, missing/unknown owner, dangling ids, empty diagram, host fallback to a written ancestor, unknown kind, collision with an existing member, duplicate diagram names, no representable host, method-owned diagrams hosted by the operation and numbered past the method's members, diagrams owned by action nodes and by a simulation configuration, a shown primitive under a member named ScalarValues — each reported exactly once. behavior_members_internal_test.go: a constraint or attribute of an opaque, function or interaction body, of an operation's opaque method, and a port of an activity or state machine are reported unmapped once and not exposed, while an activity's or state machine's constraints and attributes stay written.
  • Parser: tests/parser/testdata/parse/view_members.sysml gained a $::-qualified target; TestStdlibConformance, TestGolden, TestNegative pass.
  • Gates: gofmt -l . empty, go build ./..., go vet ./..., go test ./..., make lint (staticcheck + gosec) all clean; python3 scripts/changelog.py check, python3 scripts/check-doc-ids.py clean. Corpus gates with the corpora downloaded (OPENSYSML_REQUIRE_TRAINING_CORPUS=1 OPENSYSML_REQUIRE_PILOT_CORPORA=1 OPENSYSML_REQUIRE_PSSM_SUITE=1 go test -count=1 ./tests/corpus) pass with no ratchet movement and no baseline change.
  • Leakage audit over non-test code, docs and the changelog fragment (rg -n "TMT|APS\b|IRIS|WFOS|Time_Acq|M1CS|PEAS" internal docs/reference/sysml-v1-migration.md changes/unreleased/migrate-diagrams-to-views.added.md on the added lines): no matches. The only tool names in production code are the provenance the reader already keyed on (the xmi:Extension extender).

Proof model (Cameo 2024x, 1,347 diagrams)

develop (with #514) this branch
migrated 46,915 48,296
mapped 32,043 32,225
approximated 10,104 11,484
unmapped 4,768 4,587
skipped (profile, library, notation-only) 2,031 684
skipped (nothing refers to) 764 764
-validate errors 0 0
-validate warnings 2,234 2,865

Report rows moved between categories, develop → this branch:

rows element from → to
1,186 Diagram skipped → approximated
161 Diagram skipped → mapped
215 «Expose» Dependency unmapped → mapped
168 + 13 + 13 Class, Port, «ProxyPort» Port mapped → approximated (anonymous, written under an invented name, now noted)
14 + 12 + 5 + 3 Operation, Property, FunctionBehavior, Activity absent → unmapped (members of an opaque behavior, now reported)

All 1,347 diagrams are written as views (525 asTreeDiagram, 107 asInterconnectionDiagram, 100 asElementTable, 615 asTextualNotation); none is unmapped. Of the 1,186 approximated: 1,024 drop unwritten shown elements (259 of them expose nothing because none is written), 241 have dangling shown ids, 178 are hosted by a written ancestor because their owner has no v2 body and 19 by the operation whose method owns them, 134 show nothing, 95 are renamed for a collision (several reasons can apply to one diagram). The 3 «Expose» rows still unmapped expose a model outside the document or come from a client that is not a view.

Warnings. The 631 additional warnings are all Duplicate of inherited member name on view x :> Y subsetting lines of «View» usages (1,404 → 2,035 in total; 828 → 1,107 of them on a single such line): a «View» usage exposing packages with ::** now also inherits the diagram views those packages own and their exposed members, so same-named members reach the subsetting view by two paths. An anonymous «View»-typed property a diagram shows is now named so it can be exposed (view overview :> Overview where develop writes view :> Overview), which moves those warnings onto the named line. Diagram views themselves expose specific elements, never ::**, and none of the warnings is on a diagram view.

Execution. sysml out.sysml -compare-results out.migration-results.json on both outputs (the sidecar is byte-identical: 62 configurations): 17 configurations run and compare without a seed, 24 more with -seed 1 — the same 41 on develop and here — and 21 are refused on both for the same reasons (no migrated behavior). The 41 result tables (242 rows) and the 6 execution errors are identical. The single textual difference is one error message that names an invoked action by its qualified name instead of its bare name: the enclosing state def now owns a view named after that action's diagram, so the existing shadowing rule qualifies the reference — the call resolves to the same action.

Checklist

  • make test and make lint pass locally
  • Tests added or updated for the change
  • Documentation extended where it already covers the surface (see CONTRIBUTING.md)
  • Changelog entry added as changes/unreleased/<slug>.<section>.md, not as an edit to CHANGELOG.md
  • baselines regenerated and make docs-counts run if a gate count moved (compliance rows need nothing: the census is counted at docs build) — no gate count moved
  • No internal work-item labels (waves, slices, F4, K5) in the body, docs, or changelog

devin-ai-integration Bot and others added 9 commits September 22, 2026 00:41
…records

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…member target

Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…e/migrate-diagrams-to-views

An «Expose» whose supplier is a diagram exposes the diagram's view, and a
diagram's view takes a numbered name beside a same-named «View» usage.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ber references with ::

An expose names a member by its qualified name, never a feature chain: memberRef writes Package::Def::feature where ref chains Def.feature for a subsetting. An anonymous association written as an actor of a use case is exposed as that actor, and is no connection def of its own.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration
devin-ai-integration Bot marked this pull request as ready for review September 22, 2026 02:32
devin-ai-integration[bot]

This comment was marked as resolved.

…one, and harden root and viewpoint edge cases

- The diagram reader takes its kind from the first descendant carrying a plain type or umlType attribute, never from an xmi:type, and reads the shown elements beneath that object only; a diagram nested in another is left to its own record.
- A «View» package at the document root is classified as a view; only a root Model, whose members are flattened to the top level, is not.
- An InterfaceRealization at the document root is refused with a note instead of dereferencing its missing owner.
- A «Viewpoint» concernList entry that is not a Comment frames no concern and downgrades the viewpoint, leaving the named element's verdict alone.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

@devin-ai-integration
devin-ai-integration Bot changed the base branch from develop to feature/migrate-views-usecases-expressions September 22, 2026 13:09
@devin-ai-integration
devin-ai-integration Bot added this pull request to stack #528 September 22, 2026 13:10
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 2 commits September 22, 2026 13:29
… method's diagram in its operation

The representation object is selected by how it is held or tagged (a
diagramRepresentation child or a DiagramRepresentationObject), or failing
that by the umlType it states; a child with a plain type attribute is no
longer taken for it, and only the representation's own usedElements are shown.

A diagram owned by a behavior that is an operation's method is written in
the operation's body, where the behavior itself is written, instead of
beside the owning classifier under a numbered name.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
… diagram views

Co-Authored-By: jason.han <hanhuijun@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

…e ScalarValues is shadowed

Co-Authored-By: jason.han <hanhuijun@gmail.com>
devin-ai-integration Bot and others added 2 commits September 22, 2026 14:01
… diagram views

Co-Authored-By: jason.han <hanhuijun@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 2 commits September 22, 2026 14:10
…data def

Co-Authored-By: jason.han <hanhuijun@gmail.com>
… diagram views

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@devin-ai-integration

devin-ai-integration Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

On the "multi-operand powers associate backward" flag (internal/translate/migrate/expression.go, fold): the observation is right — ** is right-associative in the v2 grammar (precExponent in internal/syntax/parser/expr.go), so a ** tree with three or more operands written as a ** b ** c parses as a ** (b ** c) while fold documents a left-to-right fold. Fixed in db54f90: fold parenthesizes what it has folded so far for **, writing (a ** b) ** c; the other operators are left-associative in v2 and need nothing. Regression: the "a power over three operands folds left, as the other operators do" case in TestExpressionTreeLowering.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 3 commits September 22, 2026 14:53
…y their written names

A member of an operation's method behavior is written in the operation's body, so its qualified name — in an `expose` and in the report's target column — now runs through the operation, not the behavior. Association ends are named once, ahead of writing, so a declaration and every reference to a renamed end agree. An «Expose» whose supplier is a proxy with a local `href` fragment naming a diagram exposes that diagram's view.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…owers left

An ElementValue a tool keeps in an xmi:Extension is adopted as an operand wherever the tool nests it — a direct child or under several wrappers — while a diagram's own content and reference extensions are still not read. A power tree over three or more operands is written `(a ** b) ** c`: the migrator folds every operator left to right, and v2 groups `**` to the right.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

Shown elements are read only from the diagram's representation object; when a tool serialized none, other content of the diagram (a legend, its history) is not scanned for usedElements, so the view is empty as its note says.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

…e/migrate-diagrams-to-views

Co-Authored-By: jason.han <hanhuijun@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 2 commits September 22, 2026 16:56
…written as

An action node is written as a usage inside its activity's action def (or the
operation whose method the activity is), and that usage opens no body views
could be members of. Planning a view under the node left the diagram unmapped
and unwritten. The planner now resolves each candidate host to the element
whose v2 body stands for it and refuses action nodes, so the walk falls back to
the containing action def or operation, noted as approximated.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…xpressions' into feature/migrate-diagrams-to-views

Co-Authored-By: jason.han <hanhuijun@gmail.com>

# Conflicts:
#	internal/translate/migrate/expression.go
#	internal/translate/xmi/sysmlv1/xmi.go
#	internal/translate/xmi/sysmlv1/xmi_test.go
…xpressions' into feature/migrate-diagrams-to-views
devin-ai-integration[bot]

This comment was marked as resolved.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
Base automatically changed from feature/migrate-views-usecases-expressions to develop September 22, 2026 17:54
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 3 commits September 22, 2026 18:26
A behavior's member whose role no behavior writer declares was taken as
written, so a constraint of an opaque or function behavior, or a port of
an activity or state machine, was dropped silently and a diagram could
try to expose it. Every behavior body now names the roles it writes and
reports the rest unmapped, once, with the kind of the member and of the
body.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…members

A method behavior's members are written in the operation's body beside
the diagram's view, but the view's name was checked only against the
operation's own children, so a diagram named like a method's attribute,
nested definition or action node redeclared its name.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

… in a root package

A diagram held directly under xmi:XMI names no owner and no holder, and
its view was placed in the first root package that hosts views, making
it that package's member. The top level is now a nil host of its own:
the root model, whose members are written there, resolves to it, and
top-level views are written after the roots. Top-level name reservation
now sees the root model's members and the other roots' declarations.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

An association's connection def is declared under the name its references
and report rows use, so a later sibling sharing a name gets its distinct
name; anonymity is still judged from the source name, so an anonymous
association something refers to is still written as its member ends.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@HuiJun
HuiJun merged commit cbcd667 into develop Sep 22, 2026
15 checks passed
@HuiJun
HuiJun deleted the feature/migrate-diagrams-to-views branch September 22, 2026 20:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant