Skip to content

Markdown-SysML Format Specification

Version: 1.0
Status: Draft
Reference: OMG SysML v2.0 (formal/2026-03-02)


Disclaimer: Syscribe is a modeling and documentation tool provided "as-is" without warranty of any kind. The authors and contributors accept no responsibility or liability for the use of this tool in safety-critical, life-critical, or mission-critical applications.

Output from Syscribe — including validation results, generated templates, traceability reports, and analysis artifacts — must be independently reviewed and verified by qualified engineers before use in any certification, regulatory submission, or safety case. Compliance with standards such as ISO 26262, IEC 61508, ISO 13849-1, ISO/SAE 21434, IEC 61025, DO-178C, or any other functional safety or cybersecurity standard remains the sole responsibility of the user and their organisation.

This tool does not replace a certified safety process, a qualified safety engineer, or a formal assessment body.


Table of Contents

  1. Overview and Design Principles
  2. Element Type Inventory
  3. Common Frontmatter Schema
  4. Directory and Namespace Conventions
  5. Cross-Reference Syntax
  6. Multiplicity and Cardinality Syntax
  7. Inline vs File-per-Element Rules
  8. Element-Specific Schemas
  9. 8.1 Package and Namespace Elements
  10. 8.2 Structural Elements: Part, Item, Occurrence, Individual
  11. 8.3 Port and Interface Elements
  12. 8.4 Connection, Binding, and Succession Elements
  13. 8.5 Attribute and Enumeration Elements
  14. 8.6 Flow Elements
  15. 8.7 Action and Behavior Elements
  16. 8.8 State Machine Elements
  17. 8.9 Calculation Elements
  18. 8.10 Constraint Elements
  19. 8.11 Requirement Elements
  20. 8.12 Case Elements (Analysis, Verification, Use Case)
  21. 8.13 Allocation Elements
  22. 8.14 View, Viewpoint, and Rendering Elements
  23. 8.15 Metadata Elements
  24. 8.16 Diagram Elements
  25. 8.17 Architecture Decision Records (ADR)
  26. 8.18 Safety and Security Analysis Elements
  27. 8.19 Release Baselines (Baseline)
  28. Variability and Variation Points
  29. 9.1–9.4 Structural Variation (isVariation / isVariant)
  30. 9.5 Product Line Engineering Overview
  31. 9.6 FeatureDef: Feature Model Element
  32. 9.6a Single-file authoring: type: FeatureModel (REQ-TRS-FM-005)
  33. 9.7 Feature Parametrization
  34. 9.8 Configuration: Feature Selection
  35. 9.9 Two-Level Feature Models
  36. 9.10 appliesWhen: Conditional Model Elements
  37. 9.11 PLE Validation Rules
  38. 9.12 PLE Graph Edges
  39. Worked Examples
  40. Parser and Tool Contract
  41. Traceability Rules and Domain Conventions
  42. 12.1 OSLC Link Direction Convention
  43. 12.2 Requirement Breakdown and ADRs
  44. 12.3 Leaf-Level Assignment Rule
  45. 12.4 Parent Requirements Cannot Be Assigned
  46. 12.5 Requirement Domain Classification
  47. 12.6 Hardware/Software Architecture Independence
  48. 12.7 Safety/Security Integrity Level Propagation
  49. 12.8 Implementation Trace
  50. 12.9 Allocation: Two Forms over One Edge Model
  51. 12.10 User-Defined Link Types ([linkTypes], links:)
  52. IEC 62443 Industrial Cybersecurity (Zone/Conduit Model)
  53. Multi-Repository Model Composition
  54. General-Purpose Trade Study
  55. N² Interface Matrix
  56. Change Impact Analysis
  57. SBOM Generation
  58. Review Records
  59. Behavioral Coverage
  60. ReqIF Export
  61. Extensions to Existing Sections
  62. 22.1 State Machine Completeness Validation (extends §8.8)
  63. 22.2 Budget Expression Language (extends §8.9)
  64. 22.3 ASIL/SIL Decomposition Pair Completeness (extends §12.7)
  65. 22.4 Sequence Diagram Send/Receive Completeness (extends §8.16.8.3)
  66. Native PlanningItem: Work-Item Tracking
  67. Appendix A: Frontmatter Field Reference
  68. Appendix B: Mapping of SysML Textual Keywords to type: Values

1 Overview and Design Principles

1.1 Core Concept

Every SysMLv2 model element is represented as a single .md file. The file has two parts:

  • YAML frontmatter (between --- delimiters): declares the element's type, identity, and all structural/relational metadata.
  • Markdown body: the documentation comment (doc annotation in SysML terms). This is the human-readable description of the element.

The directory hierarchy encodes namespace containment. A directory corresponds to a package. The _index.md file inside each directory carries that package's own metadata.

1.2 Design Principles

  1. One element, one file. Every named SysML definition or usage that merits independent identity gets its own .md file. Exceptions are noted in Section 7.
  2. Path is identity. An element's qualified name is derived algorithmically from its file path relative to the model root. The name: field in frontmatter overrides the display name but does not change the qualified name.
  3. YAML for structure, Markdown for prose. Relationships, types, multiplicities, and flags go in frontmatter. Human-readable descriptions go in the body.
  4. camelCase for all frontmatter keys. Consistent with SysML's own abstract syntax naming convention.
  5. Qualified names use :: separator. This matches SysML's textual notation.
  6. Defaults should match SysML defaults. Where SysML specifies a default (e.g., multiplicity 1..1 for owned usages, isAbstract: false), the Markdown-SysML format silently applies that default when the field is absent.

1.3 Relationship to SysMLv2

Markdown-SysML is a concrete syntax for SysMLv2 semantics, alternative to the official SysML textual notation (.sysml files). It trades the full expressiveness of SysML expressions for readability and LLM-friendliness. The following SysML features are fully represented:

  • All definition and usage kinds (part, item, port, connection, interface, action, state, calculation, constraint, requirement, case, view, metadata, allocation, flow, enumeration, attribute, occurrence, individual)
  • Specialization relationships (subclassification, subsetting, redefinition)
  • Multiplicity, direction, abstract, variation, variant flags
  • Port conjugation
  • Requirements with subjects, satisfactions, verifications, concerns
  • Actions with parameters, succession flows, control nodes
  • State machines with transitions, triggers, guards
  • Metadata annotation and user-defined keywords
  • Import and alias declarations
  • Viewpoint/view/rendering

The following are out of scope for this format (they require a formal expression language): - KerML/OCL constraint bodies (stored as opaque strings, not interpreted) - Calculation body expressions (stored as opaque strings) - Import filter conditions (stored as opaque strings)


2 Element Type Inventory

Every .md file must have a type: field drawn from the following table. The sysml_keyword column shows the corresponding SysML textual notation keyword sequence.

2.1 Definition Types

type: value SysML keyword(s) Description
PartDef part def Classifies physical or logical system components
ItemDef item def Classifies things that flow through a system
PortDef port def Classifies interaction points on parts
ConnectionDef connection def Classifies structural links between ports
InterfaceDef interface def Classifies compatible connection ends
ActionDef action def Classifies behavioral steps
CalculationDef calc def Classifies parameterized expressions with return values
ConstraintDef constraint def Classifies boolean-valued conditions
RequirementDef requirement def Classifies textual/formal requirements
ConcernDef concern def Classifies stakeholder concerns
CaseDef case def Classifies case definitions (base for analysis/verification/use case)
AnalysisCaseDef analysis def Classifies analysis procedures
VerificationCaseDef verification def Classifies verification procedures
UseCaseDef use case def Classifies system use cases
OccurrenceDef occurrence def Classifies things with temporal extent
EventOccurrenceDef event occurrence def Classifies momentary, instantaneous occurrences (no duration)
IndividualDef individual def Classifies specific individual occurrences
FlowDef flow def Classifies flow connections carrying items
SuccessionDef succession def Classifies temporal ordering between occurrences
StateDef state def Classifies state machine nodes
AttributeDef attribute def Classifies data values (scalars, quantities)
EnumerationDef enum def Classifies discrete-valued attributes
AllocationDef allocation def Classifies allocation relationships
MetadataDef metadata def Classifies annotation structures
ViewDef view def Classifies model views
ViewpointDef viewpoint def Classifies stakeholder-oriented viewpoints
RenderingDef rendering def Classifies rendering methods for views

2.2 Usage Types

type: value SysML keyword(s) Description
Part part Usage of a PartDef
Item item Usage of an ItemDef
Port port Usage of a PortDef
Connection connection Usage of a ConnectionDef
Interface interface Usage of an InterfaceDef
Action action Usage of an ActionDef
Calculation calc Usage of a CalculationDef
Constraint constraint Usage of a ConstraintDef
Requirement requirement Usage of a RequirementDef
Concern concern Usage of a ConcernDef
Case case Usage of a CaseDef
AnalysisCase analysis Usage of an AnalysisCaseDef
VerificationCase verification Usage of a VerificationCaseDef
UseCase use case Usage of a UseCaseDef
Occurrence occurrence Usage of an OccurrenceDef
EventOccurrence event occurrence Usage of an EventOccurrenceDef; momentary observation or signal emission. direction: in = observed, direction: out = emitted
Individual individual Usage of an IndividualDef (time-slice/snapshot)
Flow flow Usage of a FlowDef
Succession succession Temporal ordering between actions/occurrences
BindingConnector binding (connector) Equality binding between two features
State state Usage of a StateDef
ExhibitState exhibit state Referential perform-action usage that exhibits a StateDef
Attribute attribute Usage of an AttributeDef
Enumeration enum Usage of an EnumerationDef
Allocation allocation Usage of an AllocationDef
Metadata metadata Application of a MetadataDef to an element
View view Usage of a ViewDef
Rendering rendering Usage of a RenderingDef
TestCase (native) Native test-case element with stable ID, lifecycle status, and Gherkin scenarios. See §8.12.5.
FeatureDef (PLE native) Feature model node — a named, selectable characteristic with grouping semantics and optional typed parameters. See §9.6.
Configuration (PLE native) A complete feature selection with parameter bindings; produces one concrete product variant. See §9.8.
FeatureModel (PLE native) Single-file, additive alternative to a FeatureDef-per-file layout: a sheet whose featureTree:/crossTreeConstraints: explode into ordinary FeatureDef elements. See §9.6a.

2.3 Namespace/Package Types

type: value SysML keyword(s) Description
Package package Named container for model elements
LibraryPackage library package Package marked as a model library
Namespace (implicit) Root namespace; _index.md at model root with no explicit parent

2.4 Relationship Types

Relationship elements are directed relationships between named elements. Unlike definitions and usages, they are not classifiers or features — they live at package level, have no typedBy: field, and cannot be owned as sub-features of another element.

type: value SysML keyword Description
Dependency dependency Directed relationship from one or more client elements to one or more supplier elements

Specializations of Dependency (e.g., Realization, Derivation) are expressed via supertype: referencing library types rather than requiring distinct type: values:

---
type: Dependency
name: EngineControlRealization
supertype: SysML::Dependencies::Realization
clients:
  - LogicalArch::EngineControl
suppliers:
  - PhysicalArch::EngineControlUnit
---
The logical engine control function is realized by the physical ECU.

Dependency-specific fields:

Field YAML type Required Description
clients list of strings Required Qualified names of the dependent (client) elements
suppliers list of strings Required Qualified names of the elements depended upon (suppliers)

Placement convention: Dependency files live either alongside the primary client element or in a dedicated Dependencies/ package when they cross package boundaries.

2.5 Diagram Types

Diagrams are visual representations of model elements. They are not SysML language constructs but a format extension for diagrams that trace back to the model elements they depict.

type: value Description
Diagram A diagram of part of the model — structured SVG, PlantUML companion, Mermaid, inline PlantUML, or hand-authored SVG with a frontmatter manifest linking shapes and edges to model elements (diagramKind:, §8.16)

2.6 Record Types

Record elements are engineering-process artifacts that live in the model directory tree for traceability. They have no SysML counterpart. All are id-identified: the file is <id>.md, cross-references resolve by id, and name is a required free-prose label.

type: value ID pattern Description
ADR ADR-* Architecture Decision Record documenting a model-level design decision; referenced by breakdownAdr:. See §8.17.
TestPlan TP-* Groups TestCases by configuration and scope into a test campaign with a rolled-up verdict. See §8.12.6.
Baseline BL-* Sealed, content-hashed release snapshot of a model scope, anchored to a commit (written by syscribe baseline create). See §8.19.
PlanningItem PI-* Work item (epic/story/task) in a single-parent tree that achieves: Requirements. See §23.
ReviewRecord RR-* Record of a formal review of model elements, with findings and action items. See §19.
TradeStudy TRD-* Weighted-criteria evaluation of design alternatives, linked to the informing Requirement and the deciding ADR. See §15.

2.7 Safety and Security Analysis Types

Native, id-identified element types for functional safety (ISO 26262, IEC 61508, ISO 13849-1), automotive cybersecurity (ISO/SAE 21434), and industrial cybersecurity (IEC 62443).

type: value ID pattern Description
HazardousEvent HE-* Hazard in an operational situation, with HARA risk parameters. §8.18.1
SafetyGoal SG-* Top-level safety requirement from the HARA, carrying an integrity level. §8.18.1
Asset ASSET-* Item of value to protect, with the cybersecurity properties at stake. §8.18.10
DamageScenario DS-* Adverse consequence to a stakeholder if an asset is compromised. §8.18.2
ThreatScenario TS-* Potential attack causing damage scenarios; carries attack feasibility and risk treatment. §8.18.2
CybersecurityGoal CSG-* High-level security requirement countering threat scenarios, with a CAL. §8.18.2
SecurityControl SC-* Concrete countermeasure implementing cybersecurity goals. §8.18.2
VulnerabilityReport VR-* Tracked vulnerability with CVSS score and mitigations. §8.18.2
TARASheet TARA-* Single-file TARA whose tables explode into the TARA element types above. §8.18.2
ConfirmationMeasure CM-* Confirmation review, audit, or assessment with its independence level. §8.18.2
FaultTree FT-* Root of a fault tree for a SafetyGoal. §8.18.3
FaultTreeGate FTG-* Logic gate in a fault tree. §8.18.3
FaultTreeEvent FTE-* Basic, undeveloped, or house event in a fault tree. §8.18.3
FMEASheet FMEA-* FMEA table whose entries: rows become FMEAEntry elements. §8.18.4
FMEAEntry FM-* One failure-mode row, synthesized from an FMEASheet (not authored as its own file). §8.18.4
AttackTree AT-* Root of an attack tree substantiating a ThreatScenario. §8.18.5
AttackTreeGate ATG-* AND/OR combinator in an attack tree. §8.18.5
AttackStep ATS-* Leaf attack step with an attack feasibility. §8.18.5
Argument ARG-* GSN claim, strategy, or solution node in the safety argument. §8.18.6
AssumptionOfUse AOU-* Safety-related application condition constraining goals, arguments, or requirements. §8.18.6
TestEnvironment TE-* The rig a test runs on (HIL, bench, chamber, vehicle); capabilities and calibration. §8.12.7
SafetyMechanism SM-* Diagnostic/safety mechanism: covers: failure modes, coverages, reactionTime, safeState. §8.18.9
DependentFailureAnalysis DFA-* ISO 26262-9 clause 7 independence argument for decomposed or co-hosted elements. §8.18.7
Zone ZN-* IEC 62443 security zone with a target security level. §13.2
Conduit CD-* IEC 62443 conduit connecting two zones, with an achieved security level. §13.3

The native Requirement (REQ-*, §8.11.6), TestCase (TC-*, §8.12.5), FeatureDef (FEAT-*), Configuration (CONF-*) and FeatureModel types are listed with the usage types in §2.2. syscribe spec types prints the list known to the installed tool.


3 Common Frontmatter Schema

This section defines every frontmatter field that may appear on any element file. Element-specific additional fields are defined in Section 8.

All fields are optional unless marked Required. Defaults listed apply when the field is absent.

3.1 Identity Fields

Field YAML type Required Default SysML mapping
type string Required — Kind of element (Section 2)
name string optional filename stem Element label (display name). Does not contribute to the qualified name — the qname segment is always the file/directory stem; a name-identified element whose name: differs from the stem raises W058
shortName string optional absent Short name (written <shortName> in SysML textual notation); abbreviated identifier
qualifiedName string optional absent Not an identity override. The qualified name is always derived from the path (§4.2, §4.5, §11.3). qualifiedName: is meaningful only on a §3.10 locale documentation variant (a file that also sets locale:), where it names the element the variant documents. Anywhere else a value that differs from the path-derived name is warning W049 and is ignored.
visibility string optional public Membership visibility: public, protected, private. This is a property of the membership — it controls whether this element is visible to namespaces outside its owner, not a property of the element itself. private means visible only within the owning package; protected means visible within the owning package and its specializations.
extRef string or list of strings optional absent External reference(s) — this element represents an artifact managed in another tool (a requirement in DOORS Next, an element in a SysML tool, a ticket, …). See External references below.
responsibility string optional absent Accountable party/organisation for this work product (the DIA/CIA split, e.g. OEM / Supplier-X; ISO 26262-8 §5 / ISO/SAE 21434 §7). A non-draft work product with no responsibility: warns W038 (opt-in; see §11.12).

Name resolution rule: The element's qualified name segment is always the filename stem (filename without .md), or the directory name for a package; name: is only a label and never changes the qualified name. A name-identified element whose name: differs from its stem raises W058 (exempt: a name: containing a space or starting with <, and FeatureModel / FMEASheet elements); references must use the stem. The full qualified name is the :: join of all ancestor directory names and the element's own stem, starting below the model root (the root's own name is never part of a qualified name).

Basic-name grammar (naming convention). Each qualified-name segment — the file stem (or directory name) of an element — should be a SysMLv2 basic name: ^[A-Za-z_][A-Za-z0-9_]*$ — a letter or _, then letters, digits or _. Hyphens, spaces and other punctuation are not permitted: a hyphen is the subtraction operator in the appliesWhen: and parameterConstraints expression grammars, so a name like Anti-Lock parses fine as a file/qname but cannot be referenced in those expressions (it raises E209). A non-basic stem raises warning W042 (it checks the file/directory stem, not the name: label); rename using _ or CamelCase (Anti-Lock → Anti_Lock / AntiLock). Stable ids (REQ-*, TC-*, …) are exempt — they have their own grammar and legitimately contain -. (SysMLv2 unrestricted quoted names, e.g. 'Anti-Lock', are not supported.)

This applies to every qualified-name segment, including package / directory names — both a package declared with an _index.md and a plain directory without one. Requirements and other stable-id elements are unaffected (their REQ-*/TC-* ids are exempt); only the directories that contain them must be basic names.

name is the single, universal label field. Every element type — id-identified and name-identified alike — carries name as its one human-readable label (SysMLv2 declaredName). There is no longer a per-identity-class split between name and title.

  • name is the label on all types. It is optional in general but required on every type that previously required a label — the id-identified types: Requirement, TestCase, TestPlan, Configuration, ADR, ConfirmationMeasure, HazardousEvent, SafetyGoal, DamageScenario, ThreatScenario, CybersecurityGoal, SecurityControl, VulnerabilityReport, TARASheet, FaultTree, FaultTreeGate, FaultTreeEvent, FMEASheet, FMEAEntry, AttackTree, AttackTreeGate, AttackStep, Argument, AssumptionOfUse. For these id-identified types name is free prose (spaces and punctuation allowed; the basic-name grammar W042 does not apply, because identity is the stable id). For name-identified types (all SysML structural types — PartDef, Part, Port, ActionDef, … — plus Package, Diagram, and FeatureDef) name is the label, and — by convention — should equal the file/directory stem that forms the qualified-name segment (a differing name: is W058); the basic-name grammar W042 is applied to the stem.
  • id (the stable shortName, such as REQ-*, TC-*, HE-*) remains the identity of the id-identified types — files stay <id>.md and cross-references resolve by id. The id axis and the name (label) axis are independent.
  • title is removed. It is no longer a recognized label field on any element. Declaring a title: on any element — id-identified or name-identified — is error E025 ("the title field is removed — rename it to name"). Error E024 (formerly: a name: on an id-identified type) is retired — name is now the correct, required label on those types.

FeatureDef is name-identified (its name is its label; its file stem is the qualified-name segment) and also carries a mandatory stable id (its FEAT-* shortName, §9.6 — a feature with no id is E201). The id axis (shortName) and the name (label) axis are independent: under the unified rule a FeatureDef carries name + id and no title.

External references (extRef). A Syscribe element often mirrors an artifact that lives in another tool. extRef: records that correspondence as one or more opaque strings — a URI (https://dng.example/resources/4521) or a tool-qualified token (DNG:4521, cameo://model/Engine#id-99) — given either as a single string or a list. The field is optional and its syntax is unconstrained (external systems use widely varying identifier schemes). extRef is an external pointer: it is not a model cross-reference and is never a valid target for supertype:, verifies:, derivedFrom:, connections, etc. Look up the element(s) that carry a given reference with syscribe -m <root> extref <ref>. The same extRef value on two or more elements is permitted but warned (W028), since an external artifact normally maps to a single element.

# any element — represents DNG requirement 4521 and a Cameo block
extRef:
  - "DNG:4521"
  - "cameo://model/Engine#id-99"

On a TestCase, an extRef is also the key a test tool can put in a result: ingest-results --format records joins a record whose testCase is that value to the TestCase (REQ-TRS-RESJOIN-001). Because W028 keeps it unique, a tool-side test identifier such as RIG:SYS-012 is a good value.

3.2 Classification Flags

Field YAML type Required Default SysML mapping
isAbstract bool optional false abstract keyword; definition or usage is abstract
isVariation bool optional false variation keyword; element is a variation point
isVariant bool optional false variant keyword; element is one variant within a variation
isIndividual bool optional false individual keyword on occurrence usages; declares that this usage represents a specific unique individual, not an abstract role. Applies to usages only (for definitions use type: IndividualDef). Implies multiplicity "0..1" at most (an upper bound above 1 is not currently diagnosed).
isReadonly bool optional false readonly keyword; usage value cannot be changed after initialization
isDerived bool optional false derived keyword; value is fully determined by an expression
isEnd bool optional false Marks a connection end feature
isPortion bool optional false portion kind on occurrences; marks this usage as a temporal sub-extent of its owning occurrence
portionKind string optional timeslice Intended for isPortion: true (not currently enforced); timeslice (contiguous temporal extent) or snapshot (zero-duration instantaneous point)
isReference bool optional false ref keyword; usage is referential (not composite)
isComposite bool optional true (for part/item usages owned by a def/usage) Composite vs. referential ownership
isConstant bool optional false constant keyword; value fixed for the lifetime of the featuring occurrence
isOrdered bool optional false ordered keyword; multiple values are ordered (indexed)
isNonunique bool optional false nonunique keyword; same value may appear multiple times

Normative note on isReference and compositeness:

isReference: true is equivalent to the SysML ref keyword and marks a usage as referential — the containing element does not own the referenced element, it merely holds a reference to one that exists independently.

Default compositeness by context: - Usages owned inside a PartDef, ItemDef, ActionDef, or similar definition → composite (isReference: false) by default. - Usages directly owned by a Package → referential by default.

Certain usage kinds imply isReference: true regardless of the field value. These kinds are always referential; setting isReference: false explicitly on them is not currently diagnosed:

Usage kind Reason
ExhibitState A part exhibits a state; it never owns it
Sub-action with kind: PerformAction Perform always references an independently defined ActionDef
Metadata Metadata applications are always annotations, never owned instances

3.3 Typing and Specialization

Field YAML type Required Default SysML mapping
supertype string or list of strings optional absent Subclassification (:> or specializes) for definitions; may be a single string or list
typedBy string or list of strings optional absent Feature typing (defined by or :) for usages; names the definition(s)
subsets list of strings optional absent Subsetting (:> or subsets) for usages; list of subsetted usage qualified names
redefines list of strings optional absent Redefinition (:>> or redefines) for usages; list of redefined usage qualified names
conjugates string optional absent Port conjugation (~); qualified name of the definition being conjugated

Note on supertype vs typedBy: Definitions use supertype (subclassification). Usages use typedBy (feature typing). Both support a single string or a YAML list for multiple supertypes/definitions.

3.4 Multiplicity

Field YAML type Required Default SysML mapping
multiplicity string optional "1" for owned usages inside a def/usage; "0..*" otherwise SysML MultiplicityRange; see Section 6

3.5 Direction

Field YAML type Required Default SysML mapping
direction string optional absent (undirected) in, out, or inout; applies to ports, parameters, action features

3.6 Inline Features

Field YAML type Required Default SysML mapping
features list of feature maps optional absent Inline owned attribute or port usages; see Section 7 and 3.6.1

3.6.1 Inline Feature Schema

Each entry in features: is a map with these fields:

Sub-field YAML type Required Default Description
name string Required — Feature name
type string optional absent type: value (e.g., Attribute, Port) — defaults to Attribute when typedBy is a scalar type
typedBy string optional absent Qualified name of the definition typing this feature
multiplicity string optional "1" Multiplicity range string
direction string optional absent in, out, or inout
isAbstract bool optional false Abstract flag
isReadonly bool optional false Readonly flag
isDerived bool optional false Derived flag
isReference bool optional false Reference (non-composite) flag
isOrdered bool optional false ordered keyword; values are index-ordered
isNonunique bool optional false nonunique keyword; duplicate values are permitted
value string optional absent Literal default value or expression body (opaque string)
valueKind string optional bound Binding semantics: bound (=), initial (:=), default-bound (default =), default-initial (default :=)
unit string optional absent Shorthand: qualified name of the unit for quantity attributes
subsets list of strings optional absent Qualified names of subsetted features
redefines list of strings optional absent Qualified names of redefined features
metadata list of metadata application maps optional absent Metadata annotations on this inline feature

3.7 Packaging and Import Fields

Field YAML type Required Default SysML mapping
imports list of import maps or strings optional absent Import declarations; see 3.7.1
aliases list of alias maps optional absent Alias declarations; see 3.7.2
filterCondition string optional absent KerML boolean expression for package filter condition (opaque string)

3.7.1 Import Schema

Each entry in imports: may be a plain string (shorthand) or a map:

Shorthand: a qualified name string, optionally suffixed with ::* (namespace import) or ::** (recursive import).

imports:
  - VehicleSystem::*
  - ISQ::**

Full map form:

Sub-field YAML type Required Default Description
target string Required — Qualified name, optionally suffixed ::* or ::**
isPublic bool optional false true makes imported names re-exported from this namespace (public import); false is a private import visible only within this namespace
filter string optional absent KerML boolean filter expression; only members satisfying the condition are imported (opaque string)

Examples:

imports:
  # shorthand — private import of all direct members
  - VehicleSystem::*

  # public import — re-exports ISQ members downstream
  - target: ISQ::**
    isPublic: true

  # filtered import — only abstract members
  - target: VehicleSystem::*
    filter: "isAbstract"

  # filtered import — only members annotated as normative
  - target: ISQ::**
    filter: "#(ModelingMetadata::Normative)"

Note on isPublic: A public import in SysML makes the imported names visible to any namespace that imports the importing namespace. A private import (the default) makes them visible only within the importing namespace itself.

3.7.2 Alias Schema

Each entry in aliases: is a map:

Sub-field YAML type Required Default Description
name string Required — Alias name within this namespace
shortName string optional absent Alias short name
for string Required — Qualified name of the aliased element
visibility string optional public Membership visibility of the alias

Note: aliases: may appear on any element, not just packages. When declared on a non-package element (e.g., a PartDef), it creates a local alias name for another element within the scope of the declaring element's namespace — useful for shortening deep cross-references within a definition body.

3.8 Metadata Application

Field YAML type Required Default SysML mapping
metadata list of metadata application maps optional absent Annotations applied to this element

Each entry in metadata: is a map:

Sub-field YAML type Required Default Description
type string Required — Qualified name of the MetadataDef being applied
name string optional absent The application's own declared name (SysML v2 @n : T / metadata n : T); reserved, never a tagged value
about string optional absent Reserved: the written target of an application a SysML v2 submodel could not attach to its element (@T about Y; with an unresolved Y, REQ-TRS-SYSMLV2-087); an application on the element itself never carries it
<attribute_name> any optional absent Attribute values of the metadata instance; key is the attribute name, value is a literal

Example:

metadata:
  - type: ModelingMetadata::StatusInfo
    status: approved
    approver: "Jane Smith"

3.9 Dependency

Field YAML type Required Default SysML mapping
dependsOn list of strings optional absent Dependency relationships to supplier elements (qualified names); client is the containing element

3.10 Annotation Fields

These fields control how the Markdown body is interpreted as a documentation annotation.

Field YAML type Required Default SysML mapping
about string or list of strings optional absent Qualified names (or stable ids) of the elements this file's Markdown body annotates; when present, the file is a cross-element comment (SysML v2 comment … about) rather than an element with a doc of its own
locale string optional absent BCP 47 language tag (e.g., en, de, fr) tagging the Markdown body for multi-language documentation

about: usage — for cross-cutting notes that apply to one or more elements without duplicating text. The comment is its own file (e.g. model/VehicleSystem/SafetyNote.md, never a package _index.md); its type: is nominal, since a comment defines no element:

---
type: Package        # nominal — the file is a comment, not an element
name: SafetyNote
about:
  - VehicleSystem::Engine
  - VehicleSystem::Transmission
  - VehicleSystem::Brakes
---
This subsystem group requires safety analysis per ISO 26262. All three elements
share the same hazard classification and must be reviewed together.

locale: usage — for multi-language documentation, create one file per language. The primary file defines the element (its own body may be tagged with locale:); every other language is a locale variant file that sets locale: and qualifiedName: — the qualified name of the element it documents. A variant is not an element: its file name does not become a qualified name, so any file name works (by convention <Stem>.<locale>.md next to the primary file).

# model/VehicleSystem/Engine.md  (English — primary file, also defines the element)
---
type: PartDef
name: Engine
---
The engine converts fuel energy into mechanical power.
# model/VehicleSystem/Engine.de.md  (German — documentation-only variant)
---
type: PartDef
qualifiedName: VehicleSystem::Engine   # points to the same element
locale: de
---
Der Motor wandelt Kraftstoffenergie in mechanische Leistung um.

about: versus a locale variant's qualifiedName:. Both attach a file's body to other elements, but they are different annotations and neither replaces the other: a locale variant (locale: + qualifiedName:) is the element's own documentation, translated, for exactly one element — at most one per locale; an about: file is an additional comment on one or more elements, any number of which may name the same element. A file with locale:, qualifiedName: and about: is a locale variant; its about: is ignored (W051).

Parser contract for about: and locale:: - A file with about: (other than a package _index.md, and not a locale variant) is an about: comment: it contributes its Markdown body — with its name: (default: the file stem), source file and optional locale: — as a note on every listed element, and does not define a model element. Entries resolve like cross-references, by qualified name and then by stable id (so a comment can name REQ-*/TC-* ids), and against synthesized elements too. show prints one Note: <name> section per comment on each listed element. - An about: entry that resolves to no element is error E027; the comment still attaches to the entries that resolve, and when none resolves the file is kept as its own element so it is not silently lost. - A comment carries only type, name, about and locale; any other field is ignored with warning W052, as is a non-string or empty about: entry. about: on a package _index.md is ignored with W052 — the _index.md defines its package and is never a comment. - A file with both locale: and qualifiedName: is a locale variant: it contributes its Markdown body as a locale-tagged doc annotation of the element whose qualified name equals qualifiedName:, and does not define a model element of its own. A qualifiedName: that resolves to no element is error E026 (the file is then treated as its own element so it is not silently lost). - A variant does not redefine the element's structure. It may carry only type, name, locale and qualifiedName; any other field, or a type: that differs from the target's, is ignored with warning W051. - Multiple locale files for the same element are all valid; a parser collects them as a map of locale → doc string (show prints one Documentation (<locale>) section per entry). One document per locale: a second variant for a locale the element already has (from an earlier variant in walk order, or the element's own locale:) is ignored with warning W051. - A file with locale: but no qualifiedName: is an ordinary element whose own body is tagged with that locale. - qualifiedName: is never an identity override (§3.1): the qualified name is purely path-derived (§4.5, §11.3).

3.11 Constraint Fields

These fields may appear on any element, not only on RequirementDef/Requirement. When used outside a requirement context, they declare informal constraints on the element.

Field YAML type Required Default SysML mapping
requires list of strings or constraint maps optional absent Qualified names of ConstraintDefs or opaque boolean expressions that must hold; when entries are maps, they follow the constraint clause schema (§8.11.2)
assume list of strings or constraint maps optional absent Qualified names of ConstraintDefs or expressions assumed to hold (preconditions); map entries follow §8.11.2

Note: On RequirementDef and Requirement, these fields have full structural semantics (formal constraint clauses; §8.11.1). On other element types, they are treated as informal annotations and do not affect typing or redefinition.

3.12 Representation Field

Field YAML type Required Default SysML mapping
rep string optional absent Textual representation annotation; formal notation string associated with this element, used when round-tripping to/from SysML textual notation (opaque string)

3.13 Product Line Conditioning Field

Field YAML type Required Default Description
appliesWhen string or list of strings optional absent Feature condition for inclusion in a projected Configuration: a FeatureDef qualified name or FEAT-* id, an AND-list of them, or a boolean expression using and / or / not and parentheses over them. Absent = unconditionally included. See §9.10.

appliesWhen: may appear on any element type, including Requirement, PartDef, Part, TestCase, Allocation, ActionDef, Connection, Diagram, and all others — and on a Package, where it applies transitively to the whole subtree (the effective condition; §9.10, error E228 enforces one declaration per path). It is the sole mechanism by which model elements are conditioned on feature selections. A string value is either a single FeatureDef reference or a boolean expression ("A and B", "A or B", "not A", parentheses; precedence not > and > or); a list means all listed features must be selected (AND semantics). Every operand must resolve to a FeatureDef and the expression must parse — otherwise error E209 (§9.10). Feature qualified names use :: between every segment (SystemFeatures::Propulsion::HexRotorPropulsion); . is reserved for the feature/parameter boundary in parameter references (§9.7) and is not valid inside an appliesWhen: operand.

3.14 Domain Classification

These fields encode the engineering domain of an element and are used to enforce hardware/software architecture independence (§12.6).

Field YAML type Required Default Description
domain string optional system Engineering domain: system (domain-agnostic), hardware, or software. Applies to any element; most meaningful on PartDef, Part, and Package elements.
isDeploymentPackage bool optional false Marks a PartDef or Part as a deployable software artifact that must be physically allocated to a hardware element. Only meaningful on elements with domain: software.

When domain: is not set, system is assumed — the element is domain-agnostic and can interoperate with both hardware and software elements.

isDeploymentPackage: true implies domain: software; a parser should warn (W304) if isDeploymentPackage: true is combined with domain: hardware.

3.15 Custom Fields

custom_fields: is the intentional, addressable home for user-defined metadata on any element. It is the only sanctioned channel for author-defined data: an unrecognised top-level key is still captured by the parser's catch-all (and so survives a round-trip), but it is no longer silent — it raises warning W047 (§3.17) directing you to move the data here. Data placed under custom_fields: is recognised: preserved, queryable (custom.<key>), shape-checked, and exempt from W047.

Field YAML type Required Default Description
custom_fields map optional {} Flat map of string -> scalar \| list-of-scalars. Keys are freeform (no registration or name validation). Accepted on every element type.
custom_fields:
  supplier: Bosch
  costCenter: PWT-4471
  maturity: prototype
  reviewCycle: 3
  partNumbers: [A-1001, A-1002]   # lists of scalars allowed

Rules:

  • Values must be a scalar (string/number/bool/null) or a list of scalars. A nested map, or a list containing a non-scalar, raises warning W041 (custom field '<key>' must be a scalar or a list of scalars). W041 is advisory — never a hard error — and CI-gateable via --deny W041.
  • The map serialises in sorted key order so writes do not produce noisy round-trip diffs.
  • An element with no custom_fields: is unaffected; the feature adds no findings to models that do not use it.
  • custom_fields: values are never resolved as references. A relationship to another element belongs in links: under a declared link type (§12.10), where it is resolved, validated and traversable.
  • Custom fields are read-only in the web UI and CLI show; they are not editable via PUT /api/elements/<qname>.

Querying (CLI ls / find / list) — the --where predicate addresses custom fields via the custom.<key> namespace:

Form Meaning
custom.<key>=<value> exact match (scalar equals, or any list element equals)
custom.<key>=~<pattern> regex match on the value's string form (falls back to substring if the pattern is not valid regex)
custom.<key>~=<value> list membership (list contains the value; or scalar equals it)
custom.<key> presence (the field is set, any value)

--where composes (AND) with the existing type / --tag / --status filters, and multiple --where predicates are ANDed. An unparseable predicate is a usage error.

3.16 Display Order

displayOrder: is a generic, first-class numeric field, accepted on every element type, that controls the order in which an element is presented alongside its peers in an ordered output. It exists to decouple presentation order from identity: stable identifiers (REQ-*, qualified names) are opaque and must never change, so they cannot be renumbered to express the order in which elements — most importantly requirements — are meant to be read.

Field YAML type Required Default Description
displayOrder number (int or decimal) optional (unset) Primary sort key among peers. Ascending: lower sorts first. Unset sorts after every element that declares a value. Ties (equal or both-unset) fall back to stable-identifier order.
---
type: Requirement
id: REQ-SCHED-001
name: "Scheduler determinism"
status: draft
displayOrder: 20        # read after displayOrder 10, before 30
---

Rules:

  • Sort is ascending by displayOrder; an element without displayOrder sorts after every element that has one, and remaining ties break on the element's stable identifier (id else qualified name).
  • The value is a number and may be a decimal, so a new element can be inserted between two neighbours (displayOrder: 15 between 10 and 20) without renumbering. Convention: leave gaps (10, 20, 30, …).
  • displayOrder governs presentation order in the Requirements section of the Markdown validation report, the Requirement rows of the coverage matrix, and the containment tree of the web UI. It never affects validation, identity, or cross-reference resolution — it is purely a presentation hint.
  • An element with no displayOrder: is unaffected; the field adds no findings to models that do not use it. (REQ-TRS-ORDER-001)

3.17 Frontmatter Schema Strictness

The frontmatter schema is fixed: the parser recognises a defined set of field names (the fields documented in this specification) and binds each to a typed slot. A top-level key that is not a recognised field is captured by an anonymous catch-all — it round-trips through writes, but it takes no part in validation or semantics.

Because such a key is otherwise invisible, every unrecognised top-level key raises warning W047:

Code Severity Trigger Message
W047 warning A top-level frontmatter key that is not a recognised schema field and is not custom_fields unrecognized frontmatter field '<key>' — not a recognized schema field; move author-defined data undercustom_fields:(§3.15)
W049 warning qualifiedName: on a file without locale: differs from the element's path-derived qualified name. It is not an identity override (the qualified name is purely path-derived, §4.5/§11.3) and is ignored — move or rename the file instead (§3.1) `qualifiedName: <q>` is not supported as an identity override — the qualified name is path-derived ('<qname>', §4.5/§11.3); the field is ignored…

Rules:

  • W047 is advisory — a warning, never a hard error — so pre-existing models keep validating. Gate it in CI with --deny W047 to make an unrecognised field fail the build.
  • It fires per unrecognised key, naming the key and the element's file.
  • Keys under custom_fields: are exempt — that is the sanctioned home for author-defined data (§3.15). Recognised schema fields never trigger it.
  • links: (user-defined link types, §12.10) is a recognised schema field and never triggers it; its keys are validated against [linkTypes] instead (E630).
  • derive: (§3.18) is a recognised schema field and never triggers it.
  • The most common cause is a typo in a real field name (reqDomian for reqDomain, verifis for verifies): the misspelling was previously accepted and the value silently discarded. W047 surfaces the mistake instead of losing the data quietly.

(REQ-TRS-SCHEMA-001.)

3.18 Derived Fields (derive:)

derive: is a recognised field accepted on every element type. It is a mapping of field name → formula string; the tool evaluates each formula after the model is walked and records the value as a computed (read-only) field of the element, shown under Derived Fields by show. (REQ-TRS-DERIVE-001..005.)

derive:
  wcetConsumed: sum(children.custom_fields.wcet)
  wcetHeadroom: self.custom_fields.wcetBudget - self.wcetConsumed

Formula grammar: numbers, "strings", self.<field> / self.custom_fields.<key>, elements["Qualified::Name"].<field>, the aggregates sum · max · min · count · collect over children or parent (optionally .<field>), + - * /, parentheses and the null-coalesce ??.

Rules:

  • Dependency order. A derived field is evaluated after every derived field it reads — via self.<field>, elements["Q"].<field>, or a children/parent aggregate over <field> — so a reference always sees the computed value, whatever the element or entry order. Independent fields evaluate in file-walk, then block (top-to-bottom), order.
  • Cycles (E504). A field that depends on itself — directly (a: self.a + 1), through other entries of the same block, or through other elements — is a cycle. Each cycle is reported as error E504 once on every element file taking part, naming the cycle (X.a → Y.b → X.a); the fields in it are not evaluated (absent from the derived fields). A field outside the cycle that reads a cyclic field still evaluates and reads it as absent (use ?? to supply a default).
  • Malformed input (E505). A formula that does not parse, a derive: value that is not a mapping, and a formula value that is not a string are each E505; the field is not evaluated.
  • Unknown element (E506). An elements["QName"] naming no element is E506; the reference evaluates to null.
  • derive: is a recognised schema field: it never raises W047 (§3.17).

A suspect link is a trace link whose target changed after the link was last reviewed (ADR-SYS-SUSLINK-001). The element that holds the link (the source, §12.1) may record, for each reviewed target, a hash of the target's content at review time:

Field YAML type Required Default Description
traceBaselines map of string → string optional absent Key: the link target exactly as authored on the link (stable id or qualified name). Value: blake3:<hex> of the target's normative projection — its Markdown body plus its frontmatter minus the editorial fields name, displayOrder, extRef, title, traceBaselines, layout, shapes, edges, svgFile, pumlFile, and the workflow fields claimedBy, claimedAt, assignedTo and status (status is excluded only while it is ordinary lifecycle progress; it stays in the projection once it is a retiring status — deprecated, superseded, obsolete, rejected, withdrawn, wont_fix, false_positive, not_affected). One map covers every link kind on the element.

Frontmatter excerpt (the TestCase's Gherkin body is omitted; the hash is illustrative):

---
type: TestCase
id: TC-BRK-001
name: Brake response time
status: active
testLevel: L2
verifies: [REQ-BRK-001]
traceBaselines:
  REQ-BRK-001: blake3:9f2c1e0d4b7a6c5e8d3f1a2b4c6d8e0f1a3b5c7d9e1f2a4b6c8d0e2f4a6b8c0d
---

The map is written by tooling, not by hand: syscribe suspect accept <source> <target> baselines one reviewed link, suspect accept --all re-baselines every suspect link, and suspect accept --all-unbaselined baselines every link that has no baseline yet (onboarding; it never overwrites an existing entry). Links tracked: verifies, derivedFrom, satisfies, refines, implementedBy, subsets, supertype, redefines, typedBy, breakdownAdr, hazardRef, mitigatedBy, supports, confirms, scalar evidence entries, a ViewpointDef's satisfiedBy, and user-defined links: whose type does not declare suspect = false (§12.10.6).

Validation. validate recomputes each baselined target's projection hash; a mismatch is warning W090 (suspect link — review the source, then re-run suspect accept). The feature is opt-in and additive: a link with no baseline raises nothing in validate and is listed only by suspect list, and a baselined target that no longer resolves is left to the unresolved-reference checks rather than reported as W090. Gate CI on stale links with --deny W090.


4 Directory and Namespace Conventions

4.1 Model Root

The model root is a directory chosen by the user (e.g., model/). All qualified name derivation is relative to this root. A parser is initialized with the path to the root.

The root directory may contain an _index.md with type: Namespace or type: Package. If absent, the root is treated as an anonymous root namespace.

4.2 Package Directories

A directory maps to a SysML package. The package's qualified-name segment is always the directory name. A name: in the directory's _index.md is only the package's human-readable label (display name); it never changes the qualified name (§11.3).

model/
  _index.md              # type: Namespace  — root namespace
  VehicleSystem/
    _index.md            # type: Package, name: VehicleSystem
    Chassis.md           # type: PartDef   — qname: VehicleSystem::Chassis
    Powertrain/
      _index.md          # type: Package
      Engine.md          # qname: VehicleSystem::Powertrain::Engine

4.3 _index.md Schema

An _index.md file may carry any fields from Section 3, plus the following package-specific fields:

Field YAML type Description
type string Must be Package, LibraryPackage, or Namespace
name string Display label for the package. Does not change the qualified name — the segment is always the directory name (§11.3)
imports list Import declarations (Section 3.7.1)
aliases list Alias declarations (Section 3.7.2)
filterCondition string Package filter condition (opaque KerML expression)

Membership is generated, never authored (ADR-SYS-PKG-001, GH #120). A package's members are exactly the elements in its directory. Tools present that list from the directory itself — show <package> prints a ## Members (N) table (id or qualified name, type, name, status), and the web UI detail panel and export-html package pages show the same list — so it can never drift from the files on disk. The _index.md body should state the package's purpose and scope (the why), not enumerate its members (the what). lint-docs reports advisory W103 when an _index.md body mentions three or more distinct stable ids that resolve to the package's own direct members. Summary tables that carry genuine cross-element analysis (e.g. a HARA hazard → goal → ASIL table) remain legitimate; W103 is a prompt to check, not a rule.

4.4 Library Packages

A library package uses type: LibraryPackage. Library packages are treated identically to regular packages for qualified name resolution, but tools may display them differently and they implicitly export all their members publicly.

# model/ISQ/_index.md
---
type: LibraryPackage
name: ISQ
---
International System of Quantities domain library.

4.5 Name Collision Rules

  1. The qualified name is purely path-derived (§11.3): the element's last segment is its filename stem, and each package segment is a directory name. The name: field is only the element's label (SysML declaredName used for display); it never changes the qualified name and never participates in collision detection.
  2. Because the qualified name comes from the path, two elements in the same directory cannot share a qualified name — the file system already forbids two files with the same stem. Two elements in the same directory may carry the same name: label without error.
  3. For name-identified types, author name: equal to the filename stem so the label and the identity segment agree; the stem (not name:) is what :: references resolve against. For id-identified types (Requirement, TestCase, …) the stem is the stable id and name: is free prose.
  4. A file named _index.md is never assigned its own qualified name segment; it represents the containing directory's package.
  5. A qualified name produced by more than one element — e.g. a file Foo.md next to a directory Foo/ that has an _index.md, or a synthesized element (a FeatureModel sheet entry, an FMEA/TARA row, a plugin- or annotation-emitted element) colliding with a file-backed one — is error E108.

4.6 Visibility Within Directories

By default, all elements in a directory are public members of that package. Set visibility: private in frontmatter to restrict membership visibility.

4.7 Standard Library Package Inventory

The following packages are part of the SysML v2 standard library. A parser must not error on references to these packages even if their .md files are not present in the model directory — they are resolved from the built-in library.

Auto-imported at model root (always available without explicit import):

Package Contents
ScalarValues Primitive scalar types: Integer, Real, Natural, Boolean, String, Rational, Complex, Number, NumericalValue, ScalarValue, Positive
Base Base types: Anything, DataValue (plus the multiplicity features things, dataValues, naturals, exactlyOne, zeroOrOne, oneToMany, zeroToMany)

These two packages have a fully known membership, so a type reference to a recognised member (e.g. typedBy: ScalarValues::Real, supertype: Base::DataValue) resolves cleanly — it raises no unresolved-reference finding — while a reference into one of them to a member it does not declare (e.g. ScalarValues::Flota) is flagged as a likely typo with warning W043, listing the package's known members. The import-only packages below are not enumerated, so references into them are never W043 and resolve leniently as standard-library references (W404 applies only to an operation's parameter and return types, §8.3).

Available via explicit import:

Package Contents
ISQ International System of Quantities
SI SI units and unit symbols
Parts Base Part definition
Items Base Item definition
Ports Base Port definition
Actions Base Action definition
States Base StateAction definition
Calculations Base Calculation definition
Constraints Base Constraint definition
Requirements Base Requirement definition
Allocations Base Allocation definition
Connections Base Connection and Interface definitions
Transfers Base Transfer definition
Flows Base FlowConnection definition
Occurrences Base Occurrence and EventOccurrence definitions
Events Event definitions
UseCases Base UseCase definition
AnalysisCases Base AnalysisCase definition
VerificationCases Base VerificationCase definition; includes VerdictKind enumeration
Views Base View, Viewpoint, Rendering definitions
Metadata Base SemanticMetadata definition
ModelingMetadata Standard annotation metadata: StatusInfo, Issue, Rationale, Refinement

Curated recognition of ISQ and SI (open/lenient tier). Although ISQ/SI are large open packages, the tool recognises a curated subset — the ISQ quantity-value types (MassValue, ForceValue, EnergyValue, VoltageValue/ElectricPotentialValue, …) and the SI units (the seven base units plus common derived units), accepting both the full name (SI::kilogram) and the common symbol (SI::kg, or a bare kg). A recognised member resolves cleanly; an unrecognised ISQ/SI member on an inline feature's typedBy: or unit: is warning W057 (a likely typo, since the tool's tables are a subset of the library — never W043, unlike the closed ScalarValues/Base packages), and the unit: field stays fully permissive (non-SI/domain units like USD, kWh, percent are accepted). When an element/feature declares both a recognised quantity type (typedBy: ISQ::…) and a recognised unit (unit: SI::…), the tool checks their physical dimensions agree (over the seven SI base quantities) and raises warning W044 on a mismatch (e.g. ISQ::MassValue with unit: SI::metre).


5 Cross-Reference Syntax

5.1 Absolute Qualified Names

An absolute qualified name starts from a top-level package name:

typedBy: VehicleSystem::Powertrain::Engine
supertype: Requirements::FunctionalRequirementDef

5.2 Relative References

A simple unqualified name resolves within the current element's containing package first, then outward through enclosing namespaces:

typedBy: Engine        # resolves within current package first

A ./-prefixed name explicitly means "sibling in the same directory":

subsets: [./drivetrainPort]

5.3 Wildcard and Namespace Imports

In imports: fields:

Syntax Meaning
PackageName Import a single named element (membership import)
PackageName::* Import all visible members of PackageName (namespace import)
PackageName::** Recursively import PackageName and all sub-namespaces
imports:
  - ISQ::*
  - ScalarValues::**

5.4 Aliases

Declare an alias to create a short local name:

aliases:
  - name: MV
    for: ISQ::MassValue

Then reference it as MV within the same package.

5.5 Self-Reference

Use self to refer to the element defined by the current file, within constraint or expression bodies (opaque strings only; not used in structural YAML fields).

5.6 Feature Chains

Feature chains (dot-notation paths through usages) are written using . within qualified names that appear in connections:, flowConnections:, and successionConnections: entries. For example:

connections:
  - from: engine.exhaustPort
    to: exhaustSystem.inletPort

6 Multiplicity and Cardinality Syntax

The multiplicity: field and the multiplicity sub-field inside features: entries are quoted strings conforming to the following grammar:

multiplicity  ::= bound | bound ".." bound
bound         ::= integer | "*" | qualified-name
integer       ::= [0-9]+
qualified-name ::= name ("::" name)*
name          ::= [A-Za-z_][A-Za-z0-9_]*

Common forms:

String Lower bound Upper bound SysML equivalent
"1" 1 1 [1] or [1..1]
"0" 0 0 [0]
"*" 0 ∞ [*] or [0..*]
"0..1" 0 1 [0..1]
"1..*" 1 ∞ [1..*]
"0..*" 0 ∞ [0..*]
"2..4" 2 4 [2..4]
"0..maxSeats" 0 feature ref [0..maxSeats]
"minN..maxN" feature ref feature ref [minN..maxN]

Rules: - The string must be quoted in YAML to avoid misparse (e.g., "1" not bare 1). - If only a single token is given ("4" or "maxItems"), both bounds equal that value. - * is the only shorthand for an unbounded upper; it may not appear as the lower bound. - The lower bound must resolve to a non-negative integer at evaluation time. - When a bound is not a valid integer literal or *, it is treated as a qualified-name reference and resolved during the same cross-reference resolution pass as all other qualified names (§11.3). The tool does not currently resolve bound names: an unresolved name bound is not reported (only a malformed bound such as *..3 or a negative number is E118).

Feature-reference bound example:

# Vehicle.md
features:
  - name: passengers
    typedBy: Passenger
    multiplicity: "0..maxPassengers"   # upper bound from a sibling feature

  - name: maxPassengers
    typedBy: ScalarValues::Integer
    value: "8"

7 Inline vs File-per-Element Rules

7.1 Use Inline features: When

  • The feature is a simple scalar attribute (a single typed_by with a primitive or library type, no sub-features of its own).
  • The feature has no documentation body worth a separate file.
  • The feature has no metadata of its own beyond what fits on one or two lines.
  • The feature is used only within this one definition and is not referenced by qualified name from other files.

Examples of inline-suitable features:

features:
  - name: mass
    typedBy: ISQ::MassValue
    unit: SI::kg
  - name: length
    typedBy: ISQ::LengthValue
    unit: SI::m
  - name: status
    typedBy: VehicleStatus    # an EnumerationDef in the same package

7.2 Use a Separate .md File When

  • The feature has its own sub-features (nested parts, ports, attributes).
  • The feature has significant documentation.
  • The feature is a PortDef, ActionDef, StateDef, or other definition reused across multiple parent definitions.
  • The feature is a usage that itself owns usages (a composed sub-part, a sub-action, a sub-state).
  • The feature needs its own metadata annotations.
  • The feature must be reference-able by qualified name from other files.

7.3 Attribute Definitions

Simple AttributeDef elements that define a named data type (e.g., Temperature, Pressure) always get their own file so they can be typed-by from multiple locations.

Primitive inline literals may be expressed as:

features:
  - name: maxTemperature
    typedBy: ScalarValues::Real
    value: "200.0"
    unit: SI::degC

8 Element-Specific Schemas

8.1 Package and Namespace Elements

8.1.1 _index.md for Package

---
type: Package               # or LibraryPackage, Namespace
name: VehicleSystem         # optional; defaults to directory name
isAbstract: false
imports:
  - ISQ::*
  - ScalarValues::*
aliases:
  - name: Real
    for: ScalarValues::Real
filterCondition: ""         # optional KerML filter expression (opaque)
metadata:
  - type: ModelingMetadata::StatusInfo
    status: draft
---
The VehicleSystem package contains all definitions and usages
for modelling a generic vehicle.

8.2 Structural Elements: Part, Item, Occurrence, Individual

8.2.1 PartDef — Part Definition

Defines a class of physical or logical system components.

Type-specific additional fields:

Field YAML type Default Description
supertype string or list absent Subclassification of other PartDefs
features list absent Inline owned attributes and ports
connections list absent Internal connections between sub-parts (IBD edges)
flowConnections list absent Flow connection specifications
successionConnections list absent Succession flow between sub-actions
bindingConnections list absent Binding connector specifications
performs list of strings or maps absent Actions this part performs; string shorthand or full map form (see below)
exhibitsStates list of strings absent Shorthand: qualified names of StateDefs this part exhibits (no bindings, multiplicity "1"). For named exhibit-state usages with bindings or redefinition, use type: ExhibitState in features: or as a separate file; see §8.8.4
models string or list absent Architecture element(s) this executable part is a behavioural model of; with portMap, abstracts, fidelity. See §12.11

performs: field — string shorthand and full map form:

A string entry expands to {typedBy: <string>} with all other fields at their defaults. The full map form supports all perform-action usage properties:

Sub-field YAML type Required Default Description
name string optional absent Named perform-action usage
typedBy string Required (full form) — Qualified name of the ActionDef being performed
multiplicity string optional "1" Cardinality
redefines list of strings optional absent Qualified names of perform-action usages this redefines
bindingConnections list optional absent Binding connectors that bind action parameters to part features; same schema as §8.4.3
performs:
  - VehicleBehavior::ProvidePower          # shorthand — expands to {typedBy: ...}

  - name: driveAction                      # full form
    typedBy: VehicleBehavior::RegulateSpeed
    multiplicity: "1"
    redefines: [Parts::Part::performedActions]
    bindingConnections:
      - left: driveAction.inputSpeed
        right: self.currentSpeed

Example (model/VehicleSystem/Vehicle.md):

---
type: PartDef
name: Vehicle
isAbstract: true
supertype: Parts::Part
features:
  - name: mass
    typedBy: ISQ::MassValue
    unit: SI::kg
  - name: speed
    typedBy: ISQ::SpeedValue
    unit: SI::m_per_s
    isDerived: true
  - name: fuelCapacity
    typedBy: ISQ::VolumeValue
    unit: SI::L
connections:
  - from: engine.powerOut
    to: drivetrain.powerIn
    typedBy: PowerConnection
performs:
  - VehicleBehavior::providePower
---
Abstract definition of a vehicle with mass, speed, and fuel capacity.
All concrete vehicle types specialize this definition.

8.2.2 Part — Part Usage

A usage of a PartDef in a specific context.

Type-specific additional fields:

Field YAML type Default Description
typedBy string or list absent The PartDef(s) this part is an instance of
subsets list absent Subsetted part usages
redefines list absent Redefined part usages
multiplicity string "1" Cardinality
isReference bool false ref — referential rather than composite
isPortion bool false Time-slice portion of the owning occurrence
connections list absent Internal connections (if this part usage owns sub-parts)
successionConnections list absent Structural successions between the owned usages; see 8.4.4
features list absent Additional features scoped to this usage
performs list of strings absent Actions performed in this usage context

Example (model/VehicleSystem/vehicle_b.md):

---
type: Part
name: vehicle_b
typedBy: Vehicle
multiplicity: "1"
features:
  - name: engine
    type: Part
    typedBy: Powertrain::FourCylinderEngine
    multiplicity: "1"
  - name: transmission
    type: Part
    typedBy: Powertrain::AutomaticTransmission
    multiplicity: "1"
connections:
  - from: engine.powerOut
    to: transmission.powerIn
---
Specific vehicle configuration B with a four-cylinder engine
and automatic transmission.

8.2.3 ItemDef and Item

Items classify things that may flow through or be contained in a system (as opposed to the structural parts). Schema is identical to PartDef/Part with type: ItemDef / type: Item.

Notable difference: Items are always referential; isReference defaults to true for item usages inside attribute definitions and usages.

8.2.4 OccurrenceDef and Occurrence

Occurrences model things with temporal extent. An OccurrenceDef can define time-slice structure.

Note: OccurrenceDef and Occurrence are the base occurrence types. Use them for abstract occurrence hierarchies that do not specialize any of the more specific kinds (PartDef, ItemDef, ActionDef, StateDef, etc.). All more specific definition types in SysML ultimately specialize OccurrenceDef at the KerML level.

Additional fields for OccurrenceDef:

Field YAML type Default Description
timeSlices list of inline feature maps absent Portion/time-slice usages
snapshots list of inline feature maps absent Snapshot individual usages

Example (model/Lifecycle/VehicleLifecycle.md):

---
type: OccurrenceDef
name: VehicleLifecycle
supertype: Occurrences::Occurrence
timeSlices:
  - name: operatingPhase
    typedBy: VehicleSystem::Vehicle
    isPortion: true
  - name: maintenancePhase
    typedBy: VehicleSystem::Vehicle
    isPortion: true
---
Models the full lifecycle of a vehicle from manufacture to disposal.

8.2.5 IndividualDef and Individual

Individuals represent specific instances with a fixed identity.

---
type: Individual
name: myVehicle2026
typedBy: VehicleSystem::Vehicle
isIndividual: true
features:
  - name: vehicleId
    typedBy: ScalarValues::String
    value: '"VIN-1234567890ABCDEF"'
  - name: manufactureDate
    typedBy: ISQ::DateTime
---
The specific vehicle with VIN 1234567890ABCDEF manufactured in 2026.

8.3 Port and Interface Elements

8.3.0 Concepts and decision guide

Ports and interfaces are the most error-prone area to model. Orient first, then use the schemas below.

Mental model (SysML v2). A PortDef is a reusable kind of connection point that carries directed features (in/out/inout). A Port is a usage of a PortDef on a part — it lives in the part's features: list with type: Port and typedBy: the PortDef. A connection wires two ports together. An InterfaceDef is a kind of ConnectionDef — in SysML v2, "an interface is simply a connection all of whose ends are ports" (§7.14) — so use it to package a reusable, compatible pairing of two ports; a plain ConnectionDef connects arbitrary features/parts, not necessarily ports.

Which construct → when:

You want to… Use
expose an interaction point on a part a Port (in features:) typed by a PortDef
define a reusable compatible pairing of two ports InterfaceDef (ends typed by PortDefs)
connect arbitrary features/parts ConnectionDef
wire two specific ports inside a part a connection usage — connections: with from/to feature chains, optionally typedBy: the InterfaceDef
move items between connected ports FlowDef / flowConnections:
equate two features bindingConnections:

Conjugation. The receiving end of a connection is the conjugate of the sending end: every directed feature flips direction (in↔out; inout is self-conjugate — SysML v2 §7.12.3, "each port definition has a conjugated port definition whose directed features are conjugate to those of the original"). Express it either with a dedicated conjugate PortDef (conjugates: <the source PortDef>) for reuse, or with isConjugated: true on the Port usage / interface end for a one-off. Connected directed features must be conjugate-compatible — that is how a supplier's out lines up with a consumer's in.

End-to-end shape. A supplier PartDef exposes a Port typed by a source PortDef; a consumer PartDef exposes a Port typed by the conjugate PortDef; an InterfaceDef ties the two PortDefs as its ends; a parent PartDef holds both as sub-parts and wires them in connections: (from: supplier.outPort, to: consumer.inPort, typedBy: the InterfaceDef). A FlowDef/flowConnections: may carry the items that move across.

8.3.0a Data records versus flowing items (E123)

A usage is typed by a definition of the matching kind (E123 otherwise): Attribute ← AttributeDef or EnumerationDef; Item/Part ← ItemDef/PartDef/OccurrenceDef/…; Port ← PortDef; Connection ← ConnectionDef/InterfaceDef; Interface ← InterfaceDef; Action ← ActionDef; State ← StateDef (and so on for the other usage kinds; Constraint and Calculation also accept a PartDef/ItemDef context).

You are modelling Define Use
A data record or value type (a CRC record, a decision, a request's fields) AttributeDef type: Attribute
Something that flows between parts or is sent/received (signal, message, material; a send:/accept: payload; a flow's type) ItemDef type: Item
A fixed set of literals EnumerationDef type: Attribute (or Enumeration)

An Attribute typed by an ItemDef is the common mistake. Do not silence it by changing the usage to type: Item unless the thing really flows; define the record as an AttributeDef. The E123 message states this remedy.

8.3.1 PortDef — Port Definition

Defines an interaction point. Ports can be conjugated.

Type-specific additional fields:

Field YAML type Default Description
supertype string or list absent Subclassification of other PortDefs
conjugates string absent Qualified name of the PortDef this conjugates (~PortDefName). When set, this PortDef has reversed flow directions for all features.
features list absent Inline owned flows/attributes carried through this port

Example (model/Interfaces/PowerPortDef.md):

---
type: PortDef
name: PowerPortDef
features:
  - name: voltage
    typedBy: ISQ::VoltageValue
    direction: out
    unit: SI::V
  - name: current
    typedBy: ISQ::ElectricCurrentValue
    direction: out
    unit: SI::A
---
A port definition representing an electrical power interface.

Conjugated Port Example (model/Interfaces/PowerPortReceiverDef.md):

---
type: PortDef
name: PowerPortReceiverDef
conjugates: Interfaces::PowerPortDef
---
Conjugated port definition for receiving electrical power.
All features have their directions reversed relative to PowerPortDef.

8.3.2 Port — Port Usage

Type-specific additional fields:

Field YAML type Default Description
typedBy string or list absent The PortDef(s) defining this port
direction string absent in, out, inout — overrides direction at the usage level
isConjugated bool false Shorthand: if true, the port is treated as conjugated of its PortDef without requiring a separate conjugate PortDef file
multiplicity string "1" Cardinality

Example (model/VehicleSystem/Engine.md features section):

features:
  - name: powerOut
    type: Port
    typedBy: Interfaces::PowerPortDef
    direction: out
  - name: fuelIn
    type: Port
    typedBy: Interfaces::FuelPortDef
    direction: in

8.3.3 InterfaceDef and Interface

An interface definition specifies the required compatibility between two connection ends.

Type-specific additional fields for InterfaceDef:

Field YAML type Default Description
supertype string or list absent Supertype InterfaceDefs
ends list of inline feature maps absent The two (or more) end features; each typed by a PortDef
features list absent Additional owned features
constraints list of constraint maps absent Constraint usages asserted across this interface's ends (e.g., conservation laws)

Each entry in constraints::

Sub-field YAML type Required Description
name string optional Named constraint usage
typedBy string optional Qualified name of a ConstraintDef
expression string optional Inline boolean expression (opaque string)
isAsserted bool optional (default true) Whether the constraint is asserted to hold

Example (Kirchhoff's current law on an electrical interface):

constraints:
  - name: kcl
    expression: "positive.current + negative.current = 0"
    isAsserted: true

Example (model/Interfaces/PowerInterface.md):

---
type: InterfaceDef
name: PowerInterface
ends:
  - name: sourceEnd
    typedBy: Interfaces::PowerPortDef
    direction: out
    isEnd: true
  - name: receiverEnd
    typedBy: Interfaces::PowerPortReceiverDef
    direction: in
    isEnd: true
---
Interface defining a valid power connection between a source and receiver.

8.3.4 Operations on Ports and Interfaces

The operations: field declares callable operations (synchronous) or receptions (asynchronous) on a definition element. It is valid on PortDef, InterfaceDef, ConnectionDef, and any other definition element where behavioral contracts need to be specified. In SysMLv2 terms, each entry corresponds to an OperationDef (synchronous) or a ReceptionDef (when isAsync: true) owned by the enclosing definition.

Operation entry schema:

Sub-field YAML type Required Default Description
name string Required — Operation name (must be a valid identifier)
doc string optional absent Inline documentation for this operation
isQuery bool optional false If true, the operation is side-effect free (read-only)
isAsync bool optional false If true, this is a reception — caller does not block waiting for a result; returnType must be absent
parameters list optional [] Typed, directional parameters (see table below)
returnType string optional absent Qualified name of the element typing the return value; mutually exclusive with isAsync: true
errors string or list optional absent Error types the operation can raise: an ItemDef, AttributeDef or enumeration each (E670); not on an asynchronous operation
realizedBy string optional absent The ActionDef that implements the operation (E671); its parameters must match the operation's (E672): the same names, directions, types and units, and its one direction: return parameter is the returnType

Parameter entry schema (each entry in parameters:):**

Sub-field YAML type Required Default Description
name string Required — Parameter name
typedBy string optional absent Qualified name of the type; validated by W404
direction string optional in in, out, or inout
multiplicity string optional "1" Cardinality
unit string optional absent Unit (for scalar quantities)

Cross-reference rule: If typedBy on a parameter or returnType does not resolve to a known element in the model, the validator emits W404 (warning, not error, because standard library types such as ScalarValues::Boolean and ScalarValues::Integer may be unregistered external type libraries).

Mutual-exclusion rule: isAsync: true and returnType: are mutually exclusive. Specifying both is a modelling error.

Example 1 (model/Interfaces/TelemetryPortDef.md) — PortDef with synchronous query and async reception:

---
type: PortDef
name: TelemetryPortDef
supertype: Ports::Port
features:
  - name: packet
    typedBy: Items::TelemetryPacket
    direction: out
    multiplicity: "0..*"
operations:
  - name: requestLatest
    doc: "Synchronously return the most recent telemetry packet."
    isQuery: true
    isAsync: false
    parameters: []
    returnType: Items::TelemetryPacket
  - name: subscribe
    doc: "Register for periodic telemetry push at the given interval."
    isAsync: true
    parameters:
      - name: intervalMs
        typedBy: ScalarValues::Integer
        direction: in
---
Port definition for outgoing telemetry data. Components emitting telemetry expose this port.

Example 2 — InterfaceDef with operations spanning both ends:

---
type: InterfaceDef
name: CommandInterface
ends:
  - name: commander
    typedBy: Interfaces::ControlPortDef
  - name: executor
    typedBy: Interfaces::ControlPortDef
    isConjugated: true
operations:
  - name: executeCommand
    isAsync: false
    parameters:
      - name: cmd
        typedBy: Items::ControlCommand
        direction: in
    returnType: ScalarValues::Boolean
  - name: abort
    isAsync: true
    parameters: []
---
Interface definition for command dispatch between a commander and executor.

8.4 Connection, Binding, and Succession Elements

8.4.1 Connection Schema

The connections: field on PartDef, Part, ActionDef, and Action elements specifies internal block connections. Each entry is a map.

Binary shorthand — use from: and to: for the common two-end case:

Sub-field YAML type Required Default Description
name string optional absent Named connection usage
typedBy string optional absent Qualified name of the ConnectionDef (or InterfaceDef) typing this connection
from string Required* — Source feature chain; required unless ends: is used
to string Required* — Target feature chain; required unless ends: is used
multiplicity string optional "1" Cardinality
metadata list optional absent Metadata applications

N-ary form — use ends: for connections with three or more ends, or when end names matter:

Sub-field YAML type Required Default Description
name string optional absent Named connection usage
typedBy string optional absent Qualified name of the ConnectionDef typing this connection
ends list of end-binding maps Required* — Required when from:/to: are absent
multiplicity string optional "1" Cardinality
metadata list optional absent Metadata applications

Each entry in ends: (connection usage end-binding):

Sub-field YAML type Required Description
end string Required Name of the end feature on the ConnectionDef being bound
binds string Required Feature chain to the port/usage being connected

Rule: from: / to: and ends: are mutually exclusive within a single connection entry. from:/to: is syntactic sugar that expands to ends: [{end: source, binds: <from>}, {end: target, binds: <to>}].

8.4.2 ConnectionDef File

A ConnectionDef declares its ends using a top-level ends: field.

Sub-field YAML type Required Default Description
ends list of end-declaration maps optional absent Named end features; two implicit unnamed ends exist if absent

Each entry in ends: (connection definition end declaration):

Sub-field YAML type Required Default Description
name string Required — End feature name
typedBy string optional absent PortDef or ItemDef typing this end
multiplicity string optional "1" End cardinality
isAbstract bool optional false Abstract end
crossFeatures list of cross-feature maps optional absent Features navigable across to the other end(s); see below

Each entry in crossFeatures::

Sub-field YAML type Required Description
name string Required Cross feature name
typedBy string optional Definition typing the cross feature
crosses string Required Name of the opposite end this feature navigates to
direction string optional in or out relative to this end; out means the item flows away from this end toward the crossed end
multiplicity string optional Multiplicity of the cross feature

Binary example (model/Power/PowerConnection.md):

---
type: ConnectionDef
name: PowerConnection
supertype: Connections::BinaryConnection
ends:
  - name: source
    typedBy: Interfaces::PowerPortDef
  - name: receiver
    typedBy: Interfaces::PowerPortReceiverDef
---
A binary connection transmitting electrical power.

N-ary example (model/Hydraulics/HydraulicJunction.md):

---
type: ConnectionDef
name: HydraulicJunction
ends:
  - name: supply
    typedBy: HydraulicPortDef
    multiplicity: "1"
  - name: return
    typedBy: HydraulicPortDef
    multiplicity: "1"
  - name: drain
    typedBy: HydraulicPortDef
    multiplicity: "0..1"
---
A three-way hydraulic junction connecting supply, return, and optional drain lines.

Cross-feature example (model/Fluid/FluidPath.md):

---
type: ConnectionDef
name: FluidPath
ends:
  - name: source
    typedBy: FluidPortDef
    crossFeatures:
      - name: flow
        typedBy: FluidItemDef
        crosses: target
        direction: out
  - name: target
    typedBy: FluidPortDef
---
A directed fluid path carrying a FluidItem from source to target.

N-ary usage (inside a PartDef):

connections:
  - typedBy: HydraulicJunction
    ends:
      - end: supply
        binds: pump.outPort
      - end: return
        binds: tank.inPort
      - end: drain
        binds: reservoir.drainPort

8.4.3 Binding Connectors

The bindingConnections: field specifies equality bindings. Each entry:

Sub-field YAML type Required Description
name string optional Named binding usage
left string Required Feature chain for the left-hand side
right string Required Feature chain for the right-hand side
bindingConnections:
  - left: engine.speed
    right: transmission.inputSpeed

8.4.4 Successions

The successionConnections: field specifies ordering between occurrences/actions. Each entry:

Sub-field YAML type Required Description
name string optional Named succession usage
typedBy string optional The succession usage's own type (succession s : HappensBefore first a then b;)
after string Required Feature chain of the preceding action/occurrence
before string Required Feature chain of the succeeding action/occurrence
guard string optional Boolean guard expression (opaque string)
effect list of strings optional Qualified names of actions executed on the transition
multiplicity string optional Multiplicity of the succession feature itself (succession s [1] first a then b;)
afterMultiplicity string optional Multiplicity on the first end (first [0..1] a)
beforeMultiplicity string optional Multiplicity on the then end (then [1..*] b)
successionConnections:
  - after: startEngine
    before: engageTransmission
    guard: "engine.rpm > 600"

The field is accepted on ActionDef/Action (ordering between sub-actions) and on PartDef/Part, where it carries the structural successions between the owned usages (SysML v2's first a then b; / succession s first a then b; inside a part def body).

8.5 Attribute and Enumeration Elements

8.5.1 AttributeDef

Defines a data value type.

---
type: AttributeDef
name: Temperature
supertype: ISQ::ThermodynamicTemperatureValue
features:
  - name: unit
    typedBy: SI::ThermodynamicTemperatureUnit
    value: SI::K
---
A thermodynamic temperature value defaulting to Kelvin.

Value constraints (REQ-TRS-DATA-001..-003, GH #366). An AttributeDef may constrain the values of the type it defines:

Field YAML type Description
range string A numeric interval: lo..hi (closed), or [lo, hi], (lo, hi), [lo, hi), (lo, hi] with a round bracket for an open end; lo <= hi (E680)
pattern string A regular expression a string value must match in full (E680 if invalid)
default any The value taken when none is given; it must satisfy the constraints (E681)
unit string The unit range is stated in; a bound value written with a unit is converted to it

A specialising AttributeDef (a typedef) inherits range and pattern and may narrow the range, never widen it (E683); an ItemDef whose features are attributes is a struct. A value: bound to an inline feature or an Attribute typed by a constrained type must satisfy the effective constraints (E682). A type that declares none of the fields adds no check, and the three fields on any other element type are W047.

---
type: AttributeDef
name: Speed
unit: SI::m_per_s
range: "0..70"
default: 0
---
Vehicle speed in metres per second.

8.5.2 EnumerationDef

Defines a discrete-valued attribute. The enumerated values are listed in values:.

Type-specific additional fields:

Field YAML type Required Default Description
supertype string or list optional absent Subclassification of another AttributeDef (not another EnumerationDef)
values list of enumeration value maps Required — The enumerated values

Each entry in values::

Sub-field YAML type Required Description
name string Required Enumeration literal name
value string optional Bound expression (when specializing a non-enum AttributeDef)
valueKind string optional Binding semantics (default: bound); see §3.6.1
unit string optional Unit for quantity-valued enums
metadata list optional Metadata on this literal

Example (model/VehicleSystem/VehicleStatus.md):

---
type: EnumerationDef
name: VehicleStatus
values:
  - name: parked
  - name: idling
  - name: moving
  - name: maintenance
---
Operational status of a vehicle.

Example with bound values (model/Interfaces/StandardVoltage.md):

---
type: EnumerationDef
name: StandardVoltage
supertype: ISQ::VoltageValue
values:
  - name: lowVoltage
    value: "12.0"
    unit: SI::V
  - name: highVoltage
    value: "48.0"
    unit: SI::V
---
Standard vehicle bus voltage levels.

8.5.3 Attribute Usage

---
type: Attribute
name: vehicleStatus
typedBy: VehicleSystem::VehicleStatus
multiplicity: "1"
---
Current operational status of this vehicle instance.

Value and unit. A standalone Attribute may carry value: (a literal default/binding — number, string or boolean) and unit: (name of the unit of a quantity-valued value:), the same shorthand §3.6.1 defines for inline features: entries. Ingested SysML v2 attribute mass : Real = 12.5 [kg]; produces value: 12.5, unit: kg; export-sysml writes them back as = 12.5 [kg].

8.6 Flow Elements

8.6.1 FlowDef

Defines a flow connection type (items moving between ports).

Type-specific additional fields:

Field YAML type Default Description
supertype string or list absent Supertype FlowDefs
itemType string absent Shorthand: qualified name of the ItemDef carried by this flow; see payload note below
ends list absent End port features (same schema as InterfaceDef ends)

Payload shorthand note: itemType: Fuel is syntactic sugar for a payload feature that redefines Transfers::Transfer::payload:

# shorthand
itemType: VehicleSystem::Fuel

# equivalent full form (use when explicit redefinition chain is needed)
features:
  - name: payload
    redefines: [Transfers::Transfer::payload]
    typedBy: VehicleSystem::Fuel

A parser that expands itemType: to the full form can participate in type-compatibility checking on connected ports. When specializing another FlowDef, use the full form with redefines: pointing to the supertype's payload feature to preserve the redefinition chain.

---
type: FlowDef
name: FuelFlowDef
supertype: Flows::FlowConnection
itemType: VehicleSystem::Fuel
ends:
  - name: sourcePort
    typedBy: Interfaces::FuelPortDef
    direction: out
    isEnd: true
  - name: sinkPort
    typedBy: Interfaces::FuelPortReceiverDef
    direction: in
    isEnd: true
---
A flow connection carrying fuel between a fuel source and consumer.

8.6.2 flowConnections: Schema

The flowConnections: field on PartDef or Part elements:

Sub-field YAML type Required Default Description
name string optional absent Named flow usage
typedBy string optional absent Qualified name of FlowDef
kind string optional streaming Flow kind: message, streaming, or succession
from string Required — Source port feature chain
to string Required — Target port feature chain
item string optional absent Qualified name of item type flowing (overrides FlowDef.itemType)
multiplicity string optional "1" Cardinality

Flow kind semantics:

kind SysML keyword Ordering constraint
message message flow No ordering; item is transferred asynchronously
streaming flow Continuous while source and target are both active (default)
succession succession flow Source completes → transfer completes → target starts; combines data flow with sequencing

8.7 Action and Behavior Elements

8.7.1 ActionDef

Defines a behavioral step. Actions can have parameters (in/out/inout/return) and contain sub-actions connected by succession.

Type-specific additional fields:

Field YAML type Default Description
supertype string or list absent Supertype ActionDefs
parameters list of parameter maps absent Action parameters; see 8.7.2
returnType string absent Qualified name of the return type (for functions/calculations)
features list absent Owned sub-actions, attribute usages, etc. (inline)
subActions list of sub-action maps absent Named sub-action usages (alternative to file-per-subaction); see 8.7.3
successionConnections list absent Ordering between sub-actions; see 8.4.4
bindingConnections list absent Parameter bindings
controlNodes list of control node maps absent Fork, join, decision, merge nodes; see 8.7.4
body string absent Implementation body in a named language (opaque); mutually exclusive with subActions
bodyLanguage string absent Language name for body: "alf", "ocl", "sysml", or user-defined
variables list absent Executable actions: {name, value, typedBy?, unit?} local data, as on a StateDef; with assign: on a sub-action they make the action executable (E883, E884, E887, W935); a SendAction alone does not, so an activity that only sends declares variables: []. When a state machine runs an executable ActionDef (as entryAction, doAction, exitAction, initialEffect or effect), its SendActions, also those of performed callees, are emitted as messages of the machine (REQ-TRS-ACTSEND-001)

8.7.2 Parameter Schema

Each entry in parameters::

Sub-field YAML type Required Default Description
name string Required — Parameter name
typedBy string optional absent Typed by a definition
direction string Required — in, out, inout, or return
multiplicity string optional "1" Cardinality
isReadonly bool optional false Readonly flag

8.7.3 Sub-Action Schema

Each entry in subActions::

Sub-field YAML type Required Default Description
name string Required — Sub-action usage name
typedBy string optional absent Qualified name of the ActionDef
kind string optional Action Action, PerformAction, SendAction, AcceptAction, AssignmentAction, IfAction, LoopAction, TerminateAction
parameters list optional absent Parameter bindings for this invocation
guard string optional absent Boolean guard expression (opaque)
multiplicity string optional "1" Cardinality
isParallel bool optional false For LoopAction and parallel composition
assign string optional absent AssignmentAction: name := expression; … (expression language)
condition string optional absent IfAction/LoopAction condition (expression language); a while/until loop requires it
loopKind string optional while while or until (for is not supported)
after string optional absent AcceptAction: a duration; the accept completes that long after it became enabled
as string optional absent AcceptAction: names the received payload (<as>.<field>)

Sub-action kinds:

kind value SysML equivalent Notes
Action action usage Generic action usage
PerformAction perform action Reuses a separately defined ActionDef
SendAction send ... via ... Sends a message/item through a port
AcceptAction accept ... via ... Receives trigger or message
AssignmentAction := assignment Assigns a value to a target
IfAction if ... then ... else ... Conditional branching
LoopAction loop ... / while ... / for ... Looping
TerminateAction terminate Terminates the containing action
DecisionNode (control node) Exclusive branching point
ForkNode (control node) Parallel split
JoinNode (control node) Parallel join
MergeNode (control node) Merge of alternate paths

8.7.4 Control Node Schema

Each entry in controlNodes::

Sub-field YAML type Required Description
name string Required Node name
kind string Required DecisionNode, ForkNode, JoinNode, MergeNode
parameters list optional The node's own in/out/inout pin declarations (fork f { in a; out b : T; }), each {name, direction, typedBy?} as in 8.7.2; direction defaults to in

Example (model/VehicleBehavior/ProvidePower.md):

---
type: ActionDef
name: ProvidePower
parameters:
  - name: requestedPower
    typedBy: ISQ::PowerValue
    direction: in
  - name: actualPower
    typedBy: ISQ::PowerValue
    direction: out
subActions:
  - name: startEngine
    typedBy: VehicleBehavior::StartEngine
    kind: Action
  - name: regulatePower
    typedBy: VehicleBehavior::RegulatePower
    kind: Action
    parameters:
      - name: target
        value: requestedPower
  - name: measureOutput
    typedBy: VehicleBehavior::MeasurePower
    kind: Action
successionConnections:
  - after: startEngine
    before: regulatePower
  - after: regulatePower
    before: measureOutput
bindingConnections:
  - left: measureOutput.measuredPower
    right: actualPower
---
Action definition for providing engine power in response to a power request.

8.7.5 Action Usage

Same as ActionDef but with type: Action, plus typedBy: referencing the definition:

---
type: Action
name: providePower
typedBy: VehicleBehavior::ProvidePower
multiplicity: "1"
---

8.7.6 SendAction and AcceptAction

When subActions entries have kind: SendAction or kind: AcceptAction, the following additional sub-fields apply:

Sub-field YAML type Description
payload string Qualified name of the item/message being sent or accepted
via string Feature chain to the port through which the send/accept occurs
to string For SendAction — feature chain naming the receiver (send x via p to t)
trigger map For AcceptAction — trigger specification; see 8.7.7. A time or change trigger has no payload (accept after 5;, accept when c;, accept at t;)

When subActions entries have kind: IfAction, the following additional sub-fields apply:

Sub-field YAML type Required Description
condition string Required Boolean condition expression (opaque string)
then list of sub-action maps Required Sub-actions executed when condition is true; same schema as subActions:
else list of sub-action maps optional Sub-actions executed when condition is false

Example:

subActions:
  - name: checkFuel
    kind: IfAction
    condition: "fuel.level > 0"
    then:
      - name: startEngine
        kind: PerformAction
        typedBy: VehicleBehavior::StartEngine
    else:
      - name: signalEmpty
        kind: SendAction
        payload: VehicleCommands::FuelEmptySignal
        via: alertPort

When subActions entries have kind: LoopAction, the following additional sub-fields apply:

Sub-field YAML type Required Default Description
loopKind string optional while Loop form: while, until, or for
condition string optional* — Boolean expression; *required for while and until forms
untilCondition string optional — For the while form only: the trailing until condition of SysML v2's while c { ... } until d; (both conditions apply)
variable string optional* — Iteration variable name; *required for for form
sequence string optional* — Feature chain to the collection being iterated; *required for for form
body list of sub-action maps Required — Sub-actions executed each iteration; same schema as subActions:

Examples:

subActions:
  # while form
  - name: waitForReady
    kind: LoopAction
    loopKind: while
    condition: "not system.ready"
    body:
      - name: pollStatus
        kind: PerformAction
        typedBy: VehicleBehavior::PollSystemStatus

  # until form
  - name: retryConnect
    kind: LoopAction
    loopKind: until
    condition: "connection.established"
    body:
      - name: attemptConnect
        kind: PerformAction
        typedBy: VehicleBehavior::AttemptConnection

  # for form
  - name: initSensors
    kind: LoopAction
    loopKind: for
    variable: sensor
    sequence: "sensors"
    body:
      - name: initSensor
        kind: PerformAction
        typedBy: VehicleBehavior::InitializeSensor

When subActions entries have kind: AssignmentAction, the following additional sub-fields apply:

Sub-field YAML type Required Default Description
target string Required — Feature chain identifying the occurrence whose feature is being assigned (e.g., self, engine)
referent string Required — Feature chain relative to target naming the feature to assign
value string Required — Value expression (opaque string)
valueKind string optional bound Binding semantics: bound, initial, default-bound, default-initial; see §3.6.1

Example:

subActions:
  - name: setThrottle
    kind: AssignmentAction
    target: self
    referent: throttlePosition
    value: "requestedPower / maxPower"
    valueKind: initial
  - name: resetCounter
    kind: AssignmentAction
    target: controller
    referent: cycleCount
    value: "0"

8.7.7 Trigger Specification

A trigger map within an AcceptAction entry:

Sub-field YAML type Description
kind string timeOut, message, change, at
when string For timeOut: expression giving the timeout duration (opaque); for at: the absolute instant (opaque)
payload string For message: qualified name of the payload type
condition string For change: boolean change condition expression (opaque)

8.8 State Machine Elements

8.8.1 StateDef

Defines a state node in a state machine. States can own entry/do/exit behaviors and transitions.

Type-specific additional fields:

Field YAML type Default Description
supertype string or list absent Supertype StateDefs
isParallel bool false Parallel (orthogonal) state with concurrent sub-regions
variables list absent Executable machines: {name, value, typedBy?, unit?} — the machine's data; value is an expression with no names (E883)
initialEffect map absent Executable machines: {assign: "name := expr; …"} run by the initial transition (E884)
entryAction string or map absent Entry behavior; qualified name of ActionDef or inline sub-action map
doAction string or map absent Do behavior; qualified name of ActionDef or inline sub-action map
exitAction string or map absent Exit behavior; qualified name of ActionDef or inline sub-action map
subStates list of sub-state maps absent Nested substates; see 8.8.2
transitions list of transition maps absent Outgoing transitions; see 8.8.3

8.8.2 Sub-State Schema

Each entry in subStates::

Sub-field YAML type Required Description
name string Required Substate usage name
typedBy string optional Qualified name of a StateDef
isInitial bool optional true marks the initial state
isFinal bool optional true marks a final (terminal) state
entryAction string or map optional Entry behavior
doAction string or map optional Do behavior
exitAction string or map optional Exit behavior
transitions list optional Transitions from this substate

8.8.3 Transition Schema

Each entry in transitions::

Sub-field YAML type Required Default Description
name string optional absent Named transition usage
source string optional absent Local name or qualified name of the source state; required when the transition is not lexically nested inside its source state
target string Required — Local name or qualified name of the target state
accept string or map optional absent Accepter specification; string shorthand is equivalent to {payload: <string>}; see accept sub-schema below
guard string optional absent Boolean guard expression evaluated during source state performance (opaque string)
effect string or map optional absent Effect action executed during the transition; string is a qualified name shorthand; see effect sub-schema below

accept: sub-schema (the transition's AcceptAction):

Sub-field YAML type Required Description
payload string optional Qualified name of the item/message type accepted
via string optional Feature chain to the port on which the trigger arrives

effect: sub-schema (the transition's effect action usage):

Sub-field YAML type Required Description
name string optional Name of the effect action usage
typedBy string optional Qualified name of the ActionDef typing the effect
bindingConnections list optional Parameter bindings for the effect action; see §8.4.3

String shorthands: - accept: StartCommand expands to accept: {payload: StartCommand} - effect: VehicleBehavior::StartEngine expands to effect: {typedBy: VehicleBehavior::StartEngine}

Placement. A transition may be authored in either of two equivalent placements, both yielding the same (source → target) edge:

  1. nested — under a subStates: entry's own transitions: list; the enclosing substate is the implicit source (so source: may be omitted);
  2. top-level — under the StateDef's transitions: list; here source: is required.

A transition missing a required endpoint — a top-level transition with no source:, or any transition with no target: — yields no edge; it is reported as warning W929 (draft-suppressed, gateable with --deny W929) rather than silently ignored.

A single transition extractor consumes both placements, so the state-machine completeness checks (§22.1) see one consistent edge model regardless of authoring style.

Deprecated keys (W075). The keys from: / to: / trigger: are not SysMLv2 vocabulary (SysMLv2 §7.18.3 uses source / target / accept). They remain accepted as aliases — from ≡ source, to ≡ target, trigger ≡ accept.payload — so existing models keep parsing, but any transition using them raises W075 (draft-suppressed; gateable with --deny W075). Author new transitions with the canonical keys.

Example (model/VehicleBehavior/VehicleStates.md):

---
type: StateDef
name: VehicleStates
subStates:
  - name: off
    isInitial: true
    transitions:
      - target: on
        accept:
          payload: VehicleCommands::StartCommand
          via: controlPort
        guard: "fuel.level > 0"
        effect:
          name: doStart
          typedBy: VehicleBehavior::StartEngine
  - name: on
    doAction: VehicleBehavior::ProvidePower
    transitions:
      - target: off
        accept: VehicleCommands::StopCommand
        effect: VehicleBehavior::StopEngine
      - target: fault
        accept:
          payload: VehicleCommands::FaultSignal
        guard: "engine.temperature > maxTemperature"
  - name: fault
    isFinal: false
    entryAction: VehicleBehavior::RecordFault
    transitions:
      - target: off
        accept: VehicleCommands::ResetCommand
---
State machine for the top-level operational states of a vehicle.

Top-level transition (not nested inside source state):

transitions:
  - name: faultRecovery
    source: fault
    target: off
    accept: VehicleCommands::ResetCommand
    guard: "diagnostics.cleared"
    effect: VehicleBehavior::ClearFaultLog

8.8.4 ExhibitState Usage

An ExhibitState usage declares that a part or occurrence exhibits a state machine defined elsewhere. It is always referential (isReference: true is implied and must not be set to false).

Shorthand — sufficient when no bindings or redefinition are needed:

exhibitsStates:
  - VehicleStates::OperatingState

Inline feature form — use when naming the usage, adding bindings, or redefining:

features:
  - name: operatingMode
    type: ExhibitState
    typedBy: VehicleStates::OperatingState
    multiplicity: "1"
    redefines: [Parts::Part::exhibitedStates]
    bindingConnections:
      - left: operatingMode.speed
        right: self.currentSpeed

File form (model/Vehicle/ExhibitOperatingMode.md):

---
type: ExhibitState
name: operatingMode
typedBy: VehicleStates::OperatingState
multiplicity: "1"
bindingConnections:
  - left: operatingMode.speed
    right: self.currentSpeed
---
Exhibits the top-level operating state machine, binding speed to the vehicle's current speed.

Type-specific rules: - isReference is always true; an explicit false is not currently diagnosed. - typedBy is required (an exhibit-state usage must reference a StateDef). - bindingConnections follow the same schema as §8.4.3.

8.8.5 Typed Ports

A port (an inline features: entry of type: Port, or a child Port element) may be typed by a PortDef with typedBy:, and conjugated with isConjugated: true (SysML ~). The PortDef's features: entries typed by an ItemDef are its items: each names an event (the ItemDef's name) and may say direction: in | out | inout, relative to a part that owns the port unconjugated; a conjugated port reverses in and out. The ItemDef's own features: are the event's payload fields, each with typedBy: (or type:) and optional unit:: a number without a unit is dimensionless, an ISQ quantity type gives its dimension, Boolean and String their kinds, anything else is unknown and compatible with everything (GH #331).

A part that exhibitsStates: a machine is checked against its typed ports:

Check Code
send: E, via: p or accept: {payload: E, via: p} where p's type has no item E E932
the item's direction at p (after conjugation) does not allow the send or accept E932
a send payload key, or an as: m reference m.f, naming a field the item does not have E932
a send payload value, or an expression using m.f, of the wrong kind or unit dimension (by dimension, not exact unit, like every other E935) E935

An untyped port, and a port typed by a PortDef that has no items (only attribute or flow features), keep today's checks (name and port direction only). An item with no direction: is not direction-checked. An item whose ItemDef does not resolve (E111) is still carried, under the last segment of its typedBy:, with its payload fields unchecked. One ItemDef declared by two features of opposite directions flows both ways. A send payload value that forwards a field m.f of the event its transition accepted has that field's type. A PortDef types a single port; an InterfaceDef types a connection between two ends and is not used here. This version checks each machine against its own ports: it does not check that the two ends of a connection are typed as conjugates.

When an executable part models: an architecture element (§12.11), a port typed by the same PortDef and conjugation as exactly one port of the modelled elements corresponds to it without a portMap: entry, and two corresponding ports typed differently are E991. The types must be the same PortDef: a port typed by a specialisation of the other side's PortDef is E991 too.

---
type: PortDef
name: SamplePort
features:
  - {name: sample, typedBy: Interfaces::GridSample, direction: out}
---

8.8.6 In-process plants — PlantDef and plant:

A PlantDef is a continuous plant written in the expression language (§22.2), so a composite can close its loop around physics without an FMU or any other toolchain (GH #340). A part whose definition (or a definition it specialises) carries plant: is a plant part; it runs at the composite's plantStep: through the same exchange as an FMU plant (held inputs, outputs read at each communication point, the step cut at the earliest timer or delayed message).

Field (PlantDef) Shape Meaning
parameters: list of {name, value, description?} constants; value is a constant expression with a unit ("0.4 SI::ohm", "20 ms")
inputs: list of {name, unit?, value?, description?} (or bare names) written by a binding; value is the value before the first write (default 0)
outputs: list of {name, unit?, description?} read by a binding; at least one
states: list of {name, value, description?} integrated quantities; value is the initial value and fixes the dimension
equations: list of strings der(x) = expression (one per state) and y := expression (one per output)
method: rk4 (default) | euler fixed-step integration
maxStep: duration the longest integration sub-step; a communication step is split evenly so that no sub-step exceeds it

Names are basic names and distinct across the four lists. An expression may use any parameter, input, state and output; outputs are computed in an order in which each needs only earlier ones, and a cycle between outputs is an algebraic loop (E938). Every equation is checked for kind and unit dimension with the declared dimensions (E935): der(x) has the dimension of x per second. The functions exp, ln, sin, cos and tanh (dimensionless argument) join min, max, abs, sqrt and pow; they are the pure-Rust libm implementations and an integer power is computed by repeated squaring, so a model and scenario give the same trace, bit for bit, on every platform. A variable's unit: is a coherent SI unit; a scaled unit (mV) is E938.

plant: on a PartDef/Part is a PlantDef reference, or a mapping:

plant: Behavior::Plants::GridImpedance          # a PlantDef
plant: {def: Behavior::Plants::GridImpedance, parameters: {tau: "10 ms"}}
plant: {library: FirstOrderLag, unit: "SI::V", parameters: {tau: "5 ms"}}

The library plants are Gain (y := k·u), FirstOrderLag (der(x) = (k·u − x)/tau, y := x), Integrator (der(x) = k·u, y := x; unit is the output's), Saturation (y := min(max(u, lo), hi)), DelayLine (y is u delayed by delay, initial before), and ImpedanceDivider (v := vSrc − z·i, no unit). unit: applies only to a library plant; a parameter override must name a parameter and keep its dimension (E939). A part with both fmu: and plant: is E930; a composite with an in-process plant part and no plantStep: is E934; a binding to a plant must read one of its outputs or write one of its inputs, of the matching dimension, and its other end is a machine variable, never another plant (E933).

Fault models (injection: on an FMEA row or fault-tree event, E936) act on plant parts: stuck and offset may name a plant output (applied to every read from the moment of injection), silent freezes the plant (no longer stepped or read; the machines keep its last outputs), and {kind: parameter, parameter, value} changes a parameter until the fault is cleared. The composite's evidence hash covers each plant's parameters, equations, method and maxStep.

8.9 Calculation Elements

8.9.1 CalculationDef

Defines a parameterized calculation that returns a value.

Type-specific additional fields:

Field YAML type Default Description
supertype string or list absent Supertype CalculationDefs
parameters list absent Input/output parameters; same schema as ActionDef parameters (Section 8.7.2)
returnType string absent Qualified name of the return type
body string absent Expression body (opaque string in bodyLanguage)
bodyLanguage string absent Language of body: "kerml", "ocl", "alf", "python", etc.

Example (model/Analysis/FuelEconomy.md):

---
type: CalculationDef
name: FuelEconomyCalc
parameters:
  - name: distance
    typedBy: ISQ::LengthValue
    direction: in
  - name: fuelConsumed
    typedBy: ISQ::VolumeValue
    direction: in
  - name: economy
    typedBy: ISQ::FuelEconomyValue
    direction: return
bodyLanguage: kerml
body: |
  economy = distance / fuelConsumed
---
Calculates fuel economy as distance divided by fuel consumed.

8.9.2 Calculation Usage

A Calculation usage invokes a CalculationDef in a specific context. It has the same structural fields as CalculationDef plus typedBy: identifying the definition being invoked. The parameters: and returnType: are inherited from the typed definition and need not be repeated unless overriding.

Field YAML type Default Description
typedBy string absent Qualified name of the CalculationDef this usage invokes
multiplicity string "1" Cardinality
bindingConnections list absent Binding connectors that supply arguments to the calculation's parameters; same schema as §8.4.3
---
type: Calculation
name: computeThrust
typedBy: PropulsionCalcs::ThrustCalculation
multiplicity: "1"
bindingConnections:
  - left: computeThrust.massFlow
    right: self.fuelMassFlow
  - left: computeThrust.exhaustVelocity
    right: self.nozzleExitVelocity
---
Invokes the ThrustCalculation with this engine's fuel mass flow and nozzle exit velocity.

8.10 Constraint Elements

8.10.1 ConstraintDef

Defines a boolean-valued condition that can be evaluated.

Type-specific additional fields:

Field YAML type Default Description
supertype string or list absent Supertype ConstraintDefs
parameters list absent Parameters for parameterized constraints
expression string absent Constraint expression body (opaque string; the language is not declared or interpreted — there is no expressionLanguage field)

Example (model/Requirements/MassConstraint.md):

---
type: ConstraintDef
name: MassConstraint
parameters:
  - name: actualMass
    typedBy: ISQ::MassValue
    direction: in
  - name: maxMass
    typedBy: ISQ::MassValue
    direction: in
expression: "actualMass <= maxMass"
---
Constraint that the actual mass does not exceed the maximum allowed mass.

8.10.2 Constraint Usage and assert

A Constraint usage applies a ConstraintDef within context:

---
type: Constraint
name: massCheck
typedBy: Requirements::MassConstraint
---

The assert keyword in SysML is represented by isAsserted: true:

---
type: Constraint
name: massCheck
typedBy: Requirements::MassConstraint
isAsserted: true
---
Field YAML type Default Description
isAsserted bool false When true, corresponds to assert constraint — the constraint must hold
isNegated bool false When true, asserts the negation of the constraint

8.11 Requirement Elements

8.11.1 RequirementDef

Defines a class of requirements.

Type-specific additional fields:

Field YAML type Default Description
supertype string or list absent Supertype RequirementDefs
subject string absent Qualified name of the type (e.g., a PartDef) that the implicit subject feature is typed by. The subject feature is implicitly named subject with multiplicity "1" and represents the element that must satisfy this requirement.
actors list of strings absent Qualified names of stakeholder parts (actors for use-case style requirements)
stakeholders list of strings absent Qualified names of PartDef elements representing stakeholders
concerns list of strings absent Qualified names of ConcernDef or Concern elements addressed by this requirement
framedConcerns list of strings absent Concerns that this requirement explicitly frames (captures but does not resolve)
requires list of constraint maps absent Formal constraint clauses; see 8.11.2
assume list of constraint maps absent Assumption clauses
parameters list absent Requirement parameters (typed attributes that parameterize the requirement)
derivedFrom list of strings absent Qualified names of source requirements this requirement is derived from (deriveReqt relationship). A parser creates implicit Dependency edges of type RequirementDerivation (from the RequirementsVerification standard library) for each entry.

8.11.2 Requirement Constraint Clause Schema

Each entry in requires: or assume::

Sub-field YAML type Required Description
typedBy string optional Qualified name of a ConstraintDef
expression string optional Inline constraint expression (opaque; the language is not declared or interpreted)
isAsserted bool optional Whether asserted (default true for requires:, false for assume:)

Example (model/Requirements/MassRequirementDef.md):

---
type: RequirementDef
name: MassRequirementDef
subject: VehicleSystem::Vehicle
parameters:
  - name: maxMass
    typedBy: ISQ::MassValue
    direction: in
requires:
  - typedBy: Requirements::MassConstraint
stakeholders:
  - Stakeholders::VehicleEngineer
concerns:
  - Concerns::VehicleWeight
---
Requirement that a vehicle's mass shall not exceed a specified maximum.
The maximum mass is a parameter of this requirement definition.

8.11.3 Requirement Usage

---
type: Requirement
name: vehicleMassReq
typedBy: Requirements::MassRequirementDef
features:
  - name: maxMass
    typedBy: ISQ::MassValue
    value: "1500.0"
    unit: SI::kg
---
The vehicle mass shall not exceed 1500 kg.

8.11.4 Satisfying and Verifying Requirements

Satisfaction is declared on the element satisfying the requirement (typically a Part or PartDef):

# On Vehicle.md or vehicle_b.md
satisfies:
  - Requirements::vehicleMassReq

Verification is declared on a VerificationCase:

# On a VerificationCase file
verifies:
  - Requirements::vehicleMassReq

Top-level satisfaction and verification fields on any element:

Field YAML type Default Description
satisfies list of strings absent Qualified names of Requirement usages this element satisfies
implementedBy string or list absent Path(s) to the source artifact(s) realising this Part/PartDef/Interface/InterfaceDef. Resolved like sourceFile; missing local paths emit W023 (§12.8)

verifiedBy is not an authored field. It is the reverse index of verifies:, computed at load time (§11.11) and never written to a file (§12.1); author the link on the verifying element with verifies:.

8.11.5 ConcernDef and Concern

ConcernDef specializes RequirementDef in SysML and therefore inherits all RequirementDef fields. The stakeholders: field identifies who holds the concern. The following fields apply with the same semantics as on RequirementDef:

Field YAML type Default Description
supertype string or list absent Supertype ConcernDefs or RequirementDefs
subject string absent Qualified name of the type (e.g., a PartDef) that this concern is about; the implicit subject feature has multiplicity "1"
stakeholders list of strings absent Qualified names of PartDef elements representing the stakeholders who hold this concern
requires list of constraint maps absent Formal constraint clauses the concern asserts (same schema as §8.11.2)
assume list of constraint maps absent Assumption clauses (same schema as §8.11.2)
parameters list absent Concern parameters
---
type: ConcernDef
name: VehicleWeightConcern
subject: VehicleSystem::Vehicle
stakeholders:
  - Stakeholders::VehicleEngineer
  - Stakeholders::CustomerRep
requires:
  - expression: "subject.mass <= 2000.0"
---
Concern regarding the total weight of the vehicle impacting fuel economy and performance.

8.11.6 Native Requirement Type

The native Requirement type is a first-class element designed for safety-critical and regulated engineering contexts where every requirement must carry a stable, opaque identifier that never changes (regardless of file renames or restructuring), a structured lifecycle status, and a normative textual statement in the Markdown body.

This is distinct from the SysML-usage Requirement (§8.11.3), which is typed by a RequirementDef. Native requirements are dispatched by the parser based on the id: field matching the REQ-* pattern (§11.10, step 0).

Frontmatter fields:

Field Type Required Description
type literal Requirement Required Discriminator.
id string Required Stable opaque ID matching ^REQ(-[A-Z0-9]{2,12})*-[0-9]{3,8}$. Unique across the model. Never changes.
name string Required One-line human-readable label — free prose (spaces/punctuation allowed; W042 does not apply). No newlines.
status enum Required Lifecycle state: draft, review, approved, implemented, verified.
reqClass enum optional Position in the stakeholder→system decomposition: stakeholder, system, or derived. Recognised, first-class field (REQ-TRS-SCHEMA-002); records authoring intent independently of derivedFrom. Informational only: the value is not validated and drives no rule.
requirementKind enum optional Kind of requirement by its subject: stakeholder, system, software, hardware, process, regulatory, or deliverable (the last three — process capability, regulatory compliance, deliverables — are not architecture-allocatable: a leaf of such a kind raises neither W300 nor W302). Emitted by syscribe template Requirement. The value is validated — anything else is error E022. Apart from that exemption it drives no traceability rule; the domain rules of §12.5 use reqDomain.
derivedFrom list of id-or-qualname optional IDs (REQ-*) or qualified names of parent Requirements. Absent = stakeholder-level requirement.
silLevel integer 1–4 optional IEC 61508 SIL level. Mutually exclusive with asilLevel — do not set both (W006).
asilLevel enum A|B|C|D optional ISO 26262 ASIL level. Mutually exclusive with silLevel — do not set both (W006).
plLevel enum a|b|c|d|e optional ISO 13849-1 Performance Level. Should not be combined with asilLevel/silLevel (not checked — W006 covers only the ASIL/SIL pair). Not part of integrity-level propagation (§12.7).
derivedFromSafetyGoal string optional ID or qualified name of the SafetyGoal that motivated this requirement (§8.18.1). When set and the SafetyGoal carries asilLevel:/silLevel:, this element must also carry one of them (E841; §12.7).
derivedFromCybersecurityGoal string optional ID or qualified name of the CybersecurityGoal that motivated this requirement (§8.18.2). Implies verificationMethod: should be set (W807). derivedFromSecurityGoal is a legacy serde alias for this same field, honored for backward compatibility (REQ-TRS-SEC-006).
verificationMethod enum optional How this requirement will be verified: test, inspection, analysis, or demonstration. Required for ASIL B/C/D requirements (W701).
wcet string optional WCET claim (opaque). E.g. "O(1)", "≤ 200 cycles @ 72 MHz".
tags list of strings optional Free labels for filtering/grouping.
displayOrder number optional Reading/presentation order among peer requirements in the report and coverage matrix; ascending, unset last, id tie-break. Generic field — see §3.16.
reqDomain enum optional Engineering domain of this requirement: system, hardware, or software. Leaf requirements at implemented/verified status should be refined to hardware or software (warning W302).
breakdownAdr string optional ADR-* id or qualified name of the ADR documenting the rationale for this requirement's derivation from its parent(s). Required when derivedFrom: is non-empty (error E310). Also required when the requirement's integrity level is lower than its source's (W808; see §12.7).

reqClass vs requirementKind vs reqDomain. Three optional classification fields coexist; none replaces another. reqClass records where the requirement sits in the decomposition (stakeholder need, system requirement, derived requirement) and is unvalidated. requirementKind records what kind of requirement it is by subject (stakeholder, system, software, hardware, process, regulatory, deliverable) and is enum-checked (E022); the last three exempt a leaf from W300/W302. reqDomain (system/hardware/software) is the one the other traceability rules read (§12.5, E313, W302). Precedence. The three never override each other: reqClass is descriptive (and unvalidated), requirementKind (by subject) is enum-checked and only the non-allocatable kinds change a rule (W300/W302), reqDomain drives the domain rules. stakeholder/system appear in both reqClass and requirementKind; when they disagree, neither wins — prefer requirementKind for subject and reqClass for decomposition level, and keep them consistent.

Status values:

Value Meaning
draft Being authored; not yet reviewed.
review Under formal review.
approved Baseline-approved; may be implemented.
implemented Implementation exists.
verified Covered by at least one active TestCase.

ID pattern: ^REQ(-[A-Z0-9]{2,12})*-[0-9]{3,8}$ - Prefix REQ, zero or more uppercase-alphanumeric category segments (2–12 chars each; a bare REQ-001 is valid), 3–8 digit numeric suffix (default cap 8; configurable via [ids] max_digits, minimum 3). - Examples: REQ-SCHED-001, REQ-SCHED-BITMAP-001, REQ-BRAKE-CTRL-003

Configurable additional prefixes ([ids.prefixes]). The built-in prefix above is not the only one a type may accept. A project may add extra stable-ID prefixes per element type in the [ids.prefixes] table of <model_root>/.syscribe.toml — keyed by element-type name (Requirement, TestCase, …), each value a list of prefixes. Extra prefixes are additive (the built-in always stays valid) and pure identity (they affect only id recognition and id-based resolution; no other field is implied). Each prefix must match ^[A-Z][A-Z0-9]{1,11}$; an additional prefix P accepts the same id grammar as the type's built-in with P substituted, and the [ids] max_digits cap (E023) applies equally. An entry keyed by a non-id-identified type, or a prefix failing the prefix grammar, raises warning W046 and is ignored (well-formed siblings still apply). Example: Requirement = ["STK", "SYS"] makes STK-SCHED-001 and SYS-SCHED-001 valid Requirement ids alongside REQ-SCHED-001.

Body structure:

The body is free-form Markdown. The normative statement is the leading prose — all content before the first ## heading. The parser extracts this as the canonical requirement text.

Recommended layout:

<normative statement — paragraphs containing at least one "shall">

## Rationale
<why this requirement exists; safety argument>

## Notes
<WCET analysis, implementation pointers, related standards clauses>

Validation: normative text must be non-empty (E012); should contain shall (warning W001 if absent). The normative text is everything before the first line that begins ## (or #) after the start of the body; a body that begins directly with a ## heading is therefore treated as all normative text, so E012 does not fire and W001 is the result.

Complete example:

---
type: Requirement
id: REQ-SCHED-BITMAP-001
name: "Bitmap-based O(1) priority selection"
status: approved
silLevel: 4
derivedFrom:
  - REQ-SCHED-001
breakdownAdr: ADR-SW-SCHED-001
verificationMethod: test
wcet: "O(1)"
tags:
  - scheduler
  - timing-critical
---

The scheduler shall maintain a 32-bit priority bitmap and select the
highest-priority ready thread in O(1) time using a count-leading-zeros
operation. This guarantee shall hold regardless of the number of threads
or their distribution across priority levels.

## Rationale

A linear-scan selection algorithm produces a WCET proportional to
`MAX_THREADS`, violating the fixed-time scheduling requirement of
IEC 61508 SIL 4 timing analysis.

## Notes

Implementation: `kernel/src/scheduler/ready_queue.rs` — `ReadyQueue::dequeue_highest`.

Coexistence with SysML elements: Native Requirement files live in the same directory tree as RequirementDef, Part, and all other SysML element types. The walker dispatches on type: Requirement with an id: field matching the REQ pattern; files with type: Requirement but no id: (or with typedBy:) are treated as SysML requirement usages (§8.11.3).

8.12 Case Elements (Analysis, Verification, Use Case)

8.12.1 Common Case Fields

All case types (CaseDef, AnalysisCaseDef, VerificationCaseDef, UseCaseDef and their usages) share these fields in addition to common action fields:

Field YAML type Default Description
subject string absent Qualified name of the subject of the case
actors list of strings absent Qualified names of actor parts
objectives list of strings or inline objective maps absent Case objectives
result string absent Qualified name of the result type for analysis/verification cases

8.12.2 AnalysisCaseDef

---
type: AnalysisCaseDef
name: FuelEconomyAnalysis
subject: VehicleSystem::Vehicle
objectives:
  - "Determine fuel economy under standard drive cycle"
result: Analysis::FuelEconomyResult
subActions:
  - name: runSimulation
    typedBy: Analysis::DriveSimulation
    kind: Action
  - name: computeEconomy
    typedBy: Analysis::FuelEconomyCalc
    kind: PerformAction
successionConnections:
  - after: runSimulation
    before: computeEconomy
---
Analysis case for calculating the fuel economy of the vehicle
under a standard EPA drive cycle.

8.12.3 VerificationCaseDef

Additional fields:

Field YAML type Default Description
verifies list of strings absent Qualified names of RequirementDefs/Requirements this case verifies
verdictExpression string absent Expression evaluating to a pass/fail/inconclusive verdict (opaque)
verdictType string VerificationCases::VerdictKind Qualified name of the enumeration type for the verdict; defaults to the standard library VerdictKind (pass/fail/inconclusive/error)
returnType string absent Qualified name of the result type returned by the case; if absent, defaults to verdictType
---
type: VerificationCaseDef
name: MassVerificationTest
subject: VehicleSystem::vehicle_b
verifies:
  - Requirements::vehicleMassReq
subActions:
  - name: measureMass
    typedBy: TestActions::MeasureMass
    kind: Action
  - name: checkMass
    typedBy: Requirements::MassConstraint
    kind: Action
verdictExpression: "measureMass.result <= vehicleMassReq.maxMass"
---
Verification test for the vehicle mass requirement.
The vehicle is placed on a calibrated scale and the measured mass
is compared against the requirement threshold.

8.12.4 UseCaseDef

Additional fields:

Field YAML type Default Description
actors list of strings absent Qualified names of actor parts
includes list of strings absent Qualified names of included UseCaseDefs
extends list of extension maps absent Conditional extensions to other use cases
extensionPoints list of strings absent Named extension points within this use case that other use cases may extend

Each entry in extends::

Sub-field YAML type Required Description
target string Required Qualified name of the use case being extended
extensionPoint string optional Name of the extension point being targeted on the extended use case
condition string optional Boolean condition under which the extension applies (opaque string)

Example:

extends:
  - target: BasicDrive::DriveVehicle
    extensionPoint: assistanceHook
    condition: "adaptiveCruise.enabled"
extensionPoints:
  - assistanceHook
---
type: UseCaseDef
name: TransportPassenger
subject: VehicleSystem::Vehicle
refines:
  - REQ-VEH-TRANSPORT-001     # the stakeholder need this use case refines (W307)
actors:
  - Stakeholders::Driver
  - Stakeholders::Passenger
objectives:
  - "Transport passengers from origin to destination safely"
includes:
  - VehicleUseCases::StartVehicle
  - VehicleUseCases::NavigateRoute
subActions:
  - name: boardPassengers
    typedBy: VehicleActions::BoardPassengers
    kind: Action
  - name: drive
    typedBy: VehicleActions::Drive
    kind: Action
  - name: exitPassengers
    typedBy: VehicleActions::ExitPassengers
    kind: Action
successionConnections:
  - after: boardPassengers
    before: drive
  - after: drive
    before: exitPassengers
---
Use case for transporting passengers using the vehicle.

8.12.5 Native TestCase Type

The native TestCase type is a first-class element for structured, executable test specifications. It carries a stable ID, a lifecycle status, a verification level, machine-readable Gherkin scenarios, and a link to implementation source. It is the counterpart to the native Requirement type (§8.11.6).

Frontmatter fields:

Field Type Required Description
type literal TestCase Required Discriminator.
id string Required Stable opaque ID matching ^TC(-[A-Z0-9]{2,12})*-[0-9]{3,8}$. Unique across the model.
name string Required One-line human-readable label — free prose (spaces/punctuation allowed).
status enum Required Lifecycle state: draft, review, approved, active, retired.
testLevel enum L1–L5 Required Verification layer (see table below).
verifies list of id-or-qualname Required Requirements this test exercises. At least one entry. All must resolve.
sourceFile string optional Workspace-relative path to the implementation file. Forward slashes.
verifiesElement string or list optional PartDef/InterfaceDef/ActionDef/StateDef elements verified directly (qname or id). E102 unresolved, E129 wrong kind; subject to appliesWhen escape analysis (W019). An active architecture element with no such link and no verified satisfied requirement raises W104; any architecture element may carry testExempt: <rationale> to suppress it.
testFunctions list optional Structured references to individual test functions (see below).
tags list of strings optional Free labels.

Status values:

Value Meaning
draft Scenarios written; not yet reviewed.
review Under review.
approved Approved; implementation expected.
active Implementation passes in CI.
retired Superseded; no longer executed.

testLevel values:

Value Layer Typical runner
L1 Unit test — hosted cargo test on x86-64
L2 Property-based — hosted cargo test with proptest / bolero
L3 Formal proof cargo kani
L4 Integration — emulator cargo test --target thumbvNm-none-eabi + QEMU
L5 Hardware-in-the-loop HIL bench + probe-rs

The tool attaches only these semantics to the levels: L1/L2 are leaf-level checks and never satisfy a parent requirement's integration-coverage rule (W305); L3, L4 and L5 count as integration-level for W305 (and for the W310 bar on a done PlanningItem); L5 (HIL) is the level an asilLevel: D requirement needs (W702) and, unless a timing/wcet-tagged test is used, the measuring level a wcet: budget needs (W029). The verification-depth report flags a requirement verified at L5 only as hil-only. The runner column is a typical choice, not enforced.

ID pattern: ^TC(-[A-Z0-9]{2,12})*-[0-9]{3,8}$ - Examples: TC-SCHED-BITMAP-001, TC-BRAKE-FADE-002

testFunctions structure:

Each entry is a plain string (function path) or an object linking a function to a specific Gherkin scenario:

testFunctions:
  - "ready_queue_tests::tests::highest_urgency_first"
  - function: "ready_queue_tests::tests::bitmap_cleared_on_last_dequeue"
    scenario: "Bitmap bit is cleared when the last thread at a priority dequeues"
  • function — fully-qualified test path. Required in object form.
  • scenario — must exactly match a Scenario: or Scenario Outline: title in a Gherkin block in this file. Unresolved strings are model error E106.

Body structure:

Two zones in order:

  1. Preamble (optional) — free-form Markdown before the first Gherkin block.
  2. Gherkin blocks — one or more fenced blocks tagged ```gherkin. At least one is required (E011).

Gherkin block rules: - Language tag must be exactly gherkin (not cucumber, feature, or blank). - Each fenced block is an independent Gherkin document. - Feature: is required in the first block; subsequent blocks may omit it. - Background: applies only within its own block. - Scenario Outline: must contain at least one Examples: table (E014). - Permitted step keywords: Given, When, Then, And, But. The * bullet keyword is not supported. - Doc strings (""") and data tables (| col |) are permitted within steps. - Gherkin tags (@tagname) on Feature:, Scenario:, or Scenario Outline: are surfaced by tooling.

Complete example:

---
type: TestCase
id: TC-SCHED-BITMAP-001
name: "Bitmap correctly selects highest-priority ready thread"
status: active
testLevel: L1
verifies:
  - REQ-SCHED-BITMAP-001
sourceFile: "tests/host/src/ready_queue_tests.rs"
testFunctions:
  - function: "ready_queue_tests::tests::highest_urgency_first"
    scenario: "Highest-urgency thread wins when multiple priorities are ready"
  - function: "ready_queue_tests::tests::bitmap_cleared_on_last_dequeue"
    scenario: "Bitmap bit is cleared when the last thread at a priority dequeues"
tags:
  - scheduler
---

Unit tests for the priority bitmap on the hosted port (x86-64).

Run: `cargo test -p tests-host --target x86_64-unknown-linux-gnu -- ready_queue_tests`

```gherkin
Feature: Priority bitmap selection

  Background:
    Given the kernel is initialised with the hosted port
    And an empty ready queue with 32 priority levels

  Scenario: Highest-urgency thread wins when multiple priorities are ready
    Given thread A is enqueued at priority 5
    And   thread B is enqueued at priority 2
    And   thread C is enqueued at priority 8
    When  the scheduler selects the next thread
    Then  thread B is returned
    And   the bitmap bit for priority 2 is still set

  Scenario: Bitmap bit is cleared when the last thread at a priority dequeues
    Given thread A is enqueued at priority 5
    When  thread A is dequeued
    Then  the bitmap bit for priority 5 is clear
    And   a subsequent dequeue_highest returns None
```

Coexistence with SysML elements: Native TestCase files live in the same directory tree as VerificationCaseDef, AnalysisCaseDef, and all other SysML element types. The type: TestCase value is not a SysML keyword — files with this type are always routed to the dedicated TestCase handler.

8.12.7 Native TestEnvironment Type

TestEnvironment (TE-*, GH #238) is the rig an L4/L5 test needs: a HIL, bench, climatic chamber, vehicle or simulation. status is planned, available or retired; environmentKind is hil, bench, chamber, vehicle, simulation or other; capabilities: lists what it offers; calibrationStatus is valid, expired or unknown and calibrationDue a YYYY-MM-DD date. A TestCase or TestPlan names the rig(s) with runsOn: (string or list, each a TestEnvironment, else E894) and what it needs with requiresCapabilities:; a missing capability is W891 and a retired or out-of-calibration rig W892.

8.12.6 Native TestPlan Type

The native TestPlan type groups TestCases into the unit a team executes, reviews, and reports against (a smoke plan, an integration plan, a certification campaign). TestCases remain reusable atoms; a TestPlan is a curated, per-product artifact. Plans live by convention under a TestPlans/ area (not enforced).

Frontmatter fields:

Field Type Required Description
type literal TestPlan Required Discriminator.
id string Required Stable opaque ID matching ^TP(-[A-Z0-9]{2,12})+-[0-9]{3,8}$. Unique across the model.
name string Required One-line human-readable label — free prose.
status enum Required draft, review, approved, active, retired.
scope string optional Recommended vocabulary: unit, smoke, integration, hil, certification, security, regression. Free-form values are accepted but warn (W610). scope distinguishes multiple plans over the same configuration(s).
configurations scalar or list of Configuration refs optional The product variant(s) this plan is for. Absent = config-agnostic (applies to every configuration / the flat model). Each entry must resolve to a Configuration (E606).
demonstrates list of id-or-qualname optional Requirement / SafetyGoal / CybersecurityGoal / Argument elements the plan is offered as evidence for (a safety-case leg). Not mandatory. Each must resolve (E603).
testCases list of TC-* refs optional Explicit members. Each must resolve to a TestCase (E601).
selection object optional Additive membership query (see below).
tags list of strings optional Free labels (also matched by selection.tags).

selection sub-fields (all optional; an absent sub-field is no constraint; a selection with no sub-fields matches nothing):

Sub-field Type Description
testLevels list of L1–L5 Match member TestCases by testLevel (invalid value → E602).
domains list of system/hardware/software Match by the member's domain, derived transitively from its verifies: targets' reqDomain: (invalid value → E605).
tags list of strings Match member TestCases carrying any listed tag.

Effective TestCase set = explicit testCases: ∪ selection: matches, deduped by id. selection is additive (never removes a named member); draft TestCases are not swept by selection (but an explicitly named draft/retired TestCase is kept, with W613). An empty effective set warns (W612).

ID pattern: ^TP(-[A-Z0-9]{2,12})+-[0-9]{3,8}$ — e.g. TP-DELIVERY-INTEGRATION-001, TP-BRAKE-CERT-001.

Membership is computed, not stored: a plan declares which products it is a plan for; which member TestCases actually run in a given configuration is still derived from each TestCase's own appliesWhen: (§9.10). A member active in none of the plan's configurations is an escaping member (W611).

Example:

---
type: TestPlan
id: TP-DELIVERY-INTEGRATION-001
name: "UAV delivery — integration plan"
status: approved
scope: integration
configurations: [CONF-UAV-DELIVERY-001, CONF-UAV-SURVEY-001]   # reused across two products
demonstrates: [SG-UAV-001]                                     # optional safety-case leg
testCases: [TC-UAV-NAV-001, TC-UAV-SAFE-003]
selection:
  testLevels: [L3, L4]
  tags: [integration]
---
Integration plan executed before each delivery/survey release.

Tooling: the testplan command lists plans (scope, configurations, effective-TestCase count, coverage %, verdict) and shows per-plan detail (testplan TP-X, --json); coverage and the pass/fail/incomplete/empty verdict reuse the matrix coverage computation and the ingested-results fold. The --plan TP-X lens on matrix, verification-depth, and audit restricts those reports to a plan's scope (composing with --config). The validation rules E600–E606 / W610–W616 are summarised below; see syscribe help testplan for the CLI.

Validation summary: E600 (missing id/name/status or malformed TP-* id), E601 (member not a TestCase), E602 (bad selection.testLevels), E603 (demonstrates unresolved), E604 (bad status), E605 (bad selection.domains), E606 (configurations entry not a Configuration); W610 (non-recommended scope), W611 (escaping member), W612 (empty plan), W613 (pinned draft/retired member), W614 (approved plan demonstrates a goal no member verifies, honouring goal-closure), W615 (results-gated: approved plan with a Fail/Missing member), W616 (duplicate (configurations, scope)). A duplicate id is the generic E101.

8.12.8 Invariant properties: Where, Except under, within, always, never

A scenario of a composite TestCase (§8.18 campaign, GH #342) may state a property precisely and over time.

  • Where <name> is <expression> names a boolean property. Written under Feature: or Background: (before the first scenario of the block) it is visible to every scenario of the block; written under Rule: (before the rule's first scenario) it is visible to that rule's scenarios only; written in a scenario it is that scenario's own and replaces a feature or rule property of the same name. Naming a property twice in the same scope is E886. In a Scenario Outline the expression may use <column> placeholders and is checked once each row fills them in. A Gherkin description line beginning with Where is read as a property line.
  • Except under <fault> [and <fault>…]: <expression> follows a Where and belongs to it (written in a scenario, it may also extend the feature property last declared). It replaces the property while every listed fault is injected: the faults of the campaign set plus those a fault … is injected step injected and has not cleared. A fault no clause lists leaves the full property in force, so a new fault cannot weaken it. When several clauses apply, the one whose faults include those of every other is used; when none does, the scenario ends in an error, "no applicable degraded clause", naming the clauses a combined one must replace. Degradations are never combined: each clause relaxes a different part of the property, and their conjunction rebuilds the property that cannot hold.
  • Then <name> holds checks a named property at that instant.
  • Then within <duration>: <property> (or Then within <duration> <name> holds) advances the clock by the duration; the property must hold at some settled instant no later than that and at every settled instant after it, to the end of the run (reached and kept). The duration is measured from the step, which follows the event it is about.
  • Then always <property> / Then from now on <property> and Then never <property> hold, or never hold, at every settled instant from the step to the end of the run, without advancing the clock.

A property is an expression over <part>.<variable>, or <name> holds. A settled instant is one at which the composite has processed every message due: after each step and at every instant the clock stops at while it advances. A plain Then <expression> keeps its meaning, checked once where it stands.

A degraded clause counts only when the full property fails at that instant and the clause holds; never always judges the full property, since a weaker property would make it stricter. A scenario that passed only because a degraded clause was in force reports the clause (simulate: "held in a degraded clause", JSON degraded); a campaign set whose invariants all held but at least one only degraded is survived (degraded) in inject, its report and its record. A defeat names the clause that failed and when. A malformed Where/Except, an Except with no Where before it, a step naming an undeclared property, an operator outside a Then step, a property named twice in one scope, and a Where/Except in a state-machine or action TestCase (where nothing judges it) are E886; an Except fault that is not a fault of the model is E937.

Feature: protection chain
  Where the safe state is powerStage.pwm == 0 and relayA.closed == 0 and relayB.closed == 0
  Except under FTE-GTI-002: powerStage.pwm == 0 and relayB.closed == 0
  Except under FTE-GTI-003: powerStage.pwm == 0 and relayA.closed == 0
  Except under FTE-GTI-002 and FTE-GTI-003: powerStage.pwm == 0

  @invariant
  Scenario: after a grid loss the safe state is reached within 2 s and kept
    When grid receives Set with v = 0, f = 60
    Then within 2 s the safe state holds
    When 10 s elapses

8.13 Allocation Elements

8.13.1 AllocationDef

Defines a class of allocation relationship.

Type-specific additional fields for AllocationDef:

Field YAML type Default Description
supertype string or list absent Supertype AllocationDefs
ends list absent Named end features for the allocation (same schema as ConnectionDef ends; §8.4.2)
allocations list of allocation maps absent Nested sub-allocation usages owned by this AllocationDef

Each entry in allocations::

Sub-field YAML type Required Description
name string optional Named allocation usage
typedBy string optional Qualified name of an AllocationDef typing this sub-allocation
allocatedFrom string Required Feature chain of the logical/source element
allocatedTo string Required Feature chain of the physical/target element

Example (model/Allocations/FunctionAllocation.md):

---
type: AllocationDef
name: FunctionAllocation
allocations:
  - name: engineControlAlloc
    allocatedFrom: LogicalArch::EngineControl
    allocatedTo: PhysicalArch::ECU
  - name: transmissionControlAlloc
    allocatedFrom: LogicalArch::TransmissionControl
    allocatedTo: PhysicalArch::TCU
---
Composite allocation of logical control functions to physical ECUs.
---
type: AllocationDef
name: FunctionalToPhysical
supertype: Allocations::Allocation
ends:
  - name: allocatedFrom
    typedBy: Actions::Action
    isEnd: true
  - name: allocatedTo
    typedBy: Parts::Part
    isEnd: true
---
Allocation of a logical function (action) to a physical component (part).

8.13.2 Allocation Usage

The Allocation usage file specifies a concrete allocation of one element to another.

Type-specific additional fields:

Field YAML type Default Description
typedBy string absent Qualified name of the AllocationDef
allocatedFrom string Required Feature chain or qualified name of the allocated element (source)
allocatedTo string Required Feature chain or qualified name of the target element
---
type: Allocation
name: controllerToECU
typedBy: Allocations::FunctionalToPhysical
allocatedFrom: VehicleBehavior::ProvidePower
allocatedTo: PhysicalArch::EngineControlUnit
---
Allocates the ProvidePower action to the Engine Control Unit (ECU).

Bulk allocations may also be expressed on a container element using allocations: field (list of maps with from: and to: and optional typedBy:):

# On PhysicalArch/_index.md or a dedicated Allocation file
allocations:
  - from: VehicleBehavior::ProvidePower
    to: PhysicalArch::EngineControlUnit
    typedBy: Allocations::FunctionalToPhysical
  - from: VehicleBehavior::RegulateSpeed
    to: PhysicalArch::TransmissionControlUnit
    typedBy: Allocations::FunctionalToPhysical

8.14 View, Viewpoint, and Rendering Elements

8.14.1 ViewpointDef

Defines a stakeholder viewpoint describing what model information is relevant.

Type-specific additional fields:

Field YAML type Default Description
supertype string or list absent Supertype ViewpointDefs
stakeholders list of strings absent Qualified names of stakeholder PartDefs
concerns list of strings absent Qualified names of ConcernDefs addressed
methods list of strings absent Qualified names of ViewDefs or RenderingDefs satisfying this viewpoint
satisfiedBy list of strings absent ViewDefs/Views that satisfy this viewpoint. An authored SysML viewpoint-conformance list, stored as written (it is not resolved and is not merged into the computed satisfiedBy index of §11.11/§12.1, which is the reverse of satisfies:)
---
type: ViewpointDef
name: SafetyViewpoint
stakeholders:
  - Stakeholders::SafetyEngineer
concerns:
  - Concerns::SafetyHazards
  - Concerns::RiskLevel
methods:
  - Views::FaultTreeView
---
Viewpoint addressing safety concerns from the safety engineer's perspective.

8.14.2 ViewDef

Type-specific additional fields:

Field YAML type Default Description
supertype string or list absent Supertype ViewDefs
satisfies list of strings absent Qualified names of ViewpointDefs satisfied by this view
expose list of expose maps absent Elements exposed by this view; see 8.14.3
rendering string absent Qualified name of the RenderingDef applied to this view
filterCondition string absent Filter applied to everything the view exposes (@MetadataName, not, and, or) — the SysML filter @M; member; an expose: entry may add its own filter:

8.14.3 Expose Schema

Each entry in expose::

Sub-field YAML type Required Description
target string Required Qualified name or import pattern of exposed element(s): Pkg (the element), Pkg::* (its direct members), Pkg::** (its whole subtree); resolved from the view's own scope. syscribe view render <View> materialises the selection. An entry may also be a bare string
isRecursive bool optional Whether sub-elements are recursively exposed
filter string optional Filter expression (opaque)
---
type: ViewDef
name: SafetyView
satisfies:
  - Views::SafetyViewpoint
expose:
  - target: VehicleSystem::*
    isRecursive: true
    filter: "@SafetyMetadata and SafetyMetadata::SafetyAnnotation::level >= 2"
rendering: Views::FaultTreeRendering
---
Safety view exposing all safety-annotated elements of the vehicle system.

8.14.4 RenderingDef

---
type: RenderingDef
name: FaultTreeRendering
supertype: Views::Rendering
features:
  - name: diagramType
    typedBy: ScalarValues::String
    value: '"FTA"'
---
Rendering definition for Fault Tree Analysis diagrams.

8.15 Metadata Elements

8.15.1 MetadataDef

Defines a metadata annotation structure (user-defined keyword).

Type-specific additional fields:

Field YAML type Default Description
supertype string or list absent Supertype MetadataDefs
features list absent Owned attribute features of the metadata
annotates list of strings absent KerML/SysML metaclass names of elements this metadata may annotate (e.g., ["PartDef", "Part"]); omit for unrestricted
isSemantic bool false true if this metadata has user-defined semantic effect on the annotated element

Valid metaclass names for annotates: are drawn from the KerML/SysML type hierarchy. Commonly used values: Element (any element), Feature, Definition, Usage, PartDef, PartUsage, ActionDef, ActionUsage, RequirementDef, RequirementUsage, Package, Namespace. More specific metaclass names from the SysML abstract syntax are also valid. A parser should treat unrecognized metaclass names as warnings, not errors, to allow forward compatibility.

Example (model/Metadata/SafetyAnnotation.md):

---
type: MetadataDef
name: SafetyAnnotation
features:
  - name: level
    typedBy: ScalarValues::Natural
  - name: hazardId
    typedBy: ScalarValues::String
  - name: mitigation
    typedBy: ScalarValues::String
annotates:
  - PartDef
  - Part
  - ActionDef
  - Action
---
Metadata for annotating model elements with safety classification information.
Level 1 = minor hazard; Level 2 = major hazard; Level 3 = catastrophic hazard.

8.15.2 Applying Metadata

Apply metadata to any element using the metadata: field:

---
type: PartDef
name: FuelSystem
metadata:
  - type: Metadata::SafetyAnnotation
    level: 3
    hazardId: "HAZ-042"
    mitigation: "Double-walled fuel lines, automatic shutoff valve"
---
Fuel system component with catastrophic hazard classification.

A metadata: entry is either a bare reference (metadata: [Metadata::SafetyAnnotation]) or a map whose type (aliases apply/def) names the MetadataDef and whose other keys set its features as tagged values. This is the SysMLv2 way to model a stereotype (there is no UML stereotype mechanism). Validation (REQ-TRS-META-001):

  • an application that does not resolve to a MetadataDef is E317 (standard-library metadata packages ModelingMetadata/RiskMetadata are recognised);
  • applying a MetadataDef whose annotates: excludes the element's metaclass — directly or via the abstract Element/Definition/Usage — is E318;
  • a tagged-value key that is not a declared features: attribute of the MetadataDef is warning W045.
  • a features: entry may also declare typedBy (ScalarValues::Integer|Real|Boolean|String, or an element-type name such as PartDef for a reference), unit, range: "lo..hi", enumValues, multiplicity (1, 0..1, 1..*) and default. A wrong type or unit dimension is E322, a value outside range E323, outside enumValues E324, a count outside multiplicity E325, a reference that does not resolve to the stated element type E326. A feature that declares none of these is checked by name only (REQ-TRS-META-010..013);
  • a MetadataDef may specialise another with supertype: (REQ-TRS-META-030). An application of the sub-definition counts as an application of every ancestor — list --metadata, --where meta.…, has()/meta() and the ancestors' constraints: all see it — and tagged values, typing and defaults are inherited. A sub-definition may narrow a range or enumValues and annotates:, never widen them (E329, which also covers an unresolved, non-MetadataDef or cyclic supertype:); an isAbstract definition cannot be applied (E328);
  • a MetadataDef may carry checks: — expressions run on every element that carries the stereotype (or a specialisation), with has()/meta() in scope: a string, or {expr, message, severity} with severity error (default, E650) or warning (W650, draft-suppressed). A profile's checks run only when the profile is enabled. validate --deny-meta <Def.key=value> and [gates] deny_meta fail the run (exit 2) while a matching element exists, and baseline create refuses to seal one (REQ-TRS-META-050, -051, -054); [[gates.coverage]] (select: a stereotype selector, min_complete: a percentage, default 100) fails validate (exit 2) when fewer of the selected non-draft requirements are complete in the matrix --rollup sense, and is a usage error (exit 1) when malformed or when it counts nothing (REQ-TRS-META-053);
  • a stereotype selector is {stereotype: Def, where: <expr>} or the string Def where <expr>: it matches an element that applies Def (or a specialisation) and, with where, whose application satisfies the expression over its tagged values (meta/has available). A TestPlan's selection.stereotypes: lists selectors (any of them): a TestCase is selected when it, or a requirement it verifies:, matches one — malformed or unknown is E651; matrix --select keeps the requirement rows a selector matches (REQ-TRS-META-024);
  • a map application may carry appliesWhen: (a feature expression, as on an element): under --config the application is dropped from the projection where it is false, and diff reports applications that differ between two products (REQ-TRS-META-025);
  • a package with profile: true (optionally version:) is a stereotype profile (REQ-TRS-META-031): the constraints: of the definitions it contains, and its autoApply: rules, are in force only when [metadata] profiles = ["Pkg"] in .syscribe.toml enables it (W316 if that names something else). A definition outside any profile package is always checked. An autoApply: rule is {select: {type, where: {field: value, …}}, apply: {type: Def, key: value, …}}: matching elements carry the application without it being written (shown (derived); derived: true in list --json); an explicit application of the same definition wins (REQ-TRS-META-033);
  • the built-in StdMetadata package (Safety, Optional, Regulatory, External, Environment, Deployment, Budget) is part of the model whenever an element refers to StdMetadata::… or [metadata] stdlib = true; it is read-only (REQ-TRS-META-032);
  • a MetadataDef may carry constraints: — boolean expressions over its own tagged values (integrity >= 2 implies goal != "") — and a false one is E327 on the applying element (REQ-TRS-META-014). An absent String is "" and an absent Boolean false; a constraint over an absent number is undecided, not violated.

Applied stereotypes are shown by show as «Name» banners and are filterable with list <Type> --metadata <MetadataDef> (repeatable, A,B for either, !A to exclude) and by tagged value with --where meta.<Def>.<key><op><value> (REQ-TRS-META-020, -021).

In a MetadataDef's constraints:, has(Def) is true when the carrying element applies Def, and meta(Def).key is that application's tagged value (its default when unwritten). An absent stereotype or value is absent: any comparison with it is false, never an error (REQ-TRS-META-023).

8.15.3 Standard Library Metadata

The following metadata types from the SysML Standard Library are referenced by their qualified names:

Qualified name Purpose
ModelingMetadata::StatusInfo Model element status (draft, approved, deprecated)
ModelingMetadata::Issue Open issue on an element
ModelingMetadata::Rationale Design rationale annotation
ModelingMetadata::Refinement Refinement relationship annotation
RiskMetadata::Risk Risk metadata with probability and severity
RiskMetadata::RiskLevel Risk level enumeration

8.16 Diagram Elements

8.16.1 Overview

A Diagram file (type: Diagram) depicts part of the model. Its diagramKind: selects the rendering path:

Rendering path How to author Notes
Structured SVG (BDD, IBD, StateMachine, Requirement, Action) shapes: / edges: manifest plus a layout: block The web server builds the SVG from the manifest; without layout: nothing is drawn.
PlantUML companion (BDD, IBD, StateMachine, Sequence, Requirement) pumlMode: companion (+ optional pumlFile:) and an image reference to the anticipated .svg in the body syscribe plantuml generates the .puml from the manifest and the model; syscribe plantuml render turns it into SVG. Preferred for these kinds.
Mermaid (diagramKind: Mermaid) A fenced ```mermaid block in the body Rendered client-side. Annotate nodes with %% ref: <QualifiedName> (and %% link: <NodeId> <QualifiedName>) so they trace to model elements.
Inline PlantUML (diagramKind: PlantUML) A fenced ```plantuml block in the body Hand-written PlantUML source.
Hand-authored SVG (any kind, including Allocation, UseCase, Sequence, Custom) shapes: / edges: manifest plus an SVG, inline or companion (below) The SVG geometry is authored (typically by an LLM); the manifest links every shape and edge to a model element.

Source selection (REQ-TRS-VIS-003). A diagram's content comes from one of two sources, chosen by what the frontmatter declares — there is no mode field:

Frontmatter Source
has a shapes: block manifest — the author listed the content in shapes:/edges: (§8.16.3–8.16.4)
has a subject: and no shapes: derived — the generator for diagramKind: walks the subject and produces the content
has neither nothing to draw; a diagram with a diagramKind: but neither shapes: nor subject: raises no finding (W400 is a missing diagramKind:, W401 an unresolved subject:)

A shapes: key with a null value does not select the manifest. Only kinds that build an IR (everything but Mermaid and PlantUML) have a source at all.

Derived diagrams. A derived diagram is two lines of frontmatter — diagramKind: and subject: — and follows the model: adding a part, a port or a supertype: changes the picture without editing the diagram. Generators exist for BDD (§8.16.8.1), IBD (§8.16.8.2), Sequence (§8.16.8.3), StateMachine (§8.16.8.4), Requirement (§8.16.8.5), Allocation (§8.16.8.6), Action (§8.16.8.8), FeatureModel (§8.16.8.9), the safety diagrams FaultTree, AttackTree and SafetyCase (§8.16.8.10) and the analysis graphs Traceability, ZoneConduit and ThreatGraph (§8.16.8.11); a derived UseCase or Custom diagram yields an empty picture (REQ-TRS-VIS-015). Every generated shape has a deterministic id — s- followed by the depicted element's qualified name lower-cased with :: and every other non-alphanumeric run replaced by - (UAV::Power::PowerSystem::pdu → s-uav-power-powersystem-pdu; a compartment is <block id>-compartment) — so layout: pins (§8.16.2) apply to a derived diagram unchanged, survive regeneration, and a renamed element merely loses its pin. include:/exclude: (§8.16.2) narrow the content; W417/W418 (§8.16.7) report a filter that names nothing and a subject of the wrong type.

The rest of this section specifies the manifest and the hand-authored SVG conventions. In the manifest the frontmatter is the canonical source of traceability and the SVG is the visual geometry, so a parser can validate the diagram from the frontmatter without touching the SVG. The SVG uses a sysml: XML namespace (urn:syscribe:1.0) on shapes for redundant inline traceability, so the SVG can be opened standalone in any viewer.

Two SVG storage modes are supported. The frontmatter schema is identical in both; only the Markdown body differs:

Mode Body content GitHub rendering Files
Inline Fenced svg code block Code only (not rendered) One .md file
Companion <img src="./Name.svg"> HTML tag SVG rendered as image .md + .svg pair

Choose inline when GitHub rendering is not required and keeping everything in one file is preferred. Choose companion when the diagram must render on GitHub or in other Markdown viewers.

8.16.2 Diagram Frontmatter Schema

Top-level fields:

Field YAML type Required Default Description
type string Required — Diagram
name string optional filename stem Display name for the diagram
diagramKind string recommended — Diagram kind: BDD, IBD, StateMachine, Action, Sequence, Requirement, FeatureModel, FaultTree, AttackTree, SafetyCase (alias GSN), Traceability (alias HazardTrace), ZoneConduit (alias Zones), ThreatGraph (alias TARA), Mermaid, PlantUML; Allocation, UseCase and Custom are accepted for hand-authored SVG (no generator). Absent → warning W400 (suppressed when svgMode: companion). The field is diagramKind, not kind (an unknown kind: key is W047).
subject string recommended — Qualified name of the model element this diagram depicts. An unresolved subject is warning W401.
svgMode string optional inline Storage mode: inline (fenced block in body) or companion (separate .svg file)
svgFile string optional <stem>.svg Companion file path relative to the .md file; only used when svgMode: companion
shapes map optional absent Shape manifest; see §8.16.3. Its presence selects the manifest source (§8.16.1); a Diagram with a subject: and no shapes: is derived.
edges map optional absent Edge manifest; see §8.16.4
include string, list or map optional absent Derived diagrams only. As a map on a derived IBD it is a view (kinds, domains, tags, features, depth, members; §8.16.10): what it selects is drawn and the rest faded. As a list, restrict the content to the named members of the subject (and the edges joining them). Each entry is a qualified name (UAV::Power::PowerSystem::pdu) or a name relative to the subject (pdu); a BDD member is a definition in the subject package, an IBD member is an owned part usage. An entry that names no member is warning W417; on a manifest diagram the field is W417 and ignored.
exclude string or list optional absent Derived diagrams only. Remove the named members (and every edge touching them) from an otherwise complete view; same naming and W417 rules as include. Applied after include.
layout map optional absent Shape id → {x, y, w, h} pixel coordinates (an edge id → {points: [[x, y], …]} pins its routing). Entries are pins (REQ-TRS-VIS-007): a pinned shape keeps its position while the browser lays the unpinned rest out around it; w/h are optional. A key naming no shape or edge is W416; an entry without numeric x/y is E405. Two keys are hints, not shape ids: flow (left-to-right / top-to-bottom) and layers (bands in order); see §8.16.10.
style string optional absent A named style, [diagram.style.<name>] of .syscribe.toml (§8.16.10); one that names no style, or style: on an element that is not a Diagram, is W421.
pumlMode string optional absent companion (the only value; anything else is E403) opts into the PlantUML workflow. Requires diagramKind (E404); the body must reference the anticipated SVG (![…](….svg) or <img, else W413); a not-yet-generated .puml is W414.
pumlFile string optional <stem>.puml Companion .puml path relative to the .md file; only used with pumlMode: companion.

8.16.3 Shape Manifest

shapes: is a map from SVG element id to a shape descriptor. The value may be a plain string (shorthand) or a map:

String shorthand — the shape represents a single model element:

shapes:
  engine-rect: VehicleSystem::Engine
  tranny-rect: VehicleSystem::Transmission

Map form — for additional metadata on the shape:

Sub-field YAML type Required Description
ref string Required Qualified name of the model element this shape represents
kind string optional Shape role: block, port, label, compartment, annotation (default: block)
parent string optional SVG id of the enclosing/containing shape (for nested ports or compartments)
shapes:
  engine-rect:
    ref: VehicleSystem::Engine
    kind: block
  engine-out:
    ref: VehicleSystem::Engine::powerOut
    kind: port
    parent: engine-rect
  engine-label:
    ref: VehicleSystem::Engine
    kind: label
    parent: engine-rect

8.16.4 Edge Manifest

edges: is a map from SVG element id to an edge descriptor:

Sub-field YAML type Required Description
ref string Required Qualified name of the model element (Connection, Flow, Allocation, etc.) this edge represents
source string Required SVG id of the shape at the source/tail end
target string Required SVG id of the shape at the target/head end
kind string optional Edge role: connection, flow, allocation, dependency, inheritance, association (default: connection)
edges:
  power-conn:
    ref: VehicleSystem::PowerConnection
    source: engine-out
    target: tranny-in
    kind: flow
  fuel-conn:
    ref: VehicleSystem::FuelFlow
    source: tank-fuel-out
    target: engine-fuel-in
    kind: flow

8.16.5 SVG Body Conventions

Inline mode — the Markdown body contains the SVG as a fenced svg code block:

```svg
<svg xmlns="http://www.w3.org/2000/svg" ...>
  ...
</svg>
```

Companion mode — the Markdown body contains an HTML <img> tag pointing to the companion .svg file. GitHub renders this as an image:

<img src="./VehiclePowertrainIBD.svg" alt="Vehicle Powertrain IBD" width="100%"/>

The svgFile: frontmatter field identifies the companion file. If absent, the parser looks for a file with the same stem as the .md file and a .svg extension in the same directory (e.g., Foo.md → Foo.svg).

In both modes, the SVG itself must conform to the following conventions:

Namespace declaration:

<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:sysml="urn:syscribe:1.0"
     viewBox="0 0 W H">

Shape elements carry a sysml:ref attribute matching the ref in the frontmatter manifest, and an id matching the manifest key:

<use href="#sym-PartDef" id="engine-rect"
     sysml:ref="VehicleSystem::Engine"
     x="100" y="150" width="160" height="80"/>

Edge elements carry sysml:ref, sysml:source, and sysml:target:

<path id="power-conn"
      sysml:ref="VehicleSystem::PowerConnection"
      sysml:source="engine-out"
      sysml:target="tranny-in"
      d="M 260,190 H 400"
      class="connection flow"
      marker-end="url(#arrow-flow)"/>

Symbol library: reusable symbols for each SysML element kind are defined as <symbol>s in <defs>, and shapes reference them via <use href="#sym-<TypeName>">. The SVG may carry its own <defs>; in addition, the web server injects the <defs> of _diagram-symbols.svg at the model root, when that file exists (a fixed path — there is no frontmatter field for it).

CSS classes on shapes and edges correspond to SysML element types and control visual style:

CSS class Applies to
PartDef, ItemDef, PortDef, … Shapes typed to those element kinds
connection, flow, allocation Edge paths
port in, port out, port inout Port shapes with direction
abstract Any shape representing an abstract element

Deterministic exports (GH #339, REQ-TRS-VIS-032). Node, port and label sizes are computed from advance widths compiled into the tool (Liberation Sans, SIL OFL 1.1, metric-compatible with the Helvetica and Arial that head the SVG font-family stack), never from the fonts installed on the machine, so a generated SVG, Mermaid or DOT export is a pure function of the model: the same bytes on every run and machine. A viewer draws the SVG with its own sans font; the sizer's 8 % + 2 px width margin keeps a slightly wider face from clipping. syscribe diagram export --all writes every Diagram to <out-dir>/<qualified name as a path>.<ext> (default <model>/generated, or <model>/generated/<CONF-id> with --config; SVG) and names a PlantUML style !include relative to the file, so its PlantUML is reproducible across checkout locations too (a single-diagram export names the style file by absolute path). It records what it wrote in <out-dir>/.syscribe-generated and removes only a plain file inside the directory that the record names, that nothing renders any more and that no svgFile: names; it refuses a directory that contains the model. A diagram that fails to render (anything but a hand-authored body, a kind the format has no mapping for, or a companion picture) fails the run, keeps its file and removes nothing. --check exits 1 when a committed file differs from a fresh render, is missing, or is no longer produced, or a diagram fails, so a model can commit its renders and gate CI on them. A companion svgFile: may name such a render (../generated/Diagrams/X.svg) instead of keeping its own copy; saving a picture from the browser to that diagram would overwrite the render, which --check then reports.

8.16.6 Complete Examples

Inline mode (model/VehicleSystem/Diagrams/VehiclePowertrainIBD.md):

The frontmatter and SVG body live in one file. GitHub displays code, not an image.

Companion mode — two files with identical frontmatter, different bodies:

model/VehicleSystem/Diagrams/VehiclePowertrainIBD.md:

---
type: Diagram
diagramKind: IBD
name: VehiclePowertrainIBD
subject: VehicleSystem::Vehicle
svgMode: companion
shapes:
  vehicle-boundary: VehicleSystem::Vehicle
  engine-rect:
    ref: VehicleSystem::Engine
    kind: block
    parent: vehicle-boundary
  tranny-rect:
    ref: VehicleSystem::Transmission
    kind: block
    parent: vehicle-boundary
  engine-out:
    ref: VehicleSystem::Engine::powerOut
    kind: port
    parent: engine-rect
  tranny-in:
    ref: VehicleSystem::Transmission::powerIn
    kind: port
    parent: tranny-rect
edges:
  power-conn:
    ref: VehicleSystem::PowerConnection
    source: engine-out
    target: tranny-in
    kind: flow
---

<img src="./VehiclePowertrainIBD.svg" alt="Vehicle Powertrain IBD" width="100%"/>

model/VehicleSystem/Diagrams/VehiclePowertrainIBD.svg (companion file — the actual SVG geometry):

<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:sysml="urn:syscribe:1.0"
     viewBox="0 0 800 500">
  <!-- shapes, edges, etc. with sysml:ref attributes -->
</svg>

Inline mode (model/VehicleSystem/Diagrams/VehiclePowertrainIBD.md) — same frontmatter without svgMode, SVG in body:

---
type: Diagram
diagramKind: IBD
name: VehiclePowertrainIBD
subject: VehicleSystem::Vehicle
shapes:
  vehicle-boundary: VehicleSystem::Vehicle
  engine-rect:
    ref: VehicleSystem::Engine
    kind: block
    parent: vehicle-boundary
  ...
edges:
  power-conn:
    ref: VehicleSystem::PowerConnection
    source: engine-out
    target: tranny-in
    kind: flow
---

```svg
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:sysml="urn:syscribe:1.0"
     viewBox="0 0 800 500">
  ...
</svg>
```

Full inline example with complete SVG (model/VehicleSystem/Diagrams/VehiclePowertrainIBD.md):

---
type: Diagram
diagramKind: IBD
name: VehiclePowertrainIBD
subject: VehicleSystem::Vehicle
shapes:
  vehicle-boundary: VehicleSystem::Vehicle
  engine-rect:
    ref: VehicleSystem::Engine
    kind: block
    parent: vehicle-boundary
  tranny-rect:
    ref: VehicleSystem::Transmission
    kind: block
    parent: vehicle-boundary
  tank-rect:
    ref: VehicleSystem::FuelTank
    kind: block
    parent: vehicle-boundary
  engine-out:
    ref: VehicleSystem::Engine::powerOut
    kind: port
    parent: engine-rect
  tranny-in:
    ref: VehicleSystem::Transmission::powerIn
    kind: port
    parent: tranny-rect
  tank-fuel-out:
    ref: VehicleSystem::FuelTank::fuelOut
    kind: port
    parent: tank-rect
  engine-fuel-in:
    ref: VehicleSystem::Engine::fuelIn
    kind: port
    parent: engine-rect
edges:
  power-conn:
    ref: VehicleSystem::PowerConnection
    source: engine-out
    target: tranny-in
    kind: flow
  fuel-conn:
    ref: VehicleSystem::FuelFlow
    source: tank-fuel-out
    target: engine-fuel-in
    kind: flow
---

```svg
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:sysml="urn:syscribe:1.0"
     viewBox="0 0 800 500">
  <defs>
    <marker id="arrow-flow" markerWidth="10" markerHeight="7"
            refX="9" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" fill="#4a90d9"/>
    </marker>
  </defs>

  <!-- System boundary -->
  <rect id="vehicle-boundary" sysml:ref="VehicleSystem::Vehicle"
        x="40" y="40" width="720" height="420"
        class="PartDef boundary" rx="8"/>
  <text x="400" y="68" class="stereotype label">«part» Vehicle</text>

  <!-- Engine block -->
  <use href="#sym-PartDef" id="engine-rect"
       sysml:ref="VehicleSystem::Engine"
       x="80" y="160" width="160" height="80"/>
  <text x="160" y="206" class="label">Engine</text>

  <!-- Transmission block -->
  <use href="#sym-PartDef" id="tranny-rect"
       sysml:ref="VehicleSystem::Transmission"
       x="400" y="160" width="160" height="80"/>
  <text x="480" y="206" class="label">Transmission</text>

  <!-- Fuel tank block -->
  <use href="#sym-PartDef" id="tank-rect"
       sysml:ref="VehicleSystem::FuelTank"
       x="80" y="340" width="160" height="80"/>
  <text x="160" y="386" class="label">FuelTank</text>

  <!-- Ports -->
  <circle id="engine-out"     sysml:ref="VehicleSystem::Engine::powerOut"
          cx="240" cy="200" r="7" class="port out"/>
  <circle id="tranny-in"      sysml:ref="VehicleSystem::Transmission::powerIn"
          cx="400" cy="200" r="7" class="port in"/>
  <circle id="tank-fuel-out"  sysml:ref="VehicleSystem::FuelTank::fuelOut"
          cx="160" cy="340" r="7" class="port out"/>
  <circle id="engine-fuel-in" sysml:ref="VehicleSystem::Engine::fuelIn"
          cx="160" cy="240" r="7" class="port in"/>

  <!-- Connections -->
  <path id="power-conn"
        sysml:ref="VehicleSystem::PowerConnection"
        sysml:source="engine-out" sysml:target="tranny-in"
        d="M 247,200 H 393"
        class="connection flow" marker-end="url(#arrow-flow)"/>
  <text x="320" y="190" class="edge-label">«flow» power</text>

  <path id="fuel-conn"
        sysml:ref="VehicleSystem::FuelFlow"
        sysml:source="tank-fuel-out" sysml:target="engine-fuel-in"
        d="M 160,333 V 247"
        class="connection flow" marker-end="url(#arrow-flow)"/>
  <text x="170" y="295" class="edge-label">«flow» fuel</text>
</svg>
```

8.16.7 Validation Rules

A conformant parser must:

  1. Reference validation — every ref value in shapes: must resolve to a model element. An unresolved shape ref is warning W402 (suppressed when an ancestor qualified name resolves, which covers inline features such as Part::port). A shape link: that does not resolve is W411; an SVG href that matches no model element is W412.
  2. Type compatibility — the CSS class on an SVG shape is intended to match the type: of the referenced model element (e.g., a shape with class="PartDef" whose ref resolves to a RequirementDef would be a mismatch). This is not currently checked.
  3. ID consistency — for inline SVG, every id key in the frontmatter shapes: and edges: maps must appear as an id attribute in the SVG (W406), and every SVG id must appear in the manifest (W407). The check applies only to a diagram whose SVG is inline (svgMode: inline, or svgMode: absent on a hand-authored-SVG diagram). It is skipped for every rendering path that has no inline SVG by design: a PlantUML companion diagram (pumlMode: companion — its manifest feeds the generated .puml, and the .puml/.svg companions are the source of truth), a companion-SVG diagram (svgMode: companion or svgFile: set), a diagramKind: Mermaid/PlantUML diagram, and a structured diagram with a layout: block and no ```svg block in its body (the server renders its SVG).
  4. Edge endpoint validity — source and target values in edges: must be keys present in shapes:; otherwise warning W403.
  5. Subject existence — the subject: qualified name must resolve to a model element; otherwise warning W401.
  6. Companion file existence — when svgMode: companion (or svgFile: is set), the .svg file must exist at the path given by svgFile: (or the default stem-matched path). A missing companion file is error E402.
  7. Mode consistency — when svgMode: inline, the body must contain a fenced svg block. When svgMode: companion, the body must contain an <img> tag. A mismatch is warning W405.
  8. Rendering-path bodies — diagramKind: Mermaid requires a ```mermaid block (E400); diagramKind: PlantUML requires a ```plantuml block (E401). In Mermaid blocks, an unresolved %% ref: is W408, a diagram with no %% ref: annotation at all is W409, and an unresolved %% link: is W410. The pumlMode checks (E403, E404, W413, W414) are listed in §8.16.2; a missing [plantuml] style_file is W415.
  9. Completeness warnings (optional, non-blocking) — for IBD diagrams, the parser may warn if sub-parts or connections declared in the subject element's .md file do not appear in shapes: or edges:.
  10. Manifest well-formedness (REQ-TRS-VIS-002) — a shapes:, edges: or layout: value that is present but malformed is error E405 naming the entry: the value is not a map, an entry is neither a map nor a qualified-name string, a shape has no ref or an edge no source/target, a kind: is outside the §8.16.8 vocabulary, a parent: names no shape of the diagram (or forms a cycle), or a layout: entry has no numeric x/y. The offending entry is skipped and the rest of the diagram still builds — a malformed manifest never renders the diagram empty. A layout: key that names no shape or edge of the diagram is warning W416 (a stale pin). The check applies only to diagram kinds that build an IR; Mermaid and PlantUML bodies have no manifest to check.
  11. Derived-diagram filters and subject (REQ-TRS-VIS-003) — on a derived diagram (a subject: and no shapes:, §8.16.1), every include:/exclude: entry must name a member of the subject, by qualified name or by name relative to the subject; an entry that names nothing is warning W417 (the entry is ignored, the rest of the filter still applies). include:/exclude: on a manifest diagram is one W417 and the fields are ignored — the manifest is drawn as listed. A derived diagram's subject: must be of a type the kind's generator accepts (the Valid subject: types of §8.16.8); otherwise it is warning W418 and the diagram is drawn empty. An unresolved subject stays W401 (rule 5) and produces no W418.
  12. Flow kinds (REQ-TRS-VIS-028) — a flowKind: (§8.16.9) on a PortDef or Port, a port feature, a connections:/flowConnections: entry or a manifest edges: entry must name a flow kind, a port feature's side: must name a side, and neither may appear where nothing reads it; a [diagram] table in .syscribe.toml must be well formed. Anything else is warning W419 and the value or entry is ignored, so the edge or port draws as if it were absent.
  13. IBD grouping (REQ-TRS-VIS-029) — groupBy: on a derived IBD must be domain; any other value, or groupBy: on another diagram kind, on a manifest diagram or on an element that is not a Diagram, is warning W420 and the field is ignored (§8.16.8.2).
  14. Views (REQ-TRS-VIS-031) — the mapping form of include: (§8.16.10) is read only on a derived IBD; on another derived kind only its members apply, and on a manifest diagram it is ignored. An unknown key or a value of the wrong shape, a kinds entry that is not a flow kind, a domains entry that is not a domain, a features entry that is not a FeatureDef, a style: that names no [diagram.style.<name>] (or style: on an element that is not a Diagram), a layout.flow that is not left-to-right/top-to-bottom, a layout.layers that is not a list or an entry of it that matches no part: each is warning W421 and is ignored. A malformed [diagram.style] entry or an extends that names no style or closes a cycle is W419.

8.16.8 Kind-Specific Diagram Conventions

Each diagramKind: value imposes constraints on which element types are valid for subject:, which values are valid for kind: in shape and edge descriptors, which CSS classes and SVG primitives to use, and which model elements the parser must check for completeness. The Custom kind has no prescribed conventions — all shape and edge kind: values are user-defined and no completeness rules are enforced.


8.16.8.1 BDD (Block Definition Diagram)

A BDD shows classifiers (definitions) and their relationships in a given package or rooted at a given definition. It is analogous to a UML class diagram restricted to SysML block types. Inheritance hierarchies, attribute compartments, and typed associations are the primary content.

Valid subject: types: Package, PartDef, ItemDef

Shape kinds:

kind value Description SVG primitive CSS class Symbol
block A definition box representing a PartDef, ItemDef, or similar classifier <rect> block <TypeName> (e.g., block PartDef) #sym-PartDef, #sym-ItemDef
compartment An attribute or feature compartment drawn inside a block; its parent is the enclosing block's SVG id <rect> + <line> divider compartment —
label A text label (stereotype banner, element name, or annotation text) <text> label —
note A comment annotation box (dog-eared rectangle) <rect class="note"> + <line> fold note #sym-note

Edge kinds:

kind value Description SVG primitive CSS class Arrowhead
inheritance Specialization (':>') — hollow triangle at the supertype end <path> edge inheritance url(#arrow-inherit) (hollow triangle)
association Plain typed association between two classifiers <line> or <path> edge association none (or open arrowhead)
composition Composite ownership — filled diamond at the whole end <path> edge composition url(#arrow-composition) (filled diamond)
aggregation Shared aggregation — hollow diamond at the aggregate end <path> edge aggregation url(#arrow-aggregation) (hollow diamond)
dependency Dependency or usage — dashed open arrowhead <path stroke-dasharray="6 3"> edge dependency url(#arrow-open)

SVG body notes: block rectangles use <rect> with compartment dividers rendered as <line> elements; attribute names and types appear in <text> elements inside compartment groups; inheritance arrows use <path> with a hollow-triangle marker; name labels appear in a <text> above or inside the block.

Minimal YAML manifest example:

type: Diagram
diagramKind: BDD
name: PowertrainBDD
subject: VehicleSystem::Powertrain
shapes:
  engine-block:
    ref: VehicleSystem::Powertrain::Engine
    kind: block
  engine-attrs:
    ref: VehicleSystem::Powertrain::Engine
    kind: compartment
    parent: engine-block
  motor-block:
    ref: VehicleSystem::Powertrain::ElectricMotor
    kind: block
  motor-label:
    ref: VehicleSystem::Powertrain::ElectricMotor
    kind: label
    parent: motor-block
edges:
  engine-inherits:
    ref: VehicleSystem::Powertrain::Engine
    source: engine-block
    target: motor-block
    kind: inheritance
  engine-composed:
    ref: VehicleSystem::Powertrain::EngineMount
    source: engine-block
    target: motor-block
    kind: composition

Completeness rule: the parser must warn if any PartDef or ItemDef that is a direct child element of the subject package (or a direct sub-definition of the subject definition) is absent from shapes:.

Derived content (REQ-TRS-VIS-004): a BDD with a subject: and no shapes: is generated from the model. The subject is a Package (LibraryPackage and Namespace count as packages), a PartDef or an ItemDef; anything else is W418. The generator produces:

  • one block per PartDef, ItemDef, PortDef, InterfaceDef or ConnectionDef that is a direct member of the subject package — or, for a definition subject, the subject itself plus its direct sub-definitions — carrying the SysMLv2 stereotype (part def, connection def, …) and the abstract flag; other member kinds (actions, states, requirements) never appear;
  • one compartment child per block that has any, listing its attribute features as name : Type [unit] and its port features as port name : Type (direction) (a non-1 multiplicity is appended as [n]);
  • an inheritance edge from each block to the block its supertype: resolves to;
  • a composition edge from a block to each block that types one of its part usages — an inline features: entry typed by a definition, or a child Part/Item element — labelled with the usage name and, when not 1, its multiplicity (motor [2]);
  • an association edge for each ConnectionDef on the diagram whose first two ends[].typedBy resolve to blocks on the diagram, labelled with the connection definition's name.

An edge is emitted only when both of its ends are nodes of the diagram after include:/exclude:. Layout hints are layered, top-to-bottom, with inheritance edges oriented so a supertype sits above its subtypes.

Composition across packages (REQ-TRS-BDDX-001): the blocks that type the part usages of a diagram block are pulled in as external blocks — a block whose mark status is external — together with the composition edge, even when they are defined in another package. The optional depth: key (a non-negative integer, default 1) sets how many composition levels are followed beyond the subject's own blocks; 0 keeps only the subject's members, 2 also pulls in what the external blocks compose. exclude: keeps a block out. On a package subject, an include: entry may also be a name qualified relative to the subject (Hardware::Box), a full qualified name or a stable id; it adds that definition as a block of the diagram and is matched for W417.


8.16.8.2 IBD (Internal Block Diagram)

An IBD shows the internal structure of a single PartDef or Part — its owned part usages, port usages, and the connections or flows that bind them. The subject's outer boundary is always present as the first shape.

Valid subject: types: PartDef, Part

Shape kinds:

kind value Description SVG primitive CSS class Symbol
boundary The subject's outer containing box <rect> with rx for rounded corners boundary #sym-boundary
block An owned part usage inside the boundary <rect> block Part or block PartDef #sym-PartDef
port A port usage drawn on the edge of its parent block <rect> (half-in, half-out) or <circle> port in, port out, port inout #sym-port
label A text label for any shape <text> label —
note A comment annotation box <rect class="note"> + fold line note #sym-note

Edge kinds:

kind value Description SVG primitive CSS class Arrowhead
connection Structural connector between two ports or parts <path> edge connection none
flow Flow connection with directional arrow <path> edge flow url(#arrow-flow) (filled triangle)
binding Equality binding — dashed line with = midpoint label <path stroke-dasharray="4 4"> edge binding none
succession Action succession — dashed arrow with filled head <path stroke-dasharray="6 3"> edge succession url(#arrow-filled)

SVG body notes: the boundary is a large <rect> with rx="8" or similar; ports are small <rect> or <circle> positioned on the edge of their parent block (use parent: in the manifest to declare ownership); flow edges use <path> with a directional arrowhead marker; labels inside blocks use <text> centered within the block rectangle.

Minimal YAML manifest example:

type: Diagram
diagramKind: IBD
name: EngineInternalIBD
subject: VehicleSystem::Powertrain::Engine
shapes:
  engine-boundary:
    ref: VehicleSystem::Powertrain::Engine
    kind: boundary
  piston-block:
    ref: VehicleSystem::Powertrain::Engine::pistons
    kind: block
    parent: engine-boundary
  crankshaft-block:
    ref: VehicleSystem::Powertrain::Engine::crankshaft
    kind: block
    parent: engine-boundary
  piston-out:
    ref: VehicleSystem::Powertrain::Engine::pistons::powerOut
    kind: port
    parent: piston-block
  crank-in:
    ref: VehicleSystem::Powertrain::Engine::crankshaft::forceIn
    kind: port
    parent: crankshaft-block
edges:
  piston-crank-flow:
    ref: VehicleSystem::Powertrain::Engine::pistonCrankFlow
    source: piston-out
    target: crank-in
    kind: flow

Completeness rule: the parser must warn if any owned Part, Port, Connection, or Flow declared in the subject's .md file (in features: or connections:) is absent from shapes: or edges:.

Derived content (REQ-TRS-VIS-005): an IBD with a subject: and no shapes: is generated from the model. The subject is a PartDef or Part (ItemDef/Item are accepted on the same terms); anything else is W418. The generator produces:

  • a boundary for the subject carrying the subject's own port features as port children; a Part subject also carries the ports its definition declares;
  • one block child of the boundary per owned part usage — an inline features: entry typed by a PartDef/ItemDef, or a child Part/Item element (a child element is skipped when an inline feature already has its name) — labelled name : Type with a non-1 multiplicity appended as [n], with that usage's ports as port children: the usage's own port features first, then the ports its definition declares (the usage's own wins a name clash); every port carries the direction (in/out/inout) of the port feature it came from, and no fixed side unless pinned;
  • an edge per entry of the subject's connections: (connection), flowConnections: (flow), bindingConnections: (binding) and successionConnections: (succession, from after/before), labelled with the entry's name: or the short name of its typedBy:. Each endpoint is a dotted feature chain resolved against the diagram's own nodes exactly as the graph builder resolves it: engine.powerOut is port powerOut of usage engine; a single segment is a usage or a boundary port.

A chain whose endpoint is not a node of the diagram produces no edge and relies on the existing connection findings — the generator never invents a port. Layout hints are layered, left-to-right, with hierarchy handling including children and port constraints fixed to a side only for ports that have one.

Grouping and containment (REQ-TRS-VIS-029, GH #336). The generator also reads, without any hand-set layout:

  • depth: (default 1; 0 is read as 1; capped at 16) — part levels drawn. At 2 and deeper, the part usages of a usage's definition and of the usage element itself (inline features or child Part files) are drawn as nested blocks inside it with their ports, and the definition's connection lists add edges whose chains are read relative to that usage (mcu.adc inside controlBoard is port adc of controlBoard::mcu). An edge between nodes at different levels is one edge from port to port; the hierarchical layout routes it across the boundaries. A definition that contains itself, directly or through others, is drawn once on each path and not opened again. Two members whose ids coincide (a port and a part of one name, a::b beside a_b, a part named like a domain frame) are both drawn; the later one takes the id with -2, -3, … appended.
  • Deployment containment — a drawn usage of a deployment package (isDeploymentPackage: true) that is allocated to a hardware element in any §12.9 form is moved inside a drawn usage of that element, or, when the target is not drawn at this depth, inside the innermost drawn usage whose definition contains it. Among several candidates the one nearest the package wins (the deepest common ancestor, so each board's firmware goes into that board's MCU), then one not yet hosting a package of the same definition (two images, two MCUs: one each), then the first drawn. Its own nested parts move with it.
  • Marks — a usage whose element or definition declares silLevel: (1 or higher), asilLevel: (A to D, a leading ASIL ignored; a decomposed B(D) keeps its notation) or a safety applied stereotype gets a mark with that level as a badge (SIL 2, ASIL B(D), safety), tone warn and a heavy outline; one with appliesWhen: (on the inline feature, the usage element or its definition) gets the badge optional and the value when <feature>, tone neutral with a dashed outline unless it is also safety-rated. Marks are the generic NodeMark of §8.16.8.10, so every writer draws them (PlantUML as stereotypes and colours); the SVG adds a legend only when a part is marked.
  • groupBy: domain — the subject's direct parts are framed by their domain: (the usage element's, else its definition's): one swimlane per domain, ordered system, hardware, software, then the rest, with id derived_shape_id("<subject>::domain::<domain>"). A part with no domain stays in the boundary. Containment wins over the frame: a deployed package stays inside its hardware and carries its domain as a banner. groupBy: stereotype frames the parts that apply a stereotype marked frame = true in [diagram.stereotypes.<Def>] (the part's, or its definition's; a specialisation counts): one swimlane per such stereotype, strongest first, labelled with its name; a part with none stays in the boundary (REQ-TRS-META-041). Any other groupBy: value is warning W420 and is ignored; groupBy: on a diagram kind other than IBD, on a manifest diagram or on an element that is not a Diagram is also W420. DOT draws the frames, and parts with parts inside them, as clusters.
  • --config — a diagram exported or served under a Configuration is derived from the projected model, so a part inactive in that product is not drawn, and since every part left is present, none is badged optional.

Without depth:, groupBy:, an allocated deployment package, a safety level or an appliesWhen:, the derived IBD is the same as before.


8.16.8.3 Sequence

A Sequence diagram shows time-ordered message exchanges between lifelines. Time flows top-to-bottom along the Y axis. Lifelines represent parts or actors; messages are horizontal arrows between them.

Valid subject: types: ActionDef, UseCaseDef, InteractionDef

Shape kinds:

kind value Description SVG primitive CSS class Symbol
lifeline A participant — vertical dashed line with a named header box at the top <rect> (header) + <line stroke-dasharray="4 4"> (stem) lifeline #sym-lifeline
actor An external actor — stick figure or labeled box <g class="actor"> containing <circle> (head) + <line> elements (body) actor #sym-actor
activation An activation bar — narrow rectangle on a lifeline indicating an active period <rect> activation —
fragment A combined fragment (loop, alt, opt, par) — rectangle with keyword label in top-left corner <rect> + <rect class="fragment-header"> + <text> fragment #sym-fragment
note A comment annotation box <rect class="note"> + fold line note #sym-note

Edge kinds:

kind value Description SVG primitive CSS class Arrowhead
message A message send — synchronous: solid line with filled arrowhead; asynchronous: solid line with open arrowhead <path> edge message sync or edge message async url(#arrow-filled) or url(#arrow-open)
return A return message — dashed line with open arrowhead <path stroke-dasharray="6 3"> edge return url(#arrow-open)
create Object creation — dashed arrow to a new lifeline's header <path stroke-dasharray="4 4"> edge create url(#arrow-open)
destroy Lifeline termination — X mark at the bottom of the lifeline <line> pair forming an X destroy —

SVG body notes: lifeline stems are <line> with stroke-dasharray; activation bars are narrow <rect> overlaid on the lifeline stem; messages are horizontal <path> elements with arrowhead markers; time increases downward so Y-coordinates represent event ordering; fragment boxes enclose the relevant message range with the keyword (loop, alt, etc.) in a small header rectangle.

Minimal YAML manifest example:

type: Diagram
diagramKind: Sequence
name: EngineStartSequence
subject: VehicleSystem::Actions::EngineStart
shapes:
  driver-lifeline:
    ref: VehicleSystem::Actors::Driver
    kind: actor
  ecu-lifeline:
    ref: VehicleSystem::ECU
    kind: lifeline
  engine-lifeline:
    ref: VehicleSystem::Powertrain::Engine
    kind: lifeline
  ecu-activation:
    ref: VehicleSystem::ECU
    kind: activation
    parent: ecu-lifeline
edges:
  start-cmd:
    ref: VehicleSystem::Actions::EngineStart::sendStartCommand
    source: driver-lifeline
    target: ecu-lifeline
    kind: message
  ignite-msg:
    ref: VehicleSystem::Actions::EngineStart::sendIgnite
    source: ecu-lifeline
    target: engine-lifeline
    kind: message
  ignite-return:
    ref: VehicleSystem::Actions::EngineStart::igniteAck
    source: engine-lifeline
    target: ecu-lifeline
    kind: return

Hand-listed placement. A manifest Sequence diagram with no layout: pin is placed with the same geometry a derived one uses (REQ-TRS-VIS-025): lifelines and actors (root shapes of those kinds) left to right in declaration order; every edge whose two ends reach a lifeline column, directly or through parent:, a row in edges: order with horizontal waypoints stem to stem (a self message loops); each activation a bar on its parent lifeline over the rows that touch it, several on one lifeline sharing those rows in order; each fragment a box around the messages whose edge ref equals the fragment's ref or lies under it (ref::…), an enclosing fragment drawn wider. An unlabelled message is labelled by the last segment of its ref. These spans are inferred, not recorded; to control geometry, pin shapes in layout: — a diagram with any pin keeps the author's pins and is not placed.

Completeness rule: the parser must warn if any SendAction or AcceptAction in the subject ActionDef's sub-actions (reachable via subActions: or steps:) is absent from edges:. The rule applies to a manifest diagram only: a derived Sequence diagram (below) generates one edge per message itself, so W080 is never raised on it.

Derived content (a subject: and no shapes:, §8.16.2; REQ-TRS-VIS-021): for a subject that is an ActionDef or Action (or a UseCaseDef/UseCase with actors:), or a PartDef/Part (REQ-TRS-SEQ-001: its behaviour is the actions its performs: entries name, in declaration order, a Part usage with none using its definition's; a part that performs no action is W418), the generator produces:

  • one lifeline per participant, in first-appearance order: the subject itself first, then every element a SendAction's to: chain or an AcceptAction's/SendAction's via: chain resolves to (a port chain resolves to the part that owns the port — an inline feature of the subject, a port element's owner, or the part in the model that owns a port of that name, a part that performs: the subject first; an unresolved chain becomes a lifeline labelled by the chain text, drawn dashed), then each entry of the subject's actors: as an actor;
  • one message edge per SendAction (from the subject's lifeline to the to:/via: participant, labelled name(Payload) from the payload's last segment and the action name) and one per AcceptAction (from the via: participant to the subject's lifeline, labelled by the payload, else the trigger), in execution order: successionConnections: topological order when declared (ties and unmentioned steps in declaration order), else declaration order, descending into an IfAction's then/else and a LoopAction's body;
  • a SendAction or PerformAction that names an operation (operation: <owner>::<name>, §8.4 and REQ-TRS-SEQ-002) is a message labelled with the operation's signature — op(name: Type, …) over its in/inout parameters, then : ReturnType — to the participant to:/via: names, else to the element that declares the operation (its own lifeline); a synchronous operation with a returnType is followed on the next row by a dashed return edge back to the subject labelled with the return type and, if errors: are declared, ! Error1, Error2; an asynchronous operation or one with no return type has none; an operation: that does not resolve is drawn as before
  • one fragment per IfAction (keyword alt, labelled by its condition) and LoopAction (keyword loop, labelled by its condition — for v in s for the for form), spanning the messages it contains;
  • one activation on the subject's lifeline spanning its messages.

Because a sequence diagram's geometry is fixed by its order, the generator places every node itself — lifelines left to right at a fixed pitch (200 px, headers 120 × 40, an actor 120 × 56), messages top to bottom in order at a fixed pitch (48 px) below the headers, fragments around their span, the activation around the subject's messages — as pins, and gives every message edge its two horizontal waypoints (source stem to target stem at the message's row), so every renderer draws the diagram with the fixed algorithm and no layout-engine run. Shape ids are derived_shape_id("<subject>::<participant|action>") (the subject's own lifeline is derived_shape_id("<subject>"), its activation that id suffixed -activation; a message edge is the action's id with an e- prefix). include:/exclude: name participants by qualified name or short name (never the subject); a subject of another type is W418. Return, create and destroy messages are not generated (no model field declares them), and a nested PerformAction is a step in the order but is not expanded into its definition's messages.


8.16.8.4 StateMachine

A StateMachine diagram shows the states and transitions of a StateDef. Initial and final pseudostates, choice pseudostates, and history pseudostates are first-class shape kinds.

Valid subject: types: StateDef; a State or ExhibitState usage is accepted for a derived diagram and reads the StateDef it is typed by.

Shape kinds:

kind value Description SVG primitive CSS class Symbol
state A named state — rounded rectangle; may have entry/do/exit sub-labels <rect rx="12"> state #sym-state
initial Initial pseudostate — filled circle <circle class="initial"> initial #sym-initial
final Final state — bullseye (outer circle + inner filled circle) <circle class="final-outer"> + <circle class="final-inner"> final #sym-final
choice Choice pseudostate — diamond <polygon> choice #sym-choice
history History pseudostate — circle containing H or H* <circle> + <text>H</text> history #sym-history
note A comment annotation box <rect class="note"> + fold line note #sym-note

Edge kinds:

kind value Description SVG primitive CSS class Arrowhead
transition A state transition — arrow with optional label formatted as event [guard] / effect <path> edge transition url(#arrow-filled)

SVG body notes: state rectangles use <rect> with rx="12"; sub-labels (entry/do/exit) are <text> elements inside the state box below a <line> compartment divider; the initial pseudostate is a single <circle class="initial"> (no arrowhead on the circle itself); the final state is two concentric circles; transition labels follow the format event [guard] / effect and are rendered as <text> elements near the midpoint of the transition path.

Minimal YAML manifest example:

type: Diagram
diagramKind: StateMachine
name: EngineStateMachine
subject: VehicleSystem::States::EngineStateDef
shapes:
  init-pseudo:
    ref: VehicleSystem::States::EngineStateDef
    kind: initial
  stopped-state:
    ref: VehicleSystem::States::EngineStateDef::Stopped
    kind: state
  running-state:
    ref: VehicleSystem::States::EngineStateDef::Running
    kind: state
  final-pseudo:
    ref: VehicleSystem::States::EngineStateDef
    kind: final
edges:
  init-to-stopped:
    ref: VehicleSystem::States::EngineStateDef::initTransition
    source: init-pseudo
    target: stopped-state
    kind: transition
  stopped-to-running:
    ref: VehicleSystem::States::EngineStateDef::startTransition
    source: stopped-state
    target: running-state
    kind: transition
  running-to-stopped:
    ref: VehicleSystem::States::EngineStateDef::stopTransition
    source: running-state
    target: stopped-state
    kind: transition

Completeness rule: the parser must warn if any sub-state listed in the subject StateDef's subStates: field is absent from shapes:, and if any transition listed in transitions: is absent from edges:.

Derived content (REQ-TRS-VIS-018): a StateMachine with a subject: and no shapes: is generated from the model. The subject is a StateDef, or a State/ExhibitState usage (the generator reads the StateDef it is typed by); anything else is W418. The generator produces:

  • one state per subStates: entry (§8.8.2), labelled by its name, with compartment lines entry / <action>, do / <action> and exit / <action> for entryAction/doAction/exitAction (string form: the last :: segment; map form: its name, else the last segment of its typedBy); a substate whose typedBy: resolves to a StateDef with its own subStates: becomes a container holding that machine's states, one level deep;
  • one initial pseudostate per region with a transition edge to each isInitial: true state, and one final node that every isFinal: true state transitions to (a region without an initial or final state has no such node);
  • one transition edge per transition in either placement (§8.8.3) — nested under a substate, or top-level with source:; the deprecated from/to/trigger aliases are read like the canonical keys, exactly as the W075 extractor does — labelled <accept> [<guard>] / <effect> with the absent parts omitted: accept is the payload's last segment (or after <expr>/when <expr>/at <expr> for a time or change trigger), effect is the effect's name or the last segment of its typedBy;
  • an edge only when both endpoint states are nodes of the diagram; a transition with a missing endpoint produces no edge and is left to W929.

Shape ids are s- plus <subject>::<stateName> in the deterministic form of §8.16.1 (a nested state is <subject>::<container>::<stateName>; a compartment is <state id>-compartment); the pseudostates are <subject id>-initial and <subject id>-final (a nested region's are <container id>-initial/-final). include:/exclude: name top-level substates by name or <subject>::<name>. Parallel regions (isParallel: true) are drawn as sibling containers when the substates are themselves typed machines; orthogonal-region dividers, history and choice pseudostates are not generated. Layout hints are layered, top-to-bottom, hierarchical.


8.16.8.5 Requirement

A Requirement diagram shows requirements and their inter-relationships within a package or rooted at a RequirementDef. All relationship edges are dashed with «keyword» labels following SysML requirement relationship notation.

Valid subject: types: Package, RequirementDef, Requirement

Shape kinds:

kind value Description SVG primitive CSS class Symbol
requirement A requirement box — rectangle with «requirement» stereotype banner and two compartments (name, text) <rect> + <line> compartment divider + <text> stereotype requirement #sym-requirement
block A satisfying element — a system part or definition referenced by satisfy/verify links <rect> block PartDef #sym-PartDef
testcase A verification case box <rect> testcase #sym-testcase
note A comment annotation box <rect class="note"> + fold line note #sym-note

Edge kinds:

kind value Description SVG primitive CSS class Arrowhead Keyword label
containment Parent/child requirement nesting <line> edge containment none —
derive Derived requirement — «deriveReqt» <path stroke-dasharray="6 3"> edge derive url(#arrow-open) «deriveReqt»
satisfy Satisfaction by a model element — «satisfy» <path stroke-dasharray="6 3"> edge satisfy url(#arrow-open) «satisfy»
verify Verification by a test case — «verify» <path stroke-dasharray="6 3"> edge verify url(#arrow-open) «verify»
refine Refinement — «refine» <path stroke-dasharray="6 3"> edge refine url(#arrow-open) «refine»
trace Traceability — «trace» <path stroke-dasharray="6 3"> edge trace url(#arrow-open) «trace»
copy Copied requirement — «copy» <path stroke-dasharray="6 3"> edge copy url(#arrow-open) «copy»

SVG body notes: requirement boxes use <rect> with two internal compartments separated by a <line>; the top compartment contains a <text class="stereotype">«requirement»</text> and the element name; the bottom compartment contains the requirement text; all relationship edges are <path stroke-dasharray="6 3"> with an open arrowhead marker and a <text class="edge-label"> keyword.

Minimal YAML manifest example:

type: Diagram
diagramKind: Requirement
name: SafetyRequirementsDiagram
subject: VehicleSystem::Requirements::Safety
shapes:
  sys-safety-req:
    ref: VehicleSystem::Requirements::Safety::SystemSafety
    kind: requirement
  brake-req:
    ref: VehicleSystem::Requirements::Safety::BrakeResponse
    kind: requirement
  brake-sys-block:
    ref: VehicleSystem::BrakeSystem
    kind: block
  brake-test:
    ref: VehicleSystem::Tests::BrakeResponseTest
    kind: testcase
edges:
  safety-contains-brake:
    ref: VehicleSystem::Requirements::Safety::SystemSafety
    source: sys-safety-req
    target: brake-req
    kind: containment
  brake-satisfied-by:
    ref: VehicleSystem::Requirements::Safety::BrakeResponse::satisfiedBy
    source: brake-sys-block
    target: brake-req
    kind: satisfy
  brake-verified-by:
    ref: VehicleSystem::Requirements::Safety::BrakeResponse::verifiedBy
    source: brake-test
    target: brake-req
    kind: verify

Completeness rule: the parser must warn if any Requirement owned by the subject package or sub-RequirementDef is absent from shapes:, and if any satisfies:, verifies:, or derivedFrom: link declared in any requirement's .md file within the subject scope is absent from edges:.

Derived content (REQ-TRS-VIS-020): a Requirement diagram with a subject: and no shapes: is generated from the model. The subject is a Package (LibraryPackage and Namespace count as packages), a RequirementDef or a Requirement; anything else is W418. The generator produces:

  • one requirement node per native Requirement, SysML RequirementDef or Requirement whose qualified name is the subject or lies under it at any depth, carrying the SysMLv2 stereotype (requirement, requirement def) and labelled by its name (else its stable id, else its short name), with a compartment child listing id = <stable id> and status = <status> when present (no compartment when neither is);
  • a derive edge from each requirement to each derivedFrom: target on the diagram (named by stable id or qualified name) and a refine edge for each refines: target;
  • a satisfy edge from a block context node — the satisfying architecture element, drawn with its real type's stereotype (part def, part, …) — to the requirement, for every element in the model whose satisfies: names a requirement on the diagram; and a verify edge from a testcase context node for every TestCase whose verifies: names one. A context node appears only when at least one such edge reaches a requirement on the diagram; a requirement on the diagram that itself satisfies: another draws the satisfy edge from its own node;
  • a containment edge from a RequirementDef to each requirement it owns (a direct member by qualified name), when both are on the diagram.

include:/exclude: apply to requirements and to context nodes alike, each entry naming an element by qualified name, stable id or short name. Edges carry no label of their own — the «deriveReqt»/«satisfy»/«verify»/«refine» keyword is the edge kind's notation (§8.16.8 tables). Shape ids are the deterministic ids of the depicted elements' qualified names (a compartment is <requirement id>-compartment); edge ids are e-<kind>-<source id>-<target id>. Layout hints are layered, top-to-bottom, with derive, satisfy, verify and refine edges oriented so a parent requirement sits above what derives from, satisfies or verifies it. Suspect-link state (W090) and status colours are not drawn.


8.16.8.6 Allocation

An Allocation diagram shows «allocate» relationships between logical/functional elements and physical/hardware elements. The diagram is conventionally divided into two swim lanes — one for logical elements and one for physical elements.

Valid subject: types: Package, AllocationDef, Allocation

Shape kinds:

kind value Description SVG primitive CSS class Symbol
swimlane Background lane dividing logical from physical elements; spans full diagram height or width <rect class="swimlane"> swimlane —
block An allocated element — logical (function, action) or physical (hardware, node) <rect> block <TypeName> #sym-PartDef or #sym-ActionDef
label A lane title or annotation text <text> (rotated transform="rotate(-90)" for vertical lanes) label swimlane-label —
note A comment annotation box <rect class="note"> + fold line note #sym-note

Edge kinds:

kind value Description SVG primitive CSS class Arrowhead Keyword label
allocation Allocation relationship from logical to physical — «allocate» <path stroke-dasharray="8 4"> edge allocation url(#arrow-open) «allocate»

SVG body notes: swim lanes are <rect class="swimlane"> drawn first (behind all other elements) spanning the full diagram height or width; lane title labels are <text transform="rotate(-90)"> for vertical lanes; allocation arrows are <path stroke-dasharray="8 4"> with an open arrowhead and a «allocate» keyword label at the midpoint.

Minimal YAML manifest example:

type: Diagram
diagramKind: Allocation
name: FunctionToHardwareAllocation
subject: VehicleSystem::Allocations
shapes:
  logical-lane:
    ref: VehicleSystem::Allocations
    kind: swimlane
  physical-lane:
    ref: VehicleSystem::Allocations
    kind: swimlane
  engine-ctrl-fn:
    ref: VehicleSystem::Functions::EngineControl
    kind: block
    parent: logical-lane
  ecu-hw:
    ref: VehicleSystem::Hardware::ECU
    kind: block
    parent: physical-lane
  brake-fn:
    ref: VehicleSystem::Functions::BrakeControl
    kind: block
    parent: logical-lane
edges:
  engine-ctrl-alloc:
    ref: VehicleSystem::Allocations::EngineControlToECU
    source: engine-ctrl-fn
    target: ecu-hw
    kind: allocation
  brake-alloc:
    ref: VehicleSystem::Allocations::BrakeControlToECU
    source: brake-fn
    target: ecu-hw
    kind: allocation

Completeness rule: the parser must warn if any allocatedFrom/allocatedTo pair declared in any Allocation or AllocationDef within the subject package is absent from edges:.

Derived content (REQ-TRS-VIS-022): an Allocation diagram with a subject: and no shapes: is generated from the model. The subject is a Package (LibraryPackage and Namespace count as packages), an AllocationDef or an Allocation; anything else is W418. The generator collects every allocation pair declared by the subject or by any element under it (any depth): an Allocation element's top-level allocatedFrom:/allocatedTo: (the usage name being the element's name), each features: entry of type: Allocation carrying those two fields, each allocations: entry of an AllocationDef (allocatedFrom/allocatedTo, or the bulk from/to spelling, accepted on any element under the subject), and each allocatedTo: on a Part/PartDef/Action/ActionDef (its source being the element itself, with no usage name). It produces:

  • two swimlane nodes, logical and physical, labelled Logical and Physical and referencing the subject, containing one block per distinct source element and per distinct target element respectively, each drawn with its real type's stereotype (action def, part def, requirement, …); an end that does not resolve becomes a dashed (unresolved) block labelled by the reference text as written. An element that is both a source and a target is drawn once in each lane;
  • one allocation edge per pair from the source block to the target block, labelled by the allocation usage's name when present, with the «allocate» keyword as the edge kind's notation; the edge references the element that declares the pair.

include:/exclude: apply to the end elements, each entry naming one by qualified name, stable id or short name (an unresolved end by its reference text); an edge is drawn only when both of its ends are kept. Shape ids are the deterministic ids of the end elements' qualified names (the reference text for an unresolved end); an element drawn in both lanes keeps the plain id in the logical lane and takes -physical in the physical lane. The lanes are <subject id>-logical and <subject id>-physical; edge ids are e-allocation-<source id>-<target id> with -<usage name> appended when the pair is named. Layout hints are layered, left-to-right, with hierarchy handling including children so the lanes are laid out around their blocks. Allocation of requirements (RequirementAllocation) is included on the same terms when its ends resolve.


8.16.8.7 UseCase

A UseCase diagram shows actors, use cases, the system boundary, and their relationships. Use cases are enclosed within the system boundary rectangle; actors appear outside it.

Valid subject: types: UseCaseDef, Package

Shape kinds:

kind value Description SVG primitive CSS class Symbol
system-boundary Rectangle enclosing all use cases — the system under consideration <rect class="system-boundary"> system-boundary #sym-system-boundary
usecase A use case — ellipse with name label inside <ellipse> + <text> usecase #sym-usecase
actor An external actor — stick figure (<circle> head + <line> body) with name label below <g class="actor"> containing <circle> + <line> elements actor #sym-actor
note A comment annotation box <rect class="note"> + fold line note #sym-note

Edge kinds:

kind value Description SVG primitive CSS class Arrowhead Keyword label
association Plain association between an actor and a use case <line> or <path> edge association none —
include Include relationship — dashed arrow from base to included use case — «include» <path stroke-dasharray="6 3"> edge include url(#arrow-open) «include»
extend Extend relationship — dashed arrow from extension to base — «extend» <path stroke-dasharray="6 3"> edge extend url(#arrow-open) «extend»
generalization Inheritance between actors or between use cases — hollow triangle at supertype end <path> edge generalization url(#arrow-inherit) (hollow triangle) —

SVG body notes: the system boundary is a <rect class="system-boundary"> drawn first so all use cases appear on top of it; use cases are <ellipse> elements with <text> labels centered inside; actors are <g class="actor"> groups containing a <circle> for the head and <line> elements for the body, arms, and legs, with a <text> name label below; all relationship edges are <path> with the appropriate marker and, for include/extend, a <text class="edge-label"> keyword.

Minimal YAML manifest example:

type: Diagram
diagramKind: UseCase
name: VehicleUseCaseDiagram
subject: VehicleSystem::UseCases
shapes:
  system-rect:
    ref: VehicleSystem
    kind: system-boundary
  start-engine-uc:
    ref: VehicleSystem::UseCases::StartEngine
    kind: usecase
  check-fuel-uc:
    ref: VehicleSystem::UseCases::CheckFuelLevel
    kind: usecase
  driver-actor:
    ref: VehicleSystem::Actors::Driver
    kind: actor
edges:
  driver-start-assoc:
    ref: VehicleSystem::UseCases::StartEngine::driverAssoc
    source: driver-actor
    target: start-engine-uc
    kind: association
  start-includes-check:
    ref: VehicleSystem::UseCases::StartEngine::includesCheckFuel
    source: start-engine-uc
    target: check-fuel-uc
    kind: include

Completeness rule: the parser must warn if any UseCase owned by the subject, or any actor association, includes:, or extends: link declared in any use case's .md file within the subject scope, is absent from shapes: or edges:.


8.16.8.8 Action

An Action diagram (SysMLv2's action definition diagram, REQ-TRS-VIS-019) shows the action flow of an ActionDef: its sub-action steps, the control nodes that split, join, branch and merge the flow, and the successions and item flows between them. Conditionals (IfAction) are drawn as a decision diamond whose branches rejoin at a merge; loops (LoopAction) as a container holding their body.

Valid subject: types: ActionDef, Action (an Action usage reads the ActionDef it is typed by)

Shape kinds:

kind value Description SVG primitive CSS class Symbol
action A step — rounded rectangle with the step's name and a «kind» stereotype (action, perform, send, accept, assign, terminate, loop); may contain a compartment and, for a loop, nested steps <rect rx="8"> action #sym-action
compartment The step's typedBy, payload, via/to lines, drawn inside its action; parent is the step <rect> + <line> divider compartment —
fork Fork control node — thick horizontal bar <rect class="fork"> (60×6) fork #sym-fork
join Join control node — thick horizontal bar <rect class="join"> (60×6) join #sym-join
decision Decision node — diamond; its label (the condition) sits beside it <path> (28×28 diamond) decision #sym-decision
merge Merge node — diamond <path> (28×28 diamond) merge #sym-merge
initial Initial node — filled circle <circle class="initial"> (20×20) initial #sym-initial
final Final node — bullseye <circle class="final-outer"> + <circle class="final-inner"> (20×20) final #sym-final
note A comment annotation box <rect class="note"> + fold line note #sym-note

Edge kinds:

kind value Description SVG primitive CSS class Arrowhead
succession Control succession (first a then b) — solid arrow, labelled by its guard ([guard]) or branch ([then]/[else]) when present <path> edge succession url(#arrow-filled)
flow Item flow between steps (flowConnections:) — solid arrow, labelled by the flow's name or type <path> edge flow url(#arrow-filled)

SVG body notes: steps use <rect> with rx="8"; a loop step encloses its body steps; the fork and join bars are filled <rect> elements with no outline; decision and merge diamonds are closed <path> elements, a decision's condition rendered as <text> to the right of the diamond; initial and final nodes are drawn as in a StateMachine; succession labels are <text> elements near the midpoint of the path.

Minimal YAML manifest example:

type: Diagram
diagramKind: Action
name: ProvidePowerFlow
subject: VehicleBehavior::ProvidePower
shapes:
  start:
    ref: VehicleBehavior::ProvidePower
    kind: initial
  start-engine:
    ref: VehicleBehavior::ProvidePower::startEngine
    kind: action
  fuel-check:
    ref: VehicleBehavior::ProvidePower::checkFuel
    kind: decision
    label: "fuel.level > 0"
  regulate:
    ref: VehicleBehavior::ProvidePower::regulatePower
    kind: action
  done:
    ref: VehicleBehavior::ProvidePower
    kind: final
edges:
  s1:
    source: start
    target: start-engine
    kind: succession
  s2:
    source: start-engine
    target: fuel-check
    kind: succession
  s3:
    source: fuel-check
    target: regulate
    kind: succession
    label: "[then]"
  s4:
    source: regulate
    target: done
    kind: succession

Completeness rule: the parser must warn if any subActions: or controlNodes: entry of the subject ActionDef is absent from shapes:, or any successionConnections: entry is absent from edges:.

Derived content (REQ-TRS-VIS-019): an Action diagram with a subject: and no shapes: is generated from the model. The subject is an ActionDef or an Action usage (the generator reads its ActionDef); anything else is W418. The generator produces:

  • one action per subActions: entry (§8.7.3), labelled by name, stereotyped by its kind (action, perform, send, accept, assign, terminate) with a compartment line for typedBy (: <last segment>), payload (send <Item>/accept <Item>), a time/change trigger (after …/at …/when …) and the via/to chains;
  • an IfAction as a decision labelled by its condition, its then/else sub-actions as nodes, succession edges from the decision to the first action of each branch labelled [then]/[else] (an empty or absent branch succeeds straight to the merge), successions along each branch in order, and a merge that the last action of each branch succeeds to;
  • a LoopAction as a container action stereotyped loop, labelled by its name and condition (name [while c], name [until c], name [for v in seq]), holding its body sub-actions with successions in body order;
  • one fork/join/decision/merge per controlNodes: entry (and per subActions entry of those kinds), labelled by its name;
  • one succession edge per successionConnections: entry (after → before, labelled [guard] when a guard is present; an endpoint naming an IfAction enters at its decision and leaves from its merge), and one flow edge per flowConnections: entry (a dotted pin chain such as takeoff.alt names the step);
  • an initial node with a succession to every top-level step that has no incoming succession, and a final node reached from every top-level step with no outgoing one, only when the subject declares at least one succession (otherwise the steps are drawn unordered).

Shape ids are s- plus <subject>::<stepName> in the deterministic form of §8.16.1, nested branch and body names included in the path (<subject>::checkWeather::abortMission); the merge an IfAction closes with is <subject>::<ifName>::merge, a compartment is <step id>-compartment, and the initial and final nodes are <subject id>-initial/-final. include:/exclude: name top-level steps and control nodes by name or <subject>::<name>. Parameter pins and object-flow items are rendered as compartment text, not as separate nodes. Layout hints are layered, top-to-bottom, hierarchical.


8.16.8.9 FeatureModel

A FeatureModel diagram (REQ-TRS-FMED-001) is a feature diagram in FODA notation: the feature tree of a product line with the way each feature's children are grouped, whether each is a mandatory or optional member of its parent, and the cross-tree requires and excludes constraints. The browser's /features page shows the same graph with live analysis overlaid.

Valid subject: types: FeatureDef (that feature and its descendants), FeatureModel (a single-file feature sheet, §9.6a) or a Package/LibraryPackage/Namespace (every FeatureDef under it). Anything else is W418.

Shape kinds:

kind value Description Notation
feature One FeatureDef: a rectangle with the feature's name (italic when isAbstract), parameters: and cardinality: as compartment lines a filled circle above the box when it is a mandatory member of its parent, a hollow one when optional; the root has none

Edge kinds:

kind value Description Notation
child Parent feature to child feature a straight line from the parent's bottom centre to the child's top centre; where the parent's groupKind: is alternative a hollow wedge joins its children (XOR), where it is or a filled wedge (OR)
requires A cross-tree requires: constraint a dashed curve with a filled arrowhead at the required feature, labelled requires
excludes A cross-tree excludes: constraint; a mutual pair is drawn once a dashed curve with open arrowheads at both ends, labelled excludes

requires and excludes edges are overlay edges: they take no part in layout (they would pull the tree out of shape) and are drawn after the features are placed, as curves that leave and enter the features on their free sides. The layout is a tree layout (ELK mrtree) that places every parent centred over its children and several roots side by side.

Derived content: the generator reads the feature tree exactly as the SAT encoding does (parentFeature: else the nearest enclosing feature in the qualified name; FEAT-* ids accepted in requires:/excludes:), so the picture and the analysis cannot disagree. One feature node per FeatureDef under the subject, one child edge to each child, and a constraint edge only when both ends are on the diagram. include:/exclude: match by qualified name, FEAT-* id or short name; an entry matching no feature is W417. A feature kept whose parent was excluded becomes a root. Node ids are the usual deterministic s-… ids, so layout: pins apply.

Minimal example:

type: Diagram
diagramKind: FeatureModel
name: CarFeatures
subject: Features
8.16.8.10 FaultTree, AttackTree and SafetyCase (GH #223)

The safety diagrams are derive-only: a Diagram with one of these kinds and a subject: and no shapes: is generated from the model; there is no hand-listed manifest form. They draw the element graphs the safety analyses already compute, so the picture and the numbers cannot disagree. A node may carry a generic mark — a status word, a value line, badges and a tone (ok, warn, bad, neutral) — which every writer draws (the tone colours the node, the text is stacked under its name) and an emphasis flag that draws its outline heavier. Layout is layered, top-down.

Valid subject: types (otherwise W418):

diagramKind Subject
FaultTree a FaultTree, or a SafetyGoal (every FaultTree naming it as topEvent:)
AttackTree an AttackTree, or a ThreatScenario (every AttackTree naming it as threatRef:)
SafetyCase (alias GSN) a SafetyGoal, an Argument (the goals whose argument contains it) or a Package (every goal under it)

Shape kinds (shapes follow the standard notation):

kind value Notation
gate-and, gate-or, gate-xor, gate-not, gate-inhibit the IEC 61025 gate symbols for gateType AND/OR/XOR/NOT/inhibit (an attack-tree gate is gate-and/gate-or)
event-basic, event-undeveloped, event-house circle, diamond and house for eventKind basic/undeveloped/house
step an attack step: a rounded box
goal, undeveloped-goal a GSN goal (a claim or requirement is one); the undeveloped goal carries GSN's diamond
strategy, solution, context, justification, assumption parallelogram, circle (a test case is the evidence), rounded box, and ellipses marked J / A

Edge kinds: input (a gate to one of its inputs:, a plain line), criticalPath (an input on the easiest attack path, drawn heavier), supportedBy (GSN, a filled arrowhead), inContextOf (GSN, a hollow arrowhead).

Derived content. FaultTree: one node per gate and event of the tree, one input edge per gate input; the overlay is fault-tree analyze — each event shows λ and probability, its role as status and tone (single point of failure bad, a member of a dual-point cut set warn, a deeper cut set ok, no cut set or unreachable neutral), the number of minimal cut sets it is in as a badge, and the root shows the exact top-event probability, the cut-set count and the single-point count. If the analysis fails (a gate cycle) the structure is still drawn and the root says why. AttackTree: gates and steps tinted by their rolled-up feasibility (the weakest-link roll-up of §15.7 that W035 uses: high bad, medium warn, low and very low ok, unscored neutral), the easiest path emphasised with criticalPath edges, and the root carrying the W035 badge when its computed feasibility differs from its ThreatScenario's declared one. SafetyCase: the traversal of syscribe safety-case (including the implicit goal–requirement–test chain), each node toned by its rolled-up status, UNDEVELOPED on the node that originates a gap, the goal verdict on the root and the test verdict on a test case (read from the model's .syscribe/results.json sidecar, unknown only for a test case it does not cover, exactly as safety-case --format dot|mermaid reads it). The attack-tree roll-up uses the model's [cyber] configuration, as W035 does. Nodes are labelled with their stable id and carry the full name wrapped beneath it; a fault-tree gate or event is a description box with its logic symbol (AND flat-bottomed D, OR shield, XOR shield with a second arc, NOT triangle with a bubble, inhibit hexagon; basic-event circle, undeveloped-event diamond, house-event pentagon) hung on a short stub; tree edges are drawn as a bus with an arrowhead into the gate; a legend of the tone colours closes the SVG. include:/exclude: match by qualified name, stable id or short name; an entry matching nothing is W417.

Minimal example:

type: Diagram
diagramKind: FaultTree
name: FaultTreeEngine
subject: Safety::FTA::FT-ENG-001

The web detail panel of a FaultTree, AttackTree, SafetyGoal, Argument or ThreatScenario embeds the same diagram, each node linking to its element; GET /api/diagrams/model/<qname>[?kind=…] serves it to the editor without a Diagram element.

8.16.8.11 Traceability, ZoneConduit and ThreatGraph (GH #223)

Three more derive-only kinds draw the cross-analysis graphs: the hazard-to-test spine, the IEC 62443 zone model and the ISO/SAE 21434 threat chain. Like §8.16.8.10 they reuse the generic mark (status, value, badges, tone, emphasis) and add no validation codes beyond W417/W418.

Valid subject: types (otherwise W418):

diagramKind Subject
Traceability (alias HazardTrace) a SafetyGoal, a HazardousEvent (the goals that answer it), a Requirement (its lineage: ancestors, descendants and the goals they trace to) or a Package (every goal and every unanswered hazardous event under it)
ZoneConduit (alias Zones) a Package (every Zone under it and the conduits touching them), a Zone (it, its conduits and the zones at their far ends) or a Conduit (it and its two zones)
ThreatGraph (alias TARA) a Package or TARASheet (every chain under it) or one ThreatScenario, DamageScenario, Asset, CybersecurityGoal or SecurityControl (the chains through it)

Shape kinds added: zone (a compound node; a container). Every other node reuses an existing kind — goal (a safety goal, an argument), requirement, testCase, block (a hazardous event, fault tree, threat, damage scenario, asset, control, member part).

Edge kinds added: impacts (threat to damage), affects (damage to asset), protectedBy (asset, or the last link that exists, to the goal that protects it), implementedBy (goal to the control that implements it), conduit and weakConduit (between zones, undirected). Traceability reuses trace, derive and verify, drawn the way the links are held (a derived or verifying artifact points upstream, §12.1), reversed for layout so the hazards read first; its layout is left to right, as are the other two.

Derived content.

Traceability: the goal and its hazardousEvents:; its FaultTrees (topEvent:) and the Arguments that supports: it; the requirements with derivedFromSafetyGoal: naming it and, below each, the requirements derivedFrom: it; the test cases that verifies: each requirement. A test case shows its verdict (unknown in a derived diagram, which has no results sidecar; hara trace uses the ingested ones) and its level; a requirement its lifecycle status and ASIL; a goal and a hazard their ASIL (a hazardous event's from asilLevel: or the S/E/C determination). The tone is the roll-up of everything below the node — bad for a failing test or a gap, warn for an unverified or draft branch, ok for a fully passing one. Gaps are red badges naming the validator's finding: W002/W003 no test (a leaf with no active test case), W305 no integration test (a parent with no active L3–L5 test case), W300 unsatisfied (an approved leaf nothing satisfies), no requirement on a goal and no goal on a hazardous event nothing answers.

ZoneConduit: each zone a compound node holding its member parts (members: and every part whose inZone: names it; an unresolved member is drawn dashed) and the SecurityControls that contribute to it (those allocated, in any §12.9 form, to a member, the zone or a conduit touching it, and the controls a touching conduit lists in implementedBy:), labelled SL target <targetSL> / achieved <achievedSL>. A zone is bad with an SL gap badge when achievedSL < targetSL (W950), ok when it meets its target and warn when either is missing; no controls is a badge. A conduit is an edge labelled CD-… · SL <achievedSL>/<required> with its protocols, where the required level is the highest targetSL of the zones it joins; it is a weakConduit (red, heavy, dashed) when achievedSL is below that (W951). The levels are those of syscribe zones and syscribe conduits.

ThreatGraph: the chain ThreatScenario → DamageScenario → Asset → CybersecurityGoal → SecurityControl: a goal protects an asset when one of its threatScenarios: leads, through damageScenarios:, to a damage scenario whose assets: names it (a damage scenario with no assets: links straight to the goal). A threat shows its risk under the configured [cyber] method (§15, risk::threat_risk: the impact of its damage scenarios against its attack feasibility, explicit or computed from the attack-potential factors; critical is drawn heavier) with low ok, medium warn, high/critical bad; a damage scenario its impact; a goal its calLevel and bad with a W802 no control badge when no control implements it; a threat no goal treats a no goal badge. include:/exclude: match by qualified name, stable id or short name; an entry matching nothing is W417.

type: Diagram
diagramKind: ZoneConduit
name: ZoneConduitEngine
subject: Security::Zones

The web detail panel of a hazardous event, safety goal (and a requirement that traces to one), zone, conduit, threat, damage scenario, asset, cybersecurity goal or security control embeds the same diagram, each node linking to its element; GET /api/diagrams/model/<qname>[?kind=…] serves it to the editor without a Diagram element. syscribe zones --format, cyber-risk --format dot|mermaid|plantuml|svg and hara trace draw the whole-model graphs without one.

8.16.9 Flow Kinds and Port Sides

An engineer reads an architecture picture by what the lines carry: power, control commands, measurements. A connection, a port or a port definition may say so with flowKind:, one of power, signal, command, data, protection or mechanical (REQ-TRS-VIS-028).

Where Field Meaning
PortDef flowKind: What every port typed by it carries; a PortDef without one takes the nearest along its supertype: chain
Port file flowKind: What this port carries; wins over its PortDef (read by manifest diagrams)
port feature (features: entry) flowKind: What this port carries; wins over its PortDef
port feature side: north, east, south or west; places this port and wins over every rule
connections: or flowConnections: entry flowKind: What this connection carries; wins over either port
manifest edges: entry flowKind: What this edge carries; wins over either port shape
type: PortDef
name: DcPort
flowKind: power
---
type: PartDef
name: Inverter
connections:
  - from: ctl.gateOut
    to: bridge.gate
    flowKind: command

A connection or flow edge takes the entry's own flowKind:, else the source port's, else the target port's; in a manifest diagram a port shape takes its Port's or PortDef's kind. A binding or succession carries nothing and keeps its own look. signal is drawn as every edge is today; the others have built-in styles (power a heavy orange line, command dashed blue, data dotted green, protection red, mechanical heavy grey). Every writer carries the kind: SVG and sprotty as the class flow-<kind> and the stroke, DOT as class, penwidth and style=dashed/dotted, Mermaid as a %% flow: <kind> comment and a linkStyle, PlantUML as a coloured arrow with an integer thickness and a <<kind>> stereotype. Mermaid and PlantUML style an edge only when its kind changes its look, so a signal edge keeps the theme's. A model that names no flow kind draws byte-for-byte as before.

A project may restyle a kind and place ports by kind in a [diagram] table of .syscribe.toml:

[diagram.styles.power]   # stroke (or color), width, dash; "" is a solid line
stroke = "#b35900"
width = 3

[diagram.portSides]      # optional; a port's own side: wins
power = "direction"      # in west, out east: the default rule
signal = "south"
command = "north"

A port takes its kind from its own flowKind: or its PortDef, else from the first edge joining it. Without a [diagram.portSides] rule every port is placed by its direction, as before. stroke must be #rgb, #rrggbb or a colour name and dash a list of numbers, because every writer copies them as they are. A malformed entry, an unknown kind or side, or a flowKind:/side: where nothing reads it (on another element type, a non-port feature, a binding or succession, or side: on a connection) is W419 (§8.16.7 rule 12) and is ignored. Two ports of different kinds joined by one connection are not checked; the edge takes the source's.

8.16.10 Views: include:, style: and layout: hints

A view says what a diagram is about, not where things go (REQ-TRS-VIS-031, GH #338). On a derived IBD, include: may be a mapping instead of a list:

Key Meaning
kinds Flow kinds (§8.16.9): the edges that carry one, and the parts they join (or whose ports they join)
domains domain: values of the usage element or its definition
tags tags: of the usage element or its definition
features Features, by qname or FEAT-* id: parts whose appliesWhen: (inline, usage element or definition) names one of them or a feature beneath one
stereotypes Stereotype selectors (Def or Def where <expr>, REQ-TRS-META-024): parts whose usage element or definition applies one, a specialisation counting. A malformed entry or one naming no MetadataDef is W421 and ignored
depth As the diagram's depth:, which wins when both are set
members The list form's member filter (§8.16.8.2); members outside it are not drawn at all

A part is in the view when it passes every key given. features counts a feature its appliesWhen: names without negation (not Features::Afci does not select the part). An entry that is ignored (a kind that is no flow kind, …) leaves the others; a key whose every entry is ignored is ignored as a whole, so it never fades everything. A value of the wrong shape (kinds: power) is W421, never a parse error. An edge is in the view when its kind is listed (with kinds:) and both ends are on parts in the view or on the boundary. The parts that contain a part in the view stay as its frame. Everything else is marked faded: SVG draws it at a quarter opacity with the class faded, sprotty gets the cssClasses entry faded, Mermaid a faded class and a link opacity, DOT a quarter alpha, PlantUML pale grey. The list form of include: keeps its meaning, a member filter, and fades nothing.

Named styles. style: <name> selects a [diagram.style.<name>] table of .syscribe.toml:

[diagram.style.focus]
context = "fade"            # "fade" (the default) or "hide": what lies outside the view

[diagram.style.safety]
extends = "focus"           # a chain of styles
[diagram.style.safety.flows.protection]
width = 4                   # the keys of [diagram.styles.<kind>]

[diagram.style.isolate]
context = "hide"

A diagram draws with the project's [diagram.styles], then [diagram.style.default] when it is defined, then the chain of its style: from the root to the named style, the nearest winning key by key. Pins in layout: are applied on top of whatever the style gives. Under context = "hide" the faded nodes (with what is inside them) and edges are left out. A style is project configuration, not a model element: it changes how a diagram looks, never what the model says.

Style by stereotype (REQ-TRS-META-040, -043, -044). [diagram.stereotypes.<Def>] gives the nodes of the elements that apply a stereotype — or a specialisation of it — their own presentation, in every writer (browser, SVG, DOT, PlantUML, Mermaid):

[diagram.stereotypes.Safety]
stroke = "#d00000"          # fill, stroke: #rgb, #rrggbb or a colour name
fill   = "#ffe5e5"
width  = 3                  # outline width
when   = "integrity >= 2"   # optional: only where the application's tagged values satisfy this

[diagram.stereotypes.Optional]
dash     = "6,3"            # a dashed outline ("" is solid)
banner   = false            # hide the «Optional» banner
priority = 5                # higher wins a property two styles set; ties go to the later definition name

Several stereotypes on one element compose property by property. The graph carries a legend of the stereotypes that actually styled a node (strongest first), which the SVG writer draws under the diagram. With no table a diagram draws exactly as before. A malformed entry is W419 and is ignored. frame = true makes a stereotype a swimlane for an IBD with groupBy: stereotype. A stereotype applied on a connections: entry (metadata:), or on its typedBy ConnectionDef, restyles that edge (stroke, width, dash) in every writer. Not yet: icon and badge.

Layout hints. Beside its pins, layout: takes two keys a shape id never has:

Key Meaning
flow left-to-right or top-to-bottom: the direction of the layered layout
layers Bands in order. Each entry is a part's name, a flow kind (of the edges at the part or its ports) or the label of a groupBy: domain frame; the parts of each container are ordered by the first band they fall in, unmatched last, and the layered layout keeps that order across the flow

Pins keep overriding the automatic placement. The browser's Auto-layout (DELETE /api/diagrams/layout/<qname>) removes the pins and keeps the hints. flow also sets the direction Mermaid (flowchart LR/TD) and PlantUML (left to right direction) draw in.

Variant view. syscribe diagram export <qname> --config A --config B builds the diagram for each product from the model projected onto that Configuration and merges the two by node and edge id. The outermost node only A has is marked only A (tone bad; its ports and inner parts go with it), one only B has only B (tone ok), and an edge only one has carries the same words in its label. A feature parameter that differs between the products but changes no element changes nothing here; diff --config A --config B reports it.

Example (model_gti/Diagrams/GtiProtectionView.md):

type: Diagram
name: GtiProtectionView
diagramKind: IBD
subject: System::GridTieInverter
include: {kinds: [protection]}
style: safety

8.17 Architecture Decision Records (ADR)

An ADR file is a first-class model element that documents a significant design decision — particularly the rationale behind a requirement breakdown. It is referenced from native Requirement elements via the breakdownAdr: field (§8.11.6).

8.17.1 Frontmatter Schema

Field Type Required Description
type literal ADR Required Discriminator.
id string Required Stable opaque ID matching ^ADR(-[A-Z0-9]{2,12})*-[0-9]{3,8}$. Unique across the model. Never changes.
name string Required One-line human-readable label — free prose.
status enum Required Lifecycle state: proposed, accepted, deprecated, superseded.
date string optional ISO-8601 date the decision was made (e.g., "2026-05-26").
deciders list of strings optional Qualified names of stakeholder PartDef elements or free-text names of the decision-makers. Display metadata, not a cross-reference: entries are not resolved (a free-text name is legitimate) and are printed by show. A single string is accepted as a one-entry list. Recognized only on ADR; on any other type it is an unrecognized field (W047).
tags list of strings optional Free labels for filtering/grouping.

ID pattern: ^ADR(-[A-Z0-9]{2,12})*-[0-9]{3,8}$ - Prefix ADR, zero or more uppercase-alphanumeric category segments (2–12 chars each; a bare ADR-001 is valid), 3–8 digit numeric suffix (default cap 8; configurable via [ids] max_digits, minimum 3). - Examples: ADR-SYS-001, ADR-SW-SCHED-001, ADR-UAV-PWR-002

Status values:

Value Meaning
proposed Decision is under review; not yet ratified. An approved Requirement must not reference a proposed ADR (warning W303).
accepted Decision is ratified and in effect.
deprecated Decision is outdated but not replaced.
superseded Decision has been replaced by another ADR; reference the newer ADR from breakdownAdr:.

8.17.2 Body Structure

Recommended layout — free-form Markdown:

## Context

<Why this decision was needed; the forces at play.>

## Decision

<The decision made and the breakdown it describes.>

## Consequences

<What becomes easier or harder; impact on the requirement hierarchy.>

8.17.3 Placement Convention

ADR files live in a dedicated ADRs/ package at or near the model root, or in a sub-package alongside the requirements they concern:

model/
  ADRs/
    _index.md         # type: Package, name: ADRs
    SYS-001.md        # type: ADR, id: ADR-SYS-001
    SW-SCHED-001.md   # type: ADR, id: ADR-SW-SCHED-001

8.17.4 Complete Example

---
type: ADR
id: ADR-SW-SCHED-001
name: "Decompose REQ-UAV-SCHED-001 into scheduler and bitmap sub-requirements"
status: accepted
date: "2026-05-20"
deciders:
  - Stakeholders::SystemsEngineer
  - Stakeholders::SoftwareArchitect
tags:
  - scheduler
  - requirement-breakdown
---

## Context

REQ-UAV-SCHED-001 specifies O(1) real-time scheduling in aggregate. During
software architecture review it became clear that the requirement conflates two
independently verifiable sub-properties: the data structure selection (bitmap)
and the worst-case execution time (WCET) guarantee. Separate requirements allow
independent test coverage and different SIL assignments.

## Decision

REQ-UAV-SCHED-001 is decomposed into:
- REQ-SCHED-BITMAP-001 — bitmap-based priority selection
- REQ-SCHED-WCET-001 — WCET guarantee at the hardware platform clock rate

Both child requirements carry `derivedFrom: [REQ-UAV-SCHED-001]` and
`breakdownAdr: ADR-SW-SCHED-001`. The parent requirement retains
`status: approved` but must not appear in any `satisfies:` list (rule §12.4).

## Consequences

- Each child requirement can be assigned to a distinct software module.
- Verification is split: REQ-SCHED-BITMAP-001 is covered by unit tests (L1/L2);
  REQ-SCHED-WCET-001 requires WCET analysis tooling (L3/L4).
- The parent requirement REQ-UAV-SCHED-001 becomes a leaf-suppressed node in
  the coverage report.

8.18 Safety and Security Analysis Elements

This section defines element types used in functional safety (ISO 26262, IEC 61508, ISO 13849-1) and cybersecurity analysis (ISO/SAE 21434). These are native element types — like Requirement and TestCase they carry stable opaque IDs and are dispatched by the parser on type: field.

8.18.1 Tier 2 — HARA Elements

Used in Hazard Analysis and Risk Assessment (HARA) per ISO 26262-3 or IEC 61508.

Element type ID pattern Description
HazardousEvent HE-* A combination of a hazard and an operational situation; carries ISO 26262 risk parameters (severity, exposure, controllability) or IEC 61508 risk graph parameters (consequence, freqExposure, avoidance, demandRate).
SafetyGoal SG-* A top-level safety requirement derived from the HARA; carries asilLevel: (ISO 26262), silLevel: (IEC 61508), or plLevel: (ISO 13849-1), and hazardousEvents: referencing the events it addresses; optional target: (mode, value, unit, proofTestInterval; E985) overrides the quantitative target of its integrity level (§ Safety targets).

Integrity level rules (W801, W806, E841, W808): A SafetyGoal must carry an integrity level (W801). It must reference at least one HazardousEvent via hazardousEvents: (W806). Any element derived from a SafetyGoal via derivedFromSafetyGoal: must carry an integrity level — asilLevel: or silLevel: — when the goal carries one (E841), and may carry a lower level only when breakdownAdr: is set (W808; see §12.7).

8.18.2 Tier 2 — TARA Elements

Used in Threat Analysis and Risk Assessment (TARA) per ISO/SAE 21434.

Element type ID pattern Description
DamageScenario DS-* An adverse consequence to a stakeholder; carries damageSeverity: and impactCategories:. May carry hazardRef: (string or list) linking it to the HazardousEvent/SafetyGoal it endangers (safety↔security co-engineering).
ThreatScenario TS-* A potential attack scenario; carries attackFeasibility: and attackVector:. References damageScenarios:. May carry a direct hazardRef: (string or list) to a HazardousEvent/SafetyGoal, a riskTreatment: (avoid/reduce/share/retain), and a free-text residualRisk:.
CybersecurityGoal CSG-* A high-level security requirement; carries securityProperty: (confidentiality, integrity, availability, authenticity), calLevel: (CAL1–CAL4), and threatScenarios: (the TS-* threats it counters).
SecurityControl SC-* A concrete countermeasure; carries controlType: and implementsGoals:.
VulnerabilityReport VR-* A tracked vulnerability; carries cvssScore:, mitigatedBy:, and affectedElements: (model qualified names or pkg: package URLs of SBOM components). Optional cveId:, cvssVector:, cvssSeverity:, fixedIn:, threatScenarios: (the threats it realises) and rationale: (required for accepted/wont_fix). sbom emits each report as a CycloneDX vulnerabilities (VEX) entry.
TARASheet TARA-* An Option-B container: a single file whose assetTable:, damageTable:, threatTable:, goalTable:, and controlTable: sections are exploded at parse time into the individual Tier 2 element types above.

Cross-reference rules: A Requirement motivated by a cybersecurity goal should set derivedFromCybersecurityGoal: to the CSG-* ID, and must set verificationMethod: (W807). The OSLC link direction applies: the downstream element holds the reference.

Safety↔security co-engineering (ISO 26262 ⇄ ISO/SAE 21434): A DamageScenario/ThreatScenario may declare hazardRef: (string or list) pointing to the HazardousEvent/SafetyGoal it endangers, resolved by id or qualified name. A hazardRef that does not resolve, or resolves to a non-HazardousEvent/non-SafetyGoal element, is an error (E844). A DamageScenario whose impactCategories: includes safety but has no hazardRef warns W030 (opt-in, gateable with --deny W030). The co-analysis command (§ CLI) reports, per safety goal/hazard, the cyber threats that can violate it.

Cybersecurity risk determination (ISO/SAE 21434 §15.8–15.9): Each ThreatScenario has a computed risk level. Severity rank = max damageSeverity over its resolved damageScenarios (negligible=0, moderate=1, major=2, severe=3); feasibility rank from attackFeasibility (very_low=0, low=1, medium=2, high=3). If either is unknown the risk is unknown (listed, not gated); otherwise score = severity + feasibility (0..6) → low (0–1), medium (2–3), high (4), critical (5–6). A ThreatScenario records its risk-treatment decision with riskTreatment: (avoid/reduce/share/retain; invalid → E845) and an optional free-text residualRisk:. A high/critical-risk threat with no riskTreatment that is not listed by any CybersecurityGoal.threatScenarios warns W031; a CybersecurityGoal whose calLevel is below the expected CAL for its threats' max risk (low→CAL1 … critical→CAL4) warns W032. Both are gateable with --deny and promotable via [profiles]. The cyber-risk command (§ CLI) lists every threat with its risk and treatment.

Configurable risk and CAL (GH #222). The rank-sum is the default simple method. A [cyber] table in .syscribe.toml may select method = "annex" — built-in example tables modelled on the informative annexes of ISO/SAE 21434 (a 1–5 risk-value matrix over impact × feasibility, CAL from impact × attackVector, attack-potential scoring), which are not normative and must be verified against the project's own copy of the standard — and may override matrix cells ([cyber.risk_matrix.<impact>], [cyber.risk_levels]), CAL cells ([cyber.cal_table.<impact>], [cyber.cal_by_risk]) and attack-potential factor tables and thresholds ([cyber.attack_potential]). ThreatScenario/AttackStep may supply the five factors elapsedTime, expertise, knowledge, windowOfOpportunity, equipment (a table label or a non-negative integer); all five derive attackFeasibility when it is not declared (a declared value wins; mismatch W642; partial set ignored with W641; unknown label E641). DamageScenario may carry safetyImpact/financialImpact/operationalImpact/privacyImpact (severe/major/moderate/negligible, else E640); the overall impact is the max of these and damageSeverity. A malformed [cyber] entry raises W640 and falls back to its default; with no [cyber] table behaviour is unchanged.

Binding SecurityControls to architecture: allocate the control (source) to the architecture element that realises it (target) with a standalone Allocation element — allocatedFrom: SC-* + allocatedTo: <element> (§12.9 form 2); the element then lists the control in its derived allocatedFrom index. Both fields accept a single string or a list of strings, so one Allocation can bind several controls. An allocatedFrom: authored directly on the architecture element (the pre-GH #131 guidance) is still accepted as a legacy input form (§12.9), but is not recommended.

Confirmation measures (ISO 26262-2 §6 / ISO/SAE 21434 §7): A ConfirmationMeasure (type: ConfirmationMeasure, CM-* id) records a confirmation review, functional-safety audit, functional-safety assessment, or cybersecurity assessment, with its required independence level. Fields: measureType: (confirmation_review · functional_safety_audit · functional_safety_assessment · cybersecurity_assessment; invalid → E849), independenceLevel: (I1 · I2 · I3; invalid → E850), status: (planned · in_progress · completed; invalid → E924), and confirms: (string or list — the confirmed work-product ref(s), each resolved via the resolver; unresolved → E851). Missing id/name/status → E847; an id not matching CM-* → E848. A SafetyGoal/native Requirement at asilLevel: D, silLevel: 3 or silLevel: 4 not confirmed by an I3 functional_safety_assessment, a calLevel: CAL4 CybersecurityGoal not confirmed by an I3 cybersecurity_assessment, or a calLevel: CAL3 CybersecurityGoal not confirmed by an I2-or-I3 cybersecurity_assessment (REQ-TRS-SEC-007), warns W039 (opt-in — dormant unless at least one ConfirmationMeasure exists; lower levels are not gated).

8.18.3 Tier 4 — Fault Tree Analysis (FTA)

Element type ID pattern Description
FaultTree FT-* Root of a fault tree; references a SafetyGoal via topEvent:.
FaultTreeGate FTG-* Logic gate; gateType: is one of AND, OR, XOR, NOT, inhibit; inputs: lists child gate/event IDs.
FaultTreeEvent FTE-* Leaf event; eventKind: is basic, undeveloped, or house; optional failureRate: (λ, /h), diagnosticCoverage: (DC), latentDiagnosticCoverage: (DCl) — DC/DCl in 0.0–1.0 (E846), inputs to the quantitative metrics roll-up (§ above); optional ref: — a single qualified name or stable id of the model element (typically a Part/PartDef) whose failure the event represents, resolved by the standard resolver (dangling → E927) and surfaced by show, links and fault-tree render; optional probability: (0–1), ccfGroup: (common-cause group name) and ccfBeta: (beta factor 0–1) used by fault-tree analyze; optional rateSource: (text) and rateStatus: (verified, secondary, unverified; E986) for the provenance of failureRate (§ Safety targets).

Nesting rule (W900): FaultTreeGate and FaultTreeEvent elements must be placed in a subdirectory named after the FaultTree file so their qualified names are prefixed by the tree's qualified name:

Safety/FTA/FT-BRAKE-001.md          →  Safety::FTA::FT-BRAKE-001
Safety/FTA/FT-BRAKE-001/
  FTG-BRAKE-001.md                  →  Safety::FTA::FT-BRAKE-001::FTG-BRAKE-001
  FTE-BRAKE-001.md                  →  Safety::FTA::FT-BRAKE-001::FTE-BRAKE-001

8.18.4 Tier 4 — FMEA

Element type ID pattern Description
FMEASheet FMEA-* Container with an entries: list; each entry is a failure mode row with failureMode:, effect:, cause:, fmeaSeverity:, occurrence:, detection: (1–10 each), optional rpn: (auto-computed when absent), and a recommendedAction: mitigation. A row may also carry FMEDA data — failureRate: (λ, /h), diagnosticCoverage: and latentDiagnosticCoverage: (0.0–1.0) — plus ftaRef: (the reconciling FaultTreeEvent; an unresolved or non-event target is W927). A row with a failureRate: whose ftaRef: resolves supplies that event's λ/DC/DCl to the SPFM/LFM/PMHF roll-up (§ Quantitative HW safety metrics, W033), unless the event declares its own failureRate: (the event wins; never double-counted).

Each row is synthesised at parse time into a virtual FMEAEntry element (FM-* ID) for cross-reference and validation purposes.

Row integrity (GH #132). A row's id: is its identity — a row with no string id: (or a row that is not a mapping) cannot be synthesised; it is dropped, and the validator MUST report error E923 on the FMEASheet, naming the row's 1-based position in entries: and its failureMode:/name: when present. When fmeaSeverity (or its alias severity), occurrence and detection are all present the entry's RPN is S × O × D; an explicit rpn: is then ignored, and if it differs the validator MUST report warning W928 naming the row, the explicit value and the computed value. An explicit rpn: on a row missing any factor is the entry's RPN as authored.

8.18.5 Tier 4 — Attack path analysis (ISO/SAE 21434 §15.7)

The security mirror of FTA. An AttackTree substantiates a ThreatScenario and rolls up attack feasibility with a weakest-link rule, deriving a threat's feasibility from its attack paths rather than asserting it as a flat rating.

Element type ID pattern Description
AttackTree AT-* Root of an attack tree; references the ThreatScenario it substantiates via threatRef: (required, must resolve to a ThreatScenario — E917).
AttackTreeGate ATG-* Combinator; gateType: is AND (a sequential path — all sub-steps required) or OR (alternative paths); inputs: lists child gate/step IDs.
AttackStep ATS-* Leaf step; attackFeasibility: is high, medium, low, or very_low.

Nesting rule (W036): like FTA, AttackTreeGate and AttackStep elements must be placed in a subdirectory named after the AttackTree file so their qualified names are prefixed by the tree's qualified name:

Security/Attacks/AT-TORQUE-001.md     →  Security::Attacks::AT-TORQUE-001
Security/Attacks/AT-TORQUE-001/
  ATG-TORQUE-001.md                   →  Security::Attacks::AT-TORQUE-001::ATG-TORQUE-001
  ATS-TORQUE-001.md                   →  Security::Attacks::AT-TORQUE-001::ATS-TORQUE-001

Feasibility roll-up (weakest-link): rank very_low=0, low=1, medium=2, high=3. An AttackStep is its attackFeasibility rank; an AND gate (sequential path) is the MIN of its children; an OR gate (alternatives) is the MAX of its children; the AttackTree's feasibility is the value of its root node — the one gate/step of the tree that no other gate lists in its inputs:, independent of file order (no unique root → not computable, no W035) — mapped back to a label. When the computed feasibility differs from the linked ThreatScenario.attackFeasibility, the validator emits W035 (computed vs declared).

8.18.9 Safety mechanisms (SafetyMechanism)

SafetyMechanism (SM-*, GH #236) makes "which mechanism covers which failure mode, with what diagnostic coverage and reaction time" answerable from the model. status is draft, review, approved or retired. covers: (string or list) names the FMEAEntry, FaultTreeEvent, Requirement, SafetyGoal or HazardousEvent elements covered (unresolved or of another type → E897). diagnosticCoverage and latentDiagnosticCoverage are numbers in 0..1 (the generic E846 reports a value outside, or text); reactionTime is a duration (10 ms; not a duration → E896); safeState is text; allocatedTo: names where the mechanism runs. A non-draft, non-retired mechanism whose reactionTime exceeds the ftti (or ftti quantity) of a goal it covers — directly, or through a covered requirement's derivedFromSafetyGoal found along its derivedFrom chain — is W894 (FMEA rows, fault-tree events and hazardous events are not followed to a goal). syscribe mechanisms lists them; --uncovered lists the FMEAEntry rows no mechanism covers. The hardware FMEDA data feeding SPFM/LFM is carried on FMEA rows (§8.18.4), not on SafetyMechanism.

8.18.8 Structured timing quantities (quantities:)

Any element may carry quantities: (GH #237), a list of {kind, value, unit}: kind is ftti, latency, wcet or reaction; value a positive number; unit s, ms, us or ns. A SafetyGoal's ftti: string is read as an ftti quantity. Malformed entries are E895. validate compares budgets after normalising units (W893): for a Requirement or SafetyGoal with a quantity of a kind, the sum of that kind over the requirements derived from it (derivedFrom:, or derivedFromSafetyGoal: for a goal) — treated as a serial chain — must not exceed it, and for a goal with an ftti each derived requirement's latency plus reaction must fit. A child that states no quantity of the kind contributes the serial sum of its own children (the budget rolls up through intermediate requirements); draft and retired children do not count and a child listed twice counts once. A parent whose children state nothing of the kind raises nothing. A budget holder with a variant-gated child is not summed in plain validate (alternatives cannot be added up) but is recomputed in each variant by validate --config / --all-configs. One value per kind per element (a second is E895); draft budget holders are not checked.

8.18.7 Dependent failure analysis (DependentFailureAnalysis)

DependentFailureAnalysis (DFA-*, GH #235) records why the elements of an ASIL decomposition or a mixed-criticality co-hosting cannot fail from a common cause (ISO 26262-9 clause 7). status is draft, review, approved or retired. analyses: (string or list, at least two) names the elements argued independent. sharedResources: is a list of mappings {resource, kind, initiators, couplingFactor, mitigation}: resource the shared item (a power rail, clock, memory, bus master), kind one of power, clock, memory, bus, software, other, initiators the triggering events, couplingFactor a number in 0..1 and mitigation how the coupling is controlled. An approved analysis excuses W034 for every pair of its analyses. Missing fields, an unknown status, fewer than two entries → E890; an unresolved entry → E891; a malformed shared resource → E892; an approved analysis with an unmitigated shared resource → W890.

8.18.6 GSN safety-argument layer (Argument, AssumptionOfUse)

The Goal Structuring Notation (GSN) argument layer makes the safety argument a first-class, validated part of the model. It builds on the Tier-2 hazard/goal layer (HazardousEvent, SafetyGoal).

Element type ID pattern Description
Argument ARG-* A GSN node. argumentType: is claim, strategy, solution, context, justification, assumption or undeveloped (absent → claim; invalid → E854). supports: (string or list) names the SafetyGoal or parent Argument argued for; evidence: (string or list) names the supporting Requirement / TestCase / sub-Argument / AssumptionOfUse. Each supports/evidence ref resolves via the resolver (unresolved → E855). Missing id/name/status → E852; an id not matching ARG-* → E853. A claim/strategy Argument with empty supports and empty evidence is an orphan node → W040.
AssumptionOfUse AOU-* A safety-related application condition (SRAC). appliesTo: (string or list) names the SafetyGoal / Argument / Requirement it constrains (each resolves; unresolved → E858). Missing id/name/status → E856; an id not matching AOU-* → E857.

The safety-case [<SG-id>] [--json] view renders, for each SafetyGoal, the supporting Argument tree (recursing into sub-Arguments), each Argument's evidence (with the TestCase's ingested verdict when a results sidecar is present), and the AssumptionOfUse nodes. It also folds in the implicit chain SafetyGoal ← Requirement (derivedFromSafetyGoal) ← TestCase (verifies) so it is useful on models that have goals + requirements + tests but no explicit Argument nodes.

8.18.10 Asset (ISO/SAE 21434 §15.3)

An Asset is an item of value that an attacker may want to compromise — the starting point of a TARA. Damage scenarios name the assets they harm with assets:.

Field YAML type Required Description
type literal Asset Required Discriminator.
id string Required Stable id matching ^ASSET(-[A-Z0-9]{2,12})+-[0-9]{3,8}$ (at least one category segment). Malformed → E862.
name string Required Free-prose label.
status string Required Lifecycle status (e.g. draft, approved). Missing id/name/status → E861.
cybersecurityProperties list of strings optional The properties at stake: confidentiality, integrity, availability, authenticity. Any other value → E863.
assetOwner string optional Qualified name or id of the architecture element that owns the asset. Informational (not resolved).
relatedSafetyGoal string optional SG-* id or qualified name of a related SafetyGoal (safety↔security co-engineering). Informational (not resolved).

DamageScenario.assets: (string or list) references Assets by id or qualified name; an entry that does not resolve, or resolves to a non-Asset, is E864. An Asset that no DamageScenario.assets: references raises warning W810 (an identified asset with no damage scenario yet).

---
type: Asset
id: ASSET-KERNEL-001
name: "Thread Control Block (TCB)"
status: approved
cybersecurityProperties:
  - integrity
  - availability
assetOwner: Software::KernelCore
relatedSafetyGoal: SG-KERNEL-002
---
The TCB holds the thread's register save area, stack pointer, priority, and
IPC wait-list link. Corruption enables priority inversion or control-flow hijack.
---
type: DamageScenario
id: DS-KERNEL-001
name: "Corrupted scheduler state causes priority inversion"
status: approved
damageSeverity: severe
impactCategories: [safety, operational]
assets: [ASSET-KERNEL-001]
hazardRef: SG-KERNEL-002
---

8.19 Release Baselines (Baseline)

A Baseline (ADR-SYS-BASELINE-001) is a named, dated, approved, frozen snapshot of a scope of the model — the artifact an assessor points at. It is generated, not hand-authored: syscribe baseline create --tag <tag> resolves the in-scope elements, hashes each element's full canonical content (BLAKE3), aggregates a seal, records the current HEAD commit, and writes both the Baseline element (default model/Baselines/<id>.md) and a JSON manifest (default <git-root>/baselines/<id>.manifest.json). Both locations are configurable with a [baselines] table (element_dir, manifest_dir) in .syscribe.toml. The author then edits only status: (and supersedes:) as the baseline is approved and released.

ID pattern: ^BL(-[A-Z0-9]{2,12})+$ — like FEAT-*, a baseline id need not end in a number (BL-2026-07, BL-QUARTERLY-001). The id is the model identity and is distinct from the version-control tag in gitTag:.

Field YAML type Written by create Description
type literal Baseline yes Discriminator.
id string yes BL-* id (--id, else derived from --tag).
name string yes Free-prose label (--name, default the tag).
status enum yes (draft) draft · approved · released · superseded. Grades drift severity (below).
date string yes Baseline date (--date, default the HEAD commit date).
approver string when --approver is given Accountable identity that approved the baseline.
gitTag string yes (--tag) Intended source-control tag. create does not create the tag; baseline verify checks that it resolves to gitCommit when it exists.
gitCommit string yes The commit the baseline was sealed at. create expects a clean working tree (--allow-dirty overrides).
frozenScope map yes (--frozen-scope) Scope selector; all keys optional and ANDed: package (a package subtree; absent → whole model), types, status, tags (lists), config (freeze a projected product-line variant, §9.10), closureFrom (list of seeds whose transitive trace closure is the scope). Baseline elements are never in scope.
seal map yes Generated: aggregateHash (blake3:<hex>), elementCount, manifest (manifest path). Never edit by hand.
supersedes string or list no The Baseline(s) this one replaces (id or qualified name). Unresolved → E522. (On an ADR, supersedes: names the ADRs it replaces: E320/E321/W313/W314.)

Validation. Whenever a Baseline exists, validate recomputes the in-scope aggregate and compares it with the seal. Drift is graded by status:: released → error E520, approved → warning W520, draft → silent, superseded → not checked. A seal that disagrees with its manifest is E521. The full-content hash reuses the suspect-link hashing (§12.10.6) over the whole element rather than its normative projection. baseline verify, baseline diff, baseline list and baseline show operate on existing baselines.

---
type: Baseline
id: BL-2026-07
name: July 2026 safety release
status: draft
date: 2026-07-31
approver: J. Roe
gitTag: REL-2026-07
gitCommit: 3f9c2a1d8e7b6c5a4f3e2d1c0b9a8f7e6d5c4b3a
frozenScope:
  package: VehicleSystem
  status: [approved]
seal:
  aggregateHash: blake3:5d41402abc4b2a76b9719d911017c592ae2f4c7f1e0c9b8a7d6e5f4a3b2c1d0e
  elementCount: 42
  manifest: baselines/BL-2026-07.manifest.json
---
July 2026 safety release — release baseline.

8.20 Architecture Rules (ArchitectureRule)

An ArchitectureRule (AR-*, type: ArchitectureRule; REQ-TRS-ARCH-001..-005, GH #369) states a structural property of the architecture that validate then enforces, so a claim such as "the hardware trip path is independent of the firmware" is checked rather than prose.

---
type: ArchitectureRule
id: AR-GTI-001
name: The hardware trip path does not depend on software
kind: forbid-dependency        # forbid-dependency | independence | allow-only | layering | max-depth
from: {tag: protection-hw}     # selector of the components the rule starts from
to:   {domain: software}       # selector of the components it is about
via: [connections, typedBy, allocations]   # optional; default all three
severity: error                # error (default) | warning
---
Rationale: the claim this protects and where it comes from.
Field Type Required Notes
id string yes ^AR(-[A-Z0-9]{2,12})*-[0-9]{3,8}$; additional prefixes through [ids.prefixes]
name string yes The rule in a sentence
kind string yes forbid-dependency, independence, allow-only, layering or max-depth
layers list layering The layers, top first
maxDepth integer max-depth The longest allowed dependency chain
waivers list no {element, rationale, expires} entries
from, to mapping yes Selectors: package (qualified-name prefix), tag, domain (system, hardware, software), stereotype (a stereotype selector, §8.15), layer; keys are ANDed, tag and domain take a list (any of)
via list no The edge sources that count
severity string no error (default) or warning
status string no draft is not evaluated

Dependency. The components are the PartDef and Part elements. X depends on Y through connections when a connections: entry from: a.p, to: b.q joins them — the definition of b depends on that of a, the consumer on the producer, and a one-segment endpoint is a boundary port of the owner; through typedBy when X has a usage typed by Y (a composite depends on what it contains); through allocations when X is allocated to Y (allocatedTo:, allocatedFrom: or an Allocation element — software depends on the hardware it runs on).

forbid-dependency. No component chosen by from may depend, directly or transitively, on one chosen by to. Each violation is E660 (W660 under severity: warning) on the rule, naming it and the shortest path.

independence. The transitive closures of the two chosen sets must be disjoint. Each component in both is E661 (W661), with a path from each set to it; wiring one set to a member of the other makes that member shared.

allow-only. A component chosen by from may depend directly only on components chosen by from or to; any other direct dependency is E660, naming both.

layering. A PartDef/Part names its layer with layer:. A rule with layers: [ui, domain, platform] (top first, no from/to) forbids a direct dependency on a layer above: a layer may use itself and any layer below, skipping included. A component with no layer, or one that is not listed, is unconstrained. Violations are E660, naming both components and layers.

max-depth. With maxDepth: N and an optional from (default every component), the longest chain of dependency edges from a chosen component may not exceed N; longer is E663 (W663), with the chain. A chain inside a longer reported one is not reported again, and a component is visited once per chain, so a cycle does not hang.

Waivers. waivers: lists {element, rationale, expires} (expires is YYYY-MM-DD). While current, a waiver suppresses the findings whose subject is element: the dependent component for forbid-dependency, allow-only, layering and max-depth, the shared component for independence. On or after expires it stops suppressing and is W664 (expired); a waiver that suppresses nothing is also W664. A missing element, rationale or a bad date is E662.

kind: layering
layers: [ui, domain, platform]
waivers:
  - {element: App::Legacy, rationale: "calls the platform directly until ADR-APP-004 lands", expires: "2027-03-31"}

performs: and operation calls are not yet dependency sources.

A malformed rule is E662 and is not evaluated; a selector that chooses no component is W662.

8.21 Resource Budgets

REQ-TRS-BUD-001..-003 (GH #368). A software PartDef/Part declares the static memory it uses, and the hardware part it is allocated to declares what it offers; validate checks the one against the other and syscribe budgets reports it.

Field On Description
ram, flash, nvm software part Static memory used, a size
stack software part Stack, a size; counts toward RAM
ramCapacity, flashCapacity, nvmCapacity hardware part Memory offered, a size
period, deadline software part Durations (10 ms, 120 us, 0.5 s)
wcet software part Read as a duration when it is one; an opaque claim (O(1)) is left alone
priority software part Integer
cpuMargin hardware part Percentage of the schedulability bound that may be used, 0–100
scheduling hardware part edf (default, bound 1) or rate-monotonic (Liu–Layland bound)
margin hardware part Percentage of capacity that may be used, 0–100 (number or "80%"); default [budgets] margin in .syscribe.toml, else 100

A size is a number of bytes or text with a unit: B, kB/MB/GB (powers of 1000) or KiB/MiB/GiB (powers of 1024). KB is ambiguous and a malformed size or margin is E691.

For a hardware part with a capacity, the contributors are the components allocated to it (allocatedTo:, allocatedFrom: or an Allocation) and, transitively, every component in their features:, each counted once. Use above capacity × margin / 100 is E690, naming the contributors, largest first. A part with no capacity for a resource is not checked for it, and the fields on any other element type are W047.

CPU. A software part with a period and a wcet duration is a task of utilisation wcet / period. A hardware part that declares cpuMargin and/or scheduling is checked: the tasks allocated to it and contained in what is allocated, each once, sum to a utilisation that must not exceed the bound (1 for edf, n (2^(1/n) − 1) for rate-monotonic) × cpuMargin / 100, else E692. A part whose wcet exceeds its deadline is E693. period and deadline must be positive durations and priority an integer (E691). syscribe budgets --json carries a cpu array with the tasks.

---
type: PartDef
name: Stm32G474
domain: hardware
ramCapacity: 128 kB
flashCapacity: 512 kB
margin: 80
---
The microcontroller. Software allocated to it may use at most 80 % of each memory.

8.22 Deployment Structure

REQ-TRS-DEPL-001..-003 (GH #370). A PartDef/Part is a deployment node when it declares a deploymentKind; software components are deployed on nodes by the usual allocations (allocatedTo:, allocatedFrom: or an Allocation element).

Field On Description
deploymentKind node soc, core, partition, container, process or thread, outermost first; any other value is E730
replicated software part true accepts a deliberate deployment on several nodes

A node contains other nodes through features: (typedBy), and only of a later kind: a process inside a partition is valid, a partition inside a process, or a node inside one of its own kind, is E731. A component allocated to two or more nodes is E732 unless it is replicated: true; allocation to a part with no deploymentKind is not counted. A component allocated to a node whose declared asilLevel (QM < A < B < C < D) or silLevel is lower than its own is E733; a node or component that declares none is not checked. A part with no deploymentKind is outside all of this.

---
type: PartDef
name: Stm32G474
domain: hardware
deploymentKind: soc
asilLevel: B
---

8.23 Software Design Levels

REQ-TRS-UNIT-001..-002 (GH #371). A software PartDef/Part may declare level: component or level: unit, the two levels ASPICE SWE.2 and SWE.3 distinguish. Any other value is E740; on another element type the field is W047. Nothing is checked about a part with no level or component.

A non-draft unit is checked for its traces: E741 no implementedBy:; W740 an implementedBy: local path naming no symbol (path#symbol); E742 it satisfies no requirement; E743 no testLevel: L1 TestCase verifies the unit or a requirement it satisfies; E744 no level: component part contains it through features:.

---
type: PartDef
name: FrameParser
status: draft
domain: software
level: unit
implementedBy: "repo:src/frame.rs#parse_frame"
satisfies: [REQ-LINK-004]
---

9 Variability and Variation Points

9.1 Variation Definitions

A variation definition is an abstract definition whose direct sub-usages are all variants. Declare with isVariation: true:

---
type: PartDef
name: EngineChoices
typedBy: VehicleSystem::Engine
isVariation: true
isAbstract: true
---
Variation point for engine selection in the vehicle family.

9.2 Variant Usages

Each variant is declared with isVariant: true and must be a direct usage member of the variation:

---
type: Part
name: fourCylEngine
typedBy: Powertrain::FourCylinderEngine
isVariant: true
# This file lives inside EngineChoices/ directory
---
Four-cylinder engine variant.

9.3 Directory Layout for Variations

Variants live as files within a subdirectory named after the variation:

model/VehicleSystem/
  Engine.md                  # type: PartDef — base engine definition
  EngineChoices/
    _index.md                # type: PartDef, isVariation: true, typedBy: Engine
    FourCylinderEngine.md    # type: Part, isVariant: true
    SixCylinderEngine.md     # type: Part, isVariant: true
    ElectricMotor.md         # type: Part, isVariant: true

9.4 Variant References

A non-variant usage can be declared to act as a variant of a separately declared variation by using the variantOf: field:

# On a Part or PartDef file that already exists elsewhere
variantOf: VehicleSystem::EngineChoices
Field YAML type Default Description
variantOf string absent Qualified name of the variation this element acts as a variant of

9.5 Product Line Engineering Overview

Sections 9.1–9.4 cover structural variation — SysML's built-in mechanism for declaring alternative implementations of a definition within a single model. This section (§9.5–§9.12) covers Product Line Engineering (PLE) — a separate, higher-level concern that governs which combination of features constitutes a valid product, and propagates that selection through every engineering layer.

Relationship between structural variation and PLE

Structural variation (isVariation / isVariant) and PLE features are complementary:

Mechanism Answers Expressed as
isVariation / isVariant What alternative implementations exist? Model structure
FeatureDef What can a customer choose? Problem-space feature model
Configuration Which choices define this product? Feature selection + parameter bindings
appliesWhen: Which elements belong to this product? Condition on any model element

A variant part (isVariant: true) is typically selected by a FeatureDef — the two mechanisms work together. The appliesWhen: field on a variant ties it to the feature that activates it.

Design principles

  1. Problem space / solution space separation. The feature model (FeatureDef hierarchy under SystemFeatures/) is the problem space — it describes what varies from a customer perspective. Architecture, requirements, code, and tests are the solution space. The connection between them is appliesWhen: (solution → feature) and satisfies: (component configuration → system feature).
  2. Single binding direction. The solution space points at features; features do not point at the solution space. This keeps the feature model implementation-agnostic.
  3. appliesWhen: operands are always cross-references. A condition is a single FeatureDef reference, an AND-list of references, or a boolean and/or/not expression whose every operand is a FeatureDef qualified name or FEAT-* id (§9.10). Operands are therefore typed, resolvable, and graph-traversable; an unresolved operand or malformed expression is E209. No other expression language (arithmetic, parameters, comparisons) is permitted in appliesWhen:.
  4. Parametrization lives on features, not on configurations. A FeatureDef declares what parameters it carries; a Configuration assigns values to them. This separates schema from data.
  5. Two-level feature models reduce complexity. A system-level feature model captures product-visible variability; component-level feature models capture implementation variability. A system Configuration selects system features; a component Configuration implements them.

9.6 FeatureDef: Feature Model Element

A FeatureDef represents one node in a feature model tree. It may be a leaf feature (a concrete selectable characteristic) or a composite group node (organising child features). Feature models are built entirely from FeatureDef elements arranged in a directory hierarchy under a dedicated package (e.g., SystemFeatures/ or <Package>/Features/).

Frontmatter schema

Field YAML type Required Default Description
type literal FeatureDef Required — Discriminator.
name string optional filename stem Label of the feature (a FeatureDef is name-identified; the qualified-name segment is the file/directory stem, and a differing name: is W058). A title: here is error E025.
id string Required — Stable short-name matching ^FEAT(-[A-Z0-9]{2,12})+$ (e.g. FEAT-ABS, FEAT-QUADROTOR, FEAT-ABS-001). Every feature must declare one — a missing id is E201. Unlike other stable ids it need not end in a number. The feature may be referenced by this id or its qualified name in appliesWhen: and Configuration features: keys (§9.8). A malformed FEAT id is E006; a duplicate is E101. The id and name are independent axes.
groupKind enum optional optional How this feature's children are grouped (see table below).
mandatory bool optional false Membership of this feature relative to its parent, orthogonal to groupKind: true means the feature is selected whenever its parent is (parent ⇒ feature), or always selected when top-level. A node may be both mandatory: true and groupKind: alternative — a mandatory XOR group (every product selects exactly one child). The legacy groupKind: mandatory is a shorthand for mandatory: true on a feature that sets no mandatory: field.
cardinality string optional see groupKind For or and alternative groups: how many children may be selected simultaneously, as a multiplicity string (§6).
parentFeature string optional derived from directory Qualified name of the parent FeatureDef. Normally inferred from the directory hierarchy; set explicitly only when the file cannot be placed at the canonical path.
requires list of strings optional absent Qualified names of other FeatureDef elements that must also be selected when this feature is selected (cross-tree implication).
excludes list of strings optional absent Qualified names of FeatureDef elements that must NOT be selected when this feature is selected (mutual exclusion).
defaultValue bool or string optional false for optional; see groupKind Default selection state when not explicitly bound in a Configuration.
parameters list of parameter maps optional absent Typed parameters carried by this feature (see §9.7).
contributesTo string optional absent Qualified name of a system-level FeatureDef that this component-level feature partially realises. For fine-grained two-level traceability (optional; the primary binding is satisfies: on Configuration).
tags list of strings optional absent Free labels.

groupKind values

groupKind describes how a feature's children are grouped. Membership of the feature itself (mandatory vs optional relative to its parent) is the separate mandatory: field above — the two are orthogonal (ADR-FM-003).

Value Meaning Default cardinality Default for children
optional Children may be selected independently of each other "0..1" independent
alternative Exactly one child must be selected (XOR group) "1" "0..1" each
or One or more children may be selected "1..*" "0..1" each
mandatory Legacy shorthand for mandatory: true membership (a leaf that is always selected when its parent is); prefer the mandatory: field, especially on group nodes "1" n/a

The cardinality: field overrides the default. For example, an or group requiring at least two children: cardinality: "2..*".

Feature model directory convention

SystemFeatures/
  _index.md                 # Package for the feature model root
  Propulsion/
    _index.md               # FeatureDef, mandatory: true + groupKind: alternative — every product picks exactly one
    QuadRotor.md            # FeatureDef, optional
    HexRotor.md             # FeatureDef, optional
  Safety/
    _index.md               # FeatureDef, groupKind: or — one or more safety features
    DualIMU.md              # FeatureDef, optional
    ASIL_D_FC.md            # FeatureDef, optional; requires: [SystemFeatures::Safety::DualIMU]
  Communication/
    StandardLink.md         # FeatureDef, mandatory
    LongRangeLink.md        # FeatureDef, optional

Complete FeatureDef example

---
type: FeatureDef
id: FEAT-HEXROTOR-001
name: HexRotorPropulsion
groupKind: optional
requires: []
excludes:
  - SystemFeatures::Propulsion::QuadRotorPropulsion
parameters:
  - name: numMotors
    type: ScalarValues::Integer
    value: 6
    isFixed: true
  - name: motorKV
    type: ScalarValues::Real
    unit: SI::rpm_per_volt
    range: "900..1200"
    default: 1000.0
    isRequired: true
  - name: propDiameterIn
    type: ScalarValues::Real
    range: "10..15"
    default: 12.0
tags:
  - propulsion
  - heavy-lift
---

Selects the six-rotor hexacopter propulsion configuration, providing higher
thrust margin and payload capacity at the cost of increased mass and power.

9.6a Single-file authoring: type: FeatureModel (REQ-TRS-FM-005)

A FeatureDef-per-file layout is heavyweight for a large or fast-iterating feature model. As an additive, opt-in alternative, a whole feature model may be authored in one file whose type: is FeatureModel, carrying the tree as a flat featureTree: list. The walker explodes each entry into an ordinary FeatureDef RawElement before validation, so every downstream consumer sees the same kind of element regardless of authoring form; the two forms may be mixed within one model.

Field YAML type Required Default Description
type literal FeatureModel Required — Discriminator.
featureTree list of feature maps optional absent Flat list of FeatureDef-shaped entries.
crossTreeConstraints list of constraint maps optional absent Flat { feature, requires, excludes } list, kept separate from the structural tree (§9.6a.2).
parameterConstraints list of constraint maps optional absent §9.7's cross-feature numeric constraints — accepted here directly, in addition to a Package/LibraryPackage/Namespace _index.md.

9.6a.1 featureTree: — the flat, dot-named structural list

Each featureTree: entry is shaped exactly like a FeatureDef's own frontmatter (id, mandatory, groupKind, cardinality, parentFeature, contributesTo, requires, excludes, parameters, buildExports), plus an optional doc: string — except that its name: is a dot-separated path relative to the sheet, not a single basic name. Splitting that path on . and joining with :: under the sheet's own qualified name reproduces exactly the qname a directory-per-feature layout would produce; the synthesized FeatureDef's own name: is then rewritten to just the last path segment (the same leaf label a real file would carry). There is no children:/nesting in the YAML shape and no recursion — the list is flat — and an ancestor path prefix needs no entry of its own (it simply implies no parent membership/grouping is enforced, exactly as an ancestor directory that is not itself a FeatureDef does today).

# SystemFeatures/_index.md
---
type: FeatureModel
name: SystemFeatures
featureTree:
  - name: Propulsion
    mandatory: true
    groupKind: alternative
  - name: Propulsion.QuadRotor       # → SystemFeatures::Propulsion::QuadRotor
    groupKind: optional
  - name: Propulsion.HexRotor        # → SystemFeatures::Propulsion::HexRotor
    groupKind: optional
---

id: is optional here (REQ-TRS-FM-006), unlike a plain per-file FeatureDef (E201 still requires one there). Omitted, it is derived from the entry's own dotted name:: segments uppercased, non-[A-Z0-9] characters stripped, joined with -, prefixed FEAT- — Propulsion.QuadRotor → FEAT-PROPULSION-QUADROTOR. An explicit id: always wins over derivation. The result is assigned exactly as if hand-typed: a derived id that fails the FEAT-* pattern (a segment under 2 or over 12 characters) is the ordinary E006, and a collision with any other id in the model is the ordinary E101 — no new codes. A derived id changes if the entry is later renamed, the same cost a rename already imposes on every qname-based reference to the feature; give a feature an explicit id: where that matters.

9.6a.2 crossTreeConstraints: — requires/excludes as one section

Inline requires:/excludes: on a featureTree: entry still works. crossTreeConstraints: is an additive alternative that keeps every requires/excludes edge in one reviewable list:

crossTreeConstraints:
  - feature: Propulsion.HexRotor
    excludes: [Propulsion.QuadRotor]

feature/requires/excludes values all resolve the same way: containing :: → an already-absolute qname; starting with FEAT → a stable id; otherwise → a dot-separated path relative to the sheet, resolved exactly like a featureTree: entry's name:. Each entry's resolved requires/excludes merge into the matching synthesized FeatureDef's own field.

9.6a.3 Validation

Code Condition
E231 A featureTree: entry is not a mapping, has no name:, or its dotted path has an empty segment (leading, trailing, or doubled .) — the entry is dropped
E232 Two featureTree: entries (within one sheet or across sheets in the same model) resolve to the same qualified name
E233 A crossTreeConstraints: entry is malformed (not a mapping, no feature:, or a reference with an empty path segment), or its feature: does not resolve to a FeatureDef synthesized from that same sheet's own featureTree:
W048 featureTree:/crossTreeConstraints: is declared on an element whose type: is not FeatureModel, or parameterConstraints: on anything other than Package/LibraryPackage/Namespace/FeatureModel — inert, ignored

Configuration needs no single-file equivalent: it is already exactly one file (§9.8), and addresses features purely by qname/id with no dependency on how the FeatureDef was authored.


9.7 Feature Parametrization

Feature parameters allow a FeatureDef to carry typed, named values that must be bound to concrete values in any Configuration that selects the feature. This is the mechanism for quantitative variability — not just whether a feature is selected, but how it is configured.

Parameter schema

Each entry in a FeatureDef's parameters: list is a map with the following fields:

Field YAML type Required Default Description
name string Required — Parameter name, unique within this FeatureDef.
type string Required — Qualified name of the parameter type. Must resolve to a scalar type (ScalarValues::Real, ScalarValues::Integer, ScalarValues::String, ScalarValues::Boolean, ScalarValues::Natural) or an EnumerationDef.
unit string optional absent Qualified name of the SI unit. Applies only when type is a quantity type.
range string optional absent Inclusive bounds as a multiplicity-style string "min..max" (the explicit-inclusive form "min..=max" is also accepted). Both bounds are literal numbers. Applies to numeric types only.
enumValues list of strings optional absent Explicit set of valid string values when type is ScalarValues::String and finer constraint than a full EnumerationDef is desired.
default scalar optional absent Default value used when the parameter is not explicitly bound in a Configuration. If absent and isRequired: true, every selecting Configuration must bind it.
isFixed bool optional false If true, the parameter value is fixed by the feature definition itself (the default or value field) and may not be overridden in a Configuration. Validation error if a Configuration attempts to bind it.
isRequired bool optional false If true and isFixed: false, every Configuration that selects this feature must explicitly bind this parameter. Validation warning W017 if absent.
value scalar optional absent The fixed value, used when isFixed: true. Equivalent to default + isFixed: true.
derivedFrom string optional absent An opaque expression string (evaluated in the context of other parameters of the same feature) whose value is computed rather than bound. When present, the parameter is automatically isFixed: true and may not be bound in a Configuration.
bindTo string optional absent For component-level FeatureDef only: qualified path SystemFeatures::<Feature>::<paramName> of a system-level parameter to which this parameter is propagated. When a system Configuration binds the system parameter, the resolved component parameter inherits the same value. The component parameter may still specify its own range: as a narrowing constraint; validation error E202 if the propagated value falls outside the narrower range.
isArray bool optional false If true, the parameter value is a YAML list of the declared type. Used for multi-valued parameters (e.g., a list of rotor positions in degrees).
bindingTime enum optional absent When the parameter's value is resolved, from the product-line-engineering binding-time triad, ordered earliest→latest: compile (build / code generation) · load (deployment / installation / startup) · runtime (dynamically, while the system runs). See Binding time below. An unrecognised value is E230.

Parameter kinds summary

Kind Fields Behaviour
Configurable isFixed: false, no derivedFrom Must be bound (if isRequired: true) or uses default in each selecting Configuration.
Fixed isFixed: true or value: present Value is part of the feature definition. Configuration must not bind it.
Derived derivedFrom: "<expr>" Value is computed from sibling parameters. Implicitly fixed.
Propagated bindTo: "<system param path>" Component parameter inherits value from its bound system-level parameter.
Array isArray: true Value is a YAML list. range: applies per element.

Parameter binding in Configuration

In a Configuration file, parameter values are declared under parameterBindings: as a flat map. The key is the canonical dotted parameter reference: <FeatureDef qualified name>.<parameter name> — :: separates the feature's namespace/qname segments, and a single . separates the parameter (member) from its owning feature, so the feature/parameter boundary is unambiguous. This same dotted form is used everywhere a feature parameter is referenced: parameterBindings: keys, parameterConstraints expressions, and bindTo: targets. A key written in the legacy all-:: member form is malformed (E222).

parameterBindings:
  SystemFeatures::Propulsion::HexRotorPropulsion.motorKV: 1050.0
  SystemFeatures::Propulsion::HexRotorPropulsion.propDiameterIn: 13.0
  SystemFeatures::Payload::SurveyCamera.resolutionMpx: 20.0
  SystemFeatures::Communication::LongRangeLink.frequencyBandGHz: 5.8
  SystemFeatures::Mission::Endurance.minDurationMin: 30.0

Rules: - A Configuration must not bind a parameter of a feature that is not selected (features: <feature>: false or omitted when default is false). Validation error E203. - A Configuration must not bind a parameter whose isFixed: true. Validation error E204. - A bound value must satisfy the parameter's range: constraint. Validation error E205. - A bound value must be a member of enumValues: (if declared). Validation error E206.

Binding time

A parameter may declare an optional bindingTime: stating when in the product lifecycle its value is resolved — the standard product-line-engineering triad, ordered earliest → latest:

bindingTime: Resolved at
compile build / code generation
load deployment / installation / startup
runtime dynamically, while the system executes

bindingTime: is orthogonal to isFixed:/value: — those express a value fixed in the model (no variability), whereas bindingTime: records the lifecycle moment a value that genuinely varies is bound. It is optional; an absent bindingTime: is unspecified and opts the parameter out of every binding-time check below (existing models are unaffected). Rules:

  • An unrecognised bindingTime: value (not compile/load/runtime) is E230 (validate).
  • A parameter computed from a source it depends on — a sibling named in its derivedFrom: expression, or its bindTo: target — must not bind earlier than that source: if both declare a bindingTime: and the dependent's is earlier, that is impossible and reported E229 (feature-check).
  • A Configuration that binds a bindingTime: runtime parameter is warned W027 (validate): a runtime value is supplied by the running system, not at configuration time. For the same reason, the W017 "required parameter unbound" warning is suppressed for a runtime parameter left unbound by a Configuration.
parameters:
  - name: motorKV
    type: ScalarValues::Real
    range: "900..=1200"
    bindingTime: load        # chosen at deployment, not baked in at build

Cross-feature parameter constraints

Parameter constraints that involve parameters from more than one feature are declared on the feature model package _index.md or on the Configurations/ package _index.md (the configuration authority). Each constraint applies to every Configuration that selects all the referenced features.

# SystemFeatures/_index.md or Configurations/_index.md
parameterConstraints:
  - id: PC-ENERGY-001
    expression: >
      SystemFeatures::Power::Battery.capacityWh
      >= (SystemFeatures::Mission::Endurance.minDurationMin / 60.0)
         * SystemFeatures::Power::AveragePower.averagePowerW
    severity: error
    rationale: "Battery capacity must cover the minimum mission duration at nominal power draw."
  - id: PC-THRUST-001
    expression: >
      SystemFeatures::Propulsion::HexRotorPropulsion.totalThrustN
      >= 2.0 * (SystemFeatures::Mass::MaxPayload.maxMassKg + 2.5) * 9.81
    severity: error
    appliesWhen:
      - SystemFeatures::Propulsion::HexRotorPropulsion
    rationale: "Hex-rotor thrust-to-weight ratio must be at least 2.0 at maximum payload."

parameterConstraints: entries:

Field YAML type Required Description
id string Required Stable constraint ID, e.g. PC-ENERGY-001. Unique within the feature model.
expression string Required A comparison LHS <op> RHS (== != >= <= > <) over arithmetic (+ - * /, parentheses) of numeric literals and dotted parameter references. Evaluated by feature-check against every applicable Configuration; a violation is E221 (or W025 when severity: warning).
severity enum optional error (default → E221) or warning (→ W025).
appliesWhen string or list optional Boolean predicate over FeatureDef qualified names (and / or / not / parentheses; a bare name or list = AND). The constraint is evaluated only against configurations whose selections satisfy it. Absent = always checked.
rationale string optional Human-readable explanation.

Two-level parameter propagation

A component-level FeatureDef parameter may declare bindTo: to receive its value from a system-level parameter:

# UAV/Propulsion/Features/SixMotorLayout.md
---
type: FeatureDef
id: FEAT-SIXMOTOR
name: SixMotorLayout
contributesTo: SystemFeatures::Propulsion::HexRotorPropulsion
parameters:
  - name: motorKV
    type: ScalarValues::Real
    unit: SI::rpm_per_volt
    range: "900..1200"
    bindTo: SystemFeatures::Propulsion::HexRotorPropulsion.motorKV
    # When the product-line configuration sets motorKV: 1050.0,
    # this component parameter is automatically resolved to 1050.0.
    # The range here is a narrowing constraint — validation error if the
    # inherited value falls outside 900..1200.
  - name: internalCoolingRequired
    type: ScalarValues::Boolean
    derivedFrom: "motorKV > 1100"
    # Derived: automatically true when motorKV exceeds 1100 rpm/V.
    # Not bindable in any Configuration.
  - name: rotorPositionsDeg
    type: ScalarValues::Real
    isArray: true
    value: [0.0, 60.0, 120.0, 180.0, 240.0, 300.0]
    isFixed: true
    # Fixed array parameter: six equally-spaced rotor positions.
---

Six-motor layout for hex-rotor propulsion. Motor KV is propagated from
the system-level feature parameter.

Derived parameter evaluation

derivedFrom: is an opaque expression evaluated against the parameters of the same FeatureDef. The expression language is intentionally unspecified here — tools may support OCL, a simple arithmetic subset, or a custom language. The key semantics are:

  • All referenced names must be parameters of the same FeatureDef.
  • The expression is evaluated after all configurable parameters are bound, before cross-feature constraints are checked.
  • Circular derivation (parameter A derived from B, B derived from A) is a validation error E207.

9.8 Configuration: Feature Selection

A Configuration is a complete, named feature selection with parameter bindings. It is the unit of product definition — selecting a Configuration uniquely determines a projected model slice.

ID scheme

Configuration IDs follow the pattern ^CONF(-[A-Z0-9]{2,12})+$. Like FEAT-* and BL-* ids they need not end in a number, since a variant is often best named descriptively; the numeric form remains valid (a numeric last segment is still subject to the E023 digit cap): - System-level: CONF-UAV-STD-001, CONF-UAV-HVY-001, CONF-GTI-US-M3 - Component-level: CONF-PROP-HEX-001, CONF-AVI-DUAL-001

Frontmatter schema

Field YAML type Required Default Description
type literal Configuration Required — Discriminator.
id string Required — Stable ID matching CONF-* pattern.
name string Required — One-line human-readable label — free prose (spaces/punctuation allowed).
status enum Required — draft, review, approved, released, retired.
featureModel string Required — Qualified name of the feature model package this configuration selects from.
features map optional — Feature selection: FeatureDef qualified name: true/false. A feature that does not appear takes its default (§9.10); an absent or empty features: map selects nothing and raises W016. A configuration with a derivedFrom: base declares only the entries it overrides (see Configuration inheritance below).
parameterBindings map optional absent Parameter value assignments keyed by the canonical dotted reference: <FeatureDef qname>.<paramName>: <value>. Resolves transitively through subConfigurations: (§14.7) at any depth, using the parameter's ordinary, already-mounted qname.
subConfigurations string or list optional absent One or more other Configuration elements this one consolidates — reachable locally or via a [repos]-mounted peer repo (§14.7, ADR-SYS-HPLE-001).
satisfies list of strings optional absent For component Configuration only: qualified names of system-level FeatureDef elements this configuration realises. This is the sole top-down binding in the two-level model.
derivedFrom string optional absent Qualified name or CONF-* id of one base Configuration in the same model that this one extends. Inherits the base's effective features: and parameterBindings:; entries in this file override the base (see Configuration inheritance below).
baselineRef string optional absent Opaque external baseline reference (e.g., a Git tag, CM baseline ID).
tags list of strings optional absent Free labels.

Status lifecycle

Value Meaning
draft Being assembled; feature selection incomplete.
review Under formal review; parameter constraints checked.
approved Approved for development; all constraints pass.
released Shipped product; frozen.
retired End-of-life; no longer produced.

System-level configuration example

---
type: Configuration
id: CONF-UAV-HVY-001
name: "Heavy-lift survey UAV — hex-rotor, dual-IMU, long-range link"
status: approved
featureModel: SystemFeatures
features:
  SystemFeatures::Propulsion::QuadRotorPropulsion: false
  SystemFeatures::Propulsion::HexRotorPropulsion: true
  SystemFeatures::Safety::DualIMU: true
  SystemFeatures::Safety::ASIL_D_FC: false
  SystemFeatures::Communication::StandardLink: true
  SystemFeatures::Communication::LongRangeLink: true
  SystemFeatures::Payload::SurveyCamera: true
  SystemFeatures::Payload::MultispectralCamera: false
  SystemFeatures::Mission::Endurance: true
parameterBindings:
  SystemFeatures::Propulsion::HexRotorPropulsion.motorKV: 1050.0
  SystemFeatures::Propulsion::HexRotorPropulsion.propDiameterIn: 13.0
  SystemFeatures::Payload::SurveyCamera.resolutionMpx: 20.0
  SystemFeatures::Communication::LongRangeLink.frequencyBandGHz: 5.8
  SystemFeatures::Mission::Endurance.minDurationMin: 25.0
baselineRef: "UAV-HEAVY-LIFT-v2.1-RC3"
---

Heavy-lift variant targeting survey missions with payloads up to 1.5 kg.

Configuration inheritance

A configuration may extend a base configuration, inheriting its feature selections and parameter bindings and overriding only the differing entries:

---
type: Configuration
id: CONF-UAV-HVY-LR-001
name: "Heavy-lift UAV — extended-range variant"
status: draft
featureModel: SystemFeatures
derivedFrom: CONF-UAV-HVY-001   # inherits all from the heavy-lift base (id or qname)
features:
  # Only overrides differ from the base
  SystemFeatures::Communication::LongRangeLink: true  # already true — no change
parameterBindings:
  # Override a single parameter; all others inherited from base
  SystemFeatures::Communication::LongRangeLink.frequencyBandGHz: 2.4
---

Long-range variant using 2.4 GHz band for better penetration in forested areas.

Inheritance semantics (GH #137, REQ-TRS-VAR-007):

  1. One local base. derivedFrom: names exactly one Configuration of the same model, by qualified name or CONF-* id. A peer product line is consolidated with subConfigurations: (§14.7), never inherited.
  2. Effective selection. The child's effective features: is the base's effective selection overlaid by the child's own features: entries — a child entry wins over the inherited entry for the same feature (a FEAT-* id key and the FeatureDef's qualified name name the same feature). Inheritance chains compose (a grandchild inherits through its parent).
  3. Effective bindings. The child keeps every parameterBindings: entry it declares; an inherited entry is kept unless the child binds the same <feature>.<param> itself, or the child's own features: sets that feature to false (a binding for a feature the child switched off is not carried over).
  4. Nothing else is inherited — status, featureModel, subConfigurations:, buildOverrides:, baselineRef, tags stay per-file.
  5. Everything reads the effective selection — projection (--config), matrix, configure, validate --config/--all-configs, feature-check (incl. --deep configuration validity), build-config, the per-Configuration rules (E203–E206, E219/E220, W015–W017; E216–E218 are never emitted — feature-check --deep reports group violations as E225) and subConfigurations: consolidation (a consolidated Configuration's inherited bindings close its parameters for E523/W513). show marks inherited entries. The authored file is never rewritten. The effective selection is a property of the whole model (it depends on the base chain), so a tool that re-parses a single Configuration on its own — such as the language server's rename-safety check — re-derives inheritance over the full element set rather than reading the re-parsed file in isolation.
  6. Checks. A Configuration's derivedFrom: is not a requirement derivation: it never raises E105/E017 and never enters derivedChildren. Instead: E234 the base does not resolve, E235 it resolves to a non-Configuration, E236 the configuration is on an inheritance cycle, E237 more than one base is named (a child with any of these inherits nothing), and E215 the base is not approved/released.

9.9 Two-Level Feature Models

Motivation

A single flat feature model for a large product line becomes unmanageable. The two-level model partitions variability:

  • Level 1 — System Feature Model (SystemFeatures/): customer-visible, implementation-agnostic. Describes what the product does differently between variants.
  • Level 2 — Component Feature Models (<Package>/Features/): implementation-level variability within one subsystem. Describes how a component realises a system feature.

Structural convention

model/
  SystemFeatures/          # Level 1 — one per product line
    _index.md              # type: Package
    Propulsion/
      QuadRotorPropulsion.md   # type: FeatureDef
      HexRotorPropulsion.md    # type: FeatureDef
    Safety/
      DualIMU.md
      ASIL_D_FC.md

  Configurations/          # System-level configurations (select Level 1 features)
    _index.md              # type: Package
                           # parameterConstraints: [...] declared here
    StandardSurveyUAV.md   # type: Configuration, featureModel: SystemFeatures
    HeavyLiftUAV.md        # type: Configuration, featureModel: SystemFeatures

  UAV/
    Propulsion/
      Features/            # Level 2 — one per contributing package
        _index.md          # type: Package
        SixMotorLayout.md  # type: FeatureDef, contributesTo: SystemFeatures::Propulsion::HexRotorPropulsion
        FourMotorLayout.md # type: FeatureDef, contributesTo: SystemFeatures::Propulsion::QuadRotorPropulsion
        ESCProtocol/
          DSHOT.md         # type: FeatureDef, groupKind: alternative (internal — no contributesTo)
          PWM.md           # type: FeatureDef, groupKind: alternative (internal)
      Configurations/      # Component-level configurations
        _index.md          # type: Package
        HexConfig.md       # type: Configuration, satisfies: [SystemFeatures::Propulsion::HexRotorPropulsion]
        QuadConfig.md      # type: Configuration, satisfies: [SystemFeatures::Propulsion::QuadRotorPropulsion]

Binding rules

  1. A system Configuration selects system features. It does not reference component configurations directly.
  2. A component Configuration declares satisfies: listing the system-level FeatureDef elements it realises.
  3. Resolution: when a system Configuration is evaluated, the resolver scans all component Configuration files across all packages, collects those whose satisfies: intersects the set of selected system features, and includes them in the resolved product description.
  4. Uniqueness constraint: for any given system feature that is selected, exactly one component Configuration per package may satisfy it. If zero component configurations satisfy a selected system feature, validation error E210. If more than one in the same package satisfy it, validation error E211.
  5. Internal component features (no contributesTo:) are invisible to the system level. They are resolved entirely within the component's own feature model.
  6. Cross-package feature dependencies are forbidden at Level 2. A component FeatureDef must not reference FeatureDef elements from another component's feature model. All cross-component constraints live in the Level 1 feature model via requires: and excludes:.

Parameter propagation in the two-level model

When a system Configuration binds a system parameter, and a component FeatureDef parameter declares bindTo: pointing to that system parameter:

  1. The resolver reads the system parameter value from the system Configuration.parameterBindings.
  2. It writes that value into the component parameter binding as if it were explicitly declared in the component Configuration.
  3. The component's range: constraint is checked against the propagated value (error E202 on violation).
  4. The component's derivedFrom: expressions are evaluated after propagation.

The component Configuration does not need to redeclare propagated parameter values — they flow automatically.

Component-level configuration example

---
type: Configuration
id: CONF-PROP-HEX-001
name: "Six-rotor hex propulsion — DSHOT ESC protocol"
status: approved
featureModel: UAV::Propulsion::Features
features:
  UAV::Propulsion::Features::SixMotorLayout: true
  UAV::Propulsion::Features::FourMotorLayout: false
  UAV::Propulsion::Features::ESCProtocol::DSHOT: true
  UAV::Propulsion::Features::ESCProtocol::PWM: false
# motorKV is NOT bound here — it is propagated from SystemFeatures::Propulsion::HexRotorPropulsion.motorKV
# via SixMotorLayout.motorKV.bindTo
parameterBindings: {}
satisfies:
  - SystemFeatures::Propulsion::HexRotorPropulsion
---

Hex-rotor configuration using DSHOT ESC protocol. Motor KV is propagated
from the system-level configuration.

9.10 appliesWhen: Conditional Model Elements

appliesWhen: is a universal field (§3.13) that conditions any model element on one or more feature selections. It is the mechanism by which the full model is projected onto a specific Configuration.

Value forms

appliesWhen: accepts any of the following; all reduce to a boolean expression over FeatureDef qualified names:

  1. absent — the element is unconditional (always active).
  2. bare qualified name — appliesWhen: Features::Wdt. Active iff that feature is selected.
  3. list of qualified names — appliesWhen: [Features::A, Features::B]. AND semantics (all selected). (Legacy form; kept for back-compatibility.)
  4. boolean expression — and, or, not, and parentheses over qualified names:
appliesWhen: "Features::CortexM and Features::Mpu"
appliesWhen: "Features::Async or Features::Bus"
appliesWhen: "not Features::Smp"
appliesWhen: "(Features::A or Features::B) and not Features::A"

Operator precedence is not > and > or; parentheses override. Every operand qualified name must resolve to a FeatureDef — an unresolved/non-FeatureDef operand, or a malformed expression, is error E209.

Evaluation

Given a resolved Configuration, an element's appliesWhen: expression is evaluated against the configuration's features: selections (a feature absent from features: takes its FeatureDef default). The element is included iff the expression evaluates true (or is absent).

project(configuration, allElements) →
  for each element in allElements:
    include  if  element.appliesWhen is absent
             or  eval(element.appliesWhen, configuration.features) == true
    else exclude

The projected model is the input to all downstream tools (diagram generators, requirement coverage reports, integration test selection).

The --config lens (CLI). validate, list, export, diagram and the single-element query commands trace, why, who-verifies, refs and links accept --config <C> (a stored Configuration id/qname or an ad-hoc feature set) and run over project(C, allElements) only: an inactive element is absent from their output and from every reverse index they read (verifiedBy, satisfiedBy, inbound references). For a single-element query whose start element exists in the full model but is inactive in C, the command does not answer — it exits 1 with a message naming the element and the configuration (the key is matched by exact qualified name or stable id against the full model first, so a fuzzy match can never substitute a different, active element). (REQ-TRS-PROJ-001.)

Transitive package appliesWhen (the effective condition)

A Package (a namespace _index.md) may itself declare appliesWhen:. The condition then applies transitively to every element in that package's subtree — directly contained and through nested sub-packages — so a whole cohesive variant subtree (requirements + architecture + tests) can be enabled or disabled with a single declaration.

Every element therefore has an effective condition, evaluated by every projection/coverage/escaping consumer:

  • the element's own appliesWhen: if it declares one; otherwise
  • the appliesWhen: of the nearest ancestor package that declares one; otherwise
  • always active.

Effective conditions are never combined. To keep them unambiguous, at most one node on any root-to-leaf path may declare appliesWhen:. Violations are error E228:

  • a nested declaration — an element or sub-package _index.md that declares appliesWhen: while an ancestor package already does (even an identical restatement);
  • a forbidden target — because the feature model and configurations are identified by element type (there is no dedicated package type), appliesWhen: may not be declared on a FeatureDef or a Configuration, a package declaring appliesWhen: may not contain any FeatureDef/Configuration in its subtree, and the model-root package may not declare it (gating the whole model).

A package that declares appliesWhen: but contains no projectable element gates nothing — warning W026.

Because a gated subtree moves together, references between elements inside it never escape; only a reference from outside into the gated subtree escapes when the condition is off (E226/W019). A package with appliesWhen: is the all-or-nothing tool; condition individual elements (where the enclosing package does not declare appliesWhen:) when you need a strict subset.

TestCase variant membership — no runsIn field

appliesWhen: is also the sole mechanism that ties a TestCase to the variants it runs in: a TestCase runs in a Configuration iff its appliesWhen: is satisfied by that configuration's features:. A TestCase with no appliesWhen: is configuration-agnostic and runs in every Configuration. There is deliberately no separate runsIn field — membership is computed, not stored. (Where two configurations would otherwise have identical selections — e.g. an emulator vs a physical rig — model the distinguishing axis as a feature, such as an ExecEnv alternative group.)

This drives two downstream capabilities (both dormant unless the variability dimension is active — §9.10.1):

  • the syscribe matrix command — a Requirement × Configuration coverage grid whose columns are the model's Configuration elements and whose cells are N/A (requirement not active in that variant), covered, or gap;
  • the per-Configuration coverage rule W015 (§9.11) — a requirement active in a configuration with no covering in-configuration TestCase.

9.10.1 Opt-in (the variability dimension is dormant by default)

The variability dimension is active only when both (a) at least one FeatureDef exists, and (b) at least one element links to it (a Configuration, or any element/TestCase carrying appliesWhen:). When it is dormant:

  • validation output is identical to a model with no PLE elements (no W015, no projection);
  • an appliesWhen: with no feature model present is inert — except that an unresolved reference is still E209;
  • syscribe matrix prints a "no feature model present" notice and falls back to a flat requirement↔testcase view.

This lets a project adopt variability incrementally without disturbing existing validation.

Usage across layers

# Requirement — only applies to hex-rotor products
type: Requirement
id: REQ-UAV-THRUST-002
name: Hex-rotor thrust margin
status: draft
appliesWhen: SystemFeatures::Propulsion::HexRotorPropulsion
---
# Architecture variant
type: PartDef
name: HexRotorConfig
isVariant: true
variantOf: UAV::Propulsion::PropulsionSystem
appliesWhen: SystemFeatures::Propulsion::HexRotorPropulsion
---
# Test case — only run for hex-rotor products (body with the gherkin block omitted)
type: TestCase
id: TC-UAV-THRUST-002
name: Hex-rotor thrust margin test
status: draft
testLevel: L4
appliesWhen: SystemFeatures::Propulsion::HexRotorPropulsion
verifies:
  - REQ-UAV-THRUST-002
---
# Allocation — only applies when both hex-rotor AND dual-IMU are selected
type: Allocation
name: HexMixingAlgorithmAlloc
appliesWhen:
  - SystemFeatures::Propulsion::HexRotorPropulsion
  - SystemFeatures::Safety::DualIMU

appliesWhen: at Level 2

Within a component package, appliesWhen: references component-level FeatureDef elements. This is resolved against the component Configuration (not the system configuration directly).

# UAV/Propulsion/SixMotorMixing.md
type: ActionDef
name: SixMotorMixing
appliesWhen: UAV::Propulsion::Features::SixMotorLayout
sourceFile: "src/flight/mixing_hex.rs"

9.11 PLE Validation Rules

Parse-time errors (per-element)

Code Condition
E200 Configuration.id does not match CONF-* pattern
E201 A PLE element is missing a required field: a Configuration missing id, name, status, or featureModel; or a FeatureDef missing its mandatory FEAT-* id. (A FeatureDef carries name as its label; a stray title: on it is E025.)
E202 A propagated parameter value (via bindTo:) falls outside the component parameter's range:
E203 Configuration.parameterBindings binds a parameter for a feature not selected in this configuration
E204 Configuration.parameterBindings binds a parameter declared isFixed: true
E205 Bound parameter value violates range: constraint
E206 Bound parameter value is not a member of enumValues:
E207 Circular derivedFrom: dependency between parameters of the same FeatureDef

Model-time errors (cross-element)

Code Condition
E208 Not emitted — a duplicate Configuration.id is reported by the generic duplicate-id error E101
E209 appliesWhen: is malformed, or an operand references a qualified name that does not resolve to a FeatureDef
E210 Not yet implemented — a selected system feature has no component Configuration in some package satisfying it
E211 Not yet implemented — a selected system feature is satisfied by more than one component Configuration in the same package
E212 A FeatureDef.requires: or excludes: entry does not resolve to another FeatureDef (on a featureTree: entry, a dotted path relative to the sheet resolves as in crossTreeConstraints:)
E213 A cross-feature parameterConstraints expression references a parameter path that does not resolve
E214 Not yet implemented — a FeatureDef.contributesTo: does not resolve to a FeatureDef in a system feature model
E215 A Configuration.derivedFrom: base configuration is not in approved or released status
E234 A Configuration.derivedFrom: base does not resolve to any element of the model (§9.8; the base must be local)
E235 A Configuration.derivedFrom: base resolves to an element that is not a Configuration
E236 A Configuration is on a derivedFrom: inheritance cycle (each member is reported; none inherits)
E237 A Configuration.derivedFrom: names more than one base
E238 (§9) a Configuration names an abstract feature in features:, selected or not: an abstract feature is not a choice, it follows from the concrete features
E239 A Configuration's features: key does not resolve to a FeatureDef
E216 Not yet implemented (enforced by feature-check --deep as E225) — a Configuration.features map omits a mandatory feature (or sets it to false)
E217 Not yet implemented (enforced by feature-check --deep as E225) — a Configuration.features map selects both sides of an alternative group
E218 Not yet implemented (enforced by feature-check --deep as E225) — a Configuration.features map violates an or group's cardinality: constraint
E219 A FeatureDef.requires: constraint is violated by the selected features in a Configuration
E220 A FeatureDef.excludes: constraint is violated by the selected features in a Configuration
E221 A cross-feature parameterConstraints expression evaluates to false for a Configuration whose appliesWhen: predicate holds (default severity). Emitted by feature-check.
E228 Invalid appliesWhen: placement (§9.10): a declaration nested under a package that already declares appliesWhen:; or on a FeatureDef/Configuration, a package whose subtree contains one, or the model-root package.
E229 A parameter's bindingTime: is earlier than that of a derivedFrom/bindTo source it depends on — an impossible ordering (§9.7). Checked only when both endpoints declare a bindingTime:. Emitted by feature-check.
E230 A parameter declares a bindingTime: value that is not one of compile/load/runtime (§9.7). Emitted by validate.
E231 (§9.6a) A featureTree: entry is not a mapping, has no name:, or its dot-separated path has an empty segment — the entry is dropped.
E232 (§9.6a) Two featureTree: entries resolve to the same qualified name.
E233 (§9.6a) A crossTreeConstraints: entry is malformed, or its feature: does not resolve to a FeatureDef synthesized from that same sheet's own featureTree:.
E240 A {{Feature.param}} placeholder is used but the model has no FeatureDef or no Configuration
E241 A placeholder names no FeatureDef, the feature declares no such parameter, or a feature-shaped {{…}} does not parse as {{<feature>.<param>}}
E242 A placeholder's element is active in a Configuration that does not select the parameter's feature (gate it with appliesWhen)
E243 A placeholder has no binding, fixed value or default in a configuration where the element is active, and the element is approved/implemented/verified
E247 A placeholder in a typed field (failureRate, diagnosticCoverage, latentDiagnosticCoverage, silLevel, asilLevel, calLevel) resolves, in a configuration where the element is active, to a value the field does not accept
E222 A parameterBindings key does not resolve to a declared FeatureDef parameter (bad path — including the legacy all-:: member form, which must be the dotted Features::Feature.param — unknown feature, or undeclared parameter). Emitted by validate and feature-check.
E223 (feature-check --deep) The feature model is void — no valid configuration exists (reported once, with a conflict-set explanation).
E224 (feature-check --deep) A dead feature — selectable in no valid configuration.
E225 (feature-check --deep) An authored Configuration is not a valid model of the feature model (mandatory/group/cardinality/parent-selection violation; requires/excludes violations stay E219/E220).
E226 (validate --config) An active element's structural reference (typedBy/supertype/subsets/redefines/allocation) targets an element inactive in the projected variant.
E227 (feature-check --deep) A structural reference is provably violable: some valid configuration activates the source without its target (includes a witness).

Warnings

Code Condition
W011 A FeatureDef with groupKind: optional is selected in zero Configuration files (possibly dead feature)
W012 A FeatureDef with groupKind: optional and no mandatory: true is selected in every Configuration (should be mandatory: true)
W013 Not yet implemented — a component FeatureDef has no contributesTo: and no excludes: referencing any system feature — internal feature not visible from system level (informational)
W014 A parameterConstraint has appliesWhen: that references a feature not in any Configuration
W015 A requirement is active in a Configuration (its appliesWhen: holds for that configuration's features:) but no non-draft TestCase that runs in that Configuration (§9.10) verifies it. Reported once per requirement, listing every such configuration; in the --config lens only the selected configuration is judged. Emitted only when the variability dimension is active (§9.10.1); draft requirements/tests are suppressed; gate with --deny W015. For a parent requirement the message adds whether the live leaves below it are all verified in those configurations (covered through its children only — it still needs a direct test), not all verified, or none is active there.
W016 A Configuration parsed zero feature selections while a FeatureDef exists in the model — e.g. it used an unrecognized selections: key instead of the features: map (§9.8). Surfaces the otherwise-silent failure that yields an all-N/A coverage matrix.
W017 A selected feature declares a parameter isRequired: true (not fixed, no default:) that the Configuration does not bind; suppressed for a bindingTime: runtime parameter. (W010 is test-result ingestion — §11.12.)
W018 (feature-check --deep) A false-optional feature — declared optional but forced selected whenever its parent is.
W019 (validate --config) An active element's traceability reference (verifies/satisfies/derivedFrom/breakdownAdr) targets an element inactive in the projected variant.
W020 (feature-check --deep) A traceability reference is provably violable across some valid configuration.
W021 (feature-check --deep) A dead element — its appliesWhen is unsatisfiable under the feature model.
W022 (feature-check --deep) A requirement active in some configuration but covered in none.
W024 An orphan FeatureDef — referenced by no element's appliesWhen: and selected true by no Configuration, so it gates nothing and ships in nothing. Emitted by feature-check only; gate with --deny W024.
W025 A parameterConstraints violation (as E221) where the constraint declares severity: warning. Emitted by feature-check; gate with --deny W025.
W026 A Package declares appliesWhen: but its subtree contains no projectable element (it gates nothing). Gate with --deny W026.
W245 As E243, for a draft or review element
W246 A placeholder references a bindingTime: runtime parameter (no value at projection time)
W248 A feature parameter used in a state-machine expression has no value: or default:, so validate checks those expressions in the base model with a stand-in of the parameter's type; one finding per parameter, on its FeatureDef, naming the machines (not for a runtime or derived/bindTo: parameter, nor in the --config lens, where W017 covers it). validate --config checks them at the bound value
W027 A Configuration binds a parameter whose bindingTime: runtime — resolved by the running system, not at configuration time (§9.7). Gate with --deny W027.
W238 Retired (0.50.0) — no longer emitted: an abstract feature's value in a configuration is derived from the concrete selection, so an appliesWhen: that names one is meaningful
W239 (§9) an abstract feature has no children: it groups nothing
W240 (§9) more features than the deep-analysis limit (5000): abstract features' derived values are not computed
W028 The same extRef external reference is declared by two or more elements (§3). One finding per duplicated value. Gate with --deny W028.
W048 (§9.6a) featureTree:/crossTreeConstraints: is declared on an element whose type: is not FeatureModel, or parameterConstraints: on anything other than Package/LibraryPackage/Namespace/FeatureModel — inert, ignored.

Implementation note. Rules split across commands/modes: - validate (per-element, always on) enforces the single-level parameter binding rules E203–E206, the unresolved-path error E222, W017, the binding-time rules E230 (invalid value) and W027 (Configuration binds a runtime parameter; W017 is suppressed for runtime), and the Configuration-inheritance rules E215/E234–E237 (§9.8). Every per-Configuration rule reads the effective (inherited + own) selection and bindings. - feature-check (explicit, holistic) enforces the feature-model-wide rules: E212 (requires/excludes resolution), E219/E220 (requires/excludes satisfaction), E207 (circular derivedFrom:), E202 (bindTo: propagation range), E229 (binding-time ordering across derivedFrom/bindTo), E213 (unresolved or ::-member parameterConstraints path), E221/W025 (parameterConstraints expression evaluation), W011/W012/W014, and W024 (orphan feature). It also re-runs the parameter-binding rules (E203–E206/E222/W017) so a product line checked holistically gets the same range/binding enforcement as validate. - feature-check --deep (SAT-backed, over a propositional encoding of the Boolean layer; deterministic; engine is batsat (pure-Rust CDCL) — see ADR-FM-002) adds whole-configuration-space analysis: E223 void model, E224 dead feature, E225 invalid configuration (full group/cardinality semantics), W018 false-optional, plus a reported set of core features and a conflict-set explanation for each unsatisfiability.

Not yet implemented (never emitted): group-cardinality findings on feature-check without --deep (E216/E217/E218 — --deep enforces the group semantics via E225), two-level satisfies completeness (E210/E211), contributesTo: resolution (E214), the internal-feature notice (W013), and general numeric/parameter (SMT) reasoning beyond the comparison/arithmetic grammar E221 evaluates. E208 is never emitted: a duplicate Configuration id is E101. Configuration inheritance (derivedFrom:, §9.8) with E215/E234–E237 is implemented.


9.12 PLE Graph Edges

After all files are loaded, the model graph (§11.8) is extended with the following PLE-specific edge types:

Edge kind Source Target Description
ConditionalOn Any element with appliesWhen: FeatureDef Element is conditionally included when this feature is selected
Selects Configuration FeatureDef (selected=true) Configuration selects this feature
Deselects Configuration FeatureDef (selected=false) Configuration explicitly deselects this feature
BindsParameter Configuration FeatureDef Configuration binds at least one parameter of this feature
Satisfies Component Configuration System FeatureDef Component configuration realises this system feature
ContributesTo Component FeatureDef System FeatureDef Fine-grained realization link between feature levels
FeatureRequires FeatureDef FeatureDef Cross-tree implication (requires:)
FeatureExcludes FeatureDef FeatureDef Mutual exclusion (excludes:)
ParameterBoundTo Component param System param Parameter propagation via bindTo:
DerivedFrom Configuration Base Configuration Configuration inheritance

These edges enable the following standard queries without full model traversal:

  • All elements for a configuration: follow Selects edges from the Configuration, then collect all elements with matching ConditionalOn targets.
  • Requirement coverage within a configuration: filter the Verifies edges (§11.8) to those whose source and target are both included in the projected configuration.
  • Feature impact analysis: given a FeatureDef, find all elements reachable via incoming ConditionalOn edges — the complete set of model elements that change when this feature is toggled.
  • Dead feature detection: FeatureDef nodes with no incoming Selects edges from any Configuration with status: approved or higher.

10 Worked Examples

Illustrative fragments. The examples in this section show the shape of each construct. They are fragments, not self-contained models: they reference elements, types and library members (for example REQ-VEH-TRANSPORT-001, the vehicle part definitions, ModelingMetadata::StatusInfo) that are defined elsewhere or not at all, so pasting one into a model as written will raise unresolved-reference findings. Complete models are under examples/.

10.1 Vehicle System Structure (BDD + IBD)

Directory layout:

model/
  _index.md
  VehicleSystem/
    _index.md
    Vehicle.md
    Engine.md
    Transmission.md
    FuelTank.md
    vehicle_b.md
  Interfaces/
    _index.md
    PowerPortDef.md
    FuelPortDef.md
    PowerInterface.md
  Powertrain/
    _index.md
    FourCylinderEngine.md
    AutomaticTransmission.md

model/_index.md:

---
type: Namespace
imports:
  - ISQ::*
  - SI::*
  - ScalarValues::*
---
Root namespace for the Simple Vehicle Model.

model/VehicleSystem/_index.md:

---
type: Package
name: VehicleSystem
---
Contains all top-level definitions for the vehicle system.

model/VehicleSystem/Vehicle.md:

---
type: PartDef
name: Vehicle
isAbstract: true
features:
  - name: mass
    typedBy: ISQ::MassValue
    unit: SI::kg
    multiplicity: "1"
  - name: powerOut
    type: Port
    typedBy: Interfaces::PowerPortDef
    direction: out
  - name: fuelIn
    type: Port
    typedBy: Interfaces::FuelPortDef
    direction: in
  - name: engine
    type: Part
    typedBy: VehicleSystem::Engine
    multiplicity: "1"
  - name: fuelTank
    type: Part
    typedBy: VehicleSystem::FuelTank
    multiplicity: "1"
connections:
  - from: fuelTank.fuelOut
    to: engine.fuelIn
    typedBy: Interfaces::FuelInterface
performs:
  - VehicleBehavior::ProvidePower
exhibitsStates:
  - VehicleBehavior::VehicleStates
---
Abstract definition of a vehicle. Concrete vehicle types specialize this.

model/VehicleSystem/Engine.md:

---
type: PartDef
name: Engine
isAbstract: true
features:
  - name: displacement
    typedBy: ISQ::VolumeValue
    unit: SI::L
  - name: maxPower
    typedBy: ISQ::PowerValue
    unit: SI::W
  - name: powerOut
    type: Port
    typedBy: Interfaces::PowerPortDef
    direction: out
  - name: fuelIn
    type: Port
    typedBy: Interfaces::FuelPortDef
    direction: in
---
Abstract engine definition. Concrete engine types must specialize this.

model/VehicleSystem/vehicle_b.md:

---
type: Part
name: vehicle_b
typedBy: VehicleSystem::Vehicle
features:
  - name: engine
    type: Part
    typedBy: Powertrain::FourCylinderEngine
    redefines: [VehicleSystem::Vehicle::engine]
  - name: fuelTank
    type: Part
    typedBy: VehicleSystem::FuelTank
    features:
      - name: capacity
        typedBy: ISQ::VolumeValue
        value: "60.0"
        unit: SI::L
metadata:
  - type: ModelingMetadata::StatusInfo
    status: approved
    approver: "Chief Engineer"
---
Concrete vehicle configuration B with a four-cylinder gasoline engine
and a 60 L fuel tank.

10.2 Requirements Package

Directory layout:

model/
  Requirements/
    _index.md
    MassRequirementDef.md
    SpeedRequirementDef.md
    vehicleSpec/
      _index.md
      vehicleMassReq.md
      vehicleSpeedReq.md
  Stakeholders/
    _index.md
    VehicleEngineer.md
    SafetyEngineer.md
    Customer.md
  Concerns/
    _index.md
    VehicleWeight.md
    MaxSpeed.md

model/Requirements/MassRequirementDef.md:

---
type: RequirementDef
name: MassRequirementDef
subject: VehicleSystem::Vehicle
parameters:
  - name: maxMass
    typedBy: ISQ::MassValue
    direction: in
requires:
  - expression: "subject.mass <= maxMass"
stakeholders:
  - Stakeholders::VehicleEngineer
concerns:
  - Concerns::VehicleWeight
---
Requirement that the vehicle total mass shall not exceed the specified maximum.

model/Requirements/vehicleSpec/vehicleMassReq.md:

---
type: Requirement
name: vehicleMassReq
typedBy: Requirements::MassRequirementDef
features:
  - name: maxMass
    typedBy: ISQ::MassValue
    value: "1500.0"
    unit: SI::kg
---
The vehicle mass shall not exceed 1500 kg in the unladen configuration.

model/VehicleSystem/Vehicle.md (updated to include satisfaction):

satisfies:
  - Requirements::vehicleSpec::vehicleMassReq

10.3 Action Tree (Use Case)

model/VehicleBehavior/TransportPassenger.md:

---
type: UseCaseDef
name: TransportPassenger
subject: VehicleSystem::vehicle_b
refines:
  - REQ-VEH-TRANSPORT-001     # the stakeholder need this use case refines (W307)
actors:
  - Stakeholders::Driver
objectives:
  - "Safely transport a passenger to the requested destination"
subActions:
  - name: startVehicle
    typedBy: VehicleBehavior::StartEngine
    kind: Action
  - name: navigate
    typedBy: VehicleBehavior::DriveToDestination
    kind: Action
  - name: stopVehicle
    typedBy: VehicleBehavior::StopEngine
    kind: Action
successionConnections:
  - after: startVehicle
    before: navigate
  - after: navigate
    before: stopVehicle
---
Use case for transporting a passenger from origin to destination.

10.4 Allocation (Logical to Physical)

model/Allocations/_index.md:

---
type: Package
name: Allocations
---

model/Allocations/FunctionalAllocation.md:

---
type: Allocation
name: FunctionalAllocation
typedBy: Allocations::FunctionalToPhysical
allocatedFrom: VehicleBehavior::ProvidePower
allocatedTo: PhysicalArch::EngineControlUnit
metadata:
  - type: ModelingMetadata::Rationale
    text: "ECU is the only processing unit with real-time control authority over engine"
---
Allocates the ProvidePower behavioral function to the Engine Control Unit.

10.5 Metadata Annotation Example

model/Metadata/StatusInfo.md:

---
type: MetadataDef
name: StatusInfo
features:
  - name: status
    typedBy: ScalarValues::String
  - name: approver
    typedBy: ScalarValues::String
  - name: approvalDate
    typedBy: ScalarValues::String
annotates:
  - PartDef
  - Part
  - RequirementDef
  - Requirement
  - ActionDef
  - Action
---
Metadata for tracking the approval status of model elements.

Applied to a PartDef:

---
type: PartDef
name: FuelSystem
metadata:
  - type: Metadata::StatusInfo
    status: approved
    approver: "Jane Smith"
    approvalDate: "2026-03-15"
---
Fuel system component.

11 Parser and Tool Contract

This section defines the normative behavior required of a conformant Markdown-SysML parser.

11.1 File Discovery

  1. The parser is initialized with a model root directory path.
  2. The parser recursively walks all subdirectories of the model root.
  3. All files with the .md extension are candidate element files.
  4. There is no built-in ignore list: .git/, node_modules/, dot-files and every other .md file under the root are loaded (keep the model root free of unrelated Markdown). A .sysmlignore file at the model root may list ignore patterns, one per line (blank lines and lines starting with # are skipped). Only three pattern forms are supported — this is not full gitignore syntax: an exact file name or root-relative path (README.md, Notes/todo.md); a dir/ prefix, matching everything under that root-relative directory; and a * glob matched against the file name only (*.draft.md). Patterns apply to .md files only; there is no negation (!) and no **.
  5. A file named _index.md represents the package for its containing directory.
  6. A file whose frontmatter is absent is error E001, and one whose YAML cannot be parsed is error E002; neither is fatal to the load — the file contributes no typed element and the remaining files are still processed.

11.2 Frontmatter Extraction

  1. The YAML frontmatter is delimited by two --- lines at the beginning of the file. The opening --- must be the first line (no preceding whitespace).
  2. The content between the two --- delimiters is parsed as YAML 1.2.
  3. The remaining content after the closing --- is the Markdown documentation body.
  4. A file with no frontmatter is error E001; frontmatter that is not valid YAML is error E002. The file is not loaded as a typed element.
  5. A file whose frontmatter has no type: field is warning W008 (it is ignored by most commands); an unrecognised type: value is error E005.

11.3 Qualified Name Derivation Algorithm

Given a file at path <root>/<seg1>/<seg2>/.../<segN>/<filename>.md:

  1. Collect the path segments from the model root to the file, exclusive of the root itself.
  2. Each intermediate directory contributes its directory name as the package segment. A name: in that directory's _index.md is a display label only and is ignored here.
  3. The file itself contributes its filename stem (filename without .md). The file's name: field is a display label only and is ignored here.
  4. Exception: _index.md contributes no name segment; it represents the directory's package.
  5. The qualified name is seg1::seg2::...::segN::elementName.
  6. The root namespace itself has no name segment.

Example derivation:

  • File: model/VehicleSystem/Powertrain/Engine.md
  • Model root: model/
  • Intermediate directories: VehicleSystem → Powertrain
  • Filename stem: Engine
  • Qualified name: VehicleSystem::Powertrain::Engine

If model/VehicleSystem/_index.md contains name: VS, the qualified name is still VehicleSystem::Powertrain::Engine; VS is only the label shown for the package. To change a qualified name, rename the directory or file (e.g. with syscribe move).

The model-root package name: is not part of qualified names. Qualified names are derived relative to the model root, and the root package (the root _index.md) contributes no segment (step 5 above). A cross-reference therefore starts at the first sub-namespace — e.g. VehicleSystem::Powertrain::Engine, never <RootName>::VehicleSystem::Powertrain::Engine even when the root _index.md declares name: <RootName>. Writing the root package name as the leading segment is a common authoring mistake (humans and LLMs alike); when an unresolved cross-reference begins with the root package name followed by :: and the stripped remainder resolves, the tool appends a diagnostic hint naming the corrected reference (REQ-TRS-XREF-006). The hint is advisory only — it adds explanatory text to the existing unresolved-reference finding (E102/E103/E311/E316/E502/E503/E506/E632 and the structural supertype/typedBy/subsets/redefines and satisfies resolution errors E110–E114); it never changes resolution and never rewrites the model. The hint does not fire when the root package has no name:, nor when stripping the prefix still does not resolve.

11.4 Implicit Supertype Rules

When no supertype: is given on a definition element, the parser implicitly applies the following base library supertypes. These defaults match the SysML standard library implicit specialization rules (§7.6.8 of the SysML spec). When supertype: is explicitly provided, it replaces (not supplements) the implicit default.

Element type: Implicit supertype
PartDef Parts::Part
ItemDef Items::Item
PortDef Ports::Port
ActionDef Actions::Action
StateDef States::StateAction
CalculationDef Calculations::Calculation
ConstraintDef Constraints::Constraint
RequirementDef Requirements::Requirement
AttributeDef Base::DataValue
EnumerationDef Base::DataValue
FlowDef Transfers::Transfer
AllocationDef Allocations::Allocation
ConnectionDef Connections::Connection
InterfaceDef Connections::Interface
OccurrenceDef Occurrences::Occurrence
EventOccurrenceDef Occurrences::EventOccurrence
ViewDef Views::View
ViewpointDef Views::Viewpoint
RenderingDef Views::Rendering
MetadataDef Metadata::SemanticMetadata
UseCaseDef UseCases::UseCase
AnalysisCaseDef AnalysisCases::AnalysisCase
VerificationCaseDef VerificationCases::VerificationCase

11.5 Cross-Reference Resolution Order

For a reference string R encountered within element E in package P:

  1. If R begins with a valid top-level package name, resolve as an absolute qualified name: split on ::, resolve each segment as a namespace member from the global root.
  2. If R begins with ./, strip ./ and resolve as a sibling of E within package P.
  3. Otherwise, attempt relative resolution: a. Look up R as a member of package P. b. If not found, look in each enclosing package outward. c. Then look in each package imported (directly or via imports:) by P and its ancestors.
  4. If resolution fails at all levels, emit a reference error with the source location. Do not panic; continue parsing remaining elements.

A reference also resolves when it names an inline (non-file) feature of a resolvable owner (Owner::feature, or a bare feature of the referencing element's owner — including one inherited through the owner's supertype:/typedBy: chain), or a member of the SysML v2 standard library (the built-in ScalarValues/Base packages, the curated ISQ/SI recognition of §4, any reference into a standard-library package — ISQ::…, Parts::Part::…, Links::… — whose top-level name the model does not itself declare, and the bare names of the well-known library root types such as Link, Part or Real). The step-4 reference errors are, per field (REQ-TRS-XREF-007): supertype: → E110, typedBy: (element-level and inline features: entries) → E111, subsets: → E112, redefines: → E113, satisfies: → E114, alongside the existing verifies: → E102, derivedFrom: → E103 and allocatedTo: → E503. In a [repos]-configured model an unresolved reference is reported once, as E512 (§14.4), instead of the field-specific code. All of these take the model-root-name hint (§11.3) and are suppressed in the validate --config lens, where a target pruned from the variant is E226/W019.

11.6 Circular Reference Handling

  1. The parser builds a dependency graph of cross-references after the first pass.
  2. Cycles in the subclassification (supertype → E016), typing (typedBy → E107), subsetting (subsets → E018), or requirement-derivation (derivedFrom → E017) graphs are semantic errors (SysML forbids them). Report each cycle once. A self-reference (a length-1 cycle, e.g. an element whose typedBy: or supertype: names itself) is a cycle and is reported the same way — an element cannot be its own type or supertype.
  3. Cycles in satisfies, verifies, metadata, or allocations are permitted (they are non-hierarchical) and do not produce errors.
  4. Cycles in package imports are permitted (packages may mutually import) but the parser must detect them and process each import exactly once to avoid infinite loops.

11.7 Validation Requirements

A conformant parser MUST report errors for:

  • type: values not in the defined inventory (Section 2).
  • multiplicity: strings that do not conform to the syntax in Section 6.
  • visibility: values other than public, protected, or private.
  • Two elements with the same qualified name (E108) — possible only through a file/directory clash or a synthesized element, since qualified names are path-derived (§4.5).
  • supertype: used on a usage (should be typedBy: and/or subsets:).
  • isVariant: true on an element not owned by a variation element (an element with isVariation: true).
  • EnumerationDef with a supertype: that resolves to another EnumerationDef.
  • values: absent on EnumerationDef.
  • References to elements that do not exist in the model (§11.5 step 4 — e.g. E102, E103, E110–E114, E503).

A conformant parser MUST emit warnings for:

  • Unknown frontmatter fields (for forward compatibility, unknown fields are preserved but warned about — W047).

Not currently enforced. The following are specified as invalid but the tool does not report them: direction: values other than in, out, inout, return; typedBy: on a definition (use supertype:); and a concrete element that subtypes an abstract definition without a concrete instantiation.

11.8 Built Graph Structure

After parsing, a conformant tool must build an in-memory model graph with the following node and edge types:

Nodes: one node per parsed element, carrying all frontmatter fields, documentation text, source file path, and derived qualified name.

Edges: - OWNED_BY (containment): from element to its owning package or definition/usage - SUBCLASSIFIES (supertype): from definition to its supertypes - TYPED_BY (typing): from usage to its definition(s) - SUBSETS (subsetting): from usage to subsetted usage(s) - REDEFINES (redefinition): from usage to redefined usage(s) - CONJUGATES (conjugation): from conjugated PortDef to original PortDef - CONNECTS (connection): from connection to source/target port features - SATISFIES: from element to requirement(s) it satisfies - VERIFIES: from verification case to requirement(s) it verifies - ALLOCATES: from allocation to source and target elements - IMPORTS: from package to imported namespace(s) - ALIASES: from alias membership to aliased element - METADATA_OF: from metadata application to annotated element

11.9 File Watch Mode

A conformant tool supporting live updates MUST:

  1. Watch all .md files under the model root for create, modify, and delete events.
  2. On any change, re-parse the affected file(s) only.
  3. Re-run cross-reference resolution for any element whose referenced targets may have changed.
  4. Emit a structured change event over the model update channel (e.g., WebSocket) describing the changed elements and edge updates.

11.10 Native-Element ID-Based Cross-Reference Resolution

Native Requirement (§8.11.6) and TestCase (§8.12.5) elements are referenced by their stable opaque id: field in addition to their qualified name. The reference resolution algorithm for verifies: and derivedFrom: fields is extended as follows:

Step 0 (ID match — runs before steps 1–4 in §11.5):

For a reference string R in a verifies: or derivedFrom: list:

  1. If R matches ^REQ(-[A-Z0-9]{2,12})*-[0-9]{3,8}$, search all loaded elements for a native Requirement whose id: equals R. If found, bind the reference. If not found, emit model error E102 (for verifies:) or E103 (for derivedFrom:).
  2. If R matches ^TC(-[A-Z0-9]{2,12})*-[0-9]{3,8}$, search all loaded elements for a native TestCase whose id: equals R. If found, bind the reference. If not found, emit E102.
  3. If R matches neither ID pattern, fall through to the standard qualified-name resolution (§11.5 steps 1–4).

Additional cross-reference validation:

  • A verifies: reference must resolve to either (a) a native Requirement, or (b) an element of a fixed requirement/architecture-shaped kind (PartDef, Part, AttributeDef, Attribute, PortDef, Port, ConnectionDef, Connection, InterfaceDef, Interface, ItemDef, Item, Allocation, RequirementDef) that was itself synthesized by native SysMLv2/KerML submodel ingestion (a sysmlSubmodel: true package — ADR-SYS-SYSMLV2-001) or by a stdio-subprocess foreign-format plugin (a foreignFormat: <alias> package — ADR-SYS-PLUGIN-002) — never a hand-authored element of the same kind outside either mechanism. Otherwise emit E104. A conformant tool tracks synthesis origin via an internal provenance set per mechanism; the element representation itself carries no origin field. These two mechanisms are documented in docs/model-guide/sysmlv2-submodel.md and docs/model-guide/stdio-plugins.md respectively, not elsewhere in this specification.
  • If derivedFrom: resolves to an element that is not a native Requirement, emit E105.
  • A TestCase with neither a verifies: nor a verifiesElement: entry emits E013.

11.11 Computed Reverse Indices and Coverage

After all files are loaded and cross-references resolved, a conformant tool MUST build the following in-memory indices. These are never written to disk.

verifiedBy: Map<RequirementId, List<TestCaseId>> For each native Requirement, the list of native TestCase ids whose verifies: includes this requirement's id. A verify target widened per §11.10 (SysMLv2- or plugin-synthesized, not a native Requirement) is keyed by its qualified name instead, since it has no stable id — the verifying TestCase's own id is still the list value.

derivedChildren: Map<RequirementId, List<RequirementId>> For each native Requirement, the list of native Requirement ids whose derivedFrom: includes this requirement's id.

Coverage check:

A native Requirement is covered when verifiedBy is non-empty and at least one entry has status: active. The following warnings apply:

  • W002: A leaf Requirement (no derivedChildren) with status: approved or status: implemented has no active TestCase in verifiedBy. A parent requirement is checked by W305 (integration-level L3–L5 coverage) instead.
  • W003: A Requirement with status: verified has an empty verifiedBy or all entries have status: retired.

11.12 Validation Rule Reference

A finding code's first letter is its severity: E = error, W = warning, I = informational. Numbers are never reused; a retired code keeps its row, marked RETIRED.

Where codes are defined. This section is normative for the codes tabulated in it — the core format checks. Every other code family is normative in the section named by the Code families specified elsewhere table below, and a conformant tool MUST emit those codes with the meanings given there. The complete list of codes the reference implementation emits — one row per code, including the families below — is the validation-code catalogue printed by syscribe spec validation (source prompts/spec/validation.md; presented grouped, with context, in docs/validation/rules.md). The reference implementation's test suite fails if a code it can emit is missing from that catalogue or from docs/validation/rules.md, or is neither tabulated in this section nor inside a family listed below; where a section and the catalogue disagree, the catalogue reflects the implementation and the section is to be corrected.

Code families specified elsewhere

Codes Family Specified in
E050, W050 Build-system integration (buildExports:) §9.6–§9.9 fields; catalogue
E200–E247, W011–W027, W048, W239–W240, W245–W246, W248 Product-line engineering, feature-check, the --config lens, single-file feature models, Configuration inheritance §9.6a.3, §9.8, §9.10, §9.11
W090 Suspect links §3.19, §12.10.6; ADR-SYS-SUSLINK-001
W091 Stale results §11.12, ingest-results --format records; REQ-TRS-RESJOIN-001
W099–W103 Documentation linting (lint-docs) §4.3 (W103); catalogue
W308–W311, E706–E723 Native PlanningItem (E718 is a non-scalar Argument.evidence entry) §23.4, §23.7–§23.9; §8.18.6 for E718
E400–E405, W400–W421 Diagram elements §8.16.2, §8.16.7, §8.16.8.2, §8.16.9, §8.16.10
E516–E519, E523, W513 Hierarchical product-line composition §14.7
E520–E522, W520 Release baselines §8.19
E530–E532, W530–W534 Reserved for the parked sandboxed-WASM plugin design (ADR-SYS-PLUGIN-001); never emitted —
W540–W544 Native SysML v2 submodel ingestion (sysmlSubmodel:) ADR-SYS-SYSMLV2-001; catalogue
E550–E553, W550–W553 Foreign-format stdio plugins (foreignFormat:) and results plugins ADR-SYS-PLUGIN-002; REQ-TRS-RESPLUG-001; catalogue
E560–E561, W560–W563 Annotated-source ingestion (annotationFormat:) ADR-SYS-ANNOTATE-001; catalogue
E600–E606, W610–W616 Native TestPlan §8.12.6
E800–E837, E859–E864, W809, W810 Tier 2 HARA/TARA elements, their cross-references, GSN assumption targets, confirmation targets, Asset §8.18.1, §8.18.2, §8.18.6, §8.18.10
E900–E923, E927, E940, E941, W036, W037, W900–W905, W926–W928, W931, W932 Tier 4 fault trees, FMEA, attack trees, TARA sheets §8.18.3–§8.18.5
E980–E984, W980–W984, W987 Fault-tree structure (cycles, arity, ranges, reachability, strays, CCF groups, missionTime) below
E985–E986, W988–W989 Safety targets: SafetyGoal.target:, rate provenance, plausibility, [reliability] § Quantitative HW safety metrics, Safety targets
E960–E967, W960–W979 Security-analysis completeness: TARA rows, risk inputs, attack-tree shape, zones/conduits, VulnerabilityReport §13.6
E990–E992, W990–W993 Behavioural model trace (models:, role:) §12.11
W994–W995 Executable evidence: scenario @verifies claims §12.12
W996 System context: unconnected boundary ports (role: external) §12.13
E942–E944, W940–W946 Fault-injection campaigns and residual-risk decisions §12.14

The remaining subsections tabulate the core codes; a few families (W060, E865, E866–E877, E700–E705, E950–E956, …) are repeated here from their own sections for convenience.

Parse-time errors (emitted while reading a single file)

Code Condition
E000 Internal fallback for a finding raised by a walker post-processing pass (derive, SysML v2 ingestion, featureTree: explosion, plugins, annotated source) under a code the validator does not map. Every code those passes emit is mapped, so E000 indicates an implementation defect and never appears in a healthy model
E001 File does not begin with --- (missing frontmatter delimiter)
E002 YAML frontmatter is not valid YAML 1.2
E003 RETIRED — never emitted. There is no strict mode; an unrecognised top-level frontmatter key is the warning W047 (the key is preserved in the element's extra-fields map).
E004 A required field is absent
E005 type: value is not in the element type inventory (§2)
E006 id: is present but does not match the required pattern for the element type
E007 status: value is not in the allowed enum for the element type
E008 testLevel: value is not in L1–L5
E009 silLevel: value is not an integer in 1–4
E010 asilLevel: value is not QM, A–D (or decomposition notation B(D)); the message lists the valid values
E011 Native TestCase body has no ```gherkin fenced block
E012 Native Requirement body has no normative text (text before the first \n## heading is empty or whitespace only; a body that begins directly with a ## heading is not E012 — it gives W001)
E013 A native TestCase has no verifies: and no verifiesElement: entry (both absent or empty)
E014 A Scenario Outline: block has no Examples: table
E015 The first Gherkin block in a TestCase has no Feature: line
E019 dalLevel: value is not in A–E (DO-178C)
E020 verificationMethod: is not test/inspection/analysis/demonstration
E021 coverageTarget: is not statement/branch/MCDC
E022 requirementKind: is not stakeholder/system/software/hardware/process/regulatory/deliverable
E023 A stable-ID numeric suffix is longer than the configured maximum ([ids] max_digits, default 8; the minimum of 3 is enforced by E006)
E024 RETIRED — formerly flagged a name: field on an id-identified type. name is now the single, required label on every element, so this code is no longer emitted.
E025 The removed title: field is declared on an element (any type — id-identified or name-identified). The title field is removed; rename it to name.
E026 A §3.10 locale documentation variant (a file with locale: and qualifiedName:) names a qualifiedName: that resolves to no element — its documentation cannot be attached, so the file is kept as its own element (§3.10)
E027 A §3.10 about: comment names an entry that resolves to no element (by qualified name or stable id) — the comment cannot be attached to it; the other entries still attach, and when none resolves the file is kept as its own element (§3.10)
E300 ADR.id does not match the ADR-* pattern
E301 ADR is missing a required field (id, name, or status)
E302 reqDomain: value is not system, hardware, or software
E303 domain: value is not system, hardware, or software
E304 ADR.status value is not in proposed | accepted | deprecated | superseded

Model-time errors (emitted during cross-reference resolution and graph building)

Code Condition
E101 Two elements have the same id: value
E102 A reference in verifies: cannot be resolved (no element with matching id or qualified name)
E103 A reference in derivedFrom: cannot be resolved
E104 A verifies: reference resolves to an element that is neither a native Requirement nor an element of a fixed requirement/architecture-shaped kind (PartDef/Part, AttributeDef/Attribute, PortDef/Port, ConnectionDef/Connection, InterfaceDef/Interface, ItemDef/Item, Allocation, RequirementDef/Requirement) actually synthesized by SysML v2 submodel ingestion, a stdio foreign-format plugin, or annotated-source ingestion (§11.10). A link type extending verifies may relax it (§12.10.4)
E105 A derivedFrom: reference resolves to an element that is not a native Requirement
E106 A testFunctions[].scenario string does not match any Scenario: or Scenario Outline: title in this file's Gherkin blocks
E016 Cycle in the supertype: graph
E017 Cycle in the derivedFrom: graph (a Configuration inheritance cycle is E236 instead)
E018 Cycle in the subsets: graph
E107 Cycle in the typedBy: graph, including a usage typed by itself (not suppressed under validate --config)
E108 Two elements of any origin (hand-authored, FMEA/TARA row explosion, SysML v2 ingestion, stdio plugin, annotated source) share a qualified name (§11.7)
E110 A supertype: reference cannot be resolved by any §11.5 form (REQ-TRS-XREF-007)
E111 A typedBy: reference — on the element, or on an inline features: entry — cannot be resolved by any §11.5 form
E112 A subsets: reference cannot be resolved by any §11.5 form
E113 A redefines: reference cannot be resolved by any §11.5 form
E114 A satisfies: reference cannot be resolved (no element with matching id, qualified name or name)
E115 A behavior-field reference cannot be resolved (sub-action/parameter typedBy, payload, includes, extends[].target, subject, result, returnType)
E116 A succession/binding/flow endpoint names no sub-action, control node or parameter of the element
E117 A behavior field is outside its closed vocabulary (sub-action or control-node kind, loopKind, parameter direction)
E118 multiplicity: is malformed (not N, N..M, N..*, * or names) or its lower bound exceeds its upper bound — on an element or an inline features: entry
E119 visibility: is not public, protected or private
E120 supertype: is declared on a usage (Part, Port, Attribute, …) — type it with typedBy: and specialize with subsets:
E121 isVariant: true on an element that is neither a member of an isVariation: true element nor names one in variantOf:
E122 An EnumerationDef has no values:, or specializes another EnumerationDef
E123 A usage is typed by a definition of the wrong kind (Part by a PortDef, Port by a PartDef, an Attribute by an ItemDef, …; the message names the fix, see §8.3.0a), or conjugates: is not a PortDef naming a PortDef
E124 A value Enum::literal names a literal the EnumerationDef does not declare
E125 An InterfaceDef or ConnectionDef declares ends: with fewer than two entries
E126 An imports: target, aliases: for: target or dependsOn: entry does not resolve (a library package or a name in a loaded peer repo is accepted)
E127 A connections: endpoint feature chain whose first segment is not a member of the element it is looked up in (the chain is followed through typedBy:/supertype:; an untyped, library or unresolved type ends the walk)
E128 A send/accept via:/to: chain (sub-action or state-transition accept: {via:}) whose first segment names no port, part, element or inline feature in the model; flowConnections:/successionConnections: endpoints on a structural element are walked like connections: endpoints (E127/W056)
E129 A TestCase's verifiesElement: entry resolves to an element that is not a PartDef, InterfaceDef, ActionDef or StateDef (E102 when it does not resolve at all)
E310 Native Requirement has derivedFrom: entries but no breakdownAdr:
E311 breakdownAdr: cannot be resolved, or resolves to an element that is not an ADR
E312 A parent Requirement (one with derivedChildren) appears in a satisfies: list
E313 A satisfies: link connects an element (not type-restricted — §12.3) and a requirement whose domain / reqDomain values are incompatible (e.g., a software element satisfying a hardware requirement)
E314 A Part or PartDef with isDeploymentPackage: true is the source of no allocation edge (any §12.9 form: Allocation element top-level or per features: entry, allocatedTo: on the package, legacy authored allocatedFrom: on the target) to a hardware element
E315 An element with domain: software has a supertype: or typedBy: reference that resolves to an element with domain: hardware, or vice versa — cross-domain direct reference; use Allocation instead
E316 A refines: operand on a UseCaseDef/UseCase — or on a behavioral definition ActionDef/Action/StateDef/State (REQ-TRS-MG-010) — does not resolve, or resolves to an element that is not a Requirement/RequirementDef (names the offending operand, owning element, and resolved type). Base-format check — runs regardless of the MagicGrid profile (REQ-TRS-MG-001). The refinedBy reverse index includes refining behavioral elements alongside refining use cases; the W307 "missing refines" warning stays scoped to UseCaseDef
E320 ADR.supersedes: names an element that does not resolve or is not an ADR
E321 An ADR is on a supersedes: cycle (a self-reference included)
E319 Results-gated: Requirement at verified has an active verifying TestCase whose ingested verdict is Fail
E317 A metadata: application does not resolve to a MetadataDef (§8.15.2)
E318 A metadata: application's MetadataDef declares annotates: that excludes the annotated element's type (the abstract Element/Definition/Usage metaclasses match; standard-library metadata is recognised)
E322 A tagged value has the wrong type, or a unit of another dimension, than its MetadataDef feature declares; also a feature default that violates its own typing
E323 A tagged value is outside the feature's declared range
E324 A tagged value is not in the feature's declared enumValues
E325 A tagged value's count violates the feature's declared multiplicity (an omitted required value included)
E326 A reference-typed tagged value does not resolve, or resolves to an element of another type
E327 A constraints: entry of a MetadataDef is violated by an application, or does not parse
E328 An abstract MetadataDef (isAbstract: true) is applied directly; apply a specialisation of it.
E329 A MetadataDef supertype: is not a valid specialisation: it does not resolve, is not a MetadataDef, is on a cycle, or the sub-definition widens a range, adds enumValues, or annotates a kind its supertype does not.
E650 A MetadataDef checks: entry fails on an element that carries the stereotype (severity: error, the default), or the entry is malformed — no expr:, a bad severity:, an expression that does not parse, or a call naming a stereotype that does not exist (reported on the definition).
E651 A stereotype selector is malformed (a mapping needs stereotype:, optional where:; or the string Def where <expr>), its where expression does not parse, or it names no MetadataDef. Reported on the TestPlan whose selection.stereotypes holds it.
E660 An ArchitectureRule of kind forbid-dependency, allow-only or layering (severity: error) is violated: a component chosen by from depends, directly or through a chain of connections, usages and allocations, on one chosen by to. The message names the rule and the whole path.
E661 An ArchitectureRule of kind independence (severity: error) is violated: the two chosen sets both depend on a shared component. One finding per shared component, with a path from each set.
E662 An ArchitectureRule is malformed — id not AR-…, name missing, unknown kind, from/to missing (where the kind needs them), layers or maxDepth missing or malformed, a malformed waiver or a selector with an unknown key, an empty selector, a bad domain or an unknown stereotype, a bad via or severity — and is not evaluated.
E663 An ArchitectureRule of kind max-depth (severity: error) is violated: a dependency chain starting at a chosen component is longer than maxDepth. The message names the rule, the start, the depth and the chain; a chain contained in a longer reported one is not reported again (REQ-TRS-ARCH-007).
E670 An operation's errors: is wrong: an entry does not resolve or is not an ItemDef, AttributeDef or enumeration, or the operation is asynchronous (isAsync: true) and so returns nothing (REQ-TRS-OPS-001).
E671 An operation's realizedBy: does not resolve to an ActionDef (REQ-TRS-OPS-002).
E672 An operation's signature differs from that of the ActionDef that realises it: a parameter the action lacks, an extra action parameter, a different direction, type or unit, or a return that does not correspond (one finding per difference; REQ-TRS-OPS-003).
E673 A sub-action's operation: is on something other than a SendAction/PerformAction, or does not name an operation (<owner>::<name>: the owner does not resolve or declares no such operation) (REQ-TRS-OPCALL-001).
E674 A call that names an operation passes an argument that is not one of its input parameters, or omits an in/inout parameter (REQ-TRS-OPCALL-002).
E675 An argument value of a call that names an operation has a different kind (number, boolean, string) or unit dimension than its parameter declares, found statically; names and untypable values are not checked (REQ-TRS-OPCALL-003).
E680 An AttributeDef's range: is not an interval (lo..hi, [lo, hi], (lo, hi), [lo, hi) or (lo, hi], with lo <= hi) or not text, or its pattern: is not a valid regular expression (REQ-TRS-DATA-001).
E681 An AttributeDef's default: is outside its (inherited) range:, not a number under a numeric range, or does not match its pattern: (REQ-TRS-DATA-002).
E682 A value: bound to an inline feature or an Attribute element typed by a constrained AttributeDef is outside the type's (inherited) range: — converted to its unit: — or does not match its pattern: in full (REQ-TRS-DATA-003).
E683 A specialising AttributeDef widens its supertype: a range: not contained in the supertype's (an open end of the supertype is not covered by a closed end), or a different pattern: (REQ-TRS-DATA-002).
E690 The static memory of the components allocated to a hardware part (and of everything they contain, each counted once) exceeds capacity × margin / 100 for a resource it declares a capacity for (ramCapacity, flashCapacity, nvmCapacity); RAM includes stack. Names the part, the resource, the use, the limit and the contributors, largest first (REQ-TRS-BUD-002).
E691 A budget field is malformed: a size (ram, flash, nvm, stack, ramCapacity, flashCapacity, nvmCapacity) that is negative, not a number or <number> <unit>, or has an unknown or ambiguous unit (KB; use kB or KiB), or a margin outside 0–100 (REQ-TRS-BUD-001). Also: a period or deadline that is not a positive duration, a priority that is not an integer, a malformed cpuMargin, or a scheduling other than edf/rate-monotonic (REQ-TRS-BUD-004).
E692 The CPU utilisation of the tasks (software parts with a period and a wcet duration) allocated to a hardware part that declares cpuMargin or scheduling, counting contained components once, exceeds its limit: the bound (1 for edf, Liu–Layland n (2^(1/n) − 1) for rate-monotonic) × cpuMargin / 100. Names the utilisation, the limit and the tasks, largest first (REQ-TRS-BUD-006).
E693 A software part's wcet (when it is a duration) is greater than its deadline (REQ-TRS-BUD-005).
E730 A part's deploymentKind is not one of soc, core, partition, container, process, thread (REQ-TRS-DEPL-001).
E731 A deployment node contains (through features:) a node that is not of a later kind than its own, outermost first: soc > core > partition > container > process > thread (REQ-TRS-DEPL-001).
E732 A component is allocated to two or more distinct parts that declare a deploymentKind, and is not marked replicated: true (REQ-TRS-DEPL-002).
E733 A component is allocated to a deployment node that declares a lower asilLevel (QM < A < B < C < D) or silLevel than the component; nodes or components that declare none are not checked (REQ-TRS-DEPL-003).
E740 A part's level is not component or unit (REQ-TRS-UNIT-001).
E741 A non-draft level: unit part has no implementedBy: code link (REQ-TRS-UNIT-002).
E742 A non-draft level: unit part satisfies no requirement (REQ-TRS-UNIT-002).
E743 A non-draft level: unit part has no unit-level test: no testLevel: L1 TestCase verifies it or a requirement it satisfies (REQ-TRS-UNIT-002).
E744 A non-draft level: unit part is contained (through features:) by no part with level: component (REQ-TRS-UNIT-002).

Warnings

Code Condition
W001 Native Requirement normative text contains no shall
W002 Leaf native Requirement (no derivedChildren) with status: approved or status: implemented has no active TestCase in verifiedBy. A parent is verified by decomposition; its integration-level check is W305
W003 Native Requirement with status: verified but verifiedBy is empty or all entries have status: retired
W004 A local sourceFile: path does not exist on disk. For a TestCase, emitted only when status: active (see TestCase drift scoping). Remote-URI sourceFiles are accepted and not checked locally (see sourceFile location semantics).
W005 Native Requirement has no upstream link — no derivedFrom: entries, no derivedFromSafetyGoal: and no derivedFromCybersecurityGoal: (legacy derivedFromSecurityGoal:) — and no derivedChildren (possible orphan not connected to any requirement hierarchy). A goal derivation counts as upstream traceability
W006 Both silLevel: (IEC 61508) and asilLevel: (ISO 26262) are set on the same element — incompatible standards; use only one
W007 A type definition (e.g. PartDef, PortDef, ItemDef) is defined but never used as a supertype: or typedBy: type by any element. (An unrecognised frontmatter key is W047.)
W008 Element has no type: field — it is ignored by most commands
W009 A testFunctions[].function does not resolve to a definition in its (existing) sourceFile — function-level traceability drift (renamed/deleted test). Emitted only for TestCases with status: active (see TestCase drift scoping). See Function matchers below.
W010 An active TestCase's testFunctions[].function last failed, was ignored/skipped, or was absent in the ingested test results. See Test result ingestion below. Inert unless results have been ingested.
W104 An active, non-abstract PartDef/InterfaceDef/ActionDef/StateDef that no active TestCase names in verifiesElement: and that satisfies: no requirement with an active verifying TestCase, and carries no testExempt: rationale (draft-suppressed; REQ-TRS-TCELEM-001)
W300 Leaf Requirement at status: approved or status: implemented has no satisfying element (no element — structural or behavioral — has satisfies: pointing to it); not raised for requirementKind process/regulatory/deliverable
W301 RETIRED (GH #121) — formerly flagged a leaf Requirement satisfied by more than one element. A leaf may be jointly satisfied by several elements (§12.3), so this code is no longer emitted.
W302 Leaf Requirement at status: implemented or status: verified still has reqDomain: system — refine to hardware or software (not raised for non-allocatable kinds)
W303 breakdownAdr: references an ADR with status: proposed, but the Requirement itself has status: approved, implemented or verified
W304 isDeploymentPackage: true combined with domain: hardware — deployment packages must be software
W305 Parent Requirement (has derivedFrom children) at status: approved, implemented, or verified has no active TestCase at testLevel: L3, L4, or L5 — leaf-level tests on derived requirements are insufficient to verify emergent composed behaviour
W313 An accepted ADR supersedes an ADR whose status is not superseded
W314 A non-draft element's breakdownAdr: resolves to a superseded ADR (cite its successor)
W315 A done PlanningItem achieves a non-Requirement work product (SafetyGoal, CybersecurityGoal, ADR, Argument, TestPlan, Baseline) whose status is still draft/review/proposed
W316 [metadata] profiles names an element that is not a package with profile: true (or does not exist)
W650 A MetadataDef checks: entry with severity: warning fails on an element that carries the stereotype. Draft-suppressed; gate with --deny W650.
W660 The same violation under severity: warning.
W661 The same violation under severity: warning.
W662 An ArchitectureRule selector (from or to) chooses no component, so the rule would pass without checking anything.
W663 The same violation under severity: warning.
W664 An ArchitectureRule waiver is expired (expires is today or earlier, so it no longer suppresses anything) or suppresses nothing (REQ-TRS-ARCH-008).
W740 A level: unit part has an implementedBy: local path that names no symbol (path#symbol); remote URIs and package references are not judged (REQ-TRS-UNIT-002).
W312 Results-gated: Requirement at approved/implemented has an active verifying TestCase whose ingested verdict is Fail
W306 Unsatisfied safety mechanism — a high-integrity Requirement (silLevel >= 4 or asilLevel: D) that is status: draft, (for a leaf) satisfied by no element, or (with a feature model) active in no Configuration. The "satisfied by no element" sub-condition applies to leaf requirements only — a parent (has derivedChildren) is satisfied transitively and may not be satisfied directly (E312). Message names the triggering sub-condition(s). Gateable with --deny W306; promotable via [profiles]
W029 A non-draft Requirement with an integrity level (silLevel/asilLevel) declares a wcet: claim but no active measuring TestCase (testLevel L5, or tagged timing/wcet) verifies it. The timing-evidence analog of W702. Gateable with --deny W029; query with list --has-wcet
W049 qualifiedName: on a file without locale: differs from the element's path-derived qualified name. It is not an identity override (the qualified name is purely path-derived, §4.5/§11.3) and is ignored — move or rename the file instead (§3.1)
W065 An id-identified element is stored in a file whose stem differs from its id (convention <id>.md); opt-in via [ids] check_file_names = true, draft-suppressed, gate with --deny W065
W058 A name-identified element's name: differs from the file (or directory) name that forms its qualified name (exempt: a name: containing a space or starting with <; FeatureModel and FMEASheet elements)
W059 A visibility: private element is referenced from outside the namespace that owns it
W068 A redefining feature's multiplicity: is not contained in the redefined feature's multiplicity (a redefinition may only narrow)
W051 A §3.10 locale variant is partly ignored: its target already has documentation for that locale (an earlier variant, or the element's own locale: — the first wins), its type: differs from the target's, or it declares fields other than type/name/locale/qualifiedName (a variant never redefines the element's structure) (§3.10)
W052 A §3.10 about: comment is partly ignored: it declares fields other than type/name/about/locale (a comment defines no element), an about: entry is not a non-empty string, or about: lists nothing; or about: is set on a package _index.md, which defines its package and is never a comment (§3.10)
W307 A non-draft UseCaseDef carries no refines: link to a requirement (absent or empty). Advisory and draft-suppressed; gateable with --deny W307 and promoted to a gate failure by the [profiles.magicgrid] profile (REQ-TRS-MG-001)
W930 Misplaced features-form allocation — a features: entry on a non-Allocation element declares an allocation (feature-level type: Allocation, or an allocatedFrom:/allocatedTo: key). Only a type: Allocation element carries features-form allocations (§12.9 form 2), so the entry contributes no allocation edge. Use allocatedTo: on the source or a standalone Allocation element
W503 Redundant allocation — the same source → target edge is declared by more than one §12.9 form (an allocatedTo: on the source, a standalone Allocation element, a legacy authored allocatedFrom: on the target). Emitted once per duplicated edge; pick one form. A single edge in a single form raises nothing. Gateable with --deny W503
W023 A non-draft Part/PartDef/Interface/InterfaceDef has an implementedBy: path that does not exist on disk (§12.8). Opt-in (only when implementedBy: is present); remote URIs and package-registry references are not checked; gateable with --deny W023
W028 The same extRef value is declared by two or more elements (§3); one finding per duplicated value. Gateable with --deny W028
W041 A custom_fields value is not a scalar or a list of scalars (§3.15)
W042 A qualified-name segment (an element's own name or a package/directory name) is not a SysMLv2 basic name and not a stable id
W043 A type reference names a member of a closed auto-imported package (ScalarValues, Base) that it does not declare — a likely typo
W044 A recognised ISQ quantity type and a recognised SI unit on the same element/feature have different physical dimensions
W057 An ISQ:: quantity type or SI:: unit is not known to this tool's tables (a subset of the SysML v2 library) — usually a typo
W056 A connections: endpoint's later segment (the port after the part) is not found on the part it follows — advisory; models often wire a sibling port loosely
W045 A tagged-value key in a metadata: application is not a declared feature of the MetadataDef (§8.15.2)
W046 An [ids.prefixes] entry in .syscribe.toml is malformed (unknown id-identified type, or a prefix not matching ^[A-Z][A-Z0-9]{1,11}$); the entry/prefix is ignored
W047 A top-level frontmatter key is not a recognised schema field and is not custom_fields: (one finding per key; §3.17)
W500 A View's viewpoint: does not resolve to a ViewpointDef
W501 An exhibitsStates: entry does not resolve to any known element
W502 A View's expose: entry does not resolve to any known element
W600 A PartDef/Part has an empty documentation body
W601 An ActionDef/Action has an empty documentation body
W701 A Requirement with asilLevel: B, C or D has no verificationMethod:
W702 A Requirement with asilLevel: D has no active TestCase at testLevel: L5 (HIL)
W703 Both asilLevel: (ISO 26262) and dalLevel: (DO-178C) are set on the same element
W640 A [cyber] entry in .syscribe.toml is malformed (unknown key, bad method, unknown impact/feasibility/vector key, non-integer points, non-increasing attack-potential thresholds, bad CALn/level/value); that entry is ignored and the default applies
W641 A ThreatScenario/AttackStep supplies some but not all five attack-potential factors (elapsedTime, expertise, knowledge, windowOfOpportunity, equipment); the partial set is ignored
W642 A ThreatScenario/AttackStep declares an attackFeasibility that differs from the one its attack-potential factors compute to; the declared value is used

State machine completeness warnings (W070–W079, W929, §22.1)

Code Condition
W070 Dead state — substate has no incoming transitions and is not isInitial: true (per region)
W071 Trap state — substate has no outgoing transitions and is not isFinal: true
W072 Non-determinism — two transitions from the same state accept the same payload type without guards
W073 Missing initial state — a region (single-region StateDef, or a parallel region) with substates has no isInitial: true entry
W074 Multiple initial states — a region has more than one isInitial: true substate
W075 Deprecated transition keys — a transition uses from:/to:/trigger: instead of canonical source:/target:/accept: (§8.8.3)
W076 Unresolved endpoint — a transition source/target names no state anywhere in the machine and resolves to no model element
W077 Cross-region transition — a transition connects substates in two different regions of a parallel state (illegal in SysMLv2)
W078 Parallel arity — an isParallel: true state declares fewer than two regions
W079 Unresolved behavior — a state entryAction/doAction/exitAction or a transition effect references an action that resolves to no model element
W929 Incomplete transition — a top-level transition has no source: (nor from:), or any transition has no target: (nor to:); it contributes no edge, so the checks above would otherwise ignore it (§8.8.3). Draft-suppressed; gateable with --deny W929

Sequence diagram completeness (W080, §22.4)

Code Condition
W080 Sequence diagram's subject ActionDef has a SendAction/AcceptAction sub-action not referenced in the diagram's edges:

Budget expression validation (E866–E868, W060, §22.2)

Code Condition
E866 CalculationDef.evaluate: resolves to an element that is not a ConstraintDef (drafted as E800, already in use)
E867 bodyLanguage: budget body expression has a syntax error (drafted as E801)
E868 bodyLanguage: budget expression references an unresolved attribute (drafted as E802)
W060 CalculationDef with bodyLanguage: budget evaluates to a value that violates the evaluate: constraint (opt-in; gateable with --deny W060)

ASIL/SIL decomposition pair completeness (E865, W860, §22.3)

Code Condition
E865 ASIL/SIL decomposition siblings share a satisfies: target — decomposed channels must satisfy distinct elements (drafted as E860, which is already in use)
W860 Requirement at any ASIL/SIL level has children at uniformly lower levels (or a QM / decomposedFrom: channel) but fewer than two children
E878 ASIL decomposition children of a parent at ASIL A–D do not form an allowed ISO 26262-9 pair (D = C+A
E879 decomposedFrom: is not an original ASIL A–D, or is lower than the element's own asilLevel (#214)
E880 ftti: is not <number><unit> with unit ns, us, ms, s, min or h (#215)
W811 SafetyGoal asilLevel is lower than the ASIL derived from the S/E/C of its linked HazardousEvents (ISO 26262-3 Table 4) (#215)
W812 SafetyGoal at ASIL C/D or SIL 3/4 has no safeState: or ftti: (draft-suppressed) (#215)
W813 HazardousEvent has a partial ISO 26262 S/E/C set (some but not all of severity/exposure/controllability; draft-suppressed) (#215)
W814 HazardousEvent mixes ISO 26262 S/E/C parameters with IEC 61508 risk-graph parameters (#215)

Trade study validation (E869–E877, W061–W064, §15.5)

Code Condition
E869 TradeStudy missing id:, name:, status:, criteria:, alternatives:, or scores:
E870 TradeStudy.id does not match TRD-* pattern
E871 criteria: entry missing name:, weight:, or direction:
E872 criteria[].weight not in [0.0, 1.0] or all weights are zero
E873 criteria[].direction not maximize or minimize
E874 alternatives: is empty
E875 alternatives: entry missing name:
E876 scores: entry references an unknown alternative or criterion name
E877 scores: entry score: is not a number
W061 TradeStudy with status: complete has no decision: ADR
W062 TradeStudy.objective: unresolved
W063 scores: matrix is incomplete (at least one (alternative, criterion) pair missing)
W064 alternatives[].element: unresolved

Review record validation (E700–E705, W700, W704, §19.3)

Code Condition
E700 ReviewRecord missing id:, name:, status:, reviewType:, or reviews:
E701 ReviewRecord.id does not match RR-* pattern
E702 ReviewRecord.status not in open \| closed \| waived
E703 ReviewRecord.reviewType not in allowed enum
E704 reviews: entry unresolved
E705 items[].disposition not in open \| closed \| not_applicable
W700 ReviewRecord with status: closed has ≥1 items[] with disposition: open
W704 Non-draft native Requirement appears in no ReviewRecord.reviews: list — dormant unless ReviewRecords exist (opt-in; --deny W704; drafted as W701, already in use)

Allocation resolution and declarative derive (E500–E506)

Code Condition
E500 A features: entry with type: Allocation has an allocatedFrom: that does not resolve
E501 A features: entry with type: Allocation has an allocatedTo: that does not resolve
E502 An allocatedFrom: entry (any element) does not resolve to a known element
E503 An allocatedTo: entry (any element) does not resolve to a known element
E504 Cyclic dependency between derive: fields — a field reads itself directly or through other derived fields (self., elements["Q"]., children/parent aggregates). Reported once per participating element, naming the cycle; the cyclic fields are not evaluated (REQ-TRS-DERIVE-004, §3.18)
E505 A derive: formula does not parse, the derive: value is not a mapping, or a formula is not a string (REQ-TRS-DERIVE-001/005)
E506 A derive: formula's elements["QName"] names no element (REQ-TRS-DERIVE-005)

The Allocation (E500–E503) and derive (E504–E506) families are disjoint — no code carries both meanings (GH #127).

Multi-repository composition (E510–E515, W510–W512, §14.6)

Code Condition
E510 Circular repo import (repo A → repo B → … → repo A)
E511 repos.<alias>.path does not exist on disk and no ref: is configured
E512 Cross-repo cross-reference (verifies:, derivedFrom:, satisfies:, allocatedTo:, supertype:, typedBy:, subsets:, redefines:) unresolved in any loaded repo — reported instead of E102/E103/E110–E114/E503 when [repos] is configured
E513 repoImports[].repo alias not present in [repos] config
E514 repoImports[].qname does not resolve in the named repo
E515 Same stable ID appears in two repos (duplicate across composition)
W510 Repo in [repos] has no ref: — composition is not reproducible (opt-in; --deny W510)
W511 Peer repo HEAD has drifted from its configured ref: (opt-in; --deny W511)
W512 Submodule peer's ref: disagrees with the parent's gitlink pin (opt-in; --deny W512)

Active only when the model uses [linkTypes] in .syscribe.toml or a links: field — a model with neither is unaffected.

Code Condition
E630 A links: key is not a declared (valid) link type — the message lists the declared types, or states that none are declared and how to declare one
E631 links: is not a mapping, or a key's value is neither a string nor a list of strings
E632 A links: reference does not resolve (by stable id or qualified name)
E633 The link type declares sourceTypes and the holding element's type: is not listed
E634 The link type declares targetTypes and a resolved target's type: is not listed (names the target)
E635 A source holds more targets for a link type than its cardinality upper bound
E636 A cycle — including a self-link — formed solely by links of an acyclic = true type (once per cycle, naming its members)
W630 A [linkTypes.<name>] entry is malformed (bad/colliding name or inverse, unparseable cardinality, non-zero lower bound without sourceTypes, unknown element type, unsupported extends, relax/coverage without extends, non-relaxable relax code) — the entry is ignored as a whole; or an entry carries an unknown key (key ignored). Attached to .syscribe.toml
W631 A non-draft element whose type: is in a link type's sourceTypes holds fewer targets than the cardinality lower bound (opt-in; --deny W631)
W632 A malformed [traceability] entry in .syscribe.toml (unknown key, or require_breakdown_adr that is not true/false); the entry is ignored and the default kept (E310 stays in force)

IEC 62443 Zone/Conduit validation (E950–E956, E925, E926, W950–W953, §13.5)

Code Condition
E950 Zone missing id:, name:, status:, or targetSL:
E951 Zone.id does not match ZN-* pattern
E952 Conduit missing id:, name:, status:, fromZone:, or toZone:
E953 Conduit.id does not match CD-* pattern
E954 Conduit.fromZone or toZone unresolved or not a Zone
E955 Zone.members: entry unresolved or not a PartDef/Part
E956 PartDef/Part.inZone: unresolved or not a Zone
E925 targetSL:/achievedSL: on a Zone, Conduit, PartDef or Part is outside the Security Level range 1–4
E926 Zone/Conduit status: is not draft/review/approved/deprecated
W950 Zone.achievedSL < Zone.targetSL — security level not yet achieved
W951 Conduit.achievedSL < max(fromZone.targetSL, toZone.targetSL) — the conduit is below the higher connected zone's target (opt-in)
W952 PartDef/Part has targetSL: but no zone membership (opt-in)
W953 Approved Zone with targetSL >= 2 has no referencing Conduit

Security-analysis completeness (E960–E967, W960–W979, §13.6)

Beyond the per-type checks above, the validator checks the TARA/attack-tree/zone/vulnerability data for completeness and internal consistency. TARASheet accepts an assetTable: (rows become Asset elements) alongside the other four tables; securityProperty: and derivedFromCybersecurityGoal: accept a string or a list. draft elements are exempt from the completeness warnings. An AND attack-tree gate rolls up as the MIN of its inputs, an OR gate as the MAX.

Code Condition
E960 A typed security field has the wrong YAML shape — securityProperty / derivedFromCybersecurityGoal is neither a string nor a list of strings (a mapping, number, ...); the message names the field (previously a misleading E002 "not valid YAML"). Both fields now accept a string or a list
E961 A TARASheet section-table row (assetTable/damageTable/threatTable/goalTable/controlTable) is not a mapping or has no id: — it is silently dropped from validation and the risk views (mirrors FMEA E923)
E962 A TARASheet row has an unknown key or does not deserialize as an element — the field would be silently ignored (mirrors FMEA E922)
E963 Asset.assetOwner does not resolve to a model element
E964 Asset.relatedSafetyGoal does not resolve, or resolves to something other than a SafetyGoal
E965 Cycle (including a gate listing itself) in an attack tree's AttackTreeGate.inputs — the feasibility roll-up is undefined
E967 VulnerabilityReport.threatScenarios does not resolve to a ThreatScenario
E966 Error
W960 Non-draft ThreatScenario whose risk cannot be determined (no resolvable damageScenarios, no linked damageSeverity, or no attackFeasibility) — cyber-risk reports risk=unknown
W961 Non-draft DamageScenario listed in no ThreatScenario.damageScenarios (orphan; contributes to no risk)
W962 Non-draft DamageScenario with no assets (ISO/SAE 21434 §15.3)
W963 ThreatScenario with riskTreatment: reduce that no CybersecurityGoal.threatScenarios lists — the reduction is realised by no goal/control
W964 ThreatScenario with riskTreatment: retain on a computed high/critical risk and no residualRisk rationale
W965 ThreatScenario declares residualRisk but no riskTreatment
W966 An attack tree has more than one root node (no single top gate/step) — its feasibility is not computed
W967 An AttackStep has no attackFeasibility — the tree cannot be rolled up and W035 is silently skipped
W968 An AttackTreeGate lists the same input more than once
W969 Non-draft SecurityControl with no implementsGoals
W970 Non-draft CybersecurityGoal with no threatScenarios
W971 A PartDef/Part belongs to more than one Zone (Zone.members / inZone)
W972 A Conduit has fromZone and toZone naming the same zone
W973 An approved Zone or Conduit declares no achievedSL
W974 VulnerabilityReport.cveId is not of the form CVE-YYYY-NNNN...
W975 VulnerabilityReport.status is not in the documented vocabulary (draft, open, triaged, investigating, in_progress, mitigated, resolved, fixed, accepted, wont_fix, closed, not_affected, false_positive, deprecated)
W976 VulnerabilityReport is mitigated/resolved/fixed/closed but names neither mitigatedBy nor fixedIn
W977 VulnerabilityReport is accepted/wont_fix with no rationale: (frontmatter or a Rationale section)
W978 VulnerabilityReport.cvssVector is not a CVSS vector string, cvssSeverity is not none/low/medium/high/critical, or it disagrees with the bucket of cvssScore (0 none, <4 low, <7 medium, <9 high, else critical)
W979 Unresolved VulnerabilityReport (open/triaged/investigating/in_progress) with cvssScore >= 7.0 and no threatScenarios link

MagicGrid gate (MG###, REQ-TRS-MG-002..011)

The MG### namespace is opt-in: these checks fire only under the MagicGrid profile ([profiles.<name>] magicgrid = true, e.g. validate --profile magicgrid). The data they validate rides on mg_-prefixed custom_fields: and the base actors: field, all of which stay inert in the base format. The MG010–MG070 findings below are Error severity; the completeness codes MG080–MG083 further down are warnings.

MagicGrid overlay fields (custom_fields:). In addition to mg_external, mg_cell, mg_moe*, and mg_layer, three further markers are recognised (all flat scalars, inert in the base format):

  • mg_mop (bool) — marks the host (a CalculationDef/ConstraintDef/AnalysisCase) as a Measurement of Performance (MoP, cells W4/S4), with mg_mop_refines (the black-box mg_moe MoE it refines, resolved by qname or id) and optional mg_mop_unit. The tool computes the inverse index mopRefinedBy on each MoE — the MoPs that refine it — surfaced in show (and the JSON computed block) alongside refinedBy/actorIn, so the MoE→MoP measurement chain is navigable from the MoE (REQ-TRS-MG-008).
  • mg_soi (bool) — marks the System of Interest Part/PartDef (cell B3 boundary). Zero markers is not an error; when exactly one is present the magicgrid report identifies it (a System of interest: line, and a systemOfInterest JSON field) (REQ-TRS-MG-009).
  • mg_variant (bool) — marks a Configuration as a MagicGrid parametric variant, relaxing the featureModel: requirement (E201): such a Configuration denotes the empty feature selection (identity projection) and is differentiated solely by its parameterBindings. validate --config, matrix, diff, and trade-study treat it as a normal configuration column (REQ-TRS-MG-011).
Code Condition
MG010 An actors: entry (on a UseCaseDef/UseCase/use-case-style RequirementDef/Requirement) resolves to no model element
MG011 An actors: entry resolves to an element that is not a Part/PartDef (names the resolved type)
MG012 A referenced actor Part/PartDef is not marked custom_fields: { mg_external: true } — a non-external actor is a B3 modelling error
MG013 A non-draft UseCaseDef declares an empty or absent actors: list
MG020 custom_fields.mg_cell is not one of the recognised coordinates B1–B4, W1–W4, S1–S4
MG021 An element's type is incompatible with the pillar implied by its mg_cell column (col1→Requirement/RequirementDef; col2→UseCaseDef/UseCase/ActionDef/Action/StateDef/State; col3→Part/PartDef/Port/PortDef/Interface/InterfaceDef/Connection/ConnectionDef; col4→ConstraintDef/Constraint/CalculationDef/Calculation/AnalysisCase)
MG030 custom_fields.mg_moe: true on an element that is not a CalculationDef or AnalysisCase
MG031 mg_moe_measures is absent, or does not resolve (by qname/id) to a Requirement/RequirementDef
MG032 mg_moe_direction is absent or not maximize/minimize
MG033 mg_moe_threshold/mg_moe_objective not numeric, or inconsistent with the direction (maximize ⇒ objective ≥ threshold; minimize ⇒ objective ≤ threshold); or mg_moe_weight present and not numeric in [0, 1]
MG040 custom_fields.mg_layer is present on a Part/PartDef and is not logical or physical
MG041 A Part/PartDef with mg_layer: logical has no Allocation to a physical element
MG042 A logical and a physical Part/PartDef share a direct supertype:/typedBy: link — relate the layers only through an explicit Allocation
MG050 custom_fields.mg_mop: true (a Measurement of Performance) on an element that is not a CalculationDef, ConstraintDef, or AnalysisCase (REQ-TRS-MG-008)
MG051 mg_mop_refines is absent, or does not resolve (by qname/id, §11.10) to a model element
MG052 mg_mop_refines resolves to an element that is not marked custom_fields: { mg_moe: true } — a MoP must refine an MoE
MG060 custom_fields.mg_soi: true (the System of Interest) on an element that is not a Part/PartDef (REQ-TRS-MG-009)
MG061 More than one element in the model is marked mg_soi: true — a MagicGrid model has a single system of interest
MG062 An element is marked both mg_soi: true and mg_external: true — the SoI cannot also be external to itself
MG070 custom_fields.mg_variant: true on an element that is not a Configuration (REQ-TRS-MG-011)

The next four codes are completeness / coverage warnings (Severity::Warning, gated, draft-suppressed where applicable, promotable, gateable with --deny) — the gap-analysis half of MagicGrid validation. They surface in magicgrid --audit (category Coverage, REQ-TRS-MG-013/014):

Code Condition (warning)
MG080 Orphan stakeholder need: a non-draft Requirement in cell B1 (mg_cell: B1) that is neither refined by any use case/behavioral element (refinedBy empty) nor derived into any system requirement (derivedChildren empty)
MG081 Unallocated functional-analysis element: an ActionDef/Action/StateDef/State in cell W2 (mg_cell: W2) allocated to no Part/PartDef marked mg_layer: logical
MG082 Missing System of Interest: the model declares a System Context (≥1 mg_external: true element) but no element is marked mg_soi: true — emitted once, model-level
MG083 MoE without a MoP: an mg_moe element with an empty mopRefinedBy index — no Measurement of Performance refines it

Integrity-level propagation errors (E841–E843)

When an upstream element carries asilLevel: or silLevel:, every element referencing it via derivedFromSafetyGoal:, derivedFrom:, or satisfies: must carry asilLevel: or silLevel: too (either field satisfies the check; plLevel: is ignored). See §12.7.

Code Condition
E841 Element with derivedFromSafetyGoal: is missing asilLevel/silLevel when the referenced SafetyGoal carries one
E842 Element with derivedFrom: is missing asilLevel/silLevel when the parent element carries one
E843 Element with satisfies: is missing asilLevel/silLevel when the satisfied Requirement carries one

Integrity-level propagation warnings

Code Condition
W808 Element's integrity level is strictly lower than its source (derivedFromSafetyGoal:, derivedFrom:, or satisfies:) but no breakdownAdr: is set — add an ADR documenting the ASIL/SIL decomposition rationale

Safety / security analysis warnings (W800–W808)

Code Condition
W800 HazardousEvent is not referenced by any SafetyGoal.hazardousEvents
W801 SafetyGoal has no integrity level — set asilLevel (ISO 26262), silLevel (IEC 61508), or plLevel (ISO 13849-1)
W802 CybersecurityGoal is not implemented by any SecurityControl.implementsGoals
W803 VulnerabilityReport has status: open
W804 CybersecurityGoal has no Requirement with derivedFromCybersecurityGoal: pointing to it
W805 SafetyGoal has no Requirement with derivedFromSafetyGoal: pointing to it
W806 SafetyGoal has no hazardousEvents: — not grounded in any hazard analysis
W807 Requirement with derivedFromCybersecurityGoal: has no verificationMethod:

Safety↔security co-engineering (E844, W030)

Code Severity Condition
E844 Error A hazardRef: value on a DamageScenario/ThreatScenario does not resolve, or resolves to an element that is not a HazardousEvent/SafetyGoal
W030 Warning A DamageScenario whose impactCategories: includes safety has no hazardRef: (the cross-domain gap). Opt-in (safety-tagged only); gateable with --deny W030

Cybersecurity risk determination (E845, W031, W032)

Code Severity Condition
E845 Error ThreatScenario.riskTreatment: is not one of avoid/reduce/share/retain
W031 Warning A ThreatScenario whose computed risk is high/critical has no riskTreatment: and is not addressed by any CybersecurityGoal.threatScenarios. Gateable with --deny W031; promotable via [profiles]
W032 Warning A CybersecurityGoal's calLevel: is below the expected minimum CAL for the max risk of its listed threats (low→CAL1, medium→CAL2, high→CAL3, critical→CAL4). Fires only when at least one linked threat has a computable risk; gateable with --deny W032
E640 Error A per-category impact rating (safetyImpact, financialImpact, operationalImpact, privacyImpact) on a DamageScenario is not one of severe · major · moderate · negligible
E641 Error An attack-potential factor value is neither a known label of its (configurable) table nor a non-negative integer

Quantitative HW safety metrics (E846, W033)

ISO 26262-5 §8–9 hardware architectural metrics, rolled up per SafetyGoal from the FaultTreeEvents under the FaultTree(s) whose topEvent resolves to it. Each event may carry diagnosticCoverage: (DC) and latentDiagnosticCoverage: (DCl), both in 0.0–1.0. Over the contributing events that declare a failureRate: (λ, /h): Σλ = Σ λ_i; λ_RF = Σ λ_i·(1−DC_i); SPFM = 1 − λ_RF/Σλ; λ_MPFL = Σ λ_i·DC_i·(1−DCl_i) (events declaring DCl); LFM = 1 − λ_MPFL/(Σλ−λ_RF); PMHF = λ_RF + λ_MPFL. Metrics are computed and gated only when at least one contributing event declares diagnosticCoverage (opt-in; otherwise n/a, never gated). Targets — ASIL SPFM ≥ {B 0.90, C 0.97, D 0.99}, LFM ≥ {B 0.60, C 0.80, D 0.90}, PMHF < {B/C 1e-7, D 1e-8}/h; SIL gates PMHF/PFH < {SIL2 1e-6, SIL3 1e-7, SIL4 1e-8}/h only. This is a first-order FMEDA approximation and must be independently verified. The metrics command (§ CLI) reports per-goal SPFM/LFM/PMHF and pass/fail.

Code Severity Condition
E846 Error diagnosticCoverage: or latentDiagnosticCoverage: is outside 0.0–1.0
W033 Warning A SafetyGoal with diagnosticCoverage data has a computed SPFM, LFM, or PMHF that misses its ASIL/SIL target, or a goal whose project opted in (its own target: or a .syscribe.toml limit) has a credible rate-only or demand-mode figure (PFH, PFDavg) at or above it (§ Safety targets below). Gateable with --deny W033; promotable via [profiles]
W985 Warning A fault-tree event contributing to the goal's metrics declares no diagnosticCoverage (or, once any event declares it, no latentDiagnosticCoverage); treated as 0
W986 Warning The goal's fault tree has dual-point cut sets but no missionTime; a default exposure of 10000 h is used
E985 Error A SafetyGoal.target: is malformed: not a mapping, an unknown key, mode not continuous/demand, value not a positive number (at most 1 in demand mode), a unit that does not fit the mode (/h continuous, probability demand), or a demand-mode target without a positive proofTestInterval duration (GH #343)
E986 Error rateStatus: on a FaultTreeEvent or FMEA row is not one of verified, secondary, unverified (GH #343)
W988 Warning A failureRate claimed as rateStatus: verified or secondary fails with a probability above the plausibility bound over the tree's missionTime (default 0.1; [reliability] plausibleProbability); an unverified rate is labelled in the verdict instead (GH #343)
W989 Warning A malformed [reliability] entry in .syscribe.toml (a key, an unknown ASIL/SIL level or limit, a value out of range); the entry is ignored (GH #343)

Since GH #211/#213 the roll-up is driven by the minimal cut sets of the goal's fault tree (§ Fault-tree analysis below): only reachable, non-house events in a cut set contribute; an event in an order-1 cut set is single-point (λ_RF = Σ λ·(1−DC)), an event only in higher-order sets adds no residual rate, λ_MPFL counts λ·DC·(1−DCl) for single-point and λ·(1−DCl) for multi-point events, and PMHF = λ_RF + λ_DPF with λ_DPF = Σ_{order-2 {a,b}} Σ_{(x,y)} λ_x(1−DCl_x)·λ_y(1−DC_y)·T over the tree's missionTime T. SIL 1 is gated (PFH < 1e-5/h). metrics exits 2 when any goal's verdict is fail.

Safety targets (GH #343)

A goal is judged on one figure in its mode:

Goal Figure Basis
continuous mode, a contributing event declares diagnosticCoverage PMHF (ASIL) / PFH (SIL) = λ_RF + λ_DPF first-order, as above; unchanged
continuous mode, no coverage data PFH = P_top(T) / T rate only: the exact (BDD) top-event probability over the tree's missionTime T, divided by it
target.mode: demand PFDavg = Σ_{MCS} Π_i λ_DU,i · TI^k / (k+1) · Π_j p_j cut sets: λ_DU = λ·(1−DC), TI the proofTestInterval, k the events of the cut set with a rate, p_j the probability of those with none

A goal may carry its own target::

target:
  mode: demand            # continuous (default) | demand
  value: 1.0e-3           # overrides the table's PMHF/PFH (/h) or PFDavg limit
  unit: probability       # optional: /h (continuous) or probability (demand)
  proofTestInterval: 1 year   # required in demand mode

The limit is the goal's target.value, else [reliability.targets] in .syscribe.toml, else the built-in ISO 26262 / IEC 61508 table (SIL PFDavg: 1e-1, 1e-2, 1e-3, 1e-4), and every report names which (goal target, .syscribe.toml, built-in default): the values are the user's compliance decision.

[reliability]
plausibleProbability = 0.1     # the largest credible probability of one event over the mission time

[reliability.targets.sil.2]
pfh = 1.0e-6                   # also pmhf; spfm, lfm ≤ 1; pfd ≤ 1

[reliability.targets.asil.D]
spfm = 0.99
pmhf = 1.0e-8

A rate-only figure is not credible when a contributing event, or the top event, fails with a probability above plausibleProbability over the mission time: λ·T is then no longer small, the figure is no longer a rate, and the target cannot judge it. A PFDavg figure needs λ_DU·TI small instead, so in demand mode each contributing event's λ·(1−DC)·TI, and the PFDavg itself, are tested against the bound. The verdicts are pass, fail, not credible, no target and n/a. A demand-mode goal whose level has no PFDavg limit (the ASIL table has none) is no target until target.value or a pfd limit in .syscribe.toml gives one; it never passes on SPFM/LFM alone. The source a report names is that of the figure's limit (pmhf/pfh continuous, pfd demand).

failureRate on a FaultTreeEvent or an FMEA row may carry rateSource: (free text: a handbook, a data sheet, field data) and rateStatus: verified | secondary | unverified (E986 otherwise). A verdict that rests on a contributing rate that is not verified or secondary (including one with no status) is labelled "unverified rates"; list FaultTreeEvent --rate-status unverified finds them (none for an element with a failureRate but no status). A claimed (verified/secondary) rate above the plausibility bound is W988.

metrics reports the figure, the limit and its source, the basis, the verdict, the exact top-event probability, the largest contributor (Fussell-Vesely importance) and, when different, the event with the highest Birnbaum importance; --sensitivity adds, per goal, every contributing event's figure and verdict with its λ divided and multiplied by 10, marking the events that move the verdict. audit shows the same figure and verdict; safety-case adds a solution node "quantitative target: …" under the goal (supported on pass, failing on fail, undeveloped otherwise; the goal verdict is not changed). The first-order figure gates (W033, metrics exit 2) as before; a rate-only or demand figure gates only when the project opted in, by the goal's own target: or a .syscribe.toml limit for the figure, and the figure is credible. Otherwise its fail is advisory: reported, noted on stderr, exit 0. A malformed target: (E985) is ignored and the report says so.

Tree versus campaign. inject <subject> --ft <FT> --consistency [--consistency-order N] [--max-runs M] runs the tree's minimal cut sets and the single faults, then combinations of the composite's faults up to order N (default 2), skipping a combination that already contains a defeating set or a minimal cut set (mapped through an FMEA row's ftaRef), at most M runs (default 500). The comparison uses every minimal cut set of the tree, whatever --max-order bounds the campaign's own sets. It reports one table: every minimal cut set with its tree probability and executable verdict, matched through the same ftaRef mapping (confirmed when defeated, conservative or a model gap when the model survives it, not run (above the campaign's order) when its faults exist but no set ran them, not injectable otherwise), and every defeating set that contains no minimal cut set as missing from the tree. A combination the invariants cannot judge (for example one with no applicable Except under clause, §8.12.8) is an error and counted, so inject exits 2; a missing set among them goes undetected until a clause covers it. With --record the combinations join the record, so a defeat missing from the tree needs a ResidualRisk decision like any other (W940), and the record's invocation keeps --consistency-order/--max-runs.

Fault-tree analysis and structure (E980–E984, W980–W984, W987)

syscribe fault-tree analyze <FT> evaluates one FaultTree: it builds a reduced ordered BDD of the gate logic (AND/inhibit = conjunction, OR, XOR = parity, NOT; house events are constants, TRUE only at probability: 1), and reports the minimal cut sets (minimal sets of events that make the top event true with all others false), the exact top-event probability, the rare-event and min-cut-upper-bound approximations, and per-event Fussell-Vesely, Birnbaum, RAW importance and a role (single_point, dual_point, multi_point, irrelevant, unreachable, house). An event's probability is its explicit probability: else 1 − e^(−λ·t) from failureRate and the tree's missionTime ("8760 h", "1e9 h", "10 y"). A ccfGroup: (≥ 2 events) with ccfBeta: β is expanded by the beta-factor model: each member becomes OR(member_ind, CCF) with P(ind) = (1−β)p and P(CCF) = β·mean(p). The tree's top node is the node no other node references (the one reaching most nodes if several); a tree with events but no gates is an implicit OR (legacy flat form).

Code Severity Condition
E980 Error A gate cycle, including a gate listing itself in inputs
E981 Error NOT with > 1 input, XOR with > 2, or inhibit without a conditioning input (≥ 2 inputs)
E982 Error FaultTreeEvent.failureRate negative or not finite
E983 Error probability on a fault-tree element outside 0.0–1.0
E984 Error FaultTreeEvent.ccfBeta outside 0.0–1.0
W980 Warning A gate/event is not reachable from the top node
W981 Warning A gate/event is outside any FaultTree directory
W982 Warning An AND/OR gate with a single input
W983 Warning A ccfGroup with a single member
W984 Warning A ccfGroup with no ccfBeta, or members disagreeing on it
W987 Warning FaultTree.missionTime is not a positive duration

Freedom From Interference (W034)

ISO 26262-9 §7 dependent-failure analysis. Two elements share a resource when both are allocated to the same target element; allocation edges (source → target) are the §12.9 unified edge set — allocatedTo: (source = the element), a legacy allocatedFrom: authored on a non-Allocation element (target = the element), and Allocation elements' allocatedFrom/allocatedTo, top-level or per features: entry (the Allocation element itself is never an endpoint) — resolved via the resolver and inverted into a target → { sources } map. Each element's integrity tag is asilLevel, else silLevel (→ SIL<n>), else QM; two sources on a target are mixed-criticality when their tags differ (including classified vs QM). A mixed pair is excused when the target or at least one of the two sources declares a non-empty ffiRationale: string, or carries a breakdownAdr: resolving to an accepted ADR. The check is opt-in — dormant unless some element declares asilLevel or silLevel. (The cross-domain "attack surface" co-analysis bonus from the originating issue is deferred.)

Code Severity Condition
W034 Warning For an allocation target with ≥2 sources, a mixed-criticality source pair has no freedom-from-interference argument (one finding per (target, sourceA, sourceB), naming both sources and their tags). Gateable with --deny W034; promotable via [profiles]
W035 Warning An AttackTree's computed (weakest-link) feasibility does not match the attackFeasibility of the ThreatScenario it substantiates (threatRef). Gateable with --deny W035; promotable via [profiles]

Confirmation measures & DIA/CIA responsibility (E847–E851, E924, W038, W039)

ISO 26262-2 §6 confirmation measures, ISO 26262-8 §5 DIA, ISO/SAE 21434 §7 CIA. The responsibility: common field (§3) names the accountable party for a work product. A ConfirmationMeasure (CM-*) records a confirmation review / FS audit / FS assessment / cybersecurity assessment with measureType:, independenceLevel: (I1–I3), status:, and confirms: (work-product ref(s)). Both warnings are opt-in: W038 is dormant unless some element declares responsibility:; W039 is dormant unless at least one ConfirmationMeasure exists. The ASIL/CAL → independence mapping is minimal — only ASIL D and silLevel 3/4 → I3 functional-safety assessment, CAL4 → I3 cybersecurity assessment, and CAL3 → I2-or-I3 cybersecurity assessment are gated.

Code Severity Condition
E847 Error ConfirmationMeasure is missing id, name, or status
E848 Error ConfirmationMeasure.id does not match the CM-* pattern
E849 Error measureType is not confirmation_review/functional_safety_audit/functional_safety_assessment/cybersecurity_assessment
E850 Error independenceLevel is not I1/I2/I3
E851 Error a confirms: ref does not resolve to any model element
E924 Error ConfirmationMeasure.status is not planned/in_progress/completed
W038 Warning A non-draft work product (Requirement, PartDef, Part, SafetyGoal, CybersecurityGoal) declares no responsibility:. Opt-in; gateable with --deny W038; promotable
W039 Warning An asilLevel: D / silLevel: 3 / silLevel: 4 SafetyGoal/Requirement lacks an I3 functional_safety_assessment, a calLevel: CAL4 CybersecurityGoal lacks an I3 cybersecurity_assessment, or a calLevel: CAL3 CybersecurityGoal lacks an I2-or-I3 cybersecurity_assessment, confirming it. Opt-in; gateable with --deny W039; promotable

Coverage policy (E898)

The [coverage] table of .syscribe.toml (see syscribe help coverage).

Code Severity Condition
E898 Error the [coverage] table of .syscribe.toml is invalid (unparseable, unknown key, bad rule or selector value), or an integrity-rated (ASIL/CAL/SIL) parent requirement is judged by a rule looser than both

Timing quantities and budgets (E895, W893)

quantities: (§8.18.8).

Code Severity Condition
E895 Error a quantities entry is not a mapping, has an unknown kind (ftti/latency/wcet/reaction) or unit (s/ms/us/ns), a non-positive / non-numeric value, or the same kind twice on one element
W893 Warning a timing budget is exceeded: the sum of a kind over the requirements derived from a Requirement/SafetyGoal (a serial chain) is larger than its own value, or a derived requirement's latency + reaction exceeds the goal's FTTI (draft-suppressed)

Test environments (E893–E894, W891–W892)

TestEnvironment (TE-*, §8.12.7).

Code Severity Condition
E893 Error TestEnvironment is missing id, name or status, its id is not TE-*, a status / environmentKind / calibrationStatus is outside its enum, or calibrationDue is not a YYYY-MM-DD date
E894 Error a runsOn entry on a TestCase/TestPlan does not resolve, or resolves to something that is not a TestEnvironment
W891 Warning a TestCase/TestPlan lists requiresCapabilities that none of its runsOn environments offers (case-insensitive)
W892 Warning a non-draft TestCase/TestPlan runs on a retired TestEnvironment or one whose calibrationStatus is expired

Safety mechanisms (E896–E897, W894)

SafetyMechanism (SM-*, §8.18.9).

Code Severity Condition
E896 Error SafetyMechanism is missing id, name or status, its id is not SM-*, its status is not draft/review/approved/retired, or its reactionTime is not a duration (the coverages' 0..1 range is the generic E846)
E897 Error a SafetyMechanism.covers entry does not resolve, or is not an FMEAEntry, FaultTreeEvent, Requirement, SafetyGoal or HazardousEvent
W894 Warning a non-draft SafetyMechanism's reactionTime exceeds the ftti of a SafetyGoal it covers (directly or through a covered requirement's derivedFromSafetyGoal)

Dependent failure analysis (E890–E892, W890)

DependentFailureAnalysis (DFA-*, §8.18.7).

Code Severity Condition
E890 Error DependentFailureAnalysis is missing id, name, status or analyses, its id is not DFA-*, its status is not draft/review/approved/retired, or analyses names fewer than two elements
E891 Error a DependentFailureAnalysis.analyses entry does not resolve to any model element
E892 Error a sharedResources entry is not a mapping, has no resource, has an unknown kind, or a couplingFactor outside 0..1
W890 Warning an approved DependentFailureAnalysis has a shared resource with no mitigation

GSN safety-argument layer (E852–E858, W040)

The Goal Structuring Notation (GSN) argument layer (§8.18.6). Argument (ARG-*) nodes argue for a SafetyGoal/parent Argument, discharged by evidence; AssumptionOfUse (AOU-*) records a safety-related application condition (SRAC). Render the tree with syscribe safety-case.

Code Severity Condition
E852 Error Argument is missing id, name, or status
E853 Error Argument.id does not match the ARG-* pattern
E854 Error Argument.argumentType is not claim/strategy/solution/context/justification/assumption/undeveloped
E855 Error an Argument.supports or Argument.evidence ref does not resolve to any model element
E856 Error AssumptionOfUse is missing id, name, or status
E857 Error AssumptionOfUse.id does not match the AOU-* pattern
E858 Error an AssumptionOfUse.appliesTo ref does not resolve to any model element
W040 Warning a claim/strategy Argument has empty supports and empty evidence (an orphan GSN node)
E881 Error an Argument is part of a cycle (it transitively supports itself through supports:/evidence:)
E882 Error a state-machine transition's accept: has more than one trigger kind, an unknown trigger key, via/as without a payload, an after that is not a non-negative duration, a when that does not parse, or a done naming no state of the machine (GH #277)
E883 Error a variables: entry of a StateDef is malformed: not a mapping, no name, an invalid or duplicate name, or an initial value that does not evaluate (GH #277)
E884 Error a transition's or initialEffect's assign: has a statement that is not name := expression, or assigns a name that is not a declared variable (GH #277)
E885 Error internal: on a transition is not a boolean, or an internal transition has a target: (GH #277)
E886 Error a TestCase whose subject: is an executable state machine has a Gherkin step outside the executable vocabulary (GH #280); for a composite also a malformed Where/Except under line, an Except under with no Where before it, a step naming an undeclared property, or a property named twice in one scope; for a state machine or action also any Where/Except under line (GH #342, §8.12.8)
E887 Error an executable ActionDef has a wiring error: more than one else succession of a decision, an else that is not last, a decision with no outgoing succession, a decision guard not in the expression language, a while/until loop without condition:, loopKind: for, or an AcceptAction whose after: is not a non-negative duration (GH #276)
E888 Error a plant declaration or wiring is malformed (needs the fmi feature): an fmu: with an unknown key, no file, a kind other than cosimulation or unusable parameters; a TestCase plantStep that is not a positive duration, plants that do not resolve to a Part with fmu:, or plants without a state-machine subject: (GH #291)
E889 Error a plantConnections entry is wrong (needs the fmi feature): it names an unknown plant variable or machine variable, reads an input or writes an output or a parameter, names two plants alike, has a unit dimension different from the FMU variable's, or uses an unsupported type (GH #291)
E930 Error a part of a composite has more than one behaviour (several exhibitsStates, or a state machine and an fmu:), or its exhibitsStates entry does not resolve to a StateDef (needs a composite with an executable machine; GH #297). An fmu: and a plant: on one part are two behaviours too (GH #340).
E931 Error a composite connections: entry joins ports wrongly: the part or port does not exist, or a message does not go from an out/inout port to an in/inout port (GH #297)
E932 Error an executable machine sends or accepts through a port its part definition does not have, or in the wrong direction (GH #297); or, through a port typed by a PortDef, an event the type does not carry, carries the other way (after isConjugated), or a payload field its ItemDef does not have (GH #331, §8.8.5)
E933 Error a composite bindingConnections: entry names a variable the part's machine does not have, or joins variables of different type or unit dimension (GH #297). An in-process plant end must be one of its outputs (read) or inputs (written), of the matching dimension, and one end of every plant binding is a machine variable (GH #340).
E934 Error a composite is malformed: a part without a name, two parts with one name, or a connection delay: that is not a duration (GH #297). A composite with an in-process plant part (plant:) and no plantStep: is E934 too (GH #340).
E935 Error a type or unit error that the evaluator would raise whenever evaluation reaches it, found statically in an executable state machine: a guard or when that is not boolean, a comparison or sum of unequal dimensions, an assignment of the wrong kind or dimension, and/or/not over a number, min/max over unequal dimensions, sqrt of an odd dimension, a non-integer or dimensional pow exponent; names that are not variables (event payload fields) are never errors (GH #272), except a field of an event accepted through a port typed by a PortDef, which has its ItemDef field's type; a send payload value of the wrong kind or dimension for that field is reported too (GH #331, §8.8.5). The same errors in the equations: of a PlantDef, and a derivative or output whose dimension differs from the one declared, are E935 too (GH #340).
E936 Error an injection: on an FMEAEntry row or a FaultTreeEvent is malformed: not a list, an item that is not a mapping, an unknown key or kind (stuck, offset, silent, drop, delay, corrupt), a field missing for its kind, a value/by that is not an expression, or a delay whose by is not a non-negative duration; or, when a composite is built for a scenario, a fault that names a variable, port or value that does not fit the part it applies to (GH #306). A parameter fault (parameter, value) changes a parameter of an in-process plant; stuck/offset may name a plant output and silent freezes a plant (GH #340).
E937 Error a fault <id> is injected/cleared step of a composite TestCase names an id that is not an FMEAEntry or FaultTreeEvent with a well-formed injection: (GH #306), or an Except under clause names such an id (GH #342)
E938 Error a PlantDef is malformed: an entry of parameters:/inputs:/outputs:/states: without a name or with an unknown key, a value that does not evaluate, a duplicate name, no output, an equation that is neither der(state) = expression nor output := expression, a name the plant does not declare, a state without one der(…) or an output without one :=, an algebraic loop between outputs, a method: other than rk4/euler, a maxStep: that is not a positive duration; or a plant key (outputs:, states:, equations:, method:, maxStep:, inputs: as mappings) on an element that is not a PlantDef (GH #340)
E939 Error a plant: declaration is wrong: not on a PartDef/Part, not a PlantDef reference or a {def | library, unit, parameters} mapping, a def: that does not resolve to a PlantDef (or names one with errors), an unknown library: plant, a unit: on a def:, or a parameter override that names no parameter or has the wrong dimension (GH #340)
E990 Error a models: target does not resolve, or is not an architecture element (a PartDef/Part with a domain:); or a part usage carries models: without a part type (GH #330, §12.11)
E991 Error a portMap: key is not a port of the executable part, an executable port corresponds to no port of the elements it models, or the counterpart has an incompatible direction or a different PortDef type or conjugation (GH #330, #331, §12.11)
W990 Warning a port of an element named by models: has no counterpart in any part that models it and is not named in abstracts: (GH #330, §12.11)
W991 Warning an executable composite's connection has no counterpart architecture connection, or an architecture connection between two elements the composite models has no executable counterpart; HW/SW boundary connections and environment parts are skipped, and an architecture element used directly is its own counterpart (GH #330, #332, §12.11)
W992 Warning a TestCase verifies a requirement whose architecture satisfiers no part of its subject: models (or every such part names the requirement in abstracts:): its scenarios cannot be evidence for it; a part that is not the product (role: environment or role: external) is never evidence (GH #330, #332, #337, §12.11)
E992 Error a role: on a composite's features: entry that is not a known role (environment, external), or that is on a port or on an entry not typed by a PartDef/Part (GH #332, §12.11)
W993 Warning a part usage marked role: environment carries its own models: or portMap:; it is ignored, since an environment part is not the product (GH #332, §12.11)
W994 Warning a scenario's @verifies tag or # verifies: step comment names a reference that is not a requirement in its TestCase's verifies:; the claim is ignored (GH #344, §12.12)
W995 Warning every scenario of a TestCase is tagged @verifies and none claims one of its verifies: requirements: no scenario is evidence for it (GH #344, §12.12)
W996 Warning in a context composite (a part holding a role: external usage), a port of a product part's definition that no connection, flow or binding of the composite reaches (GH #337, §12.13)
E942 Error a malformed ResidualRisk: id (RISK-*), name, status or a non-empty faultSet missing; status not proposed, accepted, mitigated or rejected; accepted without approvedBy, mitigated without mitigatedBy; reviewDate/expires not a YYYY-MM-DD date; approvedBy not a username or not in the [users] roster; campaignHash not at least 12 hex digits (GH #341, §12.14)
E943 Error a ResidualRisk faultSet, goal or mitigatedBy entry that resolves to no element (GH #341, §12.14)
E944 Error a ResidualRisk reference of the wrong kind: a faultSet entry that is not an FMEA row or fault-tree event, a goal that is not a SafetyGoal, a mitigatedBy that is not a Requirement or SafetyMechanism (GH #341, §12.14)
W940 Warning a fault set defeats an invariant in the latest campaign recorded with inject --record and no ResidualRisk (proposed, accepted or mitigated) names exactly that set; sets whose faults are all in environment parts are skipped; reported on the composite (GH #341, §12.14)
W941 Warning a ResidualRisk the latest recorded campaign contradicts: proposed or accepted, but the design now survives its fault set; or mitigated, but the set still defeats an invariant (GH #341, §12.14)
W942 Warning a ResidualRisk's campaignHash matches the current hash of no composite whose recorded campaign ran its fault set: the decision was taken on a different design (GH #341, §12.14)
W943 Warning a proposed or accepted ResidualRisk whose expires date has passed (GH #341, §12.14)
W944 Warning the latest recorded campaign of a subject ran on a composite hash the composite (as projected for the record's configuration) no longer has, or the composite can no longer be built to check: its verdicts may not hold; run the recorded inject … --record command again (GH #341, §12.14)
W945 Warning the campaign record file .syscribe/campaigns.json exists but cannot be read or parsed: no campaign is judged, and inject --record refuses to overwrite it (GH #341, §12.14)
W946 Warning a proposed, accepted or mitigated decision names a fault set the latest record for its configuration did not run (a narrower re-record dropped it): the decision is no longer checked; re-run the recorded command (GH #341, §12.14)
W861 Warning an Argument of argumentType: solution has no evidence: (draft-suppressed)
W931 Warning an FMEAEntry row has no fmeaSeverity/occurrence/detection — its RPN cannot be computed (draft-suppressed)
W932 Warning an FMEAEntry with fmeaSeverity ≥ 9 has no recommendedAction regardless of RPN (severity-priority rule; skipped when W903 fires; draft-suppressed)
W933 Warning in an executable state machine, a guard: or when: that is not in the expression language or uses a bare name that is not a declared variable (GH #277)
W935 Warning in an executable ActionDef, a succession guard or condition: that is not in the expression language, or that uses a bare name which is not a parameter, a variable, an as binding or a sub-action (GH #276)
W936 Warning traffic along a composite connection that the other end does not handle: an event sent via a port that the connected part never accepts, or accepted but never sent (draft-suppressed; GH #297)
W934 Warning a malformed [audit] entry in .syscribe.toml; the entry is ignored

The Tier 2 (E800–E837, E859–E864) and Tier 4 (E900–E941, W036, W037, W900–W905, W926–W928) codes are listed in Code families specified elsewhere above; §8.18 defines the element schemas and docs/validation/rules.md groups every code by element type.

sourceFile location semantics

A sourceFile: value is interpreted by its form, so each element can choose how its path is resolved:

Form Resolves to
path (bare relative) model root + path (default)
model:<path> model root + path (explicit)
repo:<path> repository root + path
/abs/path the absolute path as-is
file://… the local path encoded in the file URI
scheme://… (any other scheme) a remote location — not resolved or read locally
<registry>:<package>@<version>[#path] a remote package-registry reference (crates.io, npm, pypi, maven, nuget, github — the §18.3 prefixes) — not resolved or read locally

The repository root is taken from repo_root in <model_root>/.syscribe.toml (resolved against the model root when relative), or auto-detected as the nearest ancestor directory containing .git.

.syscribe.toml also marks the model root. When a command is given no -m/--model flag and no SYSCRIBE_MODEL environment variable, the tool walks upward from the current working directory and uses the nearest ancestor that contains a .syscribe.toml as the model root (falling back to the literal model directory if none is found). An empty .syscribe.toml is a valid root marker. This is a tooling locator only — it does not affect qualified-name resolution or the implicit root namespace.

Local forms are subject to W004 (existence) and W009 (function resolution). Remote URIs are treated as external: W004 is not emitted and W009 is skipped, since the file cannot be read during validation — unless a download hook is enabled (see below).

Remote download hook

A model may configure a hook that fetches remote sourceFiles so they can be verified like local ones:

[remote]
download = "curl -fsSL {url} -o {dest}"
# cache_dir = ".syscribe/cache"   # optional (default shown)

The download command is run via POSIX sh; {url} and {dest} are substituted as shell-quoted values, and SYSCRIBE_URL / SYSCRIBE_DEST are set in the environment. Fetched files are cached under cache_dir keyed by URL (extension preserved so the matcher still selects the language).

For safety the hook is opt-in: defining it has no effect until the user runs validate --fetch-remote, so validating an untrusted model never executes configured commands. With --fetch-remote, a successfully fetched remote file is subject to W009; a URL the hook cannot retrieve raises W004.

TestCase drift scoping

The source-drift checks W004 (sourceFile existence) and W009 (testFunction resolution) are scoped to a TestCase's lifecycle, because drift is only meaningful once a test is claimed to exist. The TestCase status vocabulary and its drift treatment:

status Meaning Drift treatment
draft specified skeleton, not implemented planned — informational I010, no W004/W009
review spec under review planned — I010
approved spec approved, not yet implemented planned — I010
active implemented and live live — W004/W009 apply
retired decommissioned suppressed — no W004/W009/I010

A non-TestCase element that carries a sourceFile: is always checked (W004), independent of any status. This lets a model honestly carry planned verification (e.g. future HIL/WCET cases) without fake stubs or warning noise, while validate --deny W004,W009 gates the live verification set cleanly.

Informational codes

Informational findings surface a fact without failing validation. They never cause a non-zero exit on their own, but can be selected explicitly with --deny <code>.

Code Condition
I010 A planned TestCase (status draft/review/approved) has a sourceFile/testFunctions[].function that is not present yet (the planned-verification counterpart of W004/W009).
I011 Info

Function matchers (W009)

W004 confirms a local TestCase's sourceFile: exists; W009 additionally confirms each testFunctions[].function resolves to a real test/function definition in that file. The function name is the last segment of the function string after splitting on ::, ., #, or /.

Built-in, language-aware matchers (keyed by file extension):

Language Extensions Recognises
Rust .rs fn name (incl. #[test], #[kani::proof])
Java .java method declarations
C / C++ .c .h .cpp .cc .cxx .hpp .hh .hxx .ino function definitions; GoogleTest TEST / TEST_F / TEST_P
Kotlin .kt .kts fun name; backtick test names
Shell .sh .bash .bats .zsh .ksh POSIX name(); function name; bats @test "..."

Any other file type (e.g. .robot, .feature, .txt, generated test manifests) uses a generic whole-token fallback: the test name must appear as a complete token in the file. This lets any file that represents a test participate in traceability while still catching deletions.

Projects can override or add per-extension patterns via a [matchers] table in <model_root>/.syscribe.toml, where each key is an extension (no dot) and each value is a list of regexes whose capture group 1 is the defined test name:

[matchers]
py = ['def\s+(test_[A-Za-z0-9_]*)\s*\(']
feature = ['Scenario:\s*(.+)']

An override replaces the built-in patterns for that extension. W009 severity is overridable via the CI gating flags (--deny W009).

Test result ingestion (W010)

syscribe ingest-results --format <cargo-json|junit> <file> parses an external test report (libtest JSON or JUnit XML), reduces it to a per-test verdict keyed by the test's leaf name, and writes a sidecar at <model_root>/.syscribe/results.json. When that sidecar is present (or results are supplied ad-hoc with validate --results <file>), the validator emits W010 for every active TestCase whose testFunctions[].function last failed, was ignored/skipped, or was missing from the run — so "verified" can mean "covered by a test that actually passed". Passing functions are silent, and W010 is inert when no results have been ingested. Gate on it in CI with --deny W010.

--format session-log (issue #113) is the manual/exploratory-verification counterpart, for a TestCase with no testFunctions: at all (the norm for a scenario verified via a live session — a curl/MQTT/CLI walkthrough — rather than a #[test] function). Input is a JSON array of per-Gherkin-scenario records:

[{"testCase": "TC-WEB-011", "scenario": "An empty/unset allowlist denies everything",
  "steps": [{"cmd": "curl -X POST ... -d command=restart_hmi", "expect_status": 400}],
  "result": "pass", "timestamp": "2026-09-12T08:02:29Z"}]

reduced to a verdict keyed by "{testCase}::{scenario}" (scenario must match a Scenario:/Scenario Outline: title in that TestCase's body). Unlike cargo-json/junit's tolerant line-skipping, a record with an empty/missing testCase, scenario, or steps, or an unrecognized result (must be pass/fail/unknown), is a hard parse error — nothing is written, and any existing sidecar is left untouched, rather than silently producing an empty result set. matrix/trace/safety-case/testplan roll a TestCase's session-log scenario verdicts up into the same Pass/Fail/Unknown annotation testFunctions:-sourced verdicts already get ([pass]/[fail], ▣ covered, not passing, …) — Pass only when every scenario in the body has a recorded pass, exactly mirroring the "every function must pass" rule for testFunctions:.

--format records (REQ-TRS-RESREC-001) is the normalised record for system, integration and hardware-in-the-loop results, which are rarely JUnit. Input is a JSON array; each record has testCase (a TestCase id or qualified name), an optional scenario (a Scenario: title; absent means the verdict of the whole TestCase), a verdict, and optionally rationale, steps, measurements and provenance. Unknown fields are rejected.

Field Rule
verdict pass, fail, flaky, blocked, not-run, inconclusive, ignored or waived. Only pass is a pass; waived needs a non-empty rationale.
measurements[] {name, value, limit?}; value is a quantity with its unit ("4.2 ms"), limit is {op, value} with op one of <, <=, >, >=, ==. Value and limit must have the same dimension. A pass whose measurement violates its own limit is rejected.
provenance.build {commit?, version?}
provenance.environment a TestEnvironment id or qualified name; it must resolve, and is stored by id
provenance.operator free text
provenance.tool the program that produced the report, as it names itself (Robot 7.4.2)
provenance.startedAt, finishedAt RFC 3339
provenance.artefacts[] {uri, hash}; hash is sha256: or blake3: followed by 64 hex digits. Referenced, never copied.

A malformed record is a hard parse error naming the record: nothing is written and an existing sidecar is untouched. testCase is resolved as a TestCase id, then a qualified name, then an extRef value (unique per model, W028); the record is stored under the canonical id. A key that is ambiguous, or names an element that is not a TestCase, rejects the whole ingest; a key that names nothing is kept under unmatched, reported and listed by results failures, and counts as evidence for nothing (--strict rejects such a file instead). Each stored record keeps the projection hash of its TestCase and of the requirements it verifies; validate raises W091 for a non-draft TestCase whose results predate a change to either, and trace marks the verdict [stale]. Hashes a report claims for itself are discarded. If one file repeats a (test case, scenario, environment) key, the last record is kept; a leading UTF-8 byte-order mark is ignored. The sidecar gains a records section holding the latest record per (test case, scenario, environment); an ingest supersedes the records with the same key and keeps the others, and --replace drops the records the ingest does not contain. Verdicts derived from the records are kept in by_record and combined with by_scenario by taking the worse. Robot Framework (REQ-TRS-RESROBOT-001). ingest-results --format robot (or an .xml file whose root is <robot>) reads a Robot 6 or 7 output.xml and produces records, so system and acceptance tests written in Robot join the graph without a converter. A test is joined to a TestCase by a tag equal to its id or qualified name, or extref:<value> for an extRef; other tags are ignored. A test named like a Scenario: of that TestCase is recorded against it, any other test as the whole TestCase's verdict; several tests for one TestCase and scenario fold into one record with the worst verdict, the failing test and its message first in steps. PASS is pass, FAIL is fail, SKIP and NOT RUN are not-run, and a test that never ran because a parent suite's setup failed is blocked. A test that joins nothing is reported and kept under unmatched. Robot's timestamps have no zone: they are kept as written with --utc-offset (default Z). --operator, --build-commit, --environment and --artefact <file> (blake3 hash, never copied) supply what the file does not contain. RPA results are refused, keyword-level results and logs are not read.

Cucumber JSON (REQ-TRS-RESCUKE-001). ingest-results --format cucumber (or a .json file shaped like one) reads the classic json formatter of Cucumber-JVM, Cucumber.js and Cucumber-Ruby; the newer NDJSON message protocol is refused. It produces one record per scenario. A scenario is joined to a TestCase by a tag of its own or its feature's (an id or qualified name, or @extref:<value>); a scenario titled like a Scenario: of the TestCase is recorded against it, any other as the TestCase's own verdict, and one that joins nothing is reported and kept under unmatched. A scenario is fail when a step is failed or ambiguous or a hook failed, not-run when a step is pending, undefined or skipped or it has no steps of its own, otherwise pass; a background (a separate element in JVM and Ruby, merged into the scenario in JS) and its hooks count toward the scenarios after it. The rows of a Scenario Outline are <title> [row N], the outline's own title carrying the worst row. Attempts of one scenario (same feature, id and line) fold: the last decides, and a pass after a failure is flaky. A scenario's time span comes from start_timestamp and the step durations when the formatter writes them. Embedded data and step output are never stored.

Results plugins (REQ-TRS-RESPLUG-001). ingest-results --format plugin:<alias> converts a report of any format with an external program configured as [plugins.<alias>] with kind = "results" (the default kind is a foreign-format plugin, and the two do not mix: E552). Syscribe writes one JSON request to the plugin's stdin (protocolVersion 1, alias, the absolute reportPath and modelRoot, and run, config, environment when given) and reads one JSON object from its stdout, {records, diagnostics}, where records are the records of --format records and diagnostics are shown to the user. The records go through exactly the record validator, the join and the environment resolution of the built-in format; a plugin cannot store anything the built-in format could not, and hashes it supplies are discarded. A plugin that is missing, cannot start, times out, exits non-zero, writes an invalid envelope or no records, or returns records the validator rejects fails the ingest with E553 and nothing is written. --environment and --build-commit fill provenance a record leaves empty and never override a record's own value. plugins run <alias> --report <file> --dry-run prints the plugin's raw envelope without merging or validating it. A plugin is a plain OS subprocess with the user's rights, the same trust level as the foreign-format plugins; timeout_ms is the only enforced limit.

Results per TestEnvironment (REQ-TRS-RESENV-001). provenance.environment, or ingest-results --environment <TE> for records that name none, records the rig. The latest record per (test case, scenario, environment) is kept. A TestPlan with runsOn: is judged on each listed environment alone: all must pass, a rig with no passing result leaves the plan incomplete (and W615 names <test> @ <environment> for an approved plan), records on other rigs and records with no rig are ignored, and records from a retired or calibrationStatus: expired rig do not count. A plan without runsOn: takes the worst over every rig. A TestCase with its own runsOn: is judged the same way where it is read directly (trace, matrix), with the entries matched as written, so name the rigs by id. A Configuration's effective results are chosen first and the rig filters inside them. trace marks a verdict with the rigs it is not passing on (TC-X [fail] @ TE-HIL-01) and results failures lists each non-passing record with its rig.

A TestCase rolls up as pass only when every scenario passed; waived when every scenario passed or was waived and at least one was waived (shown as [waived], counted as not complete by safety-case, never as a pass); fail when any failed; otherwise unknown. A sidecar without these sections reads unchanged.

Exit-code contract (CI gating)

The validate subcommand exposes a stable exit-code contract so it can be used directly as a CI gate:

Exit code Meaning
0 No Error-severity findings and no gate failure
1 One or more Error-severity findings present
2 One or more Warning-severity findings tripped a configured gate

Gating is opt-in via flags on validate:

  • --deny <CODES> — comma-separated warning codes promoted to gate failures (e.g. --deny W004,W009).
  • --max-warnings <N> — fail when the total warning count exceeds N.
  • --warnings-as-errors — promote every warning to a gate failure.
  • --profile <name> — apply a named, optionally integrity-level-scoped gating policy declared in <model_root>/.syscribe.toml (see below).

Error findings always dominate (exit 1) regardless of gating flags. With no gating flag, warning-only models exit 0. This enables a phased rollout: warn during burndown, then --deny to enforce.

The contract is identical under the configuration lens (§9.10). validate --config <C> gates (and applies --file to) the projected variant's findings. validate --all-configs evaluates the gate per variant — --max-warnings N is a per-variant budget — and exits with the worst variant under the precedence 1 > 2 > 0: 1 if any variant has an Error finding, else 2 if any variant tripped a gate, else 0.

A usage error on validate — an undefined --profile name, an unresolvable --config argument, a malformed --max-warnings value, an unknown option, an unreadable --results file — prints a message to stderr, prints nothing to stdout, and exits 1. Exit 2 is reserved exclusively for a tripped gate, so it always means "no errors, but a gate failed".

Named severity profiles ([profiles.*])

Reusable gating policies are declared as [profiles.<name>] tables in <model_root>/.syscribe.toml and selected with validate --profile <name>. A profile promotes the warning codes in its promote list to gate failures, optionally scoped to the integrity level / status / tag of the element each finding concerns:

[profiles.safety]
promote = ["W002", "W015", "W300"]   # warning codes promoted to gate failures
# OPTIONAL scope — promotion applies only to findings on an element matching ALL fields:
sil    = "4"          # element's silLevel stringifies to "4" OR asilLevel == "4"
status = "approved"   # element's status:
tag    = "safety"     # element's tags: contains this

A finding trips the gate when its code is listed in promote and either the profile declares no scope fields, or the element whose file_path equals the finding's file matches all of the provided scope fields (a finding mapping to no element is not promoted when a scope is set). Multiple profiles may coexist, and the [profiles.*] tables parse alongside the existing [matchers] / [remote] tables and the repo_root key. An undefined profile name (or a missing .syscribe.toml) is a usage error and exits 1. --profile composes additively with --deny / --max-warnings / --warnings-as-errors.


12 Traceability Rules and Domain Conventions

This section defines mandatory traceability rules that govern how requirements, architecture elements, and design decisions are linked. These rules are normative — a conformant tool MUST enforce them via the validation codes defined in §11.12.


All traceability links in Markdown-SysML follow OSLC (Open Services for Lifecycle Collaboration) upstream-link semantics: the artifact that is derived from, implements, or verifies another always holds the link field pointing to the upstream artifact.

Link field Direction Meaning
derivedFrom: child → parent This requirement was broken down from the parent
verifies: test → requirement This test case verifies the requirement
satisfies: element → requirement This element implements the requirement — not type-restricted (§12.3)
allocatedTo: source → target The element being allocated (the logical function, software package, or security control) holds allocatedTo: naming the element that realises it (§12.9 form 1). allocatedFrom is derived — the reverse index on the target — never the recommended thing to author; a standalone Allocation element (§12.9 form 2) names both ends because it is the relationship. An allocatedFrom: authored on a non-Allocation target is accepted only as a legacy input form (§12.9)
breakdownAdr: requirement → ADR This requirement's breakdown is documented in the ADR

No reverse links are stored in model files. Reverse indices (verifiedBy, derivedChildren, satisfiedBy, allocatedFrom) are computed by the parser at load time and never written to disk; verifiedBy: and derivedChildren: are not frontmatter fields at all. Two authored fields share a name with a computed index and are not the index: a ViewpointDef's satisfiedBy: (§8.14.1), an authored viewpoint-conformance list that is stored as written and never merged into the computed index; and allocatedFrom: on a standalone Allocation element (§12.9 form 2), where naming both ends is the relationship itself (plus its legacy input form on a non-Allocation target, §12.9).

The same convention governs user-defined link types (§12.10): the element holding a links: entry is the source, and the declared inverse is computed, never authored.


12.2 Requirement Breakdown and ADRs

When a requirement is broken down into multiple child requirements, the breakdown must be documented in an Architecture Decision Record (ADR, §8.17).

Rule R-002: Every native Requirement that has one or more derivedFrom: entries must set breakdownAdr: to the id or qualified name of an accepted ADR that documents the rationale for the breakdown. (Error E310 if absent; warning W303 only when the requirement is approved, implemented or verified while the referenced ADR is still proposed.)

Breakdown procedure: 1. Author the ADR (type: ADR, status: proposed) documenting context, decision, and consequences. 2. Get the ADR reviewed and set status: accepted. 3. Create the child requirements with derivedFrom: [PARENT-ID] and breakdownAdr: ADR-ID. 4. The parent requirement's status remains unchanged; it must not be moved to verified unless all child requirements are verified.

Example:

---
type: Requirement
id: REQ-SCHED-BITMAP-001
name: "Bitmap-based O(1) priority selection"
status: approved
derivedFrom:
  - REQ-UAV-SCHED-001
breakdownAdr: ADR-SW-SCHED-001
...


12.3 Leaf-Level Assignment Rule

A requirement is a leaf requirement when no other requirement has derivedFrom: pointing to it (i.e., the computed derivedChildren index is empty for this requirement's id).

Rule R-003: A leaf requirement at status: approved or status: implemented must be assigned to at least one element — meaning at least one element must have satisfies: referencing this requirement. Several elements may jointly satisfy a leaf (for example a PartDef and the StateDef that gives it its behaviour, or redundant channels): the satisfier count never changes whether a requirement is a leaf or a parent, which is decided by derivedChildren alone, and a requirement is decomposed only when the decomposition adds requirement content — never to force one-to-one allocation. Part/PartDef is the common case, but satisfies: is not type-restricted. The following count identically to a Part/PartDef satisfier:

  • A behavioral definition (StateDef/ActionDef) — a state machine or an activity can be the artifact directly responsible for fulfilling a requirement, not only the structural element that owns it.
  • Connection/ConnectionDef, Interface/InterfaceDef, Item/ItemDef, Attribute/AttributeDef, Port/PortDef, Allocation — the same fixed set of requirement/architecture-shaped kinds E104 (§11.10) already recognises as legal verifies: targets when synthesized by SysMLv2 submodel ingestion or a stdio-subprocess plugin; extended here for consistency, since the natural direction of the claim (an architecture element declaring what it satisfies) is the mirror image of that check.
  • Flow/FlowDef — not in E104's fixed kind list, but the same "artifact that can fulfil a requirement" reasoning applies to an item flow.

No other element kind is restricted from declaring satisfies: either — the validator never gates on the source element's type — but only the kinds above are formally endorsed and regression-tested; anything else is permitted by absence of a check, not by design.

  • Zero satisfying elements → warning W300 (only at status: approved or implemented; skipped for non-allocatable requirementKind values process, regulatory and deliverable)
  • More than one satisfying element → no finding (W301 is retired — GH #121). Satisfying a parent requirement is still error E312 (§12.4).

The assignment can evolve iteratively: 1. Initially, assign to a higher-level block (satisfies: UAV::FlightController). 2. When the internal design of that block is defined, update the satisfies: to the specific sub-element (satisfies: UAV::FlightController::SchedulingModule) — or to the behavioral definition that actually implements it (satisfies: UAV::FlightController::SchedulingModule::ModeTransitionSM, a StateDef). 3. A requirement's satisfies: should point to the deepest known element — structural or behavioral — that is responsible for fulfilling it.


12.4 Parent Requirements Cannot Be Assigned

Rule R-004: A requirement that has been broken down (has a non-empty derivedChildren index) must not appear in any element's satisfies: list.

Only leaf requirements may be assigned to architecture elements. Assigning a parent requirement is an error (E312) because it conflates the requirement with its children and makes traceability ambiguous.

The parent requirement may still carry status: approved, status: implemented (when all children are implemented), or status: verified (when all children are verified) to track aggregate status.


12.5 Requirement Domain Classification

Native Requirement elements carry a reqDomain: field (§8.11.6) indicating which engineering domain they govern.

reqDomain value Meaning
system Domain-agnostic. Used for top-level stakeholder requirements before domain allocation.
hardware The requirement governs a hardware element (physical component, board, sensor, actuator).
software The requirement governs a software element (module, binary, firmware, algorithm).

Rule R-005a: A leaf requirement must be satisfied only by an element (structural or behavioral — §12.3) whose domain: matches the requirement's reqDomain:, unless either is system. (Error E313.)

Rule R-005b: A leaf requirement at status: implemented or status: verified that still has reqDomain: system should be refined to hardware or software. (Warning W302.)

During top-down decomposition, reqDomain: naturally migrates from system to hardware or software as the design progresses. This migration is an explicit model edit, not an automatic inference.


12.6 Hardware/Software Architecture Independence

The hardware and software architectures are independent hierarchies. They interact only through Allocation elements.

Rule R-006a (no direct cross-domain references): An element with domain: software must not have supertype: or typedBy: referencing an element with domain: hardware, and vice versa. Cross-domain direct references are errors (E315). The correct pattern is an explicit Allocation.

Rule R-006b (deployment allocation): A Part or PartDef with isDeploymentPackage: true must be the source of at least one allocation edge whose target is an element with domain: hardware. (Error E314.) Every form of the §12.9 unified edge set counts: an Allocation element whose allocatedFrom: is this element and whose allocatedTo: references the hardware element (top-level or per features: entry), allocatedTo: on the package itself, or a legacy allocatedFrom: authored on the hardware element.

Correct cross-domain pattern:

# model/Software/SchedulerModule.md — SW element
---
type: PartDef
name: SchedulerModule
domain: software
isDeploymentPackage: true
---

# model/Hardware/FlightComputer.md — HW element
---
type: PartDef
name: FlightComputer
domain: hardware
---

# model/Allocations/schedulerToFC.md — the only permitted cross-domain link
---
type: Allocation
name: schedulerToFC
allocatedFrom: Software::SchedulerModule
allocatedTo: Hardware::FlightComputer
---

Rule R-006c (requirement domain consistency): Hardware requirements must be satisfied by domain: hardware elements; software requirements must be satisfied by domain: software elements — structural or behavioral (§12.3). This is enforced by Rule R-005a (§12.5).

ADR for deployment decisions

When a software component is first allocated to a hardware element, or when the hardware allocation changes, an ADR should document the rationale. Reference it from the deployment Allocation using the metadata: field:

---
type: Allocation
name: schedulerToFC
allocatedFrom: Software::SchedulerModule
allocatedTo: Hardware::FlightComputer
metadata:
  - type: ModelingMetadata::Rationale
    text: "See ADR-HW-DEPLOY-001 for platform selection rationale"
---

12.7 Safety/Security Integrity Level Propagation

Rule R-007: Once an element in the traceability chain carries an ASIL or SIL integrity level (asilLevel: or silLevel:), every downstream element that references it via derivedFromSafetyGoal:, derivedFrom:, or satisfies: must also carry an integrity level — either asilLevel: or silLevel: (the check does not require the same field as the source). An element that carries neither when its upstream source carries one is an error (E841, E842, or E843 depending on the link kind). plLevel: (ISO 13849-1) does not take part in propagation: it neither triggers the rule on a source nor satisfies it on a downstream element.

Level constraint: The downstream element's level may be the same as or lower than the upstream element's. A lower level indicates an ASIL/SIL decomposition (ISO 26262-9, IEC 61508-2 §7.4.9): the model asserts that the downstream component achieves the weaker target through architectural independence or redundancy arguments.

ADR requirement for decomposition: When a downstream element carries a lower level than its source, it must set breakdownAdr: to the ADR documenting the decomposition rationale (W808 if breakdownAdr: is absent). The level is compared only when both elements use the same scale (both asilLevel: or both silLevel:); a mixed ASIL/SIL pair is never reported as lower.

Link Enforced by Missing field Lower level without ADR
derivedFromSafetyGoal: → SafetyGoal R-007 E841 W808
derivedFrom: → parent Requirement R-007 E842 W808
satisfies: → Requirement R-007 E843 W808

Level comparison rules:

  • asilLevel: ranks A < B < C < D.
  • silLevel: ranks 1 < 2 < 3 < 4.
  • plLevel: (ISO 13849-1) has values a–e; it is not compared and is ignored by R-007.
  • Mixing asilLevel: on one element with silLevel: on another satisfies the presence check but is never compared for level; W006 flags the case where both asilLevel: and silLevel: appear on the same element.

Example — ASIL D requirement decomposed to ASIL B:

# Parent SafetyGoal
---
type: SafetyGoal
id: SG-BRAKE-001
name: "Prevent unintended brake release"
status: approved
asilLevel: D
hazardousEvents: [HE-BRAKE-001]
---

# Derived requirement — decomposed to ASIL B via redundancy argument
---
type: Requirement
id: REQ-BRAKE-HYD-001
name: "Maintain hydraulic pressure within 50 ms"
status: approved
asilLevel: B
verificationMethod: test
derivedFromSafetyGoal: SG-BRAKE-001
breakdownAdr: ADR-BRAKE-DECOMP-001    # documents the decomposition rationale
---

12.8 Implementation Trace

Rule R-008: A Part, PartDef, Interface or InterfaceDef may carry an optional implementedBy: field linking the architecture element to the source artifact(s) that realise it. This closes the downstream leg of the V-model:

Requirement ─satisfies→ Architecture ─implementedBy→ Code ─verifies→ Test

implementedBy: accepts a single string or a list of strings. Each value is a path into the codebase, resolved with the same rules as a TestCase's sourceFile (§8.12.5, §11.12): model-root-relative (the default for bare paths), model:-prefixed (model-root-relative), repo:-prefixed (repository-root-relative), absolute, file://, remote scheme:// URIs, and package-registry references <registry>:<package>@<version> (crates.io:, npm:, pypi:, maven:, nuget:, github: — §18.3). Local paths are checked on disk; remote URIs and package-registry references are accepted as external pointers and not verified locally. The registry prefixes are exactly those sbom maps to a Package URL — one shared definition, so a value the SBOM emits as a package component is never also reported missing on disk. A value that merely resembles one (unknown prefix, no @<version>, a Windows drive such as C:\src) stays a local path.

Validation (W023): When a non-draft Part/PartDef/Interface/InterfaceDef declares implementedBy: and a local path does not exist on disk, the tool emits W023 (one finding per missing path) — the architecture-to-code analog of W004 for sourceFile. The rule is:

  • Opt-in — an element with no implementedBy: is never flagged.
  • Draft-suppressed — elements with status: draft are skipped (the implementation may not exist yet).
  • Remote-tolerant — remote (scheme://) targets and package-registry references (crates.io:tokio@1.38.0, npm:lodash@4.17.21, github:org/repo@v1, …; §18.3) are not verified locally.
  • Gateable — validate --deny W023 exits non-zero when any W023 is present.

The link is discoverable through tooling: syscribe links <element> lists implementedBy paths as outbound relationships, and syscribe refs <path-or-dir> reverse-maps a source path (or directory prefix) back to the declaring architecture element(s).

Example:

---
type: PartDef
name: Scheduler
domain: software
satisfies: [REQ-SCHED-001]
implementedBy:
  - src/scheduler/mod.rs
  - repo:src/scheduler/bitmap.rs
---

12.9 Allocation: Two Forms over One Edge Model

An allocation links a source element (typically a logical function or subsystem) to the target that realises it. The format supports two first-class ways to author an allocation, both feeding a single unified edge set and a derived allocatedFrom reverse index. Neither form is deprecated and there is no forced migration.

Form 1 — allocatedTo: on the source element (OSLC default). The source element being allocated holds allocatedTo: <target | [targets]>. The source is the allocatedFrom, which is derived, not authored — obeying the OSLC link-direction rule (§12.1) exactly like satisfies: / verifies: / refines:. This is the lightweight default for an allocation that needs no documentation of its own.

---
type: ActionDef
name: ComputeThrust
allocatedTo: Logical::PropulsionController   # this action → that part
---

Form 2 — a standalone Allocation element (documented allocations). A type: Allocation element names both allocatedFrom and allocatedTo, either top-level or per features: entry. This is a reified relationship artifact — kept for when the allocation itself needs a documented body (a freedom-from-interference argument, deployment rationale, or integration-test notes). Naming both endpoints is its purpose, not redundancy. The HW/SW FFI and deployment allocations of §12.6 use this form. A features: entry is recognised as an edge when it carries both allocatedFrom and allocatedTo, regardless of whether the entry also declares a feature-level type: Allocation. The features form belongs to form 2 only: a features: entry that declares an allocation (a feature-level type: Allocation, or an allocatedFrom:/allocatedTo: key) on any element that is not type: Allocation is not an allocation edge — it feeds nothing in the unified set below — and raises warning W930 so it is not silently ignored (its endpoints are still checked by E500/E501). Use allocatedTo: on the source (form 1) or move the entry to a standalone Allocation element.

---
type: Allocation
name: PropulsionAllocation
features:
  - name: computeToController
    allocatedFrom: Functions::ComputeThrust
    allocatedTo: Logical::PropulsionController
---

One unified edge set. The allocation-edge set (source → target) consumed by MG041, MG081, E314 (§12.6 R-006b), W034 (freedom from interference), the matrix --allocations view, and the derived allocatedFrom index comes from a single extractor that yields edges from both forms. So matrix --allocations, the MagicGrid gate and the safety checks can never disagree, and an allocation authored in either form produces the same edge. A standalone Allocation element is never itself an endpoint: it contributes its allocatedFrom → allocatedTo edge.

Legacy input: allocatedFrom: authored on the target. allocatedFrom is derived, and authoring it is not recommended. For backward compatibility an allocatedFrom: authored on a non-Allocation element (the realising target) is still accepted: each resolved entry S contributes the edge S → that element to the unified set — so it shows in matrix --allocations, the derived index, E314 and W034 exactly like the two forms above — and an unresolved entry raises E502. Migrate it to allocatedTo: on the source, or to a standalone Allocation element when the allocation needs documenting.

Ingested SysML v2 constraints, calcs, use cases and package docs. Inside a sysmlSubmodel: true package, constraint def/constraint become ConstraintDef/Constraint (§8.10) with parameters: from in/out/inout declarations and the body expression rendered into expression: as an opaque string; calc def/calc become CalculationDef/Calculation (§8.9) with parameters: (return declarations as direction: return), returnType: and the body expression as body: with bodyLanguage: kerml; use case def/use case become UseCaseDef/UseCase (§8.12.4) carrying subject, actors, objectives, result and isAbstract as for the case family; and a package-level doc becomes the Package element's doc text. Defs carry supertype:, usages typedBy:. include/extend, nested constraint members and assert/negation are not lifted. (REQ-TRS-SYSMLV2-033..-036.)

Ingested SysML v2 allocations. A named allocation usage inside a sysmlSubmodel: true package (ADR-SYS-SYSMLV2-001) is synthesized as a form-2 Allocation element: its allocate <source> to <target> clause becomes allocatedFrom: [<source>] / allocatedTo: [<target>], so it feeds the same unified edge set. Endpoints are resolved against the whole model at load time — the head name (possibly ::-qualified) innermost scope first from the allocation's owning namespace out to the model root, each further . feature-chain segment as a feature declared on, or inherited through the typedBy:/supertype: chain of, the element reached so far. An unresolvable chain tail is truncated to the deepest resolved prefix with warning W542; an unresolvable head is kept as written (. → ::) and reported by E502/E503. The anonymous allocate a to b; statement has no identity and is not mapped. (REQ-TRS-SYSMLV2-029, GH #144.)

Derived allocatedFrom index. Each target element gains a derived reverse index allocatedFrom listing every source allocated to it, aggregated over both forms — surfaced in show (an ## Allocated from section), links, and the export computed block exactly like verifiedBy / refinedBy / mopRefinedBy.

Resolution and redundancy.

  • Each allocatedTo operand must resolve by qualified name or stable id; an unresolved target raises E503 (and an unresolved allocatedFrom on the standalone form raises E502).
  • A features-form allocation on a non-Allocation element contributes no edge and raises W930 (see form 2 above).
  • When the same source → target edge is declared by more than one form — an allocatedTo on the source, a standalone Allocation element, or a legacy authored allocatedFrom on the target — the tool emits W503 once for that edge, naming the forms — the duplicate is redundant, so pick one form. A single edge in a single form raises nothing.

Guidance: use allocatedTo: by default; promote to a standalone Allocation element only when the allocation needs its own documentation.


Every relationship in §12.1–§12.9 is built in: a dedicated frontmatter field, a graph edge kind, and a fixed set of validator rules bound to that field. Projects routinely need relationships the format does not name — mitigates (control → hazard-derived requirement), conflictsWith (requirement ↔ requirement), partiallySatisfies, informs. User-defined link types let a project declare such a relationship once, in project configuration, give it its own constraints, optionally make it a variant of a built-in trace link with named rules relaxed, and then author, validate, traverse and suspect-check it like any built-in link. (ADR-SYS-LINKTYPE-001; REQ-TRS-LINKTYPE-000..012.)

The feature is opt-in and purely additive: a model with no [linkTypes] table and no links: field produces no new finding and no output change.

SysMLv2 mapping. A declared link type corresponds to a user-defined dependency kind (a metadata def applied to a dependency, in SysMLv2 textual terms) — a directed, source-owned relationship between two elements. Syscribe keeps the declaration in project configuration rather than as a model element (see Alternatives in the ADR): link vocabulary is project policy, like [ids.prefixes], and must be known before element validation begins.

12.10.1 Declaration — [linkTypes.<name>] in .syscribe.toml

A link type is declared as a table in the model-root .syscribe.toml:

[linkTypes.mitigates]
description = "A design control mitigates a hazard-derived safety requirement"
inverse     = "mitigatedBy"
sourceTypes = ["PartDef", "Part"]
targetTypes = ["Requirement"]
cardinality = "0..*"
acyclic     = false
suspect     = true

[linkTypes.partiallySatisfies]
description = "Contributes to, but does not by itself satisfy, a requirement"
inverse     = "partiallySatisfiedBy"
extends     = "satisfies"
relax       = ["E313"]
coverage    = false

Name grammar. The table key (the link-type name) and any inverse name must match ^[a-z][A-Za-z0-9]*$ (lowerCamel) and must not collide with:

  • a built-in link, field or reverse-index name — satisfies, verifies, derivedFrom, refines, supertype, typedBy, subsets, redefines, allocatedTo, allocatedFrom, satisfiedBy, verifiedBy, derivedChildren, refinedBy, specializedBy, links, and the other built-in relationship names;
  • another declared link-type name; or
  • another declared type's inverse.

Keys. All keys are optional. Each may be written camelCase or snake_case (sourceTypes / source_types).

Key TOML type Default Meaning
description string absent Prose shown by link-types, --agent-instructions and the MCP link_types tool. Strongly recommended — it is what an authoring agent reads to decide whether the type applies.
inverse string absent Name of the reverse direction. Usable with follow and shown by links for inbound instances. Never authored in frontmatter — reverse links are computed (§12.1).
sourceTypes list of element-type names absent (any type) Element type: values permitted to hold this link. Enforced by E633.
targetTypes list of element-type names absent (any type) Element type: values a target may resolve to. Enforced by E634.
cardinality string "0..*" Number of targets each source holds for this type: "N", "N..M", or "N..*". Upper bound enforced by E635; lower bound by W631. A non-zero lower bound requires sourceTypes (otherwise the entry is W630).
acyclic bool false When true, any cycle formed solely by links of this type (including a self-link) is E636.
suspect bool true Whether instances participate in suspect-link detection (§12.10.6).
extends string absent Makes this type a variant of a built-in trace link: one of satisfies, verifies, derivedFrom, refines (§12.10.4).
relax list of codes [] Codes of the base link's rules suppressed for instances of this type only. Requires extends; each code must be relaxable for the base (table in §12.10.4).
coverage bool true Requires extends. When false, instances keep the base rule checks but are withheld from the base's reverse index (§12.10.4).

Malformed declarations (W630). An entry that is structurally invalid — a bad or colliding name or inverse, an unparseable cardinality, a non-zero lower bound without sourceTypes, an unknown element-type name in sourceTypes/targetTypes, an extends that is not one of the four supported bases, relax/coverage without extends, or a relax code that is not relaxable for the base — raises warning W630 (attached to .syscribe.toml) naming the entry and the defect, and the whole entry is ignored. Its uses in links: then surface as E630. An unknown key inside an otherwise valid entry also raises W630 and is otherwise ignored. This mirrors the [ids.prefixes]/W046 and [users]/W309 posture: one bad entry never invalidates the rest of the table.

Instances are authored under one namespaced frontmatter field, links:, accepted on every element type:

Field YAML type Required Default Description
links map: link-type name → string | list of strings optional absent User-defined outbound links. Each key is a declared link-type name; each value is one reference or a list of references (stable id or qualified name), resolved exactly as satisfies: targets are (§11.10, §5).
---
type: PartDef
name: WatchdogMonitor
domain: software
links:
  mitigates: [REQ-BRK-002, REQ-BRK-003]   # list form
  partiallySatisfies: REQ-BRK-005         # single-reference form
---

Rules:

  • Direction (§12.1). The element holding the links: entry is the link's source; the reference is the target. Reverse links are computed (the declared inverse), never authored.
  • Namespacing. Keeping user-defined names out of the top-level frontmatter namespace means a declared type can never collide with a future built-in field. links: is a recognised schema field — it never raises W047 (§3.17).
  • Not custom_fields:. custom_fields: (§3.15) holds scalar metadata that nothing resolves. A relationship to another element belongs in links:, where it is resolved, validated and traversable.
  • Mutating commands (set, MCP create_element/update_element) accept and preserve links:, and never rewrite an extending link into its base field (or vice versa).

12.10.3 Declared-constraint checks

Code Severity Trigger
E630 error A links: key is not a declared (valid) link type. The message lists the declared link types — or, when none are declared, says so and shows how to declare one — so an author (or LLM agent) that guessed a name can self-correct.
E631 error links: is not a mapping, or a key's value is neither a string nor a list of strings.
E632 error A links: reference does not resolve to any element (by id or qualified name).
E633 error The type declares sourceTypes, and the holding element's type: is not listed.
E634 error The type declares targetTypes, and a resolved target's type: is not listed (the message names the target).
E635 error A source holds more targets for the type than the cardinality upper bound.
E636 error The type is acyclic = true and links of that type alone form a cycle — including an element linking to itself. Reported once per cycle, naming its members. Types that are not acyclic are never cycle-checked.
W631 warning An element whose type: is in the link type's sourceTypes holds fewer targets than the cardinality lower bound (including none). Suppressed for status: draft elements. Gate in CI with --deny W631.

W631 deliberately needs sourceTypes (hence the W630 rule in §12.10.1): a lower bound only makes sense against a known population of elements that are expected to carry the link.

A type with extends = "<base>" is a named variant of a built-in trace link. Each instance is treated as an instance of the base link by every rule and every reverse index that applies to the base — for example E313 domain matching and E312 no-parent-assignment for satisfies; E104 for verifies; E105/E310/W303 for derivedFrom; E316 for refines; and the coverage checks fed by satisfiedBy, verifiedBy, derivedChildren and refinedBy (W002, W300, W305, …) — in addition to the type's own declared constraints (§12.10.3). impact, trace and suspect detection likewise see it as a base-link instance.

relax suppresses the listed codes for instances of that type only. Only the base's link-scoped rules may be relaxed:

extends Relaxable codes
satisfies E312, E313
verifies E104
derivedFrom E105, E310, W303
refines E316

E310 is an element-level rule (a requirement with derivedFrom: but no breakdownAdr:): relaxing it suppresses the finding only when every derivedFrom-like link on that requirement — the built-in field and any extending types — is of a type that relaxes E310.

coverage = false keeps the base's rule checks (so an unrelaxed E313 still fires) but withholds the instances from the base's reverse index. They then neither satisfy/verify anything for coverage purposes nor make their target a "parent": a coverage = false satisfies variant does not clear W300 on its target; a coverage = false derivedFrom variant does not add its source to derivedChildren, so the target does not become a parent for E312/W305. Coverage rules — W002, W300, W305, W015 (per-configuration coverage), W614 (TestPlan demonstrates:) and the report commands' satisfied/verified lists — never credit a coverage = false instance. Traceability-presence rules still count it, because the link does exist: a TestCase whose only verification link is a coverage = false verifies variant does not raise E013, and a requirement linked upstream only by a coverage = false derivedFrom variant is not an orphan (W005). Default true.

Limitation — cross-repo targets. An extending link is presented to the base rules only when its target resolves in the local model; a target in a [repos] peer is resolved and traversable, but is not checked by the base's rules (E105, E310, …) the way a built-in cross-repo link is.

Built-in links are never relaxed. A declaration only ever affects instances of its own named type; a plain satisfies: beside a relaxing partiallySatisfies still raises E313 on a domain mismatch. Relaxation is therefore always explicit at each use site, and every relaxation in a project is auditable in one place (link-types prints it).

[linkTypes.partiallySatisfies]
description = "A cross-domain contributor; the owning-domain element holds the real satisfies:"
extends     = "satisfies"
relax       = ["E313"]
coverage    = false
---
type: PartDef
name: BrakeController
domain: software
satisfies: [REQ-BRK-002]                  # software → software: full satisfaction
links:
  partiallySatisfies: REQ-BRK-003         # software → hardware req: E313 relaxed, no coverage credit
---

12.10.5 Traversal and discovery

Command Behaviour
follow <elem> <link> [--reverse] [--transitive] [--depth N] [--format text\|json\|dot] Walks one named link from <elem> (id or qname). <link> is a declared type (forward), its declared inverse (reverse), a built-in link (satisfies, verifies, derivedFrom, refines, supertype, typedBy, allocatedTo) or a built-in reverse name (satisfiedBy, verifiedBy, derivedChildren, refinedBy, specializedBy, allocatedFrom). --reverse flips the direction. One hop by default; --transitive follows to a fixed point (terminating on cycles); --depth N bounds the hops and implies transitive. Each reached element is reported once with its hop depth, id/qname, type and name. json emits {start, link, direction, results: [{qname, id, type, name, depth, from}]}; dot emits a Graphviz digraph. An unknown element or link name exits non-zero; an unknown link name prints the available link names.
link-types [--json] Lists every valid declared type with its description, inverse, extends base, relaxed codes, coverage, source/target types, cardinality, acyclic, suspect, and the number of instances in the model. An entry ignored under W630 is not listed as usable. With none declared, says so, shows how to declare one, and exits zero.

Existing relationship commands include custom links: links lists outbound instances under the type name and inbound ones under the inverse (or <type> (inbound) when no inverse is declared); refs includes inbound custom links; impact traverses them (upstream along the link, downstream against it) and accepts custom type names in --kinds; trace lists the custom links touching the requirement; show displays the element's links:.

The MCP server exposes two read-only tools: link_types (same data as link-types --json) and follow (arguments element, link, optional reverse, transitive, depth; same data as follow --format json). Its initialize instructions point the client at link_types.

Custom link targets are trace links for suspect-link detection (ADR-SYS-SUSLINK-001): suspect list lists them, suspect accept baselines them into the source's traceBaselines: map (§3.19), and validation raises W090 when a baselined target's normative content changes. A type declaring suspect = false is excluded from all three — appropriate for purely informational relationships (conflictsWith, informs) whose validity does not depend on the target's exact wording.

12.10.7 LLM discoverability

Because the vocabulary is per-project, the generic authoring prompt cannot enumerate it. Instead:

  • The authoring prompt (--agent-instructions) documents links:, instructs the agent to discover the project's types with link-types (or the MCP link_types tool) before authoring any link, and forbids inventing undeclared types.
  • syscribe -m <root> --agent-instructions appends a "Project link types" section listing each declared type (name, description, direction/inverse, source → target types, cardinality, extends/relax) when the model declares any; nothing is appended when it declares none.
  • E630 names the declared types, so a wrong guess self-corrects on the next validation pass.

12.11 Behavioural Model Trace (models:)

Rule R-011: An executable part (a PartDef or Part with no domain:, typically one that exhibitsStates: a machine, or a composite of such parts) may declare models:, the architecture element(s) it is a behavioural model of (GH #330). Like satisfies: the link points upstream (§12.1): the behavioural element holds it. The executable model and the architecture stay two sets of elements, because their granularity differs (two machines inside one component, two instances of one part for two chains), because a scenario needs environment parts that are not part of the product, and because a model leaves things out. models: lets both differ and still be checked.

Field On YAML type Description
models PartDef/Part, or a part usage in a composite's features: string or list Architecture element(s) this part models: a PartDef/Part with a domain: on itself or its definition chain. Many-to-one (two machines model one element) and one-to-many (two usages of one part model two chains) are both allowed. A usage-level value replaces the definition's
portMap same mapping Executable port → architecture port. A value is a port name, a list (one executable port stands for several architecture ports, such as a trip line that fans out), or null (no counterpart, on purpose). Unmapped ports correspond by name, else by a shared PortDef type and conjugation when exactly one port of the modelled elements has it (§8.8.5). A usage-level map replaces the definition's, which is how two instances model two chains
abstracts same list of strings What the model leaves out. An entry naming a port of a modelled element exempts that port from W990; an entry that resolves to a Requirement says the model cannot be evidence for it (W992); any other entry is a free name recorded for the reader
fidelity same string Prose: what the model covers

models:/portMap:/abstracts: are inherited along supertype:/typedBy: (the nearest models: and portMap: win; abstracts: accumulate).

Validation. All of it is dormant until some part declares models: (or a usage carries models:/portMap:).

Code Condition
E990 A models: target does not resolve, or is not an architecture element
E991 A portMap: key is not a port of the executable part; an executable port corresponds (by name or portMap:) to no port of any element it models; or the counterpart's direction is incompatible (in against out; inout matches either), or both ports are typed and their PortDef or conjugation differs (§8.8.5). A wrong-direction counterpart still counts as covered for W990
W990 A port of a modelled element has no counterpart in any part that models it, checked on the union of those parts, and none of them names it in abstracts:
W991 In an executable composite with at least two modelling parts: a connection whose ends model two different elements has no architecture connection between the corresponding ports; or an architecture connection between two elements the composite models, whose ports are both covered and not abstracted, has no executable connection. A connection between a software and a hardware element is skipped, since §12.6 joins those through allocation, never a connection
W992 A TestCase whose subject: is a part verifies: a requirement satisfied by architecture elements, and no part reachable from the subject models one of them without naming the requirement in abstracts:: its scenarios cannot be evidence for it

W991 matches direct architecture connections only, not paths through delegated ports. A usage-level models: takes part in the checks but not in the query commands below, which read a definition's models:.

Verification composites and environment parts (GH #332). Where an executable part and an architecture element coincide, the architecture PartDef itself carries exhibitsStates: and needs no models:; models: stays for the cases where granularity or fidelity differs. The subject: of a scenario TestCase is then a verification composite: an ordinary PartDef, not an element type of its own, that holds the system under test (architecture elements used directly, executable parts that model one) and the parts of the verification set-up, such as a grid source or a sensor stimulus. Those are marked on the part usage:

Field On YAML type Description
role a part usage in a composite's features: string environment: the part belongs to the verification set-up, not to the product. external: an actor outside the product (§12.13)

An environment part has no architecture counterpart: its connections are not compared (W991), it is no evidence for a requirement (W992), and its own models:/portMap: is ignored (W993). The simulate coverage report lists it apart and leaves it out of the aggregate, and the fault campaign (inject) lists the environment parts and marks a fault set that touches only them [environment]. A part usage whose type is an architecture element is its own counterpart in W991 and exercises its requirements for W992. A composite is a verification composite when it holds an environment part or an executable part that models an architecture element. Only there does an architecture element used directly count as its own counterpart (all its ports covered for W990/W991), and a verification composite's own connections are never taken as architecture connections, even between two architecture elements. A models: on the definition of an environment part still applies to that definition wherever it is used; only a models:/portMap: on the environment usage itself is ignored (W993). The simulate --coverage gates (--min-states, --min-transitions, --min-guards) judge product parts only.

Code Condition
E992 A role: that is not a known role, or that is on a port or on an entry not typed by a PartDef/Part. Checked whether or not the model uses models:
W993 A part usage marked role: environment carries its own models: or portMap:, which is ignored

role: is the one mechanism for parts that are not the product; role: external (§12.13) marks an actor, and like an environment part it has no architecture counterpart (W991), is never evidence (W992), ignores a models:/portMap: of its own (W993, naming its role) and is left out of coverage, its reports labelled [external]. Unlike an environment part it does not make its composite a verification composite: a context composite is architecture. An environment part may have any behaviour a product part may have: a state machine now, a plant later (#340).

The link is a trace link like the others: links, refs, follow <elem> models / follow <elem> modeledBy, impact (modeledBy downstream), trace (a Behavioural models of the satisfiers table), why (an executable part answers with the requirements its modelled elements satisfy), and suspect-link detection (traceBaselines:, W090), so a change to the architecture element marks the behaviour for re-review.

Example (the grid-tie inverter, model_gti/Behavior/Parts/):

---
type: PartDef
name: GridMonitorPart
exhibitsStates: [Behavior::Machines::GridMonitor]
models: [System::Software::ProtectionManager]
fidelity: "fast-stage voltage/frequency window only"
abstracts: [arcTrip, profile, relayCmd]
portMap:
  samples: gridSamples
  out: trip
features:
  - {name: samples, type: Port, direction: in}
  - {name: out, type: Port, direction: out}
---

12.12 Executable Evidence (@verifies, requirement-coverage, exec-coverage)

A TestCase is executable when it is active, names a subject: that is a state machine, an executable action or a composite part, and has Gherkin scenarios; any other TestCase that verifies: a requirement is descriptive. The executable scenarios run in-process (the interpreter of §8.13, no external toolchain), and their results are evidence for the requirements they verify, graded by one engine that two reports show from two sides: requirement-coverage per requirement, exec-coverage per software or hardware architecture element (GH #333, #344).

Scenario-level claims. A scenario verifies the requirements of its TestCase's verifies:. A Gherkin tag narrows that set:

Feature: protection chain

  @verifies REQ-GTI-SYS-030
  Scenario: insulation below the limit prevents connection
    ...

  @invariant @verifies REQ-GTI-SYS-010,REQ-GTI-SYS-038
  Scenario: a dead grid at power-up is never energised
    When grid receives Set with v = 0, f = 60
    # verifies: REQ-GTI-SW-001
    Then supervisor is in state WaitGrid

@verifies REQ-A,REQ-B, @verifies:REQ-A and @verifies(REQ-A, REQ-B) are accepted, on the line before Scenario:/Scenario Outline: (every row of an outline inherits it) or before Feature: (every scenario). A # verifies: REQ-A comment line before a step adds its references to the scenario's claims (before a Background: step, to every scenario's); tags on an Examples: table apply to its rows. A scenario with no valid claim verifies every requirement of the TestCase. A claim never widens the set: a reference that is not a requirement in the TestCase's verifies: is ignored (W994), so verifies: stays the single source of the trace link (§12.1). A claim of a requirement the TestCase lists that is absent from the projected model (another Configuration's) still narrows the scenario and is not W994.

Evidence. A scenario is evidence for a requirement when its subject exercises an architecture element that satisfies it: the element itself carries the machine (exhibitsStates:), or the subject contains a product part (not role: environment, §12.11) that is or models it. A requirement no architecture element satisfies has nothing to compare, and any subject counts. A scenario that claims a requirement without exercising what satisfies it is reported as claimed but not exercised and is not evidence.

Verdicts.

Verdict Condition
executable Passing executable scenarios are evidence for it, above every threshold given
executable-weak Such scenarios exist, but one fails or could not run, or the coverage they give of the machines that carry the requirement (states, transitions, guard conditions) or those machines' mutation score is below a threshold
descriptive-only Only descriptive TestCases verify it
unverified Nothing verifies it
not-applicable Its verificationMethod: is inspection or analysis, or it is a parent requirement every child of which is executable or not-applicable, and no executable scenario is evidence for it

The machines that carry a requirement are the machine of a machine subject, or the machines of the product parts of a composite whose definition satisfies the requirement or models an element that does; their coverage is merged over the scenarios that are evidence for that requirement only. Under a threshold, a requirement whose evidence gives no state-machine coverage (an action subject) is executable-weak. A mutation score belongs to a machine and the subject whose scenarios judged its mutants; for a machine used by several product parts the killed and survived mutants are summed. It is read from <model>/.syscribe/mutation.json, which mutate <subject> --record writes with the hash of the machine (one record per machine version; a score whose machine has since changed or cannot be lowered is stale), or computed on demand (--mutate). Scenarios that use plants are not run by these reports.

Element view. For each Part/PartDef with domain: software or domain: hardware, exec-coverage shows whether it carries a machine itself or is modelled by executable parts (models:), the models: correspondence findings (E991, W990, W991) that name it, the number of executable scenarios whose subject exercises it (and how many passed), the mutation scores of the machines that carry or model it, and its satisfies: requirements split by verdict, executable evidence that exercised another element satisfying the same requirement kept apart.

Both reports gate CI: requirement-coverage --min-executable <pct> (the share of applicable requirements that are executable) and --require <REQ>; exec-coverage --min-modelled <pct> and --require <element> (modelled and exercised by a passing scenario). A missed gate exits 2.

Code Condition
W994 A scenario's @verifies tag or # verifies: comment names a reference that is not a requirement in its TestCase's verifies:; the claim is ignored
W995 Every scenario of a TestCase is tagged, and none claims one of the TestCase's verifies: requirements: no scenario is evidence for it

12.13 System Context (role: external)

A system context is an ordinary PartDef, a context composite, that holds the system as a part usage beside the actors it exchanges power, signals, commands or data with: the PV array, the utility grid, an operator, an installer's tool (GH #337). An actor is a part usage marked role: external; its definition is an ordinary PartDef with ports. There is no Actor element type: one field, role:, says which parts are not the product, for verification set-ups (environment, §12.11) and contexts alike.

Field On YAML type Description
role: external a part usage in a composite's features: string The part is an actor outside the product. Like an environment part it has no architecture counterpart (W991), is no evidence for a requirement (W992), ignores its own models:/portMap: (W993) and is left out of coverage (§12.11)

Diagrams. A derived IBD of a context composite draws an external usage with the stereotype «actor» and an environment usage «environment», dashed; a derived BDD draws the definition of an external usage «actor». Actors sit beside the system in the same frame, and the composite's connections join their ports to the system's boundary ports. An appliesWhen: on an actor badges it optional and --config drops it, as for any part (§8.16.8.2). SVG and sprotty dash an environment usage; DOT dashes it too, PlantUML shows the stereotype with a dashed line, and Mermaid writes the stereotype above the name. role: is read on an inline features: entry only, so an actor is always declared inline, not as a child Part file; the parts inside an actor's definition at depth: 2 are drawn as ordinary parts. A BDD marks a definition «actor» when some drawn block uses it as an actor.

Boundary check. In a context composite every port of a product part (an inline usage or a child Part file) must be reached by one of the composite's connections:, flowConnections: or bindingConnections: as <usage>.<port>; one that is not is W996. The ports are those along the part's typedBy:/supertype: chain: type: Port features, features typed by a PortDef, and child Port files. An entry counts only when each of its ends is on a part of the composite or on the composite's own port, so under --config a connection to an actor the variant drops reaches nothing. A port that is an inline feature with its own appliesWhen: is judged by the entries written, as in the whole model, because --config does not project inline features; gate such a port together with its actor. The check is opt-in by construction: a part with no role: external usage is not a context and is never checked. A context composite is the root of its context: nothing has to use it as a type (no W007).

Code Condition
W996 In a context composite, a port of a product part's definition that no connection, flow or binding of the composite reaches

Example (model_gti/Context/GtiContext.md, excerpt):

---
type: PartDef
name: Inverter Context
features:
  - {name: inverter, typedBy: System::GridTieInverter}
  - {name: pvArray, typedBy: Context::PvArray, role: external}
  - {name: grid, typedBy: Context::UtilityGrid, role: external}
connections:
  - {from: pvArray.dcOut, to: inverter.pvIn}
  - {from: inverter.gridOut, to: grid.acIn}
---

12.14 Fault-Injection Campaign Evidence and Residual Risk (inject --record, ResidualRisk)

A fault-injection campaign (inject, §8.18) reports the fault sets a composite's @invariant scenarios survive and those that defeat them. inject <subject> --record [--run <id>] [--config <C>] keeps the campaign in <model_root>/.syscribe/campaigns.json, one history per subject (the TestCase or composite named) and Configuration, latest first, at most 20 records. A record holds the composite and its hash, the invariants (title and a hash of their steps), every set with its origin, verdict and the failing invariant and step, the sets that could not be run, the cut-set order, and the syscribe version. Everything but the timestamp is a function of the model, so two runs on the same model record the same content. A record with a --run id replaces an earlier one with that id; an unnamed record replaces the earlier unnamed one. results campaigns lists the histories and results campaigns diff <subject[@config]> [<runA> <runB>] the verdicts that changed. inject --report <file> writes the record as JSON, or Markdown for a .md file, with the decisions that name each defeated set and the failureRate of its faults, as an attachment for an assessor.

A ResidualRisk element (RISK-*, id-identified, files <id>.md) records what was decided about one fault set:

Field Meaning
faultSet the FMEA rows (FMEAEntry) and fault-tree events (FaultTreeEvent) whose joint failure the decision is about, by id or qualified name. It names a set exactly as inject reports it: [A, B] explains the set A + B, not A alone
status proposed, accepted (the defeat is accepted as residual risk), mitigated (a design change removed it) or rejected (the defeat must be fixed)
goal the SafetyGoal(s) the defeated invariant protects
mitigatedBy the Requirement(s) or SafetyMechanism(s) that address it; required when mitigated
approvedBy who accepted it, a username (checked against [users] when declared); required when accepted
rationale, reviewDate, expires why, when it was decided, and when it must be reviewed again
campaignHash the composite hash inject printed when the decision was taken (at least 12 hex digits)

The failure rate of the set is not copied onto the decision: it comes from the fault-tree events (failureRate), whose provenance is the fault tree's concern. ISO 26262 and IEC 61508 call an accepted defeat residual risk; an accepted deviation from a requirement is a different record and stays a ReviewRecord or an ADR.

The validator ties decisions to the latest record of each campaign: validate judges the records made without --config, validate --config C those made with C. A defeated set no proposed, accepted or mitigated decision names is W940 (on the composite), so a new defeat is loud and a decided one quiet; a set whose faults are all in parts that are not the product (role: environment/external) is a failure of the verification set-up and is skipped. A decision the campaign contradicts is W941: proposed or accepted while the design now survives the set (close it as mitigated), or mitigated while the set still defeats an invariant. A campaignHash that is not the current hash of any composite whose record ran the set is W942, and an expired proposed or accepted decision W943. A record whose composite hash the composite (projected for the record's configuration) no longer has, or a composite that no longer builds, is W944; its message prints the recorded command to re-run. An unreadable or unparseable record file is W945: nothing is judged, and inject --record refuses to overwrite the file. Each record stores its invocation (--ft, --max-order, --fmea), so a re-record that would drop sets an earlier record ran prints a warning naming the earlier command, and a decision whose set the latest record did not run is W946, because it is no longer checked. A malformed decision is E942, a reference that resolves to nothing E943, one of the wrong kind E944.

safety-case adds a solution node "fault-injection campaign …: N sets, M survived, K defeated (J explained)" under the top-event goal of the campaign's --ft tree and under every goal a decision names, even when no decision exists yet; its children are the decisions (accepted: supported, proposed: unverified, contradicted by the campaign: failing). The node is undeveloped while any defeat is unexplained or contradicted; the goal verdicts are not changed. "Explained" means exactly what W940 means: a proposed or accepted decision names the set (a mitigated decision on a still-defeating set is contradicted, W941). audit --config C counts the decisions and lists the unexplained defeats for C; plain audit lists, per configuration with records, how many defeats no decision explains.

A RISK-* id needs at least one category segment like every other stable id (RISK-GTI-001, not RISK-001). reviewDate, expires and the W943 comparison use the UTC calendar date.

---
type: ResidualRisk
id: RISK-GTI-004
name: Both sensing chains blinded and the perturbation check silent
status: accepted
faultSet: [FTE-GTI-009, FTE-GTI-010]
goal: SG-GTI-001
mitigatedBy: [REQ-GTI-SYS-045, REQ-GTI-SW-008]
approvedBy: jdoe
reviewDate: 2026-10-12
expires: 2027-10-12
campaignHash: 1b08e8329d70
---

13 IEC 62443 Industrial Cybersecurity (Zone/Conduit Model)

13.1 Overview

IEC 62443 (Industrial Automation and Control Systems security) models security as Zones (groups of assets sharing a common security policy) connected by Conduits (communication channels between zones). Each zone has a Security Level (SL 1–4) representing required robustness.

This section extends the format with two native element types — Zone and Conduit — and adds targetSL: / achievedSL: fields to structural elements for SL-based gap analysis.

13.2 Zone Element

A Zone groups PartDef / Part elements that share a security boundary.

ID pattern: ^ZN(-[A-Z0-9]{2,12})+-[0-9]{3,8}$
Examples: ZN-CTRL-001, ZN-FIELD-001, ZN-DMZ-001

Frontmatter schema:

Field YAML type Required Description
id string Required Stable ZN-* identifier
name string Required Display label
status enum Required draft / review / approved / deprecated
targetSL integer 1–4 Required Required Security Level per IEC 62443-3-3
achievedSL integer 1–4 optional Achieved Security Level (assessed; omit when not yet assessed)
members list of strings optional Qualified names or stable IDs of PartDef/Part elements in this zone
rationale string optional Justification for the zone boundary and SL assignment

Example:

---
type: Zone
id: ZN-CTRL-001
name: Control Zone
status: approved
targetSL: 3
achievedSL: 3
members:
  - Logical::PLCController
  - Logical::SafetyRelay
rationale: >
  Hosts real-time control functions. SL 3 required due to potential for
  significant process disruption.
---
The Control Zone hosts all real-time process control components.

13.3 Conduit Element

A Conduit is a communication channel between two zones. It must implement security controls sufficient to maintain the SL of the higher-SL zone at the boundary.

ID pattern: ^CD(-[A-Z0-9]{2,12})+-[0-9]{3,8}$
Examples: CD-CTRL-FIELD-001, CD-CORP-DMZ-001

Frontmatter schema:

Field YAML type Required Description
id string Required Stable CD-* identifier
name string Required Display label
status enum Required draft / review / approved / deprecated
fromZone string Required Qualified name or stable ID of source Zone
toZone string Required Qualified name or stable ID of destination Zone
achievedSL integer 1–4 optional Achieved SL of this conduit's security controls
protocols list of strings optional Communication protocols traversing this conduit (informational)
implementedBy list of strings optional SecurityControl IDs or architecture element qnames implementing the conduit boundary controls

Example:

---
type: Conduit
id: CD-CTRL-FIELD-001
name: ControlToField
status: approved
fromZone: ZN-CTRL-001
toZone: ZN-FIELD-001
achievedSL: 3
protocols: [Modbus/TCP, OPC-UA]
implementedBy: [SC-FIREWALL-001, SC-UNIDIRECTIONAL-001]
---
Unidirectional data diode from the Control Zone to the Field Zone.

13.4 Additional Fields on Structural Elements

Any PartDef or Part may declare:

Field YAML type Description
targetSL integer 1–4 Required Security Level for this component
achievedSL integer 1–4 Assessed Security Level of this component
inZone string Qualified name or stable ID of the Zone this element belongs to

When inZone: is present, the element is implicitly added to that zone's member set (the members: field on Zone is the authoritative list; inZone: on the element is the reverse pointer for navigation).

13.5 Validation Rules

Code Condition
E950 Zone missing id:, name:, status:, or targetSL:
E951 Zone.id does not match the ZN-* pattern
E952 Conduit missing id:, name:, status:, fromZone:, or toZone:
E953 Conduit.id does not match the CD-* pattern
E954 Conduit.fromZone or Conduit.toZone unresolved, or resolves to an element that is not a Zone
E955 Zone.members: entry unresolved, or resolves to an element that is not a PartDef / Part
E956 PartDef/Part.inZone: unresolved, or resolves to a non-Zone element
E925 A targetSL: or achievedSL: on a Zone, Conduit, PartDef or Part is outside the Security Level range 1–4 (IEC 62443-3-3 defines SL 1–4 only)
E926 A Zone's or Conduit's status: is not one of draft / review / approved / deprecated
W950 Zone.achievedSL is less than Zone.targetSL — the zone's security level is not yet achieved
W951 Conduit.achievedSL is less than the higher of the two connected zones' targetSL (i.e. below the target of at least one zone it connects) — the conduit boundary is weaker than the more demanding zone (opt-in; gateable with --deny W951)
W952 PartDef/Part has targetSL: but is not referenced by any Zone.members: and declares no inZone: — isolated SL claim (opt-in)
W953 Zone with targetSL >= 2 and status: approved has no Conduit referencing it — an approved zone that is unreachable from any conduit may be a modelling gap

13.6 CLI Commands

syscribe zones [--json] — List all Zone elements with their targetSL, achievedSL, member count, and gap status.

Zone           tSL  aSL  Members  Gap
ZN-CTRL-001    3    2    2        ⚠ SL gap
ZN-FIELD-001   2    2    5        ✓

syscribe conduits [--json] — List all Conduit elements with from/to zones, achievedSL, and whether the conduit SL meets the connected zone requirements.

JSON output includes { id, name, fromZone, toZone, achievedSL, requiredSL, pass } per conduit.

syscribe zones --coverage [--json] — Cross-table: Zone × SecurityControl showing which controls contribute to which zone's SL. Sourced from Conduit.implementedBy: and zone member SecurityControl references.


14 Multi-Repository Model Composition

14.1 Motivation

Large programs partition the system model across organizational boundaries: a prime integrator assembles subcontractor models, each maintained in a separate repository. Multi-repo composition lets a Syscribe model import namespaces from peer repos, resolving cross-repo cross-references at analysis time.

14.2 Repository Configuration

Repos are declared in the model root .syscribe.toml:

[repos]
avionics = { path = "../avionics-model", root = "model/" }
brakes   = { path = "../brakes-subsystem", root = "model/", ref = "v2.1.0" }
shared   = { path = "../shared-library", root = "model/", ref = "main" }

Each entry key is the repo alias — a short identifier used in _index.md import declarations. Fields:

Key Type Required Description
path string Required File-system path to the repo root (relative to this model's .syscribe.toml)
root string optional (default "model/") Path within the repo where the Syscribe model root lives
ref string optional Git ref (tag, branch, or SHA) to check out via syscribe repos sync; absent means "use whatever is on disk"

14.3 Import Declarations in _index.md

A package _index.md may import a sub-tree from a peer repo using the repoImports: field:

---
type: Package
name: Integration
repoImports:
  - repo: avionics
    qname: Avionics                    # import the Avionics package from the avionics repo
    as: Avionics                       # mount it here as Integration::Avionics
  - repo: brakes
    qname: BrakeSystem                 # import a specific sub-package
    as: Brakes
---

repoImports: sub-schema:

Field Type Required Description
repo string Required Alias matching a key in [repos]
qname string Required Qualified name of the element / package to import from the peer repo (relative to that repo's model root)
as string optional Local alias; defaults to the last segment of qname

qname is the peer's exact qualified name, or else the trailing ::-segment(s) of exactly one peer qname (the same match E514 accepts).

14.4 Resolution Rules

  1. Imported elements are mounted in the local namespace at <package>::<as> and are read-only. A reference written through the mount — <package>::<as>::X — denotes the peer's <qname>::X (Integration::Brakes::REQ-BRK-001 → the peer's BrakeSystem::REQ-BRK-001) for every cross-reference field that resolves across repos: verifies:, derivedFrom:, satisfies:, allocatedTo:, supertype:, typedBy: (incl. inline features:), subsets:, redefines:. The peer-native qualified name and the global stable id keep working. A mount on the model-root package is just <as>; when two mounts nest, the longer (more specific) one applies. A mounted reference that names nothing in that peer is E512. (GH #138.)
  2. Cross-repo cross-references (verifies:, derivedFrom:, satisfies:, allocatedTo:, and the structural supertype:/typedBy:/subsets:/redefines:) are resolved by searching the local model first, then each loaded repo in declaration order — by global stable id, by the peer-native qualified name (exact, or its trailing ::-segments), or through a mount point (rule 1).
  3. Cross-repo supertype: / typedBy: links are permitted for structural reuse (e.g., using a shared PartDef); integrity-level propagation (E841–E843) does not cross repo boundaries — each repo's safety case is authored independently.
  4. The id-based cross-reference namespace (REQ-*, TC-*, etc.) is global across all loaded repos; a REQ-* ID must be unique across the entire composition — between the local model and a peer, and between any two peers (E515).

14.5 CLI Commands

syscribe repos list [--json] — Show all configured repos with their paths, configured refs, and on-disk sync status.

Alias     Path                   Ref      On disk   Status
avionics  ../avionics-model      —        ✓         (no ref pinned)
brakes    ../brakes-subsystem    v2.1.0   ✓         in sync
shared    ../shared-library      main     ✓         behind (3 commits)

syscribe repos sync [--all | <alias>] — For repos with a ref: configured, run git fetch --tags and then git checkout <ref> in the repo's directory. Without --all, syncs the named alias only.

syscribe repos status [--json] — Show whether each repo is at its configured ref (compares the work-tree HEAD with the commit the configured ref: resolves to, via git rev-parse --verify; not git describe). Exits 2 if any pinned repo is out of sync.

14.6 Validation Rules

Code Condition
E510 Circular repo import — repo A's composition imports from repo B which (directly or transitively) imports from repo A
E511 repos.<alias>.path does not exist on disk (and no ref: is configured)
E512 Cross-repo verifies: / derivedFrom: / satisfies: / allocatedTo: / supertype: / typedBy: / subsets: / redefines: reference cannot be resolved in the local model or any loaded repo
E513 _index.md repoImports[].repo names an alias not present in [repos]
E514 repoImports[].qname does not resolve to any element in the named repo
E515 Two repos export the same stable ID — the local model and a peer (e.g., REQ-SCHED-001 appears in both), or two different peer repos (two [repos] aliases resolving to the same peer model root are one repo)
W510 A repo in [repos] has no ref: — composition is not pinned to a reproducible snapshot (opt-in; gateable with --deny W510)
W511 A peer repo's git HEAD has drifted from its configured ref: — the checkout is not at the pinned snapshot. Detected by comparing the peer work tree's HEAD commit with the commit the ref: resolves to; never raised when drift cannot be determined (git unavailable, not a work tree, ref: unresolved). Opt-in; gateable with --deny W511 as a CI reproducibility gate. repos status reports the same drift and exits 2.
W512 A peer repo's path is a git submodule of the composing model's repository, and the commit its ref: resolves to differs from the gitlink the parent repo records for that path — i.e. .syscribe.toml disagrees with the submodule commit pinned in the parent repository's tree (.gitmodules only records the path and URL, not the commit). Detected by comparing git ls-tree HEAD <submodule-path> in the parent against the ref: commit; never raised when path is not a submodule, no ref: is configured, or either commit cannot be resolved. Independent of W511 (gitlink pin vs ref:, not checkout vs ref:). Opt-in; gateable with --deny W512.

repos sync brings a drifted repo back to its pinned ref:. Git submodules and [repos] are complementary: a submodule provides the pinned checkout, while [repos] adds the model-level cross-reference resolution and the W511/W512 reproducibility checks on top.

14.7 Hierarchical Product-Line Composition — subConfigurations:

ADR-SYS-HPLE-001. Multi-repo composition (§14.1–14.6) lets one model reference elements in a peer repo; hierarchical composition additionally lets a Configuration consolidate another repo's already-configured product line into its own — a prime integrator's Configuration built by picking one already-configured variant from each of several independently-developed, independently-versioned lower-tier product lines (an OEM vehicle line built from a battery-pack line, itself built from a cell-chemistry line). "Tier" is not a declared schema concept: it falls out structurally from whether a repo happens to import others, exactly as "leaf" already falls out structurally elsewhere in this format (a leaf Requirement has no derivedChildren).

Field. A Configuration gains an optional subConfigurations: field — a Configuration qname/id, or a list of them — naming the Configuration(s) it consolidates. Valid at any tier; naturally empty or absent at a leaf tier (one with no lower tiers to consolidate). Entries resolve exactly like any other cross-reference (§14.4): the local model first, then each loaded repo in declaration order, by the peer-native qualified name, the global stable id, or a repoImports: mount path (<package>::<as>::X → the peer's <qname>::X; a mount path naming nothing in its peer is E516) — no new addressing syntax.

Peer-validity gate. Each named Configuration must resolve to a real Configuration (E516 if dangling, E517 if it resolves to something else) that is itself internally valid — SAT-satisfiable and free of validation errors (E518 otherwise). For a peer entry this genuinely loads and validates that repo's model (not merely the existence-only qname/id index §14's [repos] otherwise uses for cross-reference checking) — confirming a peer Configuration's validity requires reading its real feature/parameter structure. A consolidation chain is bounded to a fixed depth; exceeding it also raises E518, naming the exceeded bound (defense against a circular chain, which in practice also trips E510 since it requires the underlying [repos] tables to point at each other too).

parameterBindings: reaches transitively. The existing parameterBindings: field (§9.8, §9.7) is reused verbatim — not duplicated or nested per subConfigurations: entry. A dotted key resolves against any FeatureDef reachable through subConfigurations:, at any depth, using the parameter's ordinary, already-mounted qname — its <FeatureDef> part may be the peer-native qname or a repoImports: mount path, translated through the mounts of the tier that wrote the key (so a nearer tier's mount-path binding counts as closing the parameter for E523 and W513). The parameter's own intrinsic checks apply unchanged regardless of whether the target is local or transitively resolved: E204 (fixed), E205 (range), E206 (enum), E222 (unresolved), W027 (runtime bindingTime:).

Cross-tier binding legality. A transitively-resolved binding must target a parameter that is genuinely open at the point it is bound:

  • the parameter's owning FeatureDef must actually be selected by the descendant Configuration that owns it — else E519, the cross-tier extension of E203 (which stays scoped to a Configuration's own local selection, since a transitively-reached parameter's selection state belongs to whichever tier actually selects it);
  • the parameter must not already be bound by that descendant's own Configuration, or by a nearer tier's parameterBindings: on the path down to it — else E523. The same parameter may legally be closed by any one tier along the path from where it's declared open up to wherever it's finally supplied; it is only illegal to supply it twice.

Open-parameter completeness (W513). For a Configuration with subConfigurations:, the transitive closure of every isRequired: true, no-default: parameter — of every FeatureDef actually selected anywhere in the consolidated subtree, at any depth — that remains unbound after applying every parameterBindings: entry from that Configuration down through every tier already resolved beneath it is reported as W513: opt-in, silent by default, gateable via --deny W513, following the same posture as W510/W511/W512/W023/W090. It is never escalated to a hard error purely because one tier's own isolated validation run still finds it open — a tier being validated on its own has no way to know whether it is the actual top of the hierarchy (where "still open" now means incomplete) or will itself be consolidated further by something it has never seen (where "still open" is the intended, deliberate deferral). Only whichever repo is actually positioned as the point of final assembly can correctly decide which case applies, by choosing in its own CI whether to gate on this warning. A parameter a tier author deliberately supplied a default: for never appears in this closure — it is self-sufficient by construction.

Lower-tier isolation. A lower tier carries zero awareness of, or reference to, anything above it — no authoring change, field, or foreknowledge of eventual consolidation is required for a product-line model to function as a lower tier. A descendant FeatureDef parameter needing an externally-supplied value declares this exactly as it already would in a single-model Configuration — isRequired: true, no default, isFixed: false — with no field naming, or capable of naming, whoever eventually supplies that value. bindTo: (the existing component→system parameter-propagation mechanism, §9.7) is explicitly not the mechanism for this and must not be repurposed for it: a bindTo: target continues to resolve purely within its own model, never across a subConfigurations: boundary, and this feature does not change bindTo:'s existing behavior in any way. Independent, parallel development is the entire point: a lower-tier product line is developed once and must be consolidatable by any number of higher-tier integrators — zero, one, or many — without modification.

Validation rules:

Code Condition
E516 A subConfigurations: entry does not resolve to any element, locally or in a loaded peer repo
E517 A subConfigurations: entry resolves to a real element that is not a Configuration
E518 A subConfigurations: entry resolves to a Configuration that is not itself internally valid (a validation error, a void feature model, or feature-check --deep's E225) — or the consolidation chain exceeds the bounded depth
E519 A transitively-resolved parameterBindings: entry targets a FeatureDef the owning peer Configuration does not itself select
E523 A transitively-resolved parameterBindings: entry double-binds a parameter some nearer tier on the path already supplies
W513 A selected, required, no-default parameter anywhere in the consolidated subtree remains unbound after every tier's own parameterBindings: (opt-in; gateable with --deny W513)

Out of scope (tracked as follow-on only if a concrete need arises): cross-tier appliesWhen:/feature-model constraint expressions (a parent's feature gate referencing an individual descendant FeatureDef directly, rather than treating a consolidated Configuration as a fixed, already-resolved unit); any dedicated browsing/reporting UI for a consolidated hierarchy's full parameter surface beyond what already falls out of existing show/export once the qnames are mounted; and baseline/release-freezing awareness of a multi-tier consolidation beyond what already exists per-repo. See examples/hple-multitier/ for a complete 3-tier worked example (vehicle ← battery-pack ← battery-cell).


15 General-Purpose Trade Study

15.1 Overview

A TradeStudy records a formal weighted-criteria evaluation of design alternatives. It is a first-class model element that links to the requirement it informs and the ADR recording its outcome, making the rationale for architecture decisions navigable from the model.

This section defines a general-purpose trade study facility independent of the MagicGrid profile. The trade-study command dispatches on the model's content, not on a profile: when the model contains at least one TradeStudy element it runs the facility described here (§15.4); otherwise it falls back to the MagicGrid MoE-weighted scoring of Configurations (REQ-TRS-MG-007, see the MagicGrid gate in §11.12). Neither mode requires --profile.

15.2 TradeStudy Element

ID pattern: ^TRD(-[A-Z0-9]{2,12})+-[0-9]{3,8}$
Examples: TRD-PROC-001, TRD-COMM-001

Frontmatter schema:

Field YAML type Required Description
id string Required Stable TRD-* identifier
name string Required Free-prose label
status enum Required draft / review / complete
objective string optional Qualified name or ID of the Requirement this study informs
decision string optional Qualified name or ID of the ADR recording the selected alternative
criteria list Required Evaluation criteria; see sub-schema below
alternatives list Required Options being evaluated; see sub-schema below
scores list Required Score matrix; see sub-schema below

criteria: sub-schema:

Field Type Required Description
name string Required Criterion identifier (used as key in scores:)
weight float Required Relative weight in [0.0, 1.0]; weights need not sum to 1 (tool normalises)
direction enum Required maximize or minimize
unit string optional Unit of measure (informational)
description string optional What is being measured

alternatives: sub-schema:

Field Type Required Description
name string Required Alternative identifier (used as key in scores:)
element string optional Qualified name or ID of the modeled element representing this alternative
description string optional Short description of the alternative

scores: sub-schema:

Field Type Required Description
alternative string Required Must match a name in alternatives:
criterion string Required Must match a name in criteria:
score float Required Raw score in the criterion's natural units
rationale string optional Supporting rationale for this score

Complete example:

---
type: TradeStudy
id: TRD-COMM-001
name: Communication Bus Architecture Trade
status: complete
objective: REQ-COMM-001
decision: ADR-COMM-BUS-001
criteria:
  - name: latency
    weight: 0.4
    direction: minimize
    unit: ms
    description: Worst-case message latency under peak load
  - name: bandwidth
    weight: 0.3
    direction: maximize
    unit: Mbps
  - name: cost
    weight: 0.2
    direction: minimize
    unit: USD/node
  - name: maturity
    weight: 0.1
    direction: maximize
    description: Industry adoption (1=experimental, 5=widely deployed)
alternatives:
  - name: CAN-FD
    element: Physical::CANFDBus
    description: CAN with Flexible Data-rate, ISO 11898-1:2015
  - name: Ethernet-TSN
    element: Physical::TSNNetwork
    description: IEEE 802.1Qbv Time-Sensitive Networking
  - name: FlexRay
    description: AUTOSAR FlexRay v2.1
scores:
  - { alternative: CAN-FD,       criterion: latency,    score: 0.5,  rationale: "50µs worst-case" }
  - { alternative: CAN-FD,       criterion: bandwidth,  score: 8 }
  - { alternative: CAN-FD,       criterion: cost,       score: 2.50 }
  - { alternative: CAN-FD,       criterion: maturity,   score: 5 }
  - { alternative: Ethernet-TSN, criterion: latency,    score: 0.1 }
  - { alternative: Ethernet-TSN, criterion: bandwidth,  score: 1000 }
  - { alternative: Ethernet-TSN, criterion: cost,       score: 8.00 }
  - { alternative: Ethernet-TSN, criterion: maturity,   score: 3 }
  - { alternative: FlexRay,      criterion: latency,    score: 0.3 }
  - { alternative: FlexRay,      criterion: bandwidth,  score: 10 }
  - { alternative: FlexRay,      criterion: cost,       score: 5.00 }
  - { alternative: FlexRay,      criterion: maturity,   score: 4 }
---
Trade study for the in-vehicle communication bus selecting the architecture
that best balances real-time performance, bandwidth, cost, and industry maturity.

15.3 Computed Scoring

The tool computes, but does not author, the following derived values:

  1. Normalised score per alternative per criterion — min-max normalisation within each criterion column (0 = worst in set, 1 = best). Direction is applied before normalisation.
  2. Weighted score per alternative per criterion — normalized_score × (weight / Σ weights) (weights are normalised to sum to 1)
  3. Total weighted score per alternative — sum of weighted scores across all criteria
  4. Rank — alternatives ordered by total descending

These appear in show <TRD-id> and the trade-study command output; they are never written to the .md file.

15.4 CLI Command

syscribe trade-study [<TRD-id>] [--json] — Without an ID, lists all TradeStudy elements (id, name, status, alternative count, whether complete). With an ID, prints the full scoring table and ranking.

Trade Study: TRD-COMM-001 — Communication Bus Architecture Trade
Objective: REQ-COMM-001   Decision ADR: ADR-COMM-BUS-001

| Alternative    | latency(0.4) | bandwidth(0.3) | cost(0.2) | maturity(0.1) | Total | Rank |
|---|---|---|---|---|---|---|
| Ethernet-TSN   | 1.000 | 1.000 | 0.000 | 0.000 | 0.700 | #1 |
| FlexRay        | 0.500 | 0.002 | 0.545 | 0.500 | 0.360 | #2 |
| CAN-FD         | 0.000 | 0.000 | 1.000 | 1.000 | 0.300 | #3 |

(Output of the §15.2 example. Rows are ordered by rank; each cell is the min-max-normalised score, and Total is the weight-normalised sum — here the weights already sum to 1.)

15.5 Validation Rules

Code Condition
E869 TradeStudy missing id:, name:, status:, criteria:, alternatives:, or scores:
E870 TradeStudy.id does not match the TRD-* pattern
E871 criteria: entry missing name:, weight:, or direction:
E872 criteria[].weight is not a float in [0.0, 1.0], or all weights are zero
E873 criteria[].direction is not maximize or minimize
E874 alternatives: is empty
E875 alternatives: entry missing name:
E876 scores: entry references an alternative or criterion name not present in the respective list
E877 scores: entry score: is not a number
W061 TradeStudy with status: complete has no decision: field (no ADR recording the outcome)
W062 TradeStudy.objective: is present but unresolved (no element with that qname or id)
W063 scores: matrix is incomplete — at least one (alternative, criterion) pair has no score entry
W064 TradeStudy.alternatives[].element is present but unresolved

Code note: these were drafted as E400–E408 / W400–W403, which collide with the Diagram codes (E400–E404, W400–W415, §8.16.7). They are reassigned to E869–E877 / W061–W064.


16 N² Interface Matrix

16.1 Overview

The N² diagram (also called N-squared chart) is a standard systems engineering artifact for interface identification and completeness review. It displays system elements on a diagonal; off-diagonal cells show the interfaces between elements at the row and column intersection.

Syscribe generates N² matrices from the model's existing port, connection, flow, and allocation edges — no new element types are required.

16.2 CLI Command

syscribe n2 [<qname>] [--depth <N>] [--format text|html|json] [--interfaces-only] [--allocations]

Flag Default Description
<qname> model root Scope. With no <qname> every PartDef/Part in the model is on the axis; with a <qname> the axis is that composite's subparts, expanded --depth levels
--depth N 1 Levels of nesting below <qname> to include (1 = direct subparts only)
--format text Output format: text (ASCII table), html (styled HTML fragment), json
--interfaces-only false Drop axis elements that have no interfaces; remaining empty cells still print —
--allocations false Include allocation edges in addition to port/flow/connection edges

Algorithm:

  1. Collect all PartDef/Part elements in scope at the requested depth — these become the matrix rows and columns (axis elements).
  2. For each ordered pair (row R, col C) where R ≠ C: collect all directed edges from R toward C:
  3. Connection edges: a Connection (or connections: entry) with from: inside R and to: inside C
  4. Flow edges: a Flow connecting a port on R to a port on C
  5. Port edges: PortDef/Port on R whose typedBy: matches a conjugated port on C
  6. Allocation edges (with --allocations): allocatedTo: from R to C
  7. Render: the diagonal cell is ■; a (R, C) off-diagonal cell is — when empty, otherwise the edge labels (the edge's typedBy last segment, else its name, else its kind) comma-joined — or <n> ifaces in text mode when there are more than two. The JSON form carries {kind, name} per edge.

Text output:

N² Interface Matrix — VehicleSystem (depth 1)

              Chassis  Engine  Transmission  BrakeSys
Chassis         ■       —      MechCouple    BrakeLink
Engine          —       ■      DriveShaft    —
Transmission    —       —      ■             —
BrakeSys        —       —      —             ■

JSON output:

{
  "scope": "VehicleSystem",
  "elements": ["Chassis", "Engine", "Transmission", "BrakeSys"],
  "matrix": {
    "Chassis": {
      "Transmission": [{"kind": "Connection", "name": "MechCouple"}],
      "BrakeSys":    [{"kind": "Connection", "name": "BrakeLink"}]
    }
  }
}

No new element types. No new validation rules.


17 Change Impact Analysis

17.1 Overview

When a model element is modified, all downstream elements that trace to it — through any combination of traceability links — may need to be reviewed or updated. The impact command traverses the traceability graph from a named element and reports every reachable node, its distance (hop count), and the edge kind that connects it.

This is a read-only analysis command. It does not modify the model.

17.2 CLI Command

syscribe impact <qname|id> [--direction downstream|upstream|both] [--depth <N>] [--format text|json|dot] [--kinds <csv>]

Flag Default Description
<qname\|id> — Required. Starting element
--direction downstream Traversal direction: downstream follows links from the element outward; upstream follows them back to root; both does both
--depth N unlimited Maximum hop distance to report
--format text Output: text (indented tree), json, dot (Graphviz)
--kinds csv all Restrict to specific edge kinds: verifies, derivedFrom, satisfies, supertype, appliesWhen, allocatedTo, refines, derivedFromSafetyGoal, or a declared link-type name

Downstream traversal follows these outgoing edge kinds from the named element:

Edge kind Follows
specializedBy Elements that have supertype: pointing here
derivedChildren Requirements with derivedFrom: pointing here
verifiedBy TestCases with verifies: pointing here
satisfiedBy Architecture elements with satisfies: pointing here (reverse of satisfies:)
refinedBy UseCases/behaviors with refines: pointing here
conditionalOn Elements whose appliesWhen: includes a feature here
allocatedFrom Elements allocated to this element
safetyGoalChildren Requirements with derivedFromSafetyGoal: pointing at this SafetyGoal

Upstream traversal follows:

Edge kind Follows
supertype The definition this element specialises
derivedFrom Parent requirements
verifies Requirements this TestCase verifies
derivedFromSafetyGoal Safety goal tracing
satisfies Requirements this element satisfies (structural or behavioral — §12.3)
refines Requirements this use case or behavior refines
allocatedTo Allocation targets of this element

User-defined link types (§12.10) are traversed as well: upstream along the link (source → target), downstream against it (target → source). --kinds accepts custom link-type names. A type that extends a built-in link is also traversed as that base kind.

Text output:

Impact of REQ-BRAKES-001 (downstream, unlimited depth)

REQ-BRAKES-001 [Requirement, approved]
  ├── REQ-BRAKES-ABS-001  [Requirement, approved]  via derivedChildren
  │     ├── Physical::ABSActuator  [PartDef]        via satisfiedBy
  │     └── TC-ABS-001            [TestCase, active] via verifiedBy
  └── REQ-BRAKES-PARK-001 [Requirement, draft]    via derivedChildren

4 elements reachable (2 requirements, 1 architecture, 1 test)

JSON output:

{
  "root": { "qname": "REQ-BRAKES-001", "type": "Requirement", "id": "REQ-BRAKES-001" },
  "nodes": [
    { "qname": "REQ-BRAKES-ABS-001", "type": "Requirement", "id": "REQ-BRAKES-ABS-001",
      "depth": 1, "via": "derivedChildren" },
    { "qname": "Physical::ABSActuator", "type": "PartDef",
      "depth": 2, "via": "satisfiedBy" },
    { "qname": "TC-ABS-001", "type": "TestCase", "id": "TC-ABS-001",
      "depth": 2, "via": "verifiedBy" }
  ]
}

No new element types. No new validation rules.


18 SBOM Generation

18.1 Overview

A Software Bill of Materials (SBOM) enumerates all software components in a deliverable — enabling vulnerability tracking, license compliance, and supply-chain auditing. Syscribe generates an SBOM from the implementedBy: links on Part/PartDef elements and, optionally, from TestCase.sourceFile: links.

The generated SBOM satisfies the NTIA minimum elements for an SBOM (supplier, component name, version, unique identifiers, dependency relationships, SBOM author, timestamp).

18.2 CLI Command

syscribe sbom [--format cyclonedx|spdx] [--config <CONF>] [--output <file>] [--include-tests] [--scope <qname>]

Flag Default Description
--format cyclonedx Output format: cyclonedx (CycloneDX 1.6 JSON) or spdx (SPDX 2.3 JSON)
--config CONF all variants When a feature model is present, project to this Configuration first
--output file stdout Write to file instead of stdout
--include-tests false Include TestCase.sourceFile: entries as testCase components
--scope qname model root Restrict to a namespace subtree

18.3 Remote Package URI Syntax

An implementedBy: value that matches the pattern <registry>:<package>@<version>[#<path>] (registry one of the prefixes below; package and version both non-empty) is treated as a named external dependency rather than a local file path. The same classification applies in validation: such a value is an external reference, so it never raises W023 (§12.8). Any other value — an unknown prefix, a missing @<version>, a repo:/model: path — is a local path.

Supported registries:

Registry prefix Ecosystem
crates.io: Rust (Cargo)
npm: JavaScript/Node
pypi: Python (pip)
maven: Java (Maven Central)
nuget: .NET
github: GitHub repository owner/repo@ref

Examples:

implementedBy:
  - src/scheduler/mod.rs            # local path — harvested as a file component
  - crates.io:tokio@1.38.0          # Rust crate — emitted as a package component
  - npm:lodash@4.17.21              # npm package
  - github:embedded-io-packages/embedded-hal@v1.0.0

18.4 CycloneDX Output (Default)

{
  "bomFormat": "CycloneDX",
  "specVersion": "1.6",
  "serialNumber": "urn:uuid:<generated>",
  "metadata": {
    "timestamp": "<ISO 8601>",
    "tools": [{ "vendor": "Syscribe", "name": "syscribe", "version": "<tool version>" }],
    "component": { "type": "firmware", "name": "<model root name>" }
  },
  "components": [
    {
      "type": "library",
      "name": "scheduler",
      "evidence": {
        "occurrences": [{ "location": "src/scheduler/mod.rs" }]
      },
      "externalReferences": [
        { "type": "model", "url": "syscribe://REQ-SCHED-001" }
      ]
    },
    {
      "type": "library",
      "name": "tokio",
      "version": "1.38.0",
      "purl": "pkg:cargo/tokio@1.38.0"
    }
  ]
}

The externalReferences array on locally-derived components links back to the Syscribe requirement(s) the implementing part satisfies — enabling forward traceability from the SBOM to the model.

18.5 SPDX Output

SPDX 2.3 JSON format with spdxVersion: "SPDX-2.3". Local file components become Package entries with filesAnalyzed: true; remote URI components become Package entries with filesAnalyzed: false; a component version is emitted as versionInfo. Relationships: DESCRIBES, CONTAINS, GENERATED_FROM.

No new element types. No new validation rules.


19 Review Records

19.1 Overview

A ReviewRecord captures a formal review event — design review, requirements review, hazard review, test readiness review — and links it to the model elements it covers. This enables traceability from model elements to the review evidence that authorised them.

ReviewRecord complements ConfirmationMeasure (§8.18): ConfirmationMeasure tracks assessment-level activities required by safety standards (ISO 26262 §6); ReviewRecord tracks the finer-grained, element-level review evidence appropriate for any development review, not limited to safety.

19.2 ReviewRecord Element

ID pattern: ^RR(-[A-Z0-9]{2,12})+-[0-9]{3,8}$
Examples: RR-SW-ARCH-001, RR-SYS-REQ-001

Frontmatter schema:

Field YAML type Required Description
id string Required Stable RR-* identifier
name string Required Free-prose label (e.g., "Software Architecture Review — Sprint 12")
status enum Required open / closed / waived
reviewType enum Required See enum below
reviewDate string optional ISO 8601 date (YYYY-MM-DD)
reviewedBy list of strings optional Names or org-unit identifiers of reviewers
recordedAt string optional URI of the external review record (e.g. a GitHub PR/review). A ReviewRecord is a thin, baselined anchor — the discussion lives in the linked tool; the model keeps the traceability and the coverage gate.
reviews list of strings Required Qualified names or stable IDs of elements covered by this review (≥ 1)
items list optional Action items arising from the review; see sub-schema below

reviewType enum:

Value Description
design_review Architecture or detailed design review
requirements_review Requirements elicitation or allocation review
hazard_review HARA or TARA review meeting
test_readiness_review Test Readiness Review (TRR) verifying test completeness before execution
inspection Formal Fagan-style inspection
walk_through Less-formal peer walkthrough

items: sub-schema:

Field Type Required Description
id string optional Local action-item identifier (e.g., RID-001)
description string Required Action item description
disposition enum Required open / closed / not_applicable
closedBy string optional Qualified name or ID of the element or document evidencing closure

Example:

---
type: ReviewRecord
id: RR-SW-ARCH-001
name: Software Architecture Review — ABS Subsystem
status: closed
reviewType: design_review
reviewDate: "2026-05-14"
reviewedBy: [alice.nakamura, bob.patel]
reviews:
  - Logical::ABSController
  - Logical::WheelSpeedSensor
  - REQ-ABS-001
items:
  - id: RID-001
    description: >
      ABSController missing timeout handling on sensor dropout.
      Add a watchdog requirement.
    disposition: closed
    closedBy: REQ-ABS-WATCHDOG-001
  - id: RID-002
    description: Clarify sensor sampling rate in REQ-ABS-001.
    disposition: closed
    closedBy: REQ-ABS-001
---
Design review of the ABS software architecture covering the controller
and wheel-speed sensor elements.

19.3 Validation Rules

Code Condition
E700 ReviewRecord missing id:, name:, status:, reviewType:, or reviews:
E701 ReviewRecord.id does not match the RR-* pattern
E702 ReviewRecord.status not in open \| closed \| waived
E703 ReviewRecord.reviewType not in the allowed enum
E704 reviews: entry cannot be resolved
E705 items[].disposition not in open \| closed \| not_applicable
W700 ReviewRecord with status: closed has at least one items[] entry with disposition: open — a closed review with unresolved items
W704 A non-draft native Requirement appears in no ReviewRecord.reviews: list — dormant unless the model uses ReviewRecords (opt-in; gateable with --deny W704). Drafted as W701, which is already in use (Requirement asilLevel → verificationMethod).

19.4 CLI Commands

syscribe reviews [<qname>] [--open-only] [--json] — List ReviewRecord elements as a table (ID | Status | Type | Date | Covers | Open items, the last column open/total action items). With a positional <qname> (an element id or qualified name), lists only the reviews covering that element. With --open-only, shows only records that have at least one action item whose disposition is open (the record's own status is not consulted).

| ID | Status | Type | Date | Covers | Open items |
|---|---|---|---|---|---|
| RR-SW-ARCH-001 | closed | design_review | 2026-05-14 | 4 | 0/2 |
| RR-SYS-REQ-001 | open | requirements_review | 2026-06-01 | 7 | 3/5 |

syscribe review <RR-id> [--json] — Detail a single review: elements covered, reviewer list, action items with dispositions.

syscribe reviews --coverage [--json] — Native-Requirement review-coverage cross-table: Requirement | Status | Covered | Reviews (✓/✗, with the ids of the covering ReviewRecords), followed by a Coverage: n/m requirements reviewed line.


20 Behavioral Coverage

20.1 Overview

Behavioral coverage reports how completely the set of active TestCase elements exercises the behavioral elements (ActionDef, Action, StateDef, State) in the model. It bridges the model-level view of behavior with the test-level view of verification.

This is a reporting command — no new element types or validation rules are introduced. Coverage determination uses the existing implementedBy:, sourceFile:, verifies:, and satisfies: links already present in the model.

20.2 Coverage Determination

A behavioral element B is covered by a TestCase TC when any of the following holds:

  1. Direct source overlap: TC.sourceFile: resolves to a path under (or equal to) any path in B.implementedBy: (for PartDef/ActionDef — directory containment check).
  2. Requirement chain: TC.verifies: contains a requirement R, and R is satisfied by an architecture element E, and E is typed by or specialises B.
  3. Test function: a TC.testFunctions[].file is under B.implementedBy:.
  4. Transitive via allocation: TC.verifies: → R.satisfies: → architecture element E allocated to B (via allocatedTo:).

Only active TestCase elements contribute to coverage. draft/review/approved test cases are counted separately as "planned coverage."

20.3 CLI Command

syscribe behavioral-coverage [<qname>] [--depth <N>] [--format text|json] [--uncovered-only] [--include-planned]

Flag Default Description
<qname> model root Scope: only behavioral elements in this namespace
--depth N unlimited Namespace depth limit
--format text Output format
--uncovered-only false Show only uncovered elements
--include-planned false Include draft/review/approved test cases in a "planned" coverage column

Text output:

Behavioral Coverage — VehicleSystem (active tests only)

Element                          Type       Covered  Test Cases
Propulsion::ComputeThrust        ActionDef  ✓        TC-PROP-001, TC-PROP-002
Propulsion::FuelShutoff          ActionDef  ✗        —
Braking::ABSControl              ActionDef  ✓        TC-ABS-001
Braking::EmergencyBrake          StateDef   ✗        —
Navigation::WaypointTracker      ActionDef  ✓        TC-NAV-003

Coverage: 3 / 5 (60.0%)

JSON output:

{
  "scope": "VehicleSystem",
  "covered": 3,
  "total": 5,
  "coverage_pct": 60.0,
  "elements": [
    {
      "qname": "Propulsion::ComputeThrust",
      "type": "ActionDef",
      "covered": true,
      "coveredBy": ["TC-PROP-001", "TC-PROP-002"]
    },
    {
      "qname": "Propulsion::FuelShutoff",
      "type": "ActionDef",
      "covered": false,
      "coveredBy": []
    }
  ]
}

21 ReqIF Export

21.1 Overview

ReqIF (Requirements Interchange Format, OMG formal/2016-09-01) is the standard XML-based interchange format for requirements management tools — IBM DOORS Next, PTC Integrity, Jama Connect, Polarion ALM. Exporting to ReqIF allows Syscribe models to be imported into these tools for review, annotation, or contractual handoff.

This specification defines the mapping from native Requirement elements (and their containing packages) to ReqIF 1.2 artefacts.

21.2 CLI Command

syscribe export-reqif [--output <file>] [--scope <qname>] [--config <CONF>] [--zip]

Flag Default Description
--output file stdout Write to <file>.reqif; with --zip, writes <file>.reqifz
--scope qname model root Export only requirements in this namespace subtree
--config CONF no projection Project to this Configuration and export only active requirements. On a model with no feature model it must name a stored Configuration (identity projection); anything else is a usage error (REQ-TRS-PROJ-001)
--zip false Package as .reqifz (ZIP with a content.reqif entry and any embedded XHTML assets)

21.3 Element Mapping

Requirement → SPEC-OBJECT:

Syscribe field ReqIF construct
id: SPEC-OBJECT LONG-NAME (also emitted as attribute SYSCRIBE_ID)
name: Attribute NAME (string)
Body (Markdown → XHTML) SPEC-OBJECT.DESC (XHTML content in TheValue)
status: Attribute STATUS (enumeration: draft, review, approved, implemented, verified)
silLevel: Attribute SIL_LEVEL (integer)
asilLevel: Attribute ASIL_LEVEL (string: A–D)
plLevel: Attribute PL_LEVEL (string: a–e)
reqDomain: Attribute DOMAIN (enumeration: system, hardware, software)
verificationMethod: Attribute VERIFICATION_METHOD
Qualified name Attribute QUALIFIED_NAME (string)

Package (directory) → SPECIFICATION + SPEC-HIERARCHY:

Each package becomes a SPECIFICATION element. Nested packages and requirements become SPEC-HIERARCHY entries within it, preserving the directory tree structure.

derivedFrom: links → SPEC-RELATION:

Each derivedFrom: entry produces a SPEC-RELATION with RELATION-TYPE-REF pointing to the pre-defined type DERIVED_FROM. The SOURCE is the child requirement; the TARGET is the parent (OSLC direction preserved).

verifies: (reverse index verifiedBy) → SPEC-RELATION:

Each TestCase's verifies: link produces a SPEC-RELATION with type VERIFIED_BY. SOURCE is the requirement; TARGET is the TestCase's id (emitted as a separate SPEC-OBJECT with type TEST_CASE).

TestCase → SPEC-OBJECT (type TEST_CASE, optional):

When --include-tests is specified, active TestCase elements are emitted as SPEC-OBJECT entries of a separate specification section Test Cases.

21.4 Markdown → XHTML Conversion

The Markdown body of each Requirement is converted to XHTML for SPEC-OBJECT.DESC. Supported constructs:

Markdown XHTML
**bold** <strong>
*italic* <em>
`code` <code>
## heading <h2> (stripped of # markers)
Bullet lists <ul><li>
Numbered lists <ol><li>
Fenced code blocks <pre><code>
Gherkin blocks Omitted from XHTML (test-spec artefacts, not requirement prose)

21.5 Limitations

  • Import is limited — import-reqif (§21.6) creates Requirement elements from SPEC-OBJECTs only; ReqIF relations, attribute enumerations (status/domain), nested SPEC-HIERARCHY folders and CSV/Excel mapping files are not imported.
  • SysML structural elements excluded — Only Requirement (and optionally TestCase) elements are exported. PartDef, Port, Action, etc. have no ReqIF mapping.
  • XHTML conversion is best-effort — Complex Markdown (tables, nested lists, HTML passthrough) may lose fidelity.
  • Cross-tool round-trip — Attributes added by the receiving tool during import are not preserved by Syscribe and will be lost on re-export.

21.6 Import

syscribe import-reqif <file.reqif> [--into <package>] [--id-prefix <PFX>] [--class <reqClass>] [--domain <reqDomain>] [--update] [--dry-run]

Reads the SPEC-OBJECTs of a ReqIF document (an OEM export, or a file written by export-reqif) and creates one native Requirement per object. Objects typed Package or TestCase are skipped.

Flag Default Description
--into package Requirements Target package
--id-prefix PFX REQ-IMP Stable-id prefix; ids are <PFX>-NNN, numbered after any existing id with that prefix
--class c stakeholder reqClass of created requirements
--domain d system reqDomain of created requirements
--update off Rewrite name and body of already-imported requirements
--dry-run off List what would be created or updated; write nothing

Each requirement is status: draft; its name comes from the NAME / ReqIF.Name / ReqIF.ChapterName / Title attribute (else the object's LONG-NAME) and its body from the DESC / ReqIF.Text / Text / Description attribute reduced to plain paragraphs (an object with no text gets a placeholder, since an empty body is E012). The OEM identifier (ReqIF.ForeignID, ID or SYSCRIBE_ID, else the object IDENTIFIER) is kept in extRef: ["reqif:<id>"]. Import is idempotent: an object whose reqif:<id> already exists is reported as exists and left alone; a SYSCRIBE_ID that is already a requirement id matches it, so an export-reqif file imports back onto its own model without creating anything. With --update the name and body are rewritten when they differ (every other frontmatter field is kept; a body the author has since extended with headings or code fences is kept and only the name refreshed). A changed text is then picked up by the suspect-link and baseline machinery. Exit 1 on an unreadable or malformed file, a document without requirement objects, or a write failure.

No new element types. No new validation rules.


22 Extensions to Existing Sections

22.1 State Machine Completeness Validation (extends §8.8)

The following validation rules apply to every region of a StateDef/State that declares at least one subStates: entry. For a flat (single-region, non-isParallel, non-composite) machine the region is the machine itself; a parallel machine is checked per region, and a composite machine is checked recursively, one region per level (both described below — they are not out of scope). The rules fire at model-time, after all cross-references are resolved, over the directed (source → target) edge set produced by the canonical transition extractor (§8.8.3, both nested and top-level placements).

Code Condition
W070 Dead state — a substate has no incoming transitions from any other substate and is not marked isInitial: true. The substate can never be entered once the machine starts.
W071 Trap state — a substate has no outgoing transitions and is not marked isFinal: true. Once entered, the state machine cannot exit this state.
W072 Non-determinism — two transitions from the same source state have the same accept.payload (same trigger event type) and neither carries a guard:. The machine's response to this event is ambiguous.
W073 Missing initial state — a StateDef with subStates: has no substate marked isInitial: true. The machine has no defined starting point.
W074 Multiple initial states — more than one substate in the same region is marked isInitial: true. A region has exactly one initial state.

Parallel (orthogonal) state machines. A StateDef/State with isParallel: true has concurrent regions as its direct substates (each region itself declares its own subStates: and transitions; SysMLv2 §7.18). The completeness checks are applied per region: W073/W074 (initial cardinality) are evaluated for each region, and W070/W071/W072 apply within each flat region (findings name the region). Two further rules apply to the parallel parent:

Code Condition
W077 Cross-region transition — a transition (declared at the parallel parent or inside a region) connects substates belonging to two different regions. SysMLv2 forbids transitions between the substates of a parallel state.
W078 Parallel arity — an isParallel: true state declares fewer than two regions; it is not orthogonal.

Composite (hierarchical) state machines. A substate is composite when it carries typedBy: (a reference to another StateDef) or an inline subStates: list (SysMLv2 §7.18). The checks apply recursively over the hierarchy: at each level the direct substates are checked by W070–W074 with a composite substate treated as a single node, and every inline-subStates: substate is recursed into and checked as its own region (findings name the enclosing region). A typedBy: substate is a node only — the referenced StateDef is validated as its own element. The flat single-region case is the depth-1 special case; parallel regions are checked per region by the same recursion.

Code Condition
W076 Unresolved endpoint — a transition source/target names no state anywhere in the machine's hierarchy and resolves to no model element by qualified name.

Behaviors and decision transitions. A state's entryAction/doAction/exitAction and a transition's effect reference an action (a qualified-name string or {typedBy: <qn>}); an unresolved reference raises W079. (accept.payload is not resolution-checked — payloads often name informal event labels.) Decision transitions — two or more transitions from the same source distinguished by guards (SysMLv2 §8.4.13.3) — are legitimate branching and do not raise W072; W072 fires only for same-source, same-payload transitions where none is guarded.

Code Condition
W079 Unresolved behavior — a state entryAction/doAction/exitAction or a transition effect references an action that resolves to no model element.

Incomplete transitions. Every check above consumes (source → target) edges; a transition that lacks an endpoint the schema requires (§8.8.3) produces no edge and would otherwise be ignored silently. It is reported instead — for any machine, with or without subStates::

Code Condition
W929 Incomplete transition — a top-level transition (the machine's own transitions:) has no source: (nor from:), or any transition, top-level or nested at any depth, has no target: (nor to:). A nested transition's source is implicit (its enclosing substate) and is never reported.

All of these codes are draft-suppressed (not emitted for status: draft) and gateable with --deny W07x / --deny W929.

22.2 Budget Expression Language (extends §8.9)

A restricted arithmetic expression grammar is defined for CalculationDef.body when bodyLanguage: budget. This enables the tool to evaluate and verify scalar budgets (mass, power, timing, link margin) without a full KerML expression evaluator.

Grammar (EBNF):

expr   ::= term (("+" | "-") term)*
term   ::= factor (("*" | "/") factor)*
factor ::= NUMBER | feature_ref | "(" expr ")"
NUMBER ::= [0-9]+ ("." [0-9]+)?
feature_ref ::= IDENT ("::" IDENT)*
IDENT  ::= [A-Za-z_][A-Za-z0-9_]*

Resolution: A feature_ref operand resolves to the value: or default: field of an inline attribute in the CalculationDef's own features:, or to a named inline attribute of the element declared in the typedBy: scope chain.

New field on CalculationDef:

Field YAML type Default Description
evaluate string absent Qualified name of a ConstraintDef whose expression: bounds the result. When present, the tool evaluates the body: expression and compares against the constraint.

New validation rules:

Code Condition
E866 evaluate: resolves to an element that is not a ConstraintDef (drafted as E800, which is already in use)
E867 bodyLanguage: budget expression contains a syntax error (unmatched parens, invalid token, etc.) (drafted as E801)
E868 bodyLanguage: budget expression references a feature_ref that does not resolve to an attribute (drafted as E802)
W060 CalculationDef with bodyLanguage: budget evaluates to a value that violates the evaluate: constraint (opt-in — dormant unless bodyLanguage: budget is present; gateable with --deny W060)

Tooling. Budget evaluation is part of syscribe validate: the checks above (E866–E868, W060) run over every CalculationDef with bodyLanguage: budget. There is no dedicated budgets command that tabulates computed values and bounds (not implemented); a violated budget surfaces as a W060 finding.

22.3 ASIL/SIL Decomposition Pair Completeness (extends §12.7)

The following extends Section 12.7 Rule R-007 with an additional structural check for decomposition claims.

Rule R-007b — Decomposition pair completeness. When a Requirement at any integrity level (asilLevel: A–D, or any silLevel) has derivedChildren that all carry strictly lower integrity levels, include a QM channel, or declare decomposedFrom: (a decomposition claim per ISO 26262-9 §5 / IEC 61508-2 §7.4.9), the following structural conditions must hold:

  1. Distinct satisfaction targets. No two decomposition sibling requirements (children with lower integrity levels) may name the same element — structural or behavioral (§12.3) — in their satisfies: list. Sharing a satisfying element means the decomposition is not architecturally independent.
  2. Breakdown ADR required. Each sibling must carry breakdownAdr: referencing the same accepted ADR as the parent (or its own accepted ADR), containing the freedom-from-interference argument.

New frontmatter field on Requirement:

Field YAML type Default Description
decompositionKind enum absent Documents the decomposition argument type: independent (each path independent by design), redundant (both paths active concurrently), or diverse (different implementation technologies). Informational — propagated to the safety-case report.
  1. Allowed pair (ASIL, E878). The channel levels must form an allowed ISO 26262-9 Table 1 pair: D = C+A, B+B or D+QM; C = B+A or C+QM; B = A+A or B+QM; A = A+QM. D -> A+A and D -> B+A are E878. asilLevel: QM is a valid level.

Notation. The ISO decomposition notation asilLevel: B(D) (decomposed level, original level in parentheses) is accepted; it is split at parse time into asilLevel: B plus decomposedFrom: D. decomposedFrom: D may also be written explicitly. It must name an original level A–D that is not lower than the element's own level (E879).

New validation rules:

Code Condition
E865 ASIL/SIL decomposition siblings (children of a higher-level requirement with uniformly lower levels) share a satisfies: target — the decomposed channels must satisfy distinct elements. (Drafted as E860; reassigned to E865 because E860 is already in use.)
W860 A Requirement carrying an integrity level has derivedChildren forming a decomposition claim but fewer than two children — a single-child decomposition is structurally incomplete
E878 Decomposition children do not form an allowed ISO 26262-9 pair (see above)
E879 decomposedFrom: is not an original ASIL A–D or is lower than the element's own asilLevel

22.4 Sequence Diagram Send/Receive Completeness (extends §8.16.8.3)

The completeness rule described in §8.16.8.3 for Sequence diagrams is now normative and enforced. A Sequence diagram element D with subject: pointing to an ActionDef A must include an edge entry (matched by the edge's ref:) for every SendAction and every AcceptAction reachable via A.subActions: — recursing into nested subActions: and the then/else branches of an IfAction. A.steps: is not walked.

New validation rule:

Code Condition
W080 A Sequence diagram's subject: ActionDef has a SendAction or AcceptAction in its sub-action tree that is not referenced by any entry in the diagram's edges: list — the sequence diagram is missing an edge for a known message event

W080 is draft-suppressed (not emitted for Sequence diagrams with status: draft). Gateable with --deny W080.


23 Native PlanningItem: Work-Item Tracking

23.1 Overview

Syscribe models trace Requirement → Architecture → Test, but nothing in the base format represents the work of getting there — the day-to-day breakdown a team (or an LLM being guided through development) actually executes, the shape a Jira epic/story/task or a GitHub issue hierarchy fills today. PlanningItem (ADR-SYS-PLANITEM-001) is that same shape of thing made durable, checked into git, and structurally part of the traceability graph rather than living entirely in an external tracker disconnected from derivedFrom/satisfies/verifies resolution.

PlanningItem is a strict single-parent tree, not a DAG: a child declares at most one parent:, mirroring Requirement.derivedFrom/derivedChildren rather than the more general convergent-evidence structure Graph-of-Thoughts-style planning would allow (deliberately rejected — see the ADR's Decision 1; two independent items may still cite the same evidence by value, which needs no DAG).

23.2 PlanningItem Element

ID pattern: ^PI(-[A-Z0-9]{2,12})*-[0-9]{3,8}$ Examples: PI-HPLE-001, PI-RTH-IMPL-SW-002

Frontmatter schema:

Field YAML type Required Default Description
type literal PlanningItem Required — Discriminator.
id string Required — Stable ID matching PI-* pattern.
name string Required — Free-prose label.
status enum Required — todo / in_progress / blocked / done — GitHub Projects' three built-in defaults plus blocked, needed so a consumer (human or LLM) can distinguish "not started" from "can't proceed."
itemType enum optional absent bug / task / feature — exactly GitHub's own current default Issue Types.
parent string optional absent A single other PlanningItem (id or qname) this one is a child of. Absent means top-level. Resolved like derivedFrom:; cycles are detected the same way.
achieves string or list conditionally required absent One or more native Requirements — or a SafetyGoal, CybersecurityGoal, ADR, Argument, TestPlan or Baseline (GH #240) — (id or qname) this branch of work exists to realise. Required (non-empty) on a top-level item (no parent:); optional on a non-top-level item. Deliberately a distinct field from satisfies:, which carries architecture-specific machinery (E312–E315) that does not apply here.
blockedBy string or list optional absent One or more elements this item is waiting on before it can proceed — most commonly another PlanningItem, but resolved permissively like evidence.ref: (§23.3), unrestricted by kind. A dangling entry or a cycle (through other PlanningItems, back to itself) is an error; a non-empty blockedBy: while status isn't blocked is a warning (likely stale). status: blocked with an empty/absent blockedBy: raises nothing — see §23.4.
evidence list optional absent Duck-typed entries proving completion — see §23.3.
assignedTo string optional absent A single Unix-style username responsible for this item — see §23.7. Not a cross-reference; format is always checked (E723), roster membership only when [users] is configured (E722).
claimedBy string optional absent Advisory claimant (agent/session) id — see §23.9. Never format-validated; written/cleared by syscribe claim/syscribe release.
claimedAt string optional absent Opaque timestamp paired with claimedBy — see §23.9. Conventionally ISO-8601, never itself parsed/validated.
tags list of strings optional absent Free labels.

appliesWhen: (product-line gating) needs no new mechanism — it is already a universal, type-agnostic field; a PlanningItem implementing a product-line feature sets it exactly like any other element.

23.3 evidence: Sub-Schema

Each entry is duck-typed — recognised by which key it carries, not a type: discriminator tag (the same idiom the Allocation features:-list convention already establishes):

Field Type Description
ref string Any resolvable model element (id or qname), unrestricted by kind — not limited to architecture or TestCase.
path string A file/doc path, resolved exactly like implementedBy: (§12.8): a local path is checked to exist, a remote URI is accepted as external. repo:-prefixed paths resolve relative to the git root.
rationale string Optional, on either form. A non-empty rationale: waives that one entry's own resolution check — the entry still counts toward "has evidence," it just isn't itself verified. Mirrors the ffiRationale co-located-waiver pattern. Every other entry in the same list is still checked normally; there is no blanket suppression.
evidence:
  - path: "repo:crates/syscribe-model/tests/hple_subconfigurations.rs"
  - ref: TC-RTH-BATT-001
  - path: "docs/design/rth-timing-analysis.pdf"
    rationale: "External PDF not yet committed; reviewed in RR-RTH-001"

23.4 Validation Rules

Code Condition
E706 PlanningItem.id does not match the PI-* pattern
E707 PlanningItem missing id:, name:, or status:
E708 PlanningItem.status not in todo \| in_progress \| blocked \| done
E709 PlanningItem.itemType (if present) not in bug \| task \| feature
E710 parent: reference does not resolve to any element
E711 parent: resolves to a real element that is not a PlanningItem
E712 A parent: chain forms a cycle
E713 A top-level PlanningItem (no parent:) has no achieves: entry
E714 An achieves: entry does not resolve to any element
E715 An achieves: entry resolves to a real element that is not a native Requirement, SafetyGoal, CybersecurityGoal, ADR, Argument, TestPlan or Baseline
E716 An evidence[].ref does not resolve to any model element (and is not waived)
E717 An evidence[].path does not exist on disk (and is not waived)
E719 A leaf PlanningItem (empty computed children — see §23.5) at status: done has no non-waived, resolving evidence: entry
E720 A blockedBy: entry does not resolve to any model element
E721 A blockedBy: chain forms a cycle (directly or through other PlanningItems)
W308 A non-empty blockedBy: on an item whose status is not blocked — likely stale (the blocker was resolved and status: was never updated)
E722 assignedTo: names a username not present in the declared [users] roster (only checked when that roster is non-empty — see §23.7)
E723 assignedTo: is not a valid Unix-style username (checked unconditionally, regardless of [users] — see §23.7)

E719 is graded harder than the analogous Requirement rule (W300, a warning): claiming done with zero resolvable evidence is a correctness defect, not a soft, time-bound gap the way an unassigned-but-still-approved leaf Requirement is. A non-leaf item (has children) is unconstrained by E719 regardless of its own status or evidence — its completion is a function of its children, not its own evidence list. A waived-only evidence list still fails the rule: a rationale: excuses one entry's check, it does not manufacture a passing entry.

blockedBy: and evidence: are deliberately graded oppositely: status: blocked needs no proof (an empty blockedBy: raises nothing — being blocked is a transient working state), while status: done on a leaf does (E719) — completion is a claim that needs evidence, being blocked is not.

(E718 — a non-scalar Argument.evidence entry — shares the same underlying evidence: field broadened for PlanningItem's benefit, but is not itself a PlanningItem rule; see §8.18.)

23.5 Computed Reverse Index

Mirroring Requirement.derivedChildren, the validator computes children for every PlanningItem — the set of other PlanningItems whose parent: names it. A leaf is a PlanningItem with an empty (or absent) computed children set; "leaf" is not a declared schema concept, it falls out structurally, exactly as elsewhere in this format. refs <PlanningItem> (the generic inbound-reference query) lists a PlanningItem's children (parent relationship), what it blocks (blockedBy relationship, inbound), and, for a Requirement, which PlanningItems achieves it.

23.6 CLI and MCP Surface

The only PlanningItem-specific commands are syscribe claim and syscribe release, which set and clear the advisory claimedBy:/claimedAt: markers (§23.9). There is no other dedicated CLI subcommand and no dedicated MCP tool — deliberately: the ADR holds the rest of the feature to schema + validation. Every other field is authored, listed, and inspected via the existing generic surface:

  • syscribe list PlanningItem / syscribe show <PI-id> / syscribe ls Planning / syscribe find <text> — discovery and inspection, same as any other element type.
  • syscribe refs <PI-id-or-Requirement-id> — inbound parent:/achieves: references (§23.5).
  • The MCP server's existing guarded-write tools (create_element/update_element/move_element/delete_element/apply_changes) already support PlanningItem with zero additional code, since any recognised ElementType gets a working, guarded write path for free.

Explicitly out of scope (tracked as follow-on only if a concrete need arises): MCP tools that actively drive an LLM through a PlanningItem graph (next_actionable_item, mark_evidence, etc.), a formal state-machine-backed status model, and any Jira/GitHub sync or extRef: convention specific to planning items. See examples/planning-item/ for a complete worked example (a Return-to-Home feature broken into design/impl/test items across two Configurations).

23.7 assignedTo: and the [users] Roster

assignedTo: (REQ-TRS-PLANITEM-008) names a single user responsible for a PlanningItem — a Unix-style username (e.g. alice), deliberately a scalar rather than a list, unlike achieves:/blockedBy: (mirrors parent:'s "one at a time" shape; the ADR addendum records the rejected multi-assignee alternative). It is not a cross-reference: a user is organizational metadata, not a model element the resolver can find, so there is deliberately no type: User element — see the ADR addendum's rejected alternative.

Format (E723, always checked). The value must match ^[a-z_][a-z0-9_-]{0,31}$ — lowercase, starting with a letter or underscore, then lowercase letters/digits/underscore/hyphen, 1–32 characters — the same shape useradd/adduser enforce by default. This check is unconditional: it applies whether or not [users] is configured at all, the same way an id-pattern check doesn't wait on anything else being true first.

Roster (E722, opt-in). A project declares its valid users once, in <model_root>/.syscribe.toml, mapping each username to its display name:

[users]
alice = "Alice Nakamura"
bob = "Bob Patel"
Behavior Condition
Dormant [users] absent, or empty — assignedTo: roster membership is unchecked (format is still always checked).
Checked [users] non-empty — assignedTo: must be a key in it (E722 otherwise). A value that already fails the format check (E723) is not also reported as undeclared — one finding per defect.

A key in [users] that is not itself a valid username is reported as W309 (attached to .syscribe.toml) and excluded from the effective roster — one malformed entry doesn't invalidate every other, well-formed one, mirroring [ids.prefixes]'s W046 posture exactly.

An absent assignedTo: never raises anything, regardless of whether a roster is configured. syscribe show <PI-id> resolves and prints the declared display name alongside the raw username when the roster is configured. Otherwise this is schema + validation only, matching ADR-SYS-PLANITEM-001's own posture for the rest of PlanningItem — no CLI filtering/listing by assignee in this phase.

23.8 Completion Check Against achieves: (W310)

W002/W003/W305 (§11) already warn, from a Requirement's own file, when it lacks the verification coverage its status claims — but nothing ties "this specific PlanningItem you are about to mark done" to "here specifically are the achieves: requirements that are not actually backed by evidence yet" (REQ-TRS-PLANITEM-010). W310 closes that gap, scoped to the PlanningItem rather than the model at large.

For a PlanningItem at status: done, each achieves: entry that resolves to a native Requirement (a dangling or wrong-kind entry is left to E714/E715, never re-flagged here) is checked against the exact same bar W002/W305 already apply to that Requirement, per its own kind:

  • A leaf Requirement (no derivedChildren) needs at least one active TestCase — W002's bar.
  • A parent Requirement (has derivedChildren) needs at least one active, integration-level (testLevel: L3, L4, or L5) TestCase directly on itself — W305's bar; a parent's leaf descendants carrying their own coverage does not change what W305 requires of the parent.

W310 fires once per (PlanningItem, Requirement) pair that fails its bar, attached to the PlanningItem's file — a distinct finding from any W002/W305 already present on the Requirement's own file, not a duplicate of it (different code, different file). It never fires for todo/in_progress/blocked, and applies at any tree position (leaf or non-leaf PlanningItem) — unlike E719, an achieves: claim's verification state does not depend on whether the claiming item itself has children.

23.9 Claim Markers for Concurrent Multi-Agent Work (claimedBy:/claimedAt:, W311)

claimedBy:/claimedAt: (issue #115) are optional, advisory ownership markers — a coordination signal ("is anyone already on this?") for an orchestrating process running several agents against one model concurrently, not a filesystem lock. Neither field is itself format-validated (claimedAt: is opaque free text, conventionally an ISO-8601-ish timestamp, the same posture as wcet:); nothing prevents hand-authoring them, though the intended path is the dedicated commands below.

status: in_progress
claimedBy: agent-session-01VRUS...
claimedAt: 2026-09-12T08:00:00Z

Commands. syscribe claim <PI-id> --by <agent-id> sets both fields, refusing (no file written) when the item is already status: done (nothing to claim), or already claimedBy: a different value than --by — re-claiming with the same --by is allowed and refreshes claimedAt:. syscribe release <PI-id> clears both fields unconditionally, regardless of status: (a no-op, nothing written, when the item was not claimed). Both resolve their target by qualified name or stable id, support --dry-run, and refuse on a non-PlanningItem target. claimedBy is surfaced in show <PI-id> and in list PlanningItem --json.

W311 (overlap check). Two PlanningItems are each "active" when status: in_progress or claimedBy: is non-empty (a claimed item not yet flipped to in_progress, or an in_progress item nobody ran claim on, both count). W311 fires once per active pair that overlaps by:

  • a shared achieves: Requirement, or
  • an evidence[].path entry resolving to the same repo-relative path string.

— attached to the lexically-first item's file (by stable id), so re-running validate reports each overlapping pair exactly once rather than once from each side. This is the validator-side counterpart to claim/release: it fires whether or not either item was ever actually claimed, since status: in_progress alone already signals active work.


Appendix A: Frontmatter Field Reference

A.1 below lists the core SysML-structural and common fields with their types and defaults. A.2 indexes every other recognised field by the section that specifies it. Together they cover all fields the parser recognises (any other key raises W047, §3.17); syscribe spec fields prints the reference for the installed tool.

A.1 Core fields

Field Applies to Type Default Section
type All string — (required) 3.1
name All string filename stem 3.1
shortName All string absent 3.1
qualifiedName All string absent 3.1, 3.10 (locale-variant target only; never overrides the path-derived qname)
visibility All string public 3.1
extRef All string or list absent 3.1
isAbstract All bool false 3.2
isVariation Def/Usage bool false 3.2
isVariant Usage bool false 3.2
isIndividual Occurrence bool false 3.2
isReadonly Usage bool false 3.2
isDerived Usage bool false 3.2
isEnd Usage bool false 3.2
isPortion Occurrence usage bool false 3.2
isReference Usage bool false 3.2
isComposite Usage bool true 3.2
isConstant Usage bool false 3.2
isOrdered Usage bool false 3.2
isNonunique Usage bool false 3.2
supertype Def string or list absent 3.3
typedBy Usage string or list absent 3.3
subsets Usage list absent 3.3
redefines Usage list absent 3.3
conjugates PortDef string absent 3.3
multiplicity Usage string "1" or "0..*" 3.4, 6
direction Port, Parameter string absent 3.5
features Def/Usage list absent 3.6
imports Package list absent 3.7
aliases All list absent 3.7
filterCondition Package string absent 3.7
metadata All list absent 3.8
dependsOn All list absent 3.9
requires All list absent 3.11
assume All list absent 3.11
rep All string absent 3.12
connections PartDef/Part list absent 8.4.1
flowConnections PartDef/Part list absent 8.6.2
successionConnections ActionDef/Action/PartDef/Part list absent 8.4.4
bindingConnections Def/Usage list absent 8.4.3
performs PartDef/Part list absent 8.2.1
exhibitsStates PartDef/Part list absent 8.2.1
parameters ActionDef/CalcDef/etc. list absent 8.7.2
returnType CalculationDef/VerificationCaseDef string absent 8.9.1, 8.12.3
body CalculationDef/ActionDef string absent 8.7.1, 8.9.1
bodyLanguage CalculationDef/ActionDef string absent 8.7.1, 8.9.1
subActions ActionDef/Action/CaseDef list absent 8.7.3
controlNodes ActionDef/Action list absent 8.7.4
entryAction StateDef/State string or map absent 8.8.1
doAction StateDef/State string or map absent 8.8.1
exitAction StateDef/State string or map absent 8.8.1
subStates StateDef/State list absent 8.8.2
transitions StateDef/State list absent 8.8.3
isParallel StateDef/State bool false 8.8.1
isAsserted Constraint bool false 8.10.2
isNegated Constraint bool false 8.10.2
expression ConstraintDef string absent 8.10.1
subject Req/Case string absent 8.11.1, 8.12.1
actors Req/UseCase list absent 8.11.1, 8.12.4
stakeholders Req/Viewpoint list absent 8.11.1, 8.14.1
concerns Req/Viewpoint list absent 8.11.1, 8.14.1
framedConcerns RequirementDef list absent 8.11.1
derivedFrom RequirementDef/Requirement list absent 8.11.1
satisfies Part/PartDef is the common case; not type-restricted — see §12.3 for the full endorsed shape list list absent 8.11.4, 12.3
implementedBy Part/PartDef/Interface/InterfaceDef string or list absent 8.11.4 / 12.8
verifies VerificationCase list absent 8.12.3
verdictExpression VerificationCase string absent 8.12.3
verdictType VerificationCaseDef string VerificationCases::VerdictKind 8.12.3
objectives CaseDef list absent 8.12.1
result CaseDef string absent 8.12.1
includes UseCaseDef list absent 8.12.4
extends UseCaseDef list absent 8.12.4
extensionPoints UseCaseDef list absent 8.12.4
allocations AllocationDef/Package/PartDef list absent 8.13.1, 8.13.2
constraints InterfaceDef list absent 8.3.3
expose ViewDef list absent 8.14.2
rendering ViewDef string absent 8.14.2
satisfiedBy ViewpointDef list absent 8.14.1
methods ViewpointDef list absent 8.14.1
values EnumerationDef list — (required) 8.5.2
annotates MetadataDef list absent (unrestricted) 8.15.1
isSemantic MetadataDef bool false 8.15.1
ends ConnDef/IntfDef list absent 8.3.3, 8.4.2
timeSlices OccurrenceDef list absent 8.2.4
snapshots OccurrenceDef list absent 8.2.4
variantOf Part/Usage string absent 9.4
isConjugated Port bool false 8.3.2
itemType FlowDef string absent 8.6.1
id native Requirement/TestCase string — (required) 8.11.6, 8.12.5
name native Requirement/TestCase string — (required; free prose) 8.11.6, 8.12.5
status native Requirement/TestCase string — (required) 8.11.6, 8.12.5
testLevel native TestCase string — (required) 8.12.5
silLevel native Requirement integer absent 8.11.6
asilLevel native Requirement string absent 8.11.6
plLevel native Requirement / SafetyGoal string absent 8.11.6, 8.18.1
derivedFromSafetyGoal native Requirement string absent 8.11.6, 8.18.1
derivedFromCybersecurityGoal native Requirement string absent 8.11.6, 8.18.2
verificationMethod native Requirement string absent 8.11.6
wcet native Requirement string absent 8.11.6
allocatedFrom Allocation (with allocatedTo); any other element only as a legacy input — derived reverse of allocatedTo (§12.9) string or list absent 8.13.2, 8.18.2, 12.9
allocatedTo Any element (the allocated source, §12.9 form 1) or Allocation string or list absent 8.13.2, 8.18.2, 12.9
ffiRationale Any element string absent 11.12 (W034) — freedom-from-interference / partitioning rationale; excuses a mixed-criticality shared-allocation pair
testExempt Any architecture element string absent 8.12.5, 11.12 (W104) — non-empty rationale suppressing the untested-element warning
responsibility Any element string absent 3, 11.12 (W038) — accountable party/organisation for a work product (DIA/CIA split)
measureType ConfirmationMeasure string absent 8.18.2, 11.12 (E849) — confirmation_review / functional_safety_audit / functional_safety_assessment / cybersecurity_assessment
independenceLevel ConfirmationMeasure string absent 8.18.2, 11.12 (E850) — I1 / I2 / I3
confirms ConfirmationMeasure string or list absent 8.18.2, 11.12 (E851) — confirmed work-product ref(s)
argumentType Argument enum (claim/strategy/solution/context/justification/assumption/undeveloped) claim 8.18.6, 11.12 (E854)
supports Argument string or list absent 8.18.6, 11.12 (E855) — SafetyGoal/parent Argument argued for
evidence Argument string or list absent 8.18.6, 11.12 (E855) — Requirement/TestCase/sub-Argument/AssumptionOfUse refs
appliesTo AssumptionOfUse string or list absent 8.18.6, 11.12 (E858) — SafetyGoal/Argument/Requirement constrained
covers SafetyMechanism string or list absent 8.18.9, 11.12 (E897) — FMEAEntry/FaultTreeEvent/Requirement/SafetyGoal/HazardousEvent covered
reactionTime SafetyMechanism duration string absent 8.18.9, 11.12 (W894)
quantities any (SafetyGoal / Requirement / TestCase) list of mappings absent 8.18.8, 11.12 (E895, W893) — {kind, value, unit}
runsOn TestCase / TestPlan string or list absent 8.12.7, 11.12 (E894) — TestEnvironment(s) the test executes on
requiresCapabilities TestCase / TestPlan string or list absent 8.12.7, 11.12 (W891) — capabilities needed from the environment
capabilities TestEnvironment string or list absent 8.12.7
environmentKind TestEnvironment enum absent 8.12.7, 11.12 (E893)
calibrationStatus / calibrationDue TestEnvironment enum / date absent 8.12.7, 11.12 (E893, W892)
analyses DependentFailureAnalysis string or list (≥2) absent 8.18.7, 11.12 (E890, E891) — elements argued independent
sharedResources DependentFailureAnalysis list of mappings absent 8.18.7, 11.12 (E892, W890) — {resource, kind, initiators, couplingFactor, mitigation}
hazardRef DamageScenario / ThreatScenario string or list absent 8.18.2
riskTreatment ThreatScenario enum (avoid/reduce/share/retain) absent 8.18.2
residualRisk ThreatScenario string absent 8.18.2
sourceFile native TestCase string absent 8.12.5
testFunctions native TestCase list absent 8.12.5
tags native Requirement/TestCase list of strings absent 8.11.6, 8.12.5
links Any element map: declared link-type name → string or list absent 12.10 — user-defined outbound links; keys must be declared in [linkTypes] (E630)
derive Any element map: field name → formula string absent 3.18 — computed fields, dependency-ordered; cycle E504, malformed E505, unknown element E506

A.2 Fields specified in their own sections

Area Fields Section
Common / cross-cutting appliesWhen, domain, custom_fields, displayOrder, traceBaselines, refines, rationale, locale, about, text, steps, valueKind, value, portionKind 3.8–3.19, 8.2–8.11, 12.10
Native Requirement reqClass, requirementKind, reqDomain, breakdownAdr, decompositionKind, dalLevel, decomposedFrom 8.11.6, 12.2, 12.5, 22.3
Native TestCase / TestPlan coverageTarget, securityTestMethod, scope, configurations, demonstrates, testCases, selection 8.12.5, 8.12.6
Structural extras isDeploymentPackage, operations, clients, suppliers, evaluate, objective, viewpoint 2.4, 8.3.4, 8.12, 8.14, 12.6, 22.2
Diagram diagramKind, svgMode, svgFile, pumlMode, pumlFile, shapes, edges, layout, groupBy, flowKind (also on a PortDef, a port feature and a connections: entry) 8.16, 8.16.8.2, 8.16.9
HARA severity, exposure, controllability, consequence, freqExposure, avoidance, demandRate, operationalSituation, hazardousEvents, safeState, ftti 8.18.1
TARA assets, damageSeverity, impactCategories, damageScenarios, attackFeasibility, attackVector, securityProperty, calLevel, threatScenarios, controlType, implementsGoals, cveId, cvssScore, cvssVector, cvssSeverity, fixedIn, mitigatedBy, affectedElements, assetTable, damageTable, threatTable, goalTable, controlTable, the per-category impact ratings safetyImpact, financialImpact, operationalImpact, privacyImpact, and the attack-potential factors elapsedTime, expertise, knowledge, windowOfOpportunity, equipment 8.18.2
ArchitectureRule kind, from, to, via, layers, maxDepth, waivers (plus severity, rationale); see §8.20 8.20
Architecture layers layer on a PartDef/Part (see §8.20) 8.20
AttributeDef constraints range, pattern, default (see §8.5.1) 8.5.1
Deployment nodes deploymentKind, replicated (see §8.22) 8.22
Software design levels level (see §8.23) 8.23
Resource budgets ram, flash, nvm, stack, ramCapacity, flashCapacity, nvmCapacity, margin, period, deadline, priority, cpuMargin, scheduling (see §8.21) 8.21
Stereotype profiles and checks profile, version, autoApply (on a package), checks (on a MetadataDef); see §8.15.2 8.15.2
Asset cybersecurityProperties, assetOwner, relatedSafetyGoal 8.18.10
FTA / FMEA / attack trees topEvent, missionTime, gateType, inputs, eventKind, failureRate, diagnosticCoverage, latentDiagnosticCoverage, probability, fmeaRef, entries, failureMode, effect, cause, fmeaSeverity, occurrence, detection, rpn, recommendedAction, ftaRef, threatRef, ccfGroup, ccfBeta, rateSource, rateStatus, injection 8.18.3–8.18.5, 8.8.6
Executable models and plants plant, fmu, plants, plantStep, plantConnections (parts and TestCases); on a PlantDef: parameters, inputs, outputs, states, equations, method, maxStep 8.8.6; docs/model-guide/plant-models.md, fmi-plants.md
Baseline date, approver, gitTag, gitCommit, frozenScope, seal, supersedes 8.19
Product-line engineering groupKind, mandatory, cardinality, parentFeature, excludes, contributesTo, featureModel, parameterBindings, parameterConstraints, subConfigurations, baselineRef, buildExports, buildOverrides, featureTree, crossTreeConstraints 9.6–9.10, 14.7
IEC 62443 targetSL, achievedSL, members, inZone, fromZone, toZone, protocols 13
Multi-repo, plugins, annotated source, SysMLv2 ingestion repoImports, foreignFormat, annotationFormat, marker, include, exclude, sysmlSubmodel 14.3; docs/model-guide/stdio-plugins.md, annotated-source.md, sysmlv2-submodel.md
TradeStudy criteria, alternatives, scores, decision 15.2
ReviewRecord reviewType, reviewDate, reviewedBy, reviews, items, recordedAt 19.2
PlanningItem parent, achieves, itemType, evidence, blockedBy, assignedTo, claimedBy, claimedAt 23
Removed title — parsed only to report E025 3.1

Appendix B: Mapping of SysML Textual Keywords to type: Values

SysML textual keyword Markdown-SysML type:
part def PartDef
item def ItemDef
port def PortDef
connection def ConnectionDef
interface def InterfaceDef
action def ActionDef
calc def CalculationDef
constraint def ConstraintDef
requirement def RequirementDef
concern def ConcernDef
case def CaseDef
analysis def AnalysisCaseDef
verification def VerificationCaseDef
use case def UseCaseDef
occurrence def OccurrenceDef
individual def IndividualDef
flow def FlowDef
succession def SuccessionDef
state def StateDef
attribute def AttributeDef
enum def EnumerationDef
allocation def AllocationDef
metadata def MetadataDef
view def ViewDef
viewpoint def ViewpointDef
rendering def RenderingDef
part Part
item Item
port Port
connection Connection
interface Interface
action Action
calc Calculation
constraint Constraint
requirement Requirement
concern Concern
case Case
analysis AnalysisCase
verification VerificationCase
use case UseCase
occurrence Occurrence
individual Individual
flow Flow
succession Succession
state State
attribute Attribute
enum Enumeration
allocation Allocation
metadata Metadata
view View
rendering Rendering
package Package
library package LibraryPackage
(native — no SysML keyword) Requirement (native, §8.11.6), TestCase, TestPlan, FeatureDef, FeatureModel, Configuration, Diagram, and the record and safety/security types of §2.6–§2.7