Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions docs/feature-model-bindings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Feature Binding File Format

A binding file maps features from the feature model to the artifacts and
source code that implement them. This is a separate file from the feature
model and the variant configuration: the feature model describes *what*
can be chosen, the variant says *what was chosen*, the binding says
*where the implementation lives*.

See [feature-model-schema.md](feature-model-schema.md) for the feature
model reference.

## File shape

```yaml
# artifacts/bindings.yaml
bindings:
<feature-name>:
artifacts: [<ID>, <ID>, …] # optional; artifact IDs from the project
source: [<glob>, …] # optional; source globs, e.g. src/auth/**
```

| Field | Type | Required | Meaning |
| --------- | ------------ | -------- | ------------------------------------------------------ |
| `bindings`| map | yes | Keyed by feature name; every key must appear in the |
| | | | feature model. |

Each binding entry:

| Field | Type | Default | Meaning |
| ----------- | ------------ | ------- | ----------------------------------------------------- |
| `artifacts` | list<string> | `[]` | Artifact IDs (e.g. `REQ-042`) implemented by feature. |
| `source` | list<string> | `[]` | Glob patterns for the implementing source files. |

Features not listed in `bindings` are treated as having no artifact or
source coverage — validation reports them as `unbound`.

## Worked example

```yaml
# examples/variant/bindings.yaml
bindings:
pedestrian-detection:
artifacts: [REQ-042, REQ-043]
source: ["src/perception/pedestrian/**"]
lane-keeping:
artifacts: [REQ-050]
source: ["src/control/lane_keep/**"]
adaptive-cruise:
artifacts: [REQ-051]
source: ["src/control/cruise/**"]
eu:
artifacts: [REQ-200]
asil-c:
artifacts: [REQ-101]
```

## Optional: per-variant declarations

The binding file may also declare named variants that `rivet variant
check-all` will iterate:

```yaml
variants:
- name: eu-adas-c
selects: [eu, adas, asil-c]
- name: us-autonomous-d
selects: [us, autonomous, asil-d]

bindings:
```

When present, `rivet variant check-all --model fm.yaml --binding b.yaml`
resolves every entry under `variants:` and exits non-zero if any fail.

If a binding file has no `variants` block, `check-all` reports an empty
result and exits successfully.
176 changes: 176 additions & 0 deletions docs/feature-model-schema.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# Feature Model Schema

This document is the reference for `rivet variant` YAML files:

1. **Feature model** — `feature-model.yaml` (the logical problem space).
2. **Variant configuration** — the user's feature selection.
3. **Binding model** — maps features to artifacts and source globs (see also
[feature-model-bindings.md](feature-model-bindings.md)).

Product-line engineering in rivet separates these three concerns into
independent files. A feature model captures what variants *could* exist;
a variant configuration is one user-level selection; a binding model
ties features to implementation artifacts.

Worked examples live in [`examples/variant/`](../examples/variant/).

## 1. Feature model

A FODA-style feature tree with group types and optional cross-tree
constraints expressed as s-expressions.

### Top-level keys

| Key | Type | Required | Meaning |
| -------------- | ------------------ | -------- | -------------------------------------------------------- |
| `kind` | string | no | Informational tag; conventionally `feature-model`. |
| `root` | string | yes | Name of the root feature — the variable always selected. |
| `features` | map<name, feature> | yes | Every feature in the tree, keyed by unique name. |
| `constraints` | list<s-expression> | no | Cross-tree boolean constraints (see below). |

### Feature entry (`features[name]`)

| Field | Type | Default | Meaning |
| ------------ | ------------- | --------- | ---------------------------------------------------- |
| `group` | enum | `leaf` | `mandatory`, `optional`, `alternative`, `or`, `leaf` |
| `children` | list<string> | `[]` | Names of child features. |

Group semantics when the feature is selected:

- `mandatory` — every child is auto-selected.
- `optional` — each child may be selected independently.
- `alternative` — **exactly one** child must be selected (XOR).
- `or` — **at least one** child must be selected.
- `leaf` — terminal feature, must have no children.

If a feature is listed as a child of another but has no entry in
`features`, rivet auto-inserts it as a `leaf`.

### Constraint syntax

Constraints are s-expressions. Bare feature names stand for "this feature
is selected". The supported logical forms are:

| Form | Meaning |
| ----------------- | -------------------------------------- |
| `(and A B …)` | All of A, B, … are selected. |
| `(or A B …)` | At least one of A, B, … is selected. |
| `(not A)` | A is not selected. |
| `(implies A B)` | If A is selected then B is selected. |
| `(excludes A B)` | A and B may not both be selected. |
| `(forall …)` | Universally quantified predicate. |
| `(exists …)` | Existentially quantified predicate. |

Examples from `examples/variant/feature-model.yaml`:

```s-expr
(implies eu pedestrian-detection)
(implies autonomous (and adas asil-d))
(implies adas (or asil-b asil-c asil-d))
```

The solver fails with a semantic error (not a positional one) when you
use infix notation — e.g. `A and B` produces a diagnostic pointing you at
`(and A B)`.

### Worked example

```yaml
# artifacts/feature-model.yaml
kind: feature-model
root: vehicle-platform

features:
vehicle-platform:
group: mandatory
children: [market, safety-level, feature-set]

market:
group: alternative
children: [eu, us, cn]
eu: { group: leaf }
us: { group: leaf }
cn: { group: leaf }

safety-level:
group: alternative
children: [qm, asil-a, asil-b, asil-c, asil-d]
qm: { group: leaf }
asil-a: { group: leaf }
asil-b: { group: leaf }
asil-c: { group: leaf }
asil-d: { group: leaf }

feature-set:
group: or
children: [base, adas, autonomous]

base: { group: leaf }

adas:
group: mandatory
children: [lane-keeping, adaptive-cruise, pedestrian-detection]
lane-keeping: { group: leaf }
adaptive-cruise: { group: leaf }
pedestrian-detection: { group: leaf }

autonomous:
group: mandatory
children: [path-planning, sensor-fusion]
path-planning: { group: leaf }
sensor-fusion: { group: leaf }

constraints:
- (implies eu pedestrian-detection)
- (implies autonomous (and adas asil-d))
- (implies adas (or asil-b asil-c asil-d))
```

## 2. Variant configuration

A user-level selection against a feature model.

| Field | Type | Required | Meaning |
| --------- | ------------ | -------- | ------------------------------------------------ |
| `name` | string | yes | Unique variant name — referenced by `check-all`. |
| `selects` | list<string> | yes | Feature names the user explicitly picks. |

```yaml
# eu-adas-c.yaml
name: eu-adas-c
selects: [eu, adas, asil-c]
```

The solver adds the root, every ancestor of each `selects` entry, every
mandatory descendant, and any constraint-implied feature. Output
distinguishes `mandatory`, user-`selected`, constraint-`implied by …`,
and `allowed but unbound` features.

## 3. Binding model

Maps features to the artifacts and source files that implement them.
See [feature-model-bindings.md](feature-model-bindings.md).

## CLI reference

```sh
# Create a starter feature-model.yaml + bindings/<name>.yaml pair.
rivet variant init <name>

# Inspect the feature tree.
rivet variant list --model feature-model.yaml

# Resolve a single variant (PASS/FAIL).
rivet variant check --model feature-model.yaml --variant eu-adas-c.yaml

# Iterate all variants declared in bindings and report per-variant status.
rivet variant check-all --model feature-model.yaml --binding bindings.yaml

# Solve + show bound artifacts with origin tags.
rivet variant solve --model fm.yaml --variant v.yaml --binding bindings.yaml

# Variant-scoped validation (variant is optional — model+binding validates
# the model/binding pair without resolving a specific variant).
rivet validate --model fm.yaml --binding bindings.yaml
rivet validate --model fm.yaml --variant v.yaml --binding bindings.yaml
```
3 changes: 3 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,9 @@ Graph: `reachable-from`, `reachable-to`.

Manage product variants with feature models, constraint solving, and artifact scoping.

Full schema reference: [feature-model-schema.md](feature-model-schema.md).
Binding file format: [feature-model-bindings.md](feature-model-bindings.md).

### Feature Model

Define your product line as a YAML feature tree:
Expand Down
Loading
Loading