Part X — Feature Modeling and the 150% Product Line Model¶
GUIDE · PART X — FEATURE MODELING
10.1 What the 150% model is¶
In Product Line Engineering (PLE), a 150% model is a single model that contains all requirements, architecture elements, tests, and analysis artefacts for all possible product variants simultaneously — including elements that are mutually exclusive and can never appear together in any real product. The "150%" label reflects the fact that the model is deliberately larger than any single product; it is the superset.
From this superset, you derive a 100% model — the exact set of elements active in a specific product variant — by applying a Configuration: a named feature selection that resolves every appliesWhen: expression and keeps only the elements whose predicate holds true.
Traditional PLE tools (PTC Integrity Modeler, pure::variants, Gears) implement this as a feature model + a configuration engine. syscribe does the same thing in plain text and git:
| PLE tool concept | syscribe equivalent |
|---|---|
| Feature model | FeatureDef tree |
| Feature node (optional/mandatory/alternative) | FeatureDef with groupKind: |
| Feature constraint (requires/excludes) | requires: / excludes: on FeatureDef |
| Product configuration | Configuration with features: map |
| Variant element | Any element with appliesWhen: |
| 100% projection | syscribe -m model validate --config CONF-X |
| Configuration SAT check | syscribe -m model feature-check --deep |
| Diff between variants | syscribe -m model diff --config A --config B |
10.2 Defining the feature model¶
Features are FeatureDef elements, typically in a Features/ package. The hierarchy reflects the decomposition of variability in your product line.
---
type: FeatureDef
id: FEAT-PORT-001
name: Port
groupKind: alternative # exactly one child must be selected (XOR)
mandatory: true # always selected (the root feature)
---
The hardware/software port must be exactly one of: CortexM, CortexM33,
RiscV, Hosted (for testing).
---
type: FeatureDef
id: FEAT-PORT-CM33-001
name: CortexM33
groupKind: alternative # child of the Port alternative group
requires:
- Features::MPU # CortexM33 implies MPU is available
---
ARMv8-M Mainline port. Enables PSPLIM stack-limit and MPU domain features.
---
type: FeatureDef
id: FEAT-MPU-001
name: MPU
groupKind: optional # can be present or absent (BooleanOr)
requires:
- Features::CortexM33 # MPU only makes sense with CortexM33
---
Memory Protection Unit support. When selected, enables REQ-MPU-* and the
per-thread MPU domain mechanism.
GroupKind values¶
groupKind | Meaning | Example |
|---|---|---|
mandatory | Always selected when parent is selected | A core feature every product must have |
optional | May be selected or not (boolean) | An add-on feature |
alternative | Exactly one child selected (XOR group) | Target port: Cortex-M3, Cortex-M33, RISC-V |
or | One or more children selected | Communication protocol group |
Parameterised features¶
Features can carry typed parameters — numeric or enum values that vary across configurations without requiring separate feature nodes:
---
type: FeatureDef
id: FEAT-TOPO-001
name: Topology
mandatory: true
parameters:
- name: maxCpus
type: integer
range: [1, 8]
default: 1
isRequired: true # W017 if unbound in any Configuration
---
In a Configuration, bind the parameter:
Cross-feature parameter constraints live in the Package _index.md:
# model/Features/_index.md
parameterConstraints:
- id: PC-001
expression: "Features::Topology.maxCpus >= 2" # comparison over dotted parameter refs only
appliesWhen: "Features::Smp or Features::Amp" # boolean predicate over features
severity: error # a violating Configuration is E221 (W025 with severity: warning)
feature-check evaluates the constraint against every Configuration whose appliesWhen: holds; an unresolved parameter path in expression: (e.g. a bare feature name) is E213.
10.3 Defining product configurations¶
A Configuration is a feature selection that resolves the 150% model into one product:
---
type: Configuration
id: CONF-RP-PICO2-AMP-001
name: "Raspberry Pi Pico 2 (RP2350, dual Cortex-M33) AMP — HIL"
status: approved
featureModel: Features::Sabaton
features:
Features::Port: true
Features::CortexM33: true
Features::Amp: true
Features::MPU: true
Features::StackLimit: true
Features::Async: true
Features::Single: false
Features::HostedPort: false
parameterBindings:
Features::Topology.maxCpus: 2
---
AMP configuration: Core 0 runs the kernel; Core 1 runs a safety monitor
thread under AMP isolation. Uses the RP2350 dual-core hardware.
Validate this configuration in isolation:
This projects the 150% model onto this product, runs every appliesWhen: predicate, and checks: - E226: a cross-reference that resolves globally but not in this config (an element references something that is appliesWhen: gated off) - W019: a traceability link that escapes the configuration boundary - W015: a requirement active in this config with no test case running in it
10.4 Gating elements with appliesWhen:¶
Any element — requirements, architecture parts, test cases, safety artefacts — can carry appliesWhen: to restrict it to specific variants. The expression uses FeatureDef QNames with and / or / not / parentheses.
On a Requirement:
---
type: Requirement
id: REQ-MPU-DOMAIN-001
name: "Kernel shall enforce per-thread MPU domain isolation"
status: approved
asilLevel: D
verificationMethod: test
appliesWhen: Features::MPU and Features::CortexM33
---
This requirement only appears in the coverage matrix for configurations where both MPU and CortexM33 are selected. It is — (N/A) for all others.
On an Architecture element:
---
type: PartDef
name: MpuDomainManager
domain: software
asilLevel: D # a satisfier carries the requirement's integrity level (E843)
appliesWhen: Features::MPU
satisfies:
- REQ-MPU-DOMAIN-001
---
On a TestCase:
---
type: TestCase
id: TC-MPU-FAULT-HIL-001
name: "MPU read-only region write causes MemManage trap on hardware"
status: draft
testLevel: L5
appliesWhen: Features::MPU and Features::CortexM33
verifies: [REQ-MPU-FAULT-001]
---
On an entire Package (gates the whole subtree):
Every requirement in model/Requirements/MPU/ is automatically gated on Features::MPU. One declaration per path (E228 if a parent already gates the subtree); empty gated packages warn W026.
10.5 Feature model integrity checks¶
Before depending on the feature model for certification, validate it:
syscribe -m model feature-check # constraints, orphans, dead features
syscribe -m model feature-check --deep # SAT-backed: void model, dead/core/
# false-optional features, full semantics
syscribe -m model feature-check --count # number of valid configurations
syscribe -m model feature-check --enumerate # list all valid configurations
feature-check --deep uses SAT analysis: - void model: no valid configuration exists (all features conflict) - dead feature: a feature that can never be selected in any valid configuration - core feature: always selected in every valid configuration (may as well be mandatory) - false-optional: declared optional but forced true by constraints
These findings surface design errors that are impossible to spot manually in a large feature model.
10.6 Understanding variant differences¶
Compare two configurations to see what changes:
# Elements active in AMP but not in single-core QEMU (and vice versa)
syscribe -m model diff --config CONF-RP-PICO2-AMP-001 --config CONF-LM3S-QEMU
Explain why a specific element is active in a configuration:
Output:
Element: Requirements::MPU::MpuDomain::REQ-MPU-DOMAIN-001
appliesWhen: Features::MPU and Features::CortexM33
Features::MPU → selected: true
Features::CortexM33 → selected: true
Verdict: active
syscribe -m model why-active REQ-MPU-DOMAIN-001 --config CONF-LM3S-QEMU
# Verdict: inactive (Features::MPU → selected: false)
10.7 Coverage matrix across the product line¶
The matrix command shows requirement coverage across all configurations simultaneously. This is the 150% model's "projection verification" view — every row is a requirement, every column is a configuration, every cell shows whether that requirement is covered (✓), gapped (✗), or not applicable in that variant (—):
syscribe -m model matrix # full 150% matrix (all reqs × all configs)
syscribe -m model matrix --gaps-only # only rows with at least one gap
syscribe -m model matrix --status approved # only approved requirements
syscribe -m model matrix --tag safety # only safety-tagged requirements
syscribe -m model matrix --features # feature × configuration selection grid
Interpreting the matrix for product line certification:
A requirement that shows — in all configurations is appliesWhen: gated to features that no configuration currently selects — it is dead in your current product line. Either add a configuration that selects it or remove it from the 150% model.
A requirement that shows ✗ in one configuration but ✓ in another typically means the verification is present but not yet connected (appliesWhen: on the test case may need extending to that configuration).
10.8 Safety analysis in a product line¶
The 150% model approach is especially valuable for safety work, where the cost of separate analyses per variant is very high.
Approach: author the HARA, SafetyGoals, FaultTrees, and FMEAs once in the 150% model. Gate variant-specific elements with appliesWhen:. Safety goals that apply across the whole product line have no appliesWhen:; variant-specific ones do.
# SafetyGoal that applies to all variants
---
type: SafetyGoal
id: SG-KERNEL-001
name: "Kernel scheduler shall not starve any safety-relevant thread"
status: approved
asilLevel: D
hazardousEvents: [HE-KERNEL-001]
---
# SafetyGoal specific to AMP variants only
---
type: SafetyGoal
id: SG-KERNEL-006
name: "AMP core isolation shall prevent cross-core interference"
status: approved
asilLevel: D
hazardousEvents: [HE-KERNEL-006]
appliesWhen: Features::Amp
---
FaultTree gating: if a FaultTree analysis changes between variants (e.g., the MPU hardware fault path only exists in configurations with Features::MPU), gate the relevant FaultTreeEvent elements:
---
type: FaultTreeEvent
id: FTE-KERNEL-101
name: "Stack overflow corrupts TCB"
eventKind: basic
failureRate: 5.0e-9
diagnosticCoverage: 0.97 # high DC because PSPLIM is available
latentDiagnosticCoverage: 0.75
appliesWhen: Features::StackLimit
---
---
type: FaultTreeEvent
id: FTE-KERNEL-102
name: "Stack overflow undetected (no stack-limit hardware)"
eventKind: basic
failureRate: 5.0e-9
diagnosticCoverage: 0.30 # low DC — software-only detection
latentDiagnosticCoverage: 0.15
appliesWhen: not Features::StackLimit
---
When you run syscribe metrics --config CONF-RP-PICO2-HIL, FTE-KERNEL-101 is used (PSPLIM available, high DC). When you run syscribe metrics --config CONF-LM3S-QEMU, FTE-KERNEL-102 is used instead (no PSPLIM, lower DC). The PMHF computation is automatically configuration-aware.
ASIL decomposition across variants: if variant A decomposes an ASIL D requirement into two ASIL B requirements implemented on independent channels, and variant B uses a single ASIL D implementation, both strategies can coexist in the 150% model gated by the appropriate features. The validator checks per-configuration that the ASIL integrity chain is consistent within that variant (E841–E843).
10.9 Certification per variant¶
A certifiable product variant is a named, baselined configuration. The workflow:
# Step 1: Validate the specific variant
syscribe -m model validate --config CONF-RP-PICO2-HIL
# Step 2: Check its coverage (every active requirement has test evidence)
syscribe -m model matrix --config CONF-RP-PICO2-HIL --gaps-only
# Step 3: Compute safety metrics for this variant's fault trees
syscribe -m model metrics --config CONF-RP-PICO2-HIL
# Step 4: Run the safety-readiness audit for this variant
syscribe -m model audit --config CONF-RP-PICO2-HIL
# Step 5: Baseline it in git (optionally also seal a first-class Baseline element, see below)
git tag v1.0-CONF-RP-PICO2-HIL-2026-06-14
# Step 6: Capture the evidence artefacts
syscribe -m model validate --config CONF-RP-PICO2-HIL --json \
> artifacts/validation-CONF-RP-PICO2-HIL-$(git describe --tags).json
syscribe -m model metrics --config CONF-RP-PICO2-HIL --json \
> artifacts/metrics-CONF-RP-PICO2-HIL-$(git describe --tags).json
The git tag is the baseline. For a tamper-evident, content-hashed record inside the model itself, syscribe -m model baseline create --tag <tag> --frozen-scope <sel> seals a first-class Baseline element (BL-*, E520/W520 on drift; see syscribe help baseline and baseline verify|diff|list|show). The JSON artefacts are the evidence record tied to that baseline. An auditor can reproduce either by checking out the tagged commit and re-running the same commands.
10.10 Assisted configuration¶
When a new product variant needs to be defined, the configure command helps navigate the feature model:
# Start with a draft Configuration holding the partial selection
# (model/Configurations/CONF-NEW-001.md, status: draft, with
# features: {Features::CortexM33: true, Features::Amp: true}),
# then see what is forced and what remains free
syscribe -m model configure CONF-NEW-001
configure takes a stored Configuration (id or qualified name); inline selection strings are accepted only by the --config lenses of other commands.
Output tells you: - Which features are forced true by the requires: constraints on your selection - Which features are forced false by excludes: constraints - Which features remain free for you to choose - Whether the partial selection is satisfiable at all
This replaces the manual constraint-chasing that traditionally requires a domain expert to work through feature dependency matrices in a spreadsheet.
10.11 Trade study across variants (with MagicGrid)¶
If you have used MagicGrid (Part III), the trade-study command evaluates each Configuration against the B4 Measures of Effectiveness and scores them:
For product line decisions — "which variant architecture best meets the stakeholder needs?" — this provides a structured, traceable answer grounded in the W4/S4 parameter bindings of each configuration, rather than in an informal spreadsheet.
# A Configuration as a MagicGrid trade-study variant
---
type: Configuration
id: CONF-CM33-HIGH-PERF-001
name: "Cortex-M33 high-performance variant"
status: draft
custom_fields:
mg_variant: true
featureModel: Features::Sabaton
features:
Features::CortexM33: true
Features::MPU: true
parameterBindings:
Features::Topology.maxCpus: 1
W4::ContextSwitchWCET.value: 8 # µs, matches MoP constraint
---
10.12 The 150% model in practice — guidance for new product lines¶
Start small: do not try to capture all variability up front. Begin with a single configuration (the reference product) and no appliesWhen: gates. Add features and appliesWhen: expressions as the second and third variants emerge.
Feature granularity: a feature should correspond to a coherent set of requirements, one or more architecture elements, and a set of tests that live or die together. If toggling a "feature" requires touching dozens of unrelated files, split it.
Gate packages, not files: when an entire domain only applies to one variant family (e.g., all AMP-specific requirements), gate the whole _index.md rather than adding appliesWhen: to every file. This avoids W026 false positives and keeps individual files clean.
Safety goal scope: prefer safety goals with no appliesWhen: where the goal is common to all variants, even if the mechanism that satisfies it varies. The mechanism (PartDef, FaultTreeEvent) gets appliesWhen:; the goal does not. This keeps the HARA and safety case structure stable across the product line.
Baseline discipline: every Configuration that ships to a customer must have a git tag (and, where a tamper-evident record is wanted, a sealed Baseline element via baseline create) before release. The tag name should encode the Configuration ID and the date. The audit output at that tag is the acceptance record.