MBSE iNsightsMBSE iNsights

· 5 min read

Nominal, Alternate, Exception: Routes a Model Works Out for Itself

A derived state machine arrives already colored by route — green, blue and red. Only one thing in the source model was ever annotated. Here is how the other two colors are computed, and why the stamps that carry them are deliberately not semantic metadata.

Open a derived state machine in Cameo and it arrives already colored. Green for the run you expect. Blue for the legitimate variations. Red for the parts only reachable when something has gone wrong. It looks like someone spent an afternoon in the style editor.

Nobody did. One element in the source model was annotated. The other two colors were computed by walking the graph, and the walk is the interesting part.

What you actually write

Exactly one thing: the branch that means something failed.

#exception
action 'Reject Card' { out cardKept : Boolean; }

That is the whole of it. No #nominal, no #alternate. There is no keyword for “this is the main line,” and there is no keyword for “this branch is a valid variation,” because neither of those is something you know that the model does not.

How the other two are worked out

The derivation walks every branch of every decision — ordinary branch coverage, so the number of traversals grows with the number of branches rather than as 2ⁿ. Each traversal is then classified by what it contains:

  • The first route that reaches the goal is the nominal path.
  • Any other route that reaches the goal is an alternate. It is correct; it simply is not the one you would describe first.
  • A route that passes through something you marked #exception is an exception path.

Look at what that costs you to state. The alternate is derived because there is nothing to say: where a decision has two valid branches, the use case does not nominate one as the main line, because there isn’t one. The nominal path is derived because “the first way through” is a fact about the flow. Only the valence of a failure — that this outcome is bad rather than merely different — is information the graph cannot carry, so that is the one thing you supply.

Least severe wins

Here is the rule that makes the coloring readable, and it is easy to miss.

A state can lie on more than one route. Authenticate Customer is on the nominal path, and it is also on the exception path, because the run that ends in a rejected card went through authentication to get there.

When routes disagree, the least severe classification wins. That state comes out green.

The reason is worth sitting with: the prefix every run shares is normal operation. If a state went red the moment any failing route touched it, almost the entire machine would be red, and the color would tell you nothing. Under “least severe wins,” what comes out red is precisely the set no expected run reaches — which is exactly the set worth picking out.

The part that surprises people

These three stamps are plain metadata defs. They are not semantic metadata, and that is deliberate.

metadata def <nominalPath>   NominalPath;
metadata def <alternatePath> AlternatePath;
metadata def <exceptionPath> ExceptionPath;

Compare that with #exception, which specializes SemanticMetadata and carries a baseType:

metadata def <exception> Exception :> SemanticMetadata {
    :> annotatedElement : SysML::ActionUsage;
    :> annotatedElement : SysML::SuccessionAsUsage;
    :>> baseType = exceptionBehaviors meta SysML::ActionUsage;
}

baseType asserts that the annotated element is that type. For #exception that is exactly right: the action is an ExceptionBehavior, which is why a project can declare action def PaymentDeclined :> ExceptionBehavior; and have every usage classify with nothing written on it.

For a route stamp there is no such type to reach for. A state on the exception path is not a kind of state. It is an ordinary state that happens to lie on one route — and “lies on” is a relation between an element and a traversal, not a classification of the element. Inventing a base type purely to be the target of the assertion would be circular, and nothing a project could usefully extend.

It would also be false for half of them. A baseType pins one metaclass, and these are applied to transitions as well as states.

A retreat worth admitting

The three stamps declare no annotatedElement either, and that one is a concession rather than a principle.

Naming two — StateUsage and TransitionUsage, since the stamps go on both — left every annotated state outlined in red in Cameo. A validation constraint that marks correct output as invalid is worse than no constraint at all, so the constraint came off. A plain metadata def cannot be misapplied by this tool in any case: nothing but the derivation ever writes one.

Transitions need the long form

One syntactic detail that costs an hour if you meet it cold. A state accepts prefix metadata:

#NominalPath
state validateSuggestion {
    do 'Suggest a Topic'.'Validate Suggestion';
}

A transition does not. So the derivation names the transition and annotates it by reference — this is real generated output, name and all:

transition awaitSuggestionToValidateSuggestionTransition
    first awaitSuggestion
    if submissions == SubmissionsKind::open
    then validateSuggestion;
metadata NominalPath about awaitSuggestionToValidateSuggestionTransition;

The same restriction is why #exception gained a second annotatedElement for SuccessionAsUsage. Valence really belongs on the route rather than on the behavior — the same action can be nominal on one route and exceptional on another — but #exception if … and #exception else … are both syntax errors. To annotate the branch, you name it and point at it:

succession errorBranch first check if not 'Check'.ok then 'Log Failure';
metadata exception about errorBranch;

That needs the explicit succession … if … form, after which else no longer parses, so both branches get written out.

What the stamps are for

Color is the visible half. A Cameo style sheet keys on ByMetadataAnnotationCondition and paints the machine by route, which is how the view colors itself.

The other half is query. Filter a table on @UseCaseSemantics::ExceptionPath and you have every element of the model reachable only when something has failed — the off-nominal surface, listed, without anyone maintaining a list. That question has no answer if the classification lives in the tool’s head rather than in the model.

The shape of the idea

A route is a traversal. It is not an element you can select, and it is not a property you can set. It is a fact the derivation computes by walking, and the stamps are how a computed fact becomes something the model can carry.

Which is why the vocabulary is as small as it is. You declare the exception, and the routes get walked.

More of this, on video

Walkthroughs in SysML v2 and MBSE tooling, with the model on screen.

Subscribe on YouTube