Migrate SysML models

When you open an existing SysML v2 model in Syside v1, you may see errors that earlier versions didn’t report. This guide explains how to fix them, whether you work in the Syside VS Code Extension or with the Python library. If you have Python scripts written for an earlier version, update them as well.

Fix model errors

  1. Run syside check (Check command) from each project’s root folder. In the VS Code Extension, the Problems panel lists the same errors and warnings with their rule IDs.

  2. Fix any reference-error diagnostics first, then run syside check again.

  3. For each remaining error, find its rule ID below and follow the instructions. Then fix the warnings, as described in Fix model warnings.

Feature reference expression access

error (feature-reference-expression-access): Feature reference must be accessible from
the outer scope, did you mean to use chain notation?

Rule: feature-reference-expression-access. Use the fix for your case:

  • If the path starts from a usage, replace :: with . after the usage’s name.

    // before
    action def Responses {
        action restart;
        action shutdown;
    }
    action def FaultHandling {
        action responses : Responses;
        ref action recommendation = responses::restart;
    }
    
    // after
    action def Responses {
        action restart;
        action shutdown;
    }
    action def FaultHandling {
        action responses : Responses;
        ref action recommendation = responses.restart;
    }
    
  • If the path starts from a definition’s name, use a feature typed by that definition instead. For example, to read a feature of a case’s subject, start the path with the subject’s name.

    // before
    part def BatteryMonitor { attribute reportedAccuracy : ScalarValues::Real; }
    verification def VerifyMonitorAccuracy {
        subject monitor : BatteryMonitor;
        action evaluateData {
            in measuredAccuracy : ScalarValues::Real = BatteryMonitor::reportedAccuracy;
        }
    }
    
    // after
    part def BatteryMonitor { attribute reportedAccuracy : ScalarValues::Real; }
    verification def VerifyMonitorAccuracy {
        subject monitor : BatteryMonitor;
        action evaluateData {
            in measuredAccuracy : ScalarValues::Real = monitor.reportedAccuracy;
        }
    }
    
  • If a name reaches a feature of another definition without a path, through an import or an alias, add a reference typed by that definition and chain through it. The diagnostic’s note names the element that owns the feature.

    // before
    part def Hazards {
        part overheating;
    }
    analysis def AnalysisABC {
        private import Hazards::*;
        ref part mainHazard = overheating;
    }
    
    // after
    part def Hazards {
        part overheating;
    }
    analysis def AnalysisABC {
        ref part hazards : Hazards;
        ref part mainHazard = hazards.overheating;
    }
    

    An alias is fixed the same way: replace alias SOI for Vehicle::powertrain::battery; with ref part SOI = vehicle.powertrain.battery; after ref part vehicle : Vehicle;. When the note names a usage, such as Vehicle::powertrain, start the chain at the definition that owns it.

  • If a filter reads a metadata attribute, use a cast such as (as Review).isApproved. A filter that only checks for a tag, such as filter @Review;, needs no change.

    // before
    metadata def Review { attribute isApproved : ScalarValues::Boolean; }
    view def ApprovedOnly { filter @ Review and Review::isApproved; }
    
    // after
    metadata def Review { attribute isApproved : ScalarValues::Boolean; }
    view def ApprovedOnly { filter @ Review and (as Review).isApproved; }
    

    After this change, tables or diagrams that use the filter may show fewer elements because it now checks the attribute value correctly.

Usage types

error (view-usage-types): A view usage must be defined by a single view definition
error (enumeration-usage-types): Enumeration usage must be defined by a single
enumeration definition
error (item-usage-types): An item usage must only be defined by item definitions or
KerML structures

These rules check which definitions can type a usage, as required by chapter 7 of the SysML v2 specification. Syside v1 adds these checks, so an existing model may report errors even if it passed validation before. The fix depends on the kind of usage.

Single-definition usages

These usages must be defined by a single definition of their own kind:

Enumeration usages also need a single definition. See Enumeration usages for their fix. If a usage is typed by a definition of the wrong kind, see Matching-type usages.

  • If the usage lists several definitions, create a definition that specializes all of them, then type the usage by that new definition. The new definition can be concrete even if one of its parents is abstract. If an extra definition only sets values, you can move those values into the usage’s body instead.

    // before
    abstract view def Filter_PartsActions {
        filter (
            as SysML::PartUsage hastype SysML::PartUsage or
            as SysML::ActionUsage hastype SysML::ActionUsage
        );
    }
    occurrence def Diagrams {
        view GV_PartsActions
            : StandardViewDefinitions::GeneralView, Filter_PartsActions;
    }
    
    // after
    abstract view def Filter_PartsActions {
        filter (
            as SysML::PartUsage hastype SysML::PartUsage or
            as SysML::ActionUsage hastype SysML::ActionUsage
        );
    }
    view def GeneralView_PartsActions
        specializes StandardViewDefinitions::GeneralView, Filter_PartsActions;
    
    occurrence def Diagrams {
        view GV_PartsActions : GeneralView_PartsActions;
    }
    
  • If the usage has a semantic metadata tag (metadata that specializes Metaobjects::SemanticMetadata), the tag makes the usage specialize its base feature. This gives the usage a second definition. Move the tag to the usage’s definition, then type the usage by that definition alone. This applies to requirements, concerns, constraints, cases, views, viewpoints and renderings.

    // before
    concern def Tracked;
    abstract concern trackedConcerns : Tracked [*];
    metadata def <tracked> TrackedMetadata specializes Metaobjects::SemanticMetadata {
        :>> baseType = trackedConcerns meta SysML::Type;
    }
    concern def Cost;
    concern def ProjectConcerns {
        #tracked concern cost : Cost;
    }
    
    // after
    concern def Tracked;
    abstract concern trackedConcerns : Tracked [*];
    metadata def <tracked> TrackedMetadata specializes Metaobjects::SemanticMetadata {
        :>> baseType = trackedConcerns meta SysML::Type;
    }
    #tracked concern def Cost;
    concern def ProjectConcerns {
        concern cost : Cost;
    }
    

    Moving the tag means an @tracked filter no longer selects the usage. If you need the filter to keep selecting it, leave the tag on the usage and make its definition specialize the type of the tag’s base feature (Tracked in this example):

    // after
    concern def Tracked;
    abstract concern trackedConcerns : Tracked [*];
    metadata def <tracked> TrackedMetadata specializes Metaobjects::SemanticMetadata {
        :>> baseType = trackedConcerns meta SysML::Type;
    }
    concern def Cost specializes Tracked;
    concern def ProjectConcerns {
        #tracked concern cost : Cost;
    }
    

Matching-type usages

These usages can have multiple definitions, but only of their own kind or the matching KerML classifier:

A usage and its definition must describe the same kind of thing. An attribute def describes a value, while an item def describes an object. If you type an item by an attribute def, change either the usage’s keyword or the definition’s keyword to match what you’re modeling.

Use attribute for data typed by an attribute def or a ScalarValues type. If you’re modeling an item, use an item def for its definition instead.

The same requirement applies to single-definition usages. For example, a requirement typed by a constraint def needs a requirement def. A concern, including a frame concern, needs a concern def. A mismatch reports requirement-usage-types.

// before
attribute def ManifestEntry { attribute stationId : ScalarValues::String; }
action def LoadVehicle {
    in item manifest : ManifestEntry [*] ordered;
    out item doorClosed : ScalarValues::Boolean;
}

// after
attribute def ManifestEntry { attribute stationId : ScalarValues::String; }
action def LoadVehicle {
    in attribute manifest : ManifestEntry [*] ordered;
    out attribute doorClosed : ScalarValues::Boolean;
}

A frame concern also reports requirement-usage-types when its concern def has the same name as the requirement def that frames it. Inside requirement def Safety, the name Safety means that definition before any imported Safety, so qualify the name:

// before
package Needs {
    concern def Safety;
}
package Reqs {
    private import Needs::*;
    requirement def Safety {
        frame concern : Safety;
    }
}

// after
package Needs {
    concern def Safety;
}
package Reqs {
    private import Needs::*;
    requirement def Safety {
        frame concern : Needs::Safety;
    }
}

Enumeration usages

An enum declared without def is an enumeration usage: it picks a value from an enumeration definition. If your enum lists its own values, move those values into an enum def and type the usage by it.

The enumeration-usage-types rule applies only outside an enumeration definition, where each usage needs a single enumeration definition.

// before
part def MyContext {
    enum choices {
        enum choice1;
        enum choice2;
    }
}

// after
enum def Choices {
    enum choice1;
    enum choice2;
}
part def MyContext {
    enum choices : Choices;
}

Boolean expression results

error (boolean-expression-result-type): Boolean expression result must be boolean
evaluable

Rule: boolean-expression-result-type. The result of a Boolean expression, such as a constraint body, must be true or false. A common cause is ^ written for exclusive or: in SysML, ^ raises a number to a power. Use xor instead.

// before
part def Valve {
    attribute isOpen : ScalarValues::Boolean;
    attribute isShut : ScalarValues::Boolean;
    assert constraint exactlyOne { isOpen ^ isShut }
}

// after
part def Valve {
    attribute isOpen : ScalarValues::Boolean;
    attribute isShut : ScalarValues::Boolean;
    assert constraint exactlyOne { isOpen xor isShut }
}

Bindings between features of different types

error (type-error): Antenna::externalIn does not conform to RFPort::'~RFPort'

Rule: type-error. A binding connects two features that must have conforming types. Syside v1 reports a binding between a typed feature and an untyped one, which earlier versions accepted. Give the untyped feature the same type:

// before
port def RFPort;
part def Antenna { port externalIn; }
part def Gateway {
    part rfAntenna : Antenna;
    port extLoraIn : ~RFPort;
    bind extLoraIn = rfAntenna.externalIn;
}

// after
port def RFPort;
part def Antenna { port externalIn : ~RFPort; }
part def Gateway {
    part rfAntenna : Antenna;
    port extLoraIn : ~RFPort;
    bind extLoraIn = rfAntenna.externalIn;
}

The same error appears when externalIn is typed RFPort: a binding makes both sides one feature, so they need the same type, conjugation included. The fix is the same.

Names from indirect Sysand dependencies

error (reference-error): No Namespace named 'CLib' found.

A project can now use names only from the Sysand projects it depends on directly, the ones listed under usage in its .project.json. Names that a dependency brings in from its own dependencies no longer resolve. In a Sysand workspace, the same applies to each member project. Add every project whose names the model uses as a direct dependency. For example, if your project depends on b-lib, which depends on c-lib, and your model imports CLib from c-lib:

syside project add acme/c-lib --version-constraint 1.0.0

This adds c-lib to usage in .project.json. Its files are usually installed already, since b-lib brought them in.

Fix model warnings

Rules new in Syside v1 also report warnings. syside check still passes with them, or fails on them if you run it with --warnings-as-errors. Fix them so that they don’t hide new ones. For each warning, find its rule ID below and follow the instructions.

Renamed reserved features

warning (reserved-feature-name): Feature with reserved name `this` should only be
redefined by features with the same name

Rule: reserved-feature-name. The library features self, that and this have fixed meanings, so a feature that redefines one must keep its name. Models often redefine this under their own name, such as context, so that an action can reach the part that performs it. Keep the reserved name, and give your name as the short name. Feature chains through the short name work as before:

// before
part def Vehicle {
    attribute mass :> ISQ::mass;
}
action def Drive {
    ref part context : Vehicle :>> this;
    attribute load :> ISQ::mass = context.mass;
}

// after
part def Vehicle {
    attribute mass :> ISQ::mass;
}
action def Drive {
    ref part <context> this : Vehicle :>> this;
    attribute load :> ISQ::mass = context.mass;
}

The same form fixes a renamed self or that, such as abstract ref occurrence <thisFailureMode> self :>> self;. A specializing definition can redefine the feature by its short name, such as ref occurrence :>> thisFailureMode : BrakeFailure;.

This form fails with namespace-distinguishability in two cases:

  • The redefinition is written in the body of a typed action usage, one that names an action definition as its type, such as action drive : Drive { ... } inside a part definition. The same redefinition in the action definition Drive works, and so does one in an untyped action drive { ... }.

  • A nested action redefines the feature, such as ref part :>> context; in a subaction. The redefinition inherits the name this.

In the first case, you can remove the redefinition instead and use the part’s features by name, such as mass instead of context.mass. This reads the values of each part usage, where the redefinition read the definition’s, so a part usage that overrides mass gets a different result. Check the values that depend on it. In the second case, keep the existing redefinition for now: it gives the warning above, not an error.

A grid column that finds the feature by name needs updating (see Migrate grid views).

Decision node successions

warning (decision-node-successions): Outgoing successions from decision nodes should
be conditional

Rule: decision-node-successions. Give each succession out of a decide an if guard, and the last one else:

// before
action def HandleFault {
    in attribute isCritical : ScalarValues::Boolean;
    first start;
    then decide;
        then shutDown;
        then restart;
    action shutDown;
    action restart;
}

// after
action def HandleFault {
    in attribute isCritical : ScalarValues::Boolean;
    first start;
    then decide;
        if isCritical then shutDown;
        else restart;
    action shutDown;
    action restart;
}

Rule settings

You can change the severity of some rules in the [lint] table of syside.toml. Of the rules on this page, feature-reference-expression-access, reserved-feature-name, decision-node-successions and type-error allow this. Set lint.feature-reference-expression-access to "warning", or to "off" to hide it.

[lint]
feature-reference-expression-access = "warning"

Note

The validation rules list marks each configurable rule with a icon.

Instructions for an LLM agent

If an LLM agent helps you migrate the model, give it the migration guides and these instructions:

  • Fix one rule at a time across the model, then run syside check --warnings-as-errors. Stop when it exits 0, or when all that remains is what these instructions leave on purpose: a reserved-feature-name warning for a redefinition the guide says to keep, or errors in a copied library file.

  • After each rule, export the views with --allow-errors, since the model may still have errors, and compare them with the Syside v0.11 exports (Compare the exports). A passing check doesn’t prove a fix kept the values.

  • Make only the changes needed to fix that rule. Don’t rename or restructure anything else.

  • Never make a check pass by downgrading a rule in [lint] or adding an exclude entry.

  • If a library file copied into the project has errors, report them and leave the file unchanged.

  • Ask before making a modeling decision, such as choosing the kind of a definition.

  • Report every change in the exports and ask a person to review it.