Skip to content

feat(migrate): map calls to the fUML and Alf library primitives to the v2 library - #487

Open
devin-ai-integration[bot] wants to merge 9 commits into
feature/composite-state-connection-pointsfrom
feature/fuml-alf-primitive-behaviors
Open

devin-ai-integration[bot] wants to merge 9 commits into
feature/composite-state-connection-pointsfrom
feature/fuml-alf-primitive-behaviors

Conversation

@devin-ai-integration

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

Copy link
Copy Markdown
Contributor

What and why

sysml model.xmi -convert sysml refused every CallBehaviorAction whose behavior is a primitive of the fUML library (http://www.omg.org/spec/FUML/<date>/fUML_Library.xmi) or the Alf library (http://www.omg.org/spec/ALF/<date>/Alf-Library.xmi) as "a behavior with no v2 declaration", and the flows out of them then carried nothing, so any migrated activity that builds a string or a list was inert. On the OMG PSSM suite that was 586 refused calls (398 Including, 104 Concat, 18 Integer ToString, 13 Boolean ToString, 9 plus, 9 Size, 5 lt, 5 WriteLine, …).

The migrator now maps these calls to the SysML v2 / KerML standard library:

  • Recognition by provenance, never by name. primitiveAt(href) keys on the library document the href names (FUML/<any date>/fUML_Library.xmi, ALF/<any date>/Alf-Library.xmi, http or https, host www.omg.org) and the exact fragment within it. A model's own Concat is an ordinary call. Nothing is loaded from the network or the file system.
  • Bundled libraries. sysmlv1.Element.RefIDs (new, the raw ids a role refers to) keeps an href as written even once the model bundles a copy of the library and the reference resolves to that copy. migration.primitiveCalled maps by that href; the copy's element is still what the pins' types are read from.
  • Library modules of the tool's own (MagicDraw, Cameo). These tools do not point at the OMG document: the library is a used project, the call's href is fUML-Library.mdzip#_jJIy…, and beside it MagicDraw records referentPath="fUML_Library::PrimitiveBehaviors::ListFunctions::ListSize" referentType="FunctionBehavior"; the bundled snapshot is a uml:Package fUML_LibraryPrimitiveBehaviorsListFunctionsuml:FunctionBehavior ListSize with the OMG structure and different ids. primitiveNamed recognizes such a call by the library's identity, and needs both halves: the href's document must be the library's own (fUML-Library/fUML_Library, Alf-Library, whatever the extension or path — libraryIdentities), and the behavior it resolves to — the bundled copy when the module's contents are in the archive, else the referentPath — must be a behavior sitting under the library's own root package, family and name (fUML_Library:: or FoundationalModelLibrary:: for fUML; Alf::Library:: for Alf, which is what Alf-Library.xmi's root model and package are called), keyed into the same primitives table by path (primitivePaths, the second index over the one table). A package of the model's own that happens to be called fUML_Library, or a referentPath naming the library beside an href into some other module, matches neither and stays what it is. Each report entry says which provenance identified the call: "known by its OMG href …", "known by the copy of the library the model bundles as fUML-Library.mdzip, which its href resolves to", or "known by the referentPath … recorded beside its href into the library module fUML-Library.mdzip".
  • Lowering. activity.callPrimitive writes the call through the existing pin writer: the action with its pins, each result pin valued by the v2 expression over the argument pins in v1 order (out result : ScalarValues::String = StringFunctions::'+'(x, y);, out result[0..*] ordered nonunique = SequenceFunctions::including(seq, element);). The expression is a side table (activity.computed) consulted by declarePins; producesAt reports the result pins as producing, so the existing flow/starvation analysis carries the values on. Pin declarations now also carry ordered/nonunique from the pin, which the value's sequence type needs.
  • Refusals keep the existing placeholder logic. primitiveRefusal runs before the "no v2 declaration" check. A behavior the v2 library has no equivalent for is Unmapped with the reason. A scalar parameter the call passes no pin for, or whose pin only flows from something producing no value (valueless), or that admits none and nothing fills (holdsNone), starves the call as in v1 (as in fUML, where an action does not fire until every input pin holds the values its lower bound requires) — the action is written empty and carries the token, Approximated with the exact reason. Sequence parameters fire on nothing (the empty sequence).
  • Complete inventory. primitives.go holds every behavior of both documents — the 58 of fUML_Library.xmi (Integer, Real, UnlimitedNatural, Boolean, String, List functions and BasicInputOutput) plus ListConcat, which fUML 1.5 Table 9.7 lists but the 2018 document omits, and the 82 of Alf-Library.xmi (Bit­String, Sequence and Collection functions) — as data: library, family, fragment, v1 parameter names and order, whether each must hold a value, one v2 expression template per result (with $n naming the v1 argument, so permutations such as IncludingAt(seq, index, element)includingAt($1, $3, $2) are explicit), verdict and note. TestPrimitivesCoverBothLibraries pins the inventory against the two documents' parameter lists.

Verdict summary (the full table is in docs/reference/sysml-v1-migration.md, "Calls to the fUML and Alf libraries"):

Family mapped approximated (the note says where v2 differs) unmapped (the note says why)
fUML IntegerFunctions Neg Abs + - * Max Min < > <= >= ToString / Div Mod (v2 fails on a 0 divisor, v1 gives no result), ToUnlimitedNatural (negative), ToInteger (non-numeric text)
fUML RealFunctions Neg Abs Floor Round + - * Max Min < > <= >= ToInteger Inv / (0), ToReal (text), ToString (v2 writes the shortest round-trip text, v1 leaves the format open)
fUML UnlimitedNaturalFunctions all 9: v2 Natural has no *, bounded values behave as in v1
fUML BooleanFunctions Or Xor And Implies Not ToString (true/false) ToBoolean (v1 reads TRUE, v2 only true)
fUML StringFunctions Concat Size Substring (bounds outside 1..Size)
fUML ListFunctions ListSize ListConcat ListGet (index outside 1..ListSize)
fUML BasicInputOutput WriteLine ReadLine (v2 has no console)
Alf IntegerFunctions ToNatural (v2 reads decimal text only, not 0b/0o/0x)
Alf BitStringFunctions all 13 (v2 has no BitString)
Alf SequenceFunctions Size Includes Excludes Count IsEmpty NotEmpty IncludesAll ExcludesAll Equals First Last Union Intersection Difference Including Excluding At IncludeAt InsertAt IncludeAllAt ExcludeAt ReplacingAt Subsequence (v2 fails on an index outside the sequence where v1 gives no result or the sequence unchanged) IndexOf ExcludingOne Replacing ReplacingOne ToOrderedSet (no v2 function for the first occurrence, for replacement, or for de-duplication)
Alf CollectionFunctions size includes excludes count isEmpty notEmpty includesAll excludesAll equals first last union intersection difference including excluding clear the 7 index functions as above; the in-place add addAll addAt addAllAt remove removeAll removeAt replaceAt (the inout parameter and the result are one value in v2) indexOf excludingOne replacing replacingOne toOrderedSet removeOne replace replaceOne

Specification basis

fUML 1.5 §9.3 (the primitive behaviors), §9.4 (basic input and output); Alf 1.1 §11.4 (primitive behaviors), §11.6 (collection functions) and Annex B; the fUML execution model (an action fires once every input pin holds the values its lower bound requires). No row of docs/project/spec-compliance.md moves: the record does not cover v1 migration.

How it was verified

New fixture tests/migrate/testdata/xmi/library_calls.xmi (hand-written, where the other construct fixtures live) chains Integer ToStringConcatIncludingIncludingSize, plus Boolean ToString, a refused IndexOf, a refused WriteLine, and a call fed by the refused one; golden .sysml and .report.txt. tests/migrate/library_test.go runs the migrated model through the REPL session runtime (the precedent runAction helpers of the package) and asserts "42", "n=42", ["n=42"], ["n=42", "n=42"], 2, "true", and that the refused call's result and the starved call's input stay empty; it also pins Boolean/Integer/Real ToString formatting, Concat with a missing pin, an unfed pin and an optional pin, href matching (any date, https, exact fragment, no bare names), a bundled copy of the library, and extra pins. tests/migrate/library_sequences_test.go executes every mapped Alf Sequence and Collection function and every fUML primitive on generated fixtures and asserts the results, duplicates included. internal/translate/migrate/primitives_internal_test.go pins the inventory against both library documents' parameter lists, the href recognizer, and the result templates.

Two more fixtures, in the MagicDraw form: bundled_library.xmi (exporter MagicDraw UML; a bundled fUML_Library::PrimitiveBehaviors::… package whose FunctionBehaviors the hrefs fUML-Library.mdzip#… resolve to, plus hrefs into the same module that resolve to nothing but their referentPath) chains ToStringConcatListSize/ListGet and a refused WriteLine; TestBundledLibraryCallsAreKnownByIdentity executes it ("3", "n=3", 1, "n=3"), checks each provenance note, and that the bundled library package is not written into the user's output. user_library.xmi is the negative: a user package named fUML_Library::PrimitiveBehaviors::ListFunctions with its own ListSize activity, and a ListGet whose href goes into Helpers.mdzip with a referentPath naming the library; TestUserPackageNamedLikeTheLibraryIsNotIt pins that the first stays an ordinary action size : fUML_Library::…::ListSize, the second stays "has no v2 declaration", and no SequenceFunctions:: expression or provenance note appears. TestPrimitiveNamed covers the lookup directly: fUML and Alf by bundled copy and by referentPath, both fUML roots, case, a missing family, a non-behavior target, a wrong document, the fUML path under the Alf document, a same-named user package, and an unresolved target.

Gates, all green on this branch:

go build ./... && go vet ./... && gofmt -l .          # clean
go test ./...                                        # ok (79 packages)
go test -C tools ./... && go vet -C tools ./...      # ok
./scripts/download-training-examples.sh && ./scripts/download-pilot-corpora.sh && ./scripts/download-pilot-library-xmi.sh
OPENSYSML_REQUIRE_TRAINING_CORPUS=1 OPENSYSML_REQUIRE_PILOT_CORPORA=1 OPENSYSML_REQUIRE_PILOT_LIBRARY_XMI=1 go test -count=1 ./...   # ok, no ratchet moved
make lint && make docs-check && make man-check       # ✓
python3 scripts/changelog.py check && python3 scripts/check-doc-ids.py   # ✓

PSSM suite (./scripts/download-pssm-suite.sh)

Migration report, develop → this branch (47303 elements both):

mapped approximated unmapped
develop 23512 13562 10229
this branch 23613 14039 9651

has no v2 declaration: 627 → 41; the 41 left are all Comment notes ("annotates 'S1', which has no v2 declaration in the document"), none a call. Every one of the 586 library calls moved; by id:

  • 544 CallBehaviorAction unmapped → approximated: 398 for reasons that already applied to other calls (fan-out through a fork, fan-in through a join, a sibling of the same name), 105 Including whose element pin is fed by an activity-level fork that the migrator already did not trace ("nothing the flow carries comes from a pin or parameter", unchanged), 6 Concat/ToString in Util::Tracing::formatParameterValue starved by an unmapped upstream cast, 1 UnlimitedNatural ToString (*), 1 ToUnlimitedNatural.
  • 36 CallBehaviorAction unmapped → mapped: Integer/Boolean ToString, Size, IsEmpty, NotEmpty with values on all pins.
  • 68 ObjectFlow approximated → mapped: the result flows of the calls, which now carry a value.
  • 4 InputPin mapped → approximated and 2 ObjectFlow approximated → unmapped (Event::019, Standalone::003): call('trace') was a starved placeholder with every pin declared, so the flow carrying this to its target pin was written; the call is now live and written as action x : Owner::Op in the caller's context, whose existing rule leaves the target pin unwritten and reports the flow.
  • 1 SendSignalAction approximated → mapped (Util::Architecture::SemanticTestSuite): its target is Call('At')'s result, which now carries a value.
  • 6 calls stay unmapped with the new reason: 5 WriteLine ("no v2 library function: v2 has no console"), 1 BitString ToInteger.

Not achieved, on develop as well as here: bin/sysml build/pssm/PSSM_TestSuite.xmi -convert sysml -migration-report … -o … stops before writing either file with 12343:41: expected the target of the transition after 'then' — the state writer emits transition 'T1.2' first 'S1.1' do action effect; then done; for an empty transition effect, which the parser rejects; that writer is fixed separately in #485 and this PR does not touch states.go. The reports above were produced through migrate.Migrate directly. bin/sysml <out> -validate on both outputs reports that same one syntax error each; with that line removed by hand, both report the same set of unresolved-member and junction diagnostics (no difference between develop and this branch).

Behavior 001 execution: the migrated Util::Tracing::TraceBuilder::addSegment now computes its Concat/Including/Size/IsEmpty calls, but its trace still cannot be produced, for three reasons that predate this PR and are outside the library-call path: Util::Architecture::Target::trace reads traceBuilder through a ReadSelfAction inside an Alf-compiled Body and is refused ("the object read, this, is a …::Body, which has no feature traceBuilder"); the Target constructor's CreateObjectAction 'Create(TraceBuilder)' is refused, so traceBuilder is never assigned; and %instantiate of any PSSM Target does not terminate, because the constructor pattern action target : Target; (an action usage whose out return is typed by the enclosing part def) recurses on instantiation — with those four usages removed from a copy the model instantiates in 0.1 s, enters S1, accepts Start, and T2 then fails at read(traceBuilder): uninitialized feature value. Each is reproducible on develop's output.

TMT model

On the OMG-href-only head (9b0a70603) the model was unaffected: the .sysml byte-identical to develop's, -validate no errors, counts unchanged at 28,626 / 10,319 / 5,191 mapped / approximated / unmapped, and three refusals left — the behavior fUML_Library::PrimitiveBehaviors::ListFunctions::ListSize / ListGet it calls has no v2 declaration — because Cameo references the library as a used project (fUML-Library.mdzip#… with a referentPath), never by the OMG URI.

With recognition by library identity (761e053fd, TMT-2024x): 44,136 entries, 28,633 / 10,315 / 5,188 mapped / approximated / unmapped, -validate 0 errors. All three bundled ListSize / ListGet calls now write SequenceFunctions::size(…) / SequenceFunctions::'#'(…) with their flows and binds restored, and their report notes read "known by the referentPath … recorded beside its href into the library module fUML-Library.mdzip"; nothing else in the model moved. The command:

bin/sysml <TMT>.mdzip -convert sysml -migration-report /tmp/tmt-report.txt -o /tmp/tmt.sysml
grep -c 'has no v2 declaration' /tmp/tmt-report.txt   # before and after
head -2 /tmp/tmt-report.txt                            # mapped/approximated/unmapped

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 (none moved)
  • No internal work-item labels (waves, slices, F4, K5) in the body, docs, or changelog

devin-ai-integration Bot and others added 3 commits September 21, 2026 04:46
RefIDs keeps an href as written even once the model bundles a copy of the
document it names and the reference resolves to that copy, so a reader can
tell which document an element came from.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…e v2 library

A CallBehaviorAction whose behavior is an element of fUML_Library.xmi or
Alf-Library.xmi is known by the library document its href names and the
fragment within it, whatever the date segment of the URI, never by the
behavior's bare name. The call is written with its pins, and each result
pin takes the v2 library expression over the argument pins, so the flows
out of it carry the computed value. Every behavior of both documents is
inventoried: mapped, approximated with the semantic difference in the note,
or refused with the reason. A scalar parameter the call passes nothing for,
or whose pin nothing valued feeds, starves the call as in v1.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
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 21, 2026 05:21
devin-ai-integration[bot]

This comment was marked as resolved.

…gicDraw modules

A CallBehaviorAction whose behavior href points into the library's own
module (fUML-Library.mdzip, Alf-Library.mdzip) is a primitive when its
target — the bundled copy the href resolves to, or the referentPath
MagicDraw records beside the href — sits under the library's root
package, family and name. Both are required, so a package of the
model's own named fUML_Library stays its own. Each call's note says
which provenance identified it: the OMG href, the bundled copy, or the
referentPath.

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 3 commits September 21, 2026 06:25
Add the ListFunctions behavior fUML 1.5 Table 9.7 lists but the 2018
library document omits, with runtime coverage of the fUML list functions,
and state in the reference the trusted-module-name boundary of bundled
library recognition and that pins bind by position, unchecked against
the primitive's signature.

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

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

# Conflicts:
#	internal/translate/migrate/arguments.go
…L/Alf primitive mappings

Unions the golden construct fixtures from both lines. The PSSM suite
ratchet now records the library calls this branch computes: 580
CallBehaviorActions on fUML/Alf primitives (Including, Concat, ToString,
Size, Not, lt, ...) leave unmapped for mapped or approximated, the 68
object flows feeding them are written again, and four trace calls whose
segment argument those primitives compute are performed instead of
carried as inert placeholders. Validation errors stay at 21.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@devin-ai-integration
devin-ai-integration Bot changed the base branch from develop to feature/composite-state-connection-points September 21, 2026 15:44
devin-ai-integration[bot]

This comment was marked as resolved.

@devin-ai-integration
devin-ai-integration Bot added this pull request to stack #496 September 21, 2026 15:53
…cannot write

A value pin holding an unlimited natural * or a non-finite real was taken
as supplied, so the call was written live over a pin whose initializer was
dropped and failed at run time. The call is now a placeholder with the
value and the reason in its note.

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

This comment was marked as resolved.

A value pin whose expression names a sibling pin was judged unwritable
before the pins had names, so the call was deadened. The pins are settled
first, as the writer does, so the value reads them.

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

This branch has not been deployed

No deployments
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