Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Metadata and Views

Your coffee machine model is built. It has structure, behavior, states, constraints, requirements, and verification cases. But models do not exist in isolation. People need to review them, approve them, and understand them – and different people care about different things.

This chapter covers two features that serve that human side of modeling: metadata for attaching governance information to model elements, and views for presenting the right slice of the model to the right stakeholder.

Comments vs. doc vs. Metadata

You have already used // comments throughout the model. Comments are notes in the source text. A tool ignores them during analysis. They are for the person reading the .sysml file, not for the model itself.

SysML v2 provides two more layers for human-oriented information, and the distinction matters.

doc is a model-level documentation string. Unlike a comment, doc is part of the model. A tool can extract it, display it in a hover tooltip, or include it in a generated report. You place it inside the element it describes:

part def Brewer {
    doc /* Heats water and forces it through ground coffee
           under controlled pressure and temperature. */

    attribute waterTemp : Real;
    attribute brewPressure : Real;
}

Metadata is structured, queryable data attached to model elements. Review status, maturity level, safety classification, responsible team – these are not free text. They have known fields and known values, and you need to filter or report on them. Metadata is the right tool for that job.

The rule of thumb: if it must be queried, filtered, or enforced by tooling, make it metadata. If it explains intent to a human reader, use doc. If it is a local note to yourself, use a comment.

Defining Metadata

A metadata definition looks like any other definition in SysML v2. You declare it with metadata def, and it can have attributes:

package CoffeeMachineGovernance {
    import ScalarValues::*;

    metadata def ReviewStatus {
        attribute status : String;
        attribute reviewer : String;
    }

    metadata def SafetyClass {
        attribute level : String;
    }
}

ReviewStatus is now a reusable type. It says: anything that carries a review status has a status string and a reviewer string. You define it once, in one package, and every team uses the same structure.

This is important. If one team invents ReviewInfo with a state field and another invents ApprovalStatus with a currentStatus field, you end up with incompatible schemas that no tool can query uniformly. Centralize your metadata definitions early.

Annotating Elements with Metadata

To attach metadata to a model element, you use the @ annotation syntax. The annotation goes immediately before the element it describes:

package CoffeeMachineStructure {
    import ScalarValues::*;
    import CoffeeMachineGovernance::*;

    @ReviewStatus {
        :>> status = "approved";
        :>> reviewer = "Systems Team";
    }
    part def CoffeeMachine {
        doc /* Top-level system definition for the Smart Coffee Machine. */

        part grinder : Grinder;
        part brewer : Brewer;
        part waterTank : WaterTank;
        attribute serialNumber : String;
    }
}

The @ReviewStatus { ... } block annotates the CoffeeMachine definition. The :>> syntax redefines the metadata attributes with specific values. After this annotation, any tool that queries the model can determine that CoffeeMachine has been approved by the Systems Team.

You can annotate any element – definitions, usages, requirements, actions, states. The annotation always appears directly before the element:

@SafetyClass { :>> level = "SIL-2"; }
requirement def SafetyInterlockReq {
    doc /* The machine shall disable the heater if water
           temperature exceeds 100 degrees Celsius. */
}

Now the safety interlock requirement carries its safety integrity level as structured data, not as a note buried in a comment.

Multiple Annotations

An element can carry more than one annotation. Stack them:

@ReviewStatus {
    :>> status = "in-review";
    :>> reviewer = "Safety Board";
}
@SafetyClass { :>> level = "SIL-2"; }
part def Brewer {
    doc /* Heating subsystem. Under safety review. */

    attribute waterTemp : Real;
    attribute brewPressure : Real;
}

The Brewer now carries both governance metadata and safety classification. A review dashboard could show all elements where status is "in-review", or all elements classified as SIL-2.

A Maturity Tracking Example

Metadata is especially useful for tracking model maturity across a project lifecycle. Here is a pattern that covers the typical progression:

package CoffeeMachineGovernance {
    import ScalarValues::*;

    metadata def ReviewStatus {
        attribute status : String;
        attribute reviewer : String;
    }

    metadata def SafetyClass {
        attribute level : String;
    }

    metadata def MaturityLevel {
        attribute level : String;
    }
}

Then, as the model evolves, annotations track where each element stands:

@MaturityLevel { :>> level = "preliminary"; }
action def BrewCycle { /* ... */ }

@MaturityLevel { :>> level = "baselined"; }
part def WaterTank { /* ... */ }

@MaturityLevel { :>> level = "verified"; }
requirement def BrewTempReq { /* ... */ }

A project manager does not need to understand the brew cycle control flow to know that the action definition is still preliminary. The metadata tells them directly.

From Metadata to Views

Metadata tells tools about the status of individual elements. But stakeholders do not review individual elements. They review slices of the model that are relevant to their concerns. A safety engineer cares about hazards, interlocks, and safety-classified requirements. An architect cares about structural decomposition and interfaces. Showing either of them the entire model is counterproductive.

SysML v2 formalizes this with two concepts: viewpoints and views.

Viewpoints: Defining Stakeholder Concerns

A viewpoint declares what a particular stakeholder cares about. It does not select specific model elements – it describes the kind of information that matters for a particular review purpose.

package CoffeeMachineViews {
    viewpoint def SafetyViewpoint {
        doc /* Concerns: hazard mitigations, safety interlocks,
               and safety-classified requirements. */
    }

    viewpoint def ArchitectureViewpoint {
        doc /* Concerns: structural decomposition, subsystem
               interfaces, and key part definitions. */
    }
}

Think of a viewpoint as a lens. SafetyViewpoint says: when you look through this lens, you want to see safety-related elements. ArchitectureViewpoint says: through this lens, you want to see structure.

A viewpoint can also specify stakeholder to name who uses it, and purpose to explain why:

viewpoint def VerificationViewpoint {
    doc /* Requirement-to-test traceability for verification closure. */
}

Views: Selecting Model Content

A view usage is the concrete selection. It is typed by a view def, can satisfy one or more viewpoints, and uses expose to pull specific elements into the view.

Before getting into stakeholder-specific views, it helps to see the simplest possible case: one view definition, one usage, one expose. This is the running anchor we will refine through the rest of the chapter.

package CoffeeMachineViews {
    import CoffeeMachineStructure::*;

    view def CoffeeOverview;

    view overview : CoffeeOverview {
        expose CoffeeMachine;
    }
}

That’s a complete, valid view: a definition that names the kind of view, and a usage that picks up the definition and exposes the top-level part. Anything that walks the model can ask “what does the overview view contain?” and get back the CoffeeMachine element and – by transitive containment – everything inside it.

Notice which half carries the expose. The grammar is strict about this division of labour: a view definition body admits filter and render clauses (plus ordinary definition members), while expose is legal only in a view usage body. The definition is the reusable recipe – what kinds of elements, presented how. The usage is the concrete selection – these elements, from this model. Keep that split in mind through the rest of the chapter; it is the single easiest thing to get backwards. (sysml-rs’s parser currently accepts an expose inside a view def without a diagnostic – a known parser gap, not a licence. The construct is not in the grammar, and conformant tools reject it.)

Now, the more interesting case. A view usage can satisfy one or more viewpoints, and can expose more than one element. satisfy reaches a viewpoint because a ViewpointDefinition is a RequirementDefinition in the abstract syntax – satisfying a viewpoint and satisfying a requirement are the same relationship:

package CoffeeMachineViews {
    import CoffeeMachineStructure::*;
    import CoffeeMachineRequirements::*;

    viewpoint def SafetyViewpoint {
        doc /* Concerns: hazard mitigations and safety interlocks. */
    }

    viewpoint def ArchitectureViewpoint {
        doc /* Concerns: structural decomposition and interfaces. */
    }

    view def SafetyReviewView;
    view def ArchitectureReviewView;

    view safetyReview : SafetyReviewView {
        satisfy SafetyViewpoint;
        expose CoffeeMachineRequirements::SafetyInterlockReq;
        expose CoffeeMachineRequirements::BrewTempReq;
    }

    view architectureReview : ArchitectureReviewView {
        satisfy ArchitectureViewpoint;
        expose CoffeeMachineStructure::CoffeeMachine;
        expose CoffeeMachineStructure::Brewer;
        expose CoffeeMachineStructure::Grinder;
        expose CoffeeMachineStructure::WaterTank;
    }
}

expose is the key operation. It does not copy the element into the view. It references it. The safetyReview view usage satisfies SafetyViewpoint and exposes two requirements. The architectureReview view usage satisfies ArchitectureViewpoint and exposes four structural definitions. A tool rendering the safety review shows only the safety requirements – the grinder and its grind settings are nowhere in sight.

This is the critical difference between a view and a package. A package owns its members. A view exposes references to members that live elsewhere. The underlying model is unchanged. Views are projections, not copies.

Two flavours of expose: member and namespace

expose comes in two forms, and the choice is more than syntactic sugar.

view interfaceReview : ArchitectureReviewView {
    expose CoffeeMachineStructure::Brewer;          // member expose
    expose CoffeeMachineStructure::*;               // namespace expose
}

The first form is a member expose: it pulls one named element into the view. The second is a namespace expose: it pulls every member of the named namespace into the view. The trailing ::* is the cue.

Member expose is precise; namespace expose is sweeping. You reach for namespace expose when the natural unit of review is a whole package – the safety package, the requirements package, the API surface – and you do not want the view to drift behind the package as new elements are added. Once CoffeeMachineStructure::* is exposed, anything new you drop into that package is automatically visible in the view.

Both forms are subject to the same qualified-name resolution. If you misspell the qname, the view exposes nothing for that line. There is no error – the line is parsed, the qname does not resolve, and tools simply have nothing to show. That silent failure mode is one of the more common authoring mistakes; we return to it in Common Mistakes below.

Anonymous view usages

A view usage does not have to be named. When you want a one-off slice of the model – to share a screenshot in a review, to drop a quick visualization into a diagram, to test what a particular expose would surface – you can write the usage inline without giving it a name:

view scratch { expose CoffeeMachine; }

That is a complete view usage. There is no view def, no satisfaction, no rendering – just a name and a body. Tools treat it like any other view usage: they resolve the expose, project the element, and render the result. Anonymous views are the modeling equivalent of a scratch buffer. Reach for them when you want a slice now and have not yet decided whether the slice deserves a definition.

Combining Metadata and Views

Metadata and views work well together. You can use metadata annotations to mark which elements belong in which review, and then build views that surface exactly those elements:

package CoffeeMachineReview {
    import ScalarValues::*;
    import CoffeeMachineGovernance::*;
    import CoffeeMachineStructure::*;
    import CoffeeMachineRequirements::*;

    viewpoint def SafetyViewpoint {
        doc /* All elements classified SIL-2 or above,
               plus their associated requirements. */
    }

    view def SafetyReviewView;

    // The safety review exposes only safety-relevant elements
    view safetyReview : SafetyReviewView {
        satisfy SafetyViewpoint;
        expose CoffeeMachineRequirements::SafetyInterlockReq;
        expose CoffeeMachineRequirements::BrewTempReq;
        expose CoffeeMachineStructure::Brewer;
    }

    viewpoint def ArchitectureViewpoint {
        doc /* System decomposition for architecture board review. */
    }

    view def ArchitectureReviewView;

    // The architecture review exposes structural elements
    view architectureReview : ArchitectureReviewView {
        satisfy ArchitectureViewpoint;
        expose CoffeeMachineStructure::CoffeeMachine;
        expose CoffeeMachineStructure::Grinder;
        expose CoffeeMachineStructure::Brewer;
        expose CoffeeMachineStructure::WaterTank;
    }
}

Notice that Brewer appears in both views. The safety engineer sees it because it contains the heater that is safety-classified. The architect sees it because it is a major subsystem. Same element, different reasons, different context. The model does not duplicate anything.

Filtering a View with filter

expose says “pull this element (or namespace) in”. Sometimes you want the opposite shape: pull a whole namespace in, then filter out the parts that do not belong in this particular view. That is what filter is for.

Note where each half lives: expose sits in the view usage, while filter typically sits in the view definition, where it becomes part of the reusable recipe and screens whatever any usage of that definition exposes. (A usage body may carry its own filter too, for one-off narrowing.)

A filter clause is a Boolean expression evaluated once per element that the view exposes. If the expression is true, the element stays in the view; if it is false, the element is dropped.

The spec’s own filter idiom is a classification test: the @ operator asks whether the current element is classified by a metaclass or a metadata definition. That is the portable form, and it is what the standard library’s own filtered packages use:

view def PartsOnlyPortable {
    filter @SysML::PartUsage;                                  // metaclass test
}

view partsOnly : PartsOnlyPortable {
    expose CoffeeMachineStructure::*;
}

view def MandatorySafety {
    filter @SafetyClassification and SafetyClassification::level == "SIL-2";
}

view mandatorySafety : MandatorySafety {
    expose CoffeeMachineStructure::*;
}

The second form is the pattern for filtering on a metadata field: classify with @, then reach into the annotation’s attribute through a ::-qualified reference.

The simplest filters are literal:

view def AllOfIt    { filter true;  }    // accept everything
view def NoneOfIt   { filter false; }    // accept nothing

filter true and filter false are useful as scaffolding – a starting point you can sharpen.

sysml-rs extends the spec here. In addition to the spec’s classification tests, sysml-rs’s filter evaluator binds four convenience names for the element under test: self (the element), kind (its element kind, as a string), name (its declared name), and id (its identifier). These are not spec language – another tool will not resolve them. They are convenient for exploratory slices in this toolchain; use @ classification tests for filters that have to travel.

With those bindings in hand, the next step up is a predicate over kind or name:

view def PartsOnly {
    filter kind == "PartUsage";
}

view def JustTheBrewer {
    filter name == "Brewer";
}

view structureParts : PartsOnly {
    expose CoffeeMachineStructure::*;
}

view brewerOnly : JustTheBrewer {
    expose CoffeeMachineStructure::*;
}

structureParts exposes everything in CoffeeMachineStructure; its definition keeps only the elements whose kind is "PartUsage". brewerOnly exposes the same package; its definition keeps only the element named "Brewer". The kind == ... predicate is the most common shape because it lets you say “the structural slice”, “the requirements slice”, “the actions slice” without listing each element by hand.

You can stack filters. Multiple filter clauses on the same view compose as logical AND: an element passes only if every filter accepts it.

view def SafetyParts {
    filter kind == "PartUsage";          // first filter
    filter name != "Grinder";            // second filter
}

view safetyParts : SafetyParts {
    expose CoffeeMachineStructure::*;
}

safetyParts is the parts of the structure package that are not the grinder. Both filters must return true for an element to survive. Stacking is how you build up a complex viewCondition from small, readable predicates.

sysml-rs extends the spec here. The spec defines ElementFilterMembership – a Boolean expression bound to a view – but does not mandate what happens when that expression cannot be evaluated. sysml-rs’s evaluator is conservative: a filter expression that the runtime cannot reach (an unsupported operator, a reference that does not resolve, a type the evaluator does not yet handle) falls through to true. The element passes. The reasoning: a half-implemented filter that drops elements silently is more dangerous than one that admits too much. The default is over-inclusion, which the reader can see, rather than under-inclusion, which the reader cannot. Spec compliance: do not rely on this fallthrough as a contract – write filter expressions you are confident the evaluator supports, and treat any “this view shows more than I expected” surprise as a possible safe-default trip rather than a real model issue.

The expressions inside filter are real Boolean expressions, written in the same language as constraints (chapter 9). You can compose them with and, or, not; you can index members of self; you can compare id to a qualified-name literal. In practice, most filters stay close to the kind == "..." and name == "..." shape – those two predicates cover most stakeholder slices.

Renderings

A view says what to show. A rendering says how to show it. They are separate concepts because the same slice of the model can sensibly appear as a table, a tree, a diagram, or a matrix depending on who is reading it.

The spec gives you a small kit:

  • rendering def Foo; declares a rendering by name.
  • A view definition picks up a rendering with a render clause in its body. Two forms: name an existing rendering usage (render asTreeDiagram; – the standard library’s Views package ships asTextualNotation, asTreeDiagram, and asInterconnectionDiagram ready-made), or declare one inline from a rendering definition (render rendering standard : StandardRendering;). The bare-reference form is spec, but sysml-rs currently parses only the inline form; the runnable examples in this book use the inline form throughout.
  • A view usage can override its definition’s rendering by writing its own render clause in the usage body.

Here is the running coffee-machine example with a rendering attached:

package CoffeeMachineViews {
    import CoffeeMachineStructure::*;
    import CoffeeMachineRequirements::*;

    rendering def StandardRendering;

    view def SafetyReviewView {
        render rendering standard : StandardRendering;
    }

    view safetyReview : SafetyReviewView {
        satisfy SafetyViewpoint;
        expose CoffeeMachineRequirements::SafetyInterlockReq;
        expose CoffeeMachineRequirements::BrewTempReq;
    }
}

The render rendering standard : StandardRendering; line is a ViewRenderingMembership: it declares a rendering usage inside the view and types it by the rendering definition. The view uses the rendering by owning a usage of it.

A rendering definition can carry attributes – things like layout direction, paper size, page break preferences – but most authors start with an empty rendering def and let the tool’s defaults take over. The point of the rendering is the binding: the view declares its intent, and the tool that consumes the view picks up the matching rendering and presents the slice accordingly.

What Kind of Diagram Does a View Become?

You have written view def SafetyReviewView and view def CoffeeOverview. When a tool renders one of these, what shape does it choose? A flat list? A tree? A diagram with boxes and lines? The answer is determined by the view definition’s supertype.

The standard library package StandardViewDefinitions defines exactly eight standard view definitions, each corresponding to a recognizable diagram shape. When you write view def Foo :> InterconnectionView, you are saying “this view should be presented as an interconnection diagram”. When you write view def Foo :> ActionFlowView, you are saying “this view should be presented as an action-flow diagram”. The supertype is the contract.

The eight standard view definitions are:

SupertypeWhat it shows
GeneralViewAny exposed elements, as a general graph of nodes and edges
InterconnectionViewFeatures as (nested) nodes and the connections between them as edges
ActionFlowViewActions, their parameters, and the flows between them
StateTransitionViewStates and the transitions between them
SequenceViewTime-ordered event occurrences and messages on lifelines
GeometryViewSpatial items visualized in two or three dimensions
GridViewElements and relationships in a rectangular grid (tables, matrices)
BrowserViewThe hierarchical membership structure (containment tree)

Pick the supertype that matches the question the view is answering. “Show me how the parts connect” → InterconnectionView. “Show me the order of actions in the brew cycle” → ActionFlowView. “Show me the state machine” → StateTransitionView. The supertype is the difference between a view that lands as a useful diagram and one that lands as a generic table.

Two diagram kinds you might expect are deliberately not on the list. There is no UseCaseView and no RequirementView in the standard library. For those, the library’s own documentation on GeneralView gives the recipe: specialize GeneralView and narrow it with a filter. A requirement view, for example, is a GeneralView filtered to requirement elements:

view def RequirementOverview :> GeneralView {
    filter @SysML::RequirementDefinition or @SysML::RequirementUsage
        or @SysML::SatisfyRequirementUsage;
}

The same pattern gives you a package view, a definition-and-usage view, or any other slice-by-kind: GeneralView plus the metaclass filter naming the kinds you want.

view def CoffeeStructure :> InterconnectionView;
view def BrewSequence :> ActionFlowView;

view def HeaterStateMachine :> StateTransitionView {
    filter kind == "StateUsage" or kind == "TransitionUsage";
}

view structure : CoffeeStructure { expose CoffeeMachine; }
view brewOrder : BrewSequence { expose BrewCycle; }
view heaterStates : HeaterStateMachine { expose Brewer; }

Each of those views asks for model elements wrapped in a different presentation contract. The tool consuming them picks the appropriate diagram engine based on the supertype. You must write the :> explicitly. A view def Foo with no supertype is treated as the most general kind of view – GeneralView is the root of the family – and renders as a generic projection; the renderer does not infer a kind from the view’s name.

The Stakeholder → Viewpoint → View → Rendering Chain

Everything in this chapter so far has been one element at a time. Pulling them together: SysML v2’s view model is a single chain that runs from a person’s concern at one end to a rendered artifact at the other.

Stakeholder      a person or role with a question
     │
     │ has
     ▼
Viewpoint        the question, captured as a requirement
     │           (`viewpoint def` is a kind of `requirement def`)
     │ satisfied by
     ▼
View Usage       the concrete view — exposes the content and declares
     │           which viewpoints it satisfies
     │           (typed by a View Definition: the reusable recipe —
     │            supertype + filters + rendering;
     │            `view def` is a kind of `part def`)
     │ rendered through
     ▼
Rendering Usage  the visual style applied
     │
     │ produces
     ▼
View artifact    what a human sees

Here is the full chain in 25 lines:

package SafetyReview {
    import CoffeeMachineStructure::*;

    // Stakeholder — a role
    part def SafetyEngineer;

    // Viewpoint — the concern, expressed as a requirement
    viewpoint def SafetyConcernViewpoint {
        stakeholder reviewer : SafetyEngineer;
        doc /* Reviewers must see all SIL-2-classified parts. */
    }

    // Rendering — the style
    rendering def DarkInterconnection;

    // View Definition — the recipe
    view def SafetySIL2View :> InterconnectionView {
        render rendering dark : DarkInterconnection;
        filter @SafetyClassification and SafetyClassification::level == "SIL-2";
    }

    // View Usage — the concrete instance
    view sil2Diagram : SafetySIL2View {
        satisfy SafetyConcernViewpoint;
        expose CoffeeMachine;
    }
}

Reading top to bottom: a SafetyEngineer is the stakeholder; the SafetyConcernViewpoint captures their concern; the DarkInterconnection is a reusable rendering style; the SafetySIL2View is the recipe that filters to SIL-2 parts only and applies the dark rendering; the sil2Diagram is the concrete view a tool actually renders – it exposes the content and declares that it satisfies the viewpoint.

(Strictly, satisfy is an ordinary usage member, so the grammar admits it inside a view def body as well – the standard library’s own abstract View definition carries one. But the satisfaction claim reads most naturally on the concrete view, and that is where this book puts it.)

You do not have to use every link in the chain. Most authors start at the View end (view def + view usage) and add viewpoints + stakeholders only when traceability becomes a requirement. But when you do need traceability, every link is queryable: “which views satisfy this viewpoint”, “which viewpoints concern this stakeholder”, “what is the rendering for this view”. The chain is the trace.

How the Editor Surfaces Views

Two panels matter when you are working with views in an editor.

The Views panel lists every declared view def and view usage in the open file (or workspace). Clicking one renders the view exactly as the author specified — supertype-driven shape, exposes resolved, filters applied, rendering bound. The Views panel is the place to see views; if a view is not listed there, the model has not declared it.

The Outline panel shows the containment tree of the open file — packages, parts, ports, attributes, requirements. The Outline is not a view in the spec sense. It is a code outline, like the symbol pane in any IDE. It exists so you can navigate the model file structurally; it does not respect viewpoints, exposes, or filters. Clicking a node in the Outline reveals it in the source; it does not produce a diagram.

The two panels answer different questions:

  • “What views does this model publish?” → Views panel.
  • “What is in this file structurally?” → Outline panel.

If you find yourself wanting “show me this file as an interconnection diagram” with no view def in the model, the spec-faithful answer is to write a one-line scratch view and let the Views panel render it:

view scratch : InterconnectionView { expose CoffeeMachine; }

This is the same vocabulary the rest of the chapter uses — no special command, no editor mode, no separate API. Once the scratch view is in the file, it appears in the Views panel like any other view; rename it when you want to keep it. The editor may offer a “create view from selection” affordance that inserts this snippet for you, but the artifact it produces is a real view usage you can edit, name, and commit.

When to Add Metadata and Views

You do not need metadata or views from day one. They become valuable when the model is shared beyond the original author:

  • Add doc early. As soon as someone else will read your definitions, document intent. It costs almost nothing and saves review time.
  • Add metadata when governance starts. When the project has formal reviews, maturity gates, or safety classifications, metadata ensures that status is tracked in the model, not in a spreadsheet that drifts out of sync.
  • Add views when stakeholders diverge. When different reviewers need different slices, views prevent the “here is the entire model, find what you need” problem.

If you are working alone on a small model, comments and doc may be all you need. If you are on a team with safety reviews, architecture boards, and verification milestones, metadata and views are essential governance infrastructure.

Visualizing Views and Metadata

{{#tab name="Views" }}
Browser view: view definitions and their contents in the model tree.
{{#endtab }} {{#tab name="Metadata" }}
General view: metadata annotations attached to model elements.
{{#endtab }} {{#endtabs }}

Common Mistakes

Storing review status only in comments. A comment like // approved by safety board 2024-03-15 is invisible to tooling. If you need to query which elements are approved, that information must be metadata. Use @ReviewStatus { :>> status = "approved"; } instead.

Creating one giant view for all stakeholders. A view that exposes everything is just the model again. The value of a view is in what it leaves out. Keep views small and purpose-driven. If a stakeholder does not need to see the grinder to make their decision, do not expose it.

Duplicating model content in views. Views reference elements with expose. They do not redefine them. If you find yourself copying attribute lists or requirement text into a view, you are doing it wrong. Expose the original element and let the tool present it.

Proliferating metadata definitions. If the safety team defines SafetyLevel and the quality team defines SafetyRating with the same fields, you have a schema drift problem. Agree on shared metadata definitions early and put them in a governance package that everyone imports.

Writing a filter predicate the evaluator cannot reach. A filter expression that the runtime cannot evaluate falls through to true and admits every element (sysml-rs’s safe-default behaviour). If a view “shows more than you expected”, the most common cause is a filter expression with an unsupported operator, an unresolved reference, or a binding the evaluator does not yet handle. Stick to predicates over kind, name, id, and self’s direct attributes – those compose cleanly. If you see a stranger surface in a view, suspect a tripped safe-default before suspecting the model.

Misspelling an expose qname. expose CoffeeMachineRequirements::SaftyInterlockReq; (note the typo) does not produce an error. The qname does not resolve, the line exposes nothing, and the view comes up smaller than you expected. There is no “expose a name that does not exist” diagnostic – the line parses fine, it just has no effect. When a view comes up missing an element, check the qnames first.

Treating render as a styling hook. render rendering standard : StandardRendering; selects a RenderingDefinition. It does not set fonts, colours, or themes. If you want a view to look a particular way, the visual style is a property of the tool that consumes the view, not of the view itself. Use render to declare presentation intent (“table”, “diagram”, “matrix”) and let the tool’s visual style apply.

Expecting the Outline panel to behave like a SysML view. The Outline lists the structural contents of a file; it is not a view in the spec sense. It does not respect viewpoints, exposes, or filters, and clicking a node does not produce a diagram. If you want a diagram, the model must declare a view def and a view usage. The Views panel is where declared views live; the Outline is where you navigate the source.

Forgetting the supertype on view def. A bare view def Foo; is legal but renders as a generic projection – the most general view, GeneralView. For an interconnection, action-flow, or state-transition diagram, you must specialize from the matching standard definition: view def Foo :> InterconnectionView { ... }. There is no name-based fallback — a view called FooInterconnection without an explicit :> InterconnectionView still renders as a general view.

Putting expose inside a view def body. The grammar admits expose only in a view usage body; a view definition body takes filter and render. This one is easy to get wrong because sysml-rs currently parses an expose inside a view def without a diagnostic – the file looks fine, but the construct has no spec meaning and conformant tools reject it. “It parses” is not evidence here. Keep the recipe (supertype, filters, rendering) in the definition and the selection (expose) in the usage.

What You Have Built

The model can now describe itself and present itself.

Metadata attaches structured facts about elements – maturity, owner, safety classification – distinct from doc, which carries prose for a human, and from comments, which are not in the model at all.

Views turn that into something a reader can use. The chain runs stakeholder → viewpoint → view → rendering: someone has a concern, a viewpoint states it, a view selects the elements that answer it, and a rendering says what form the answer takes. expose (in the usage) chooses content, filter (in the definition) narrows it, and the definition’s supertype decides what kind of diagram you get.

The thing most worth remembering is that a view is part of the model. It is not a diagram someone drew and saved – it is a declaration of what a particular reader needs, which is why it can be reviewed, versioned, and regenerated when the model changes.

Chapter 14 turns outward to what you did not write: the standard library, and how much of the language is defined in it.