> ## Documentation Index
> Fetch the complete documentation index at: https://docs.roboticks.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Requirement Hierarchy

> The parent_id model, system → subsystem → component decomposition, and how coverage rolls up across the tree.

# Requirement Hierarchy

Real systems have requirements that decompose. *"The AMR shall operate safely in shared spaces"* is the system-level statement; underneath it sit a dozen subsystem requirements, and underneath each of those, the testable component statements. Roboticks models this as a tree via the `parent_id` field, and rolls coverage up the tree automatically.

## The tree

```mermaid theme={null}
%%{init: {"theme": "neutral", "themeVariables": {"primaryColor": "#4040ff"}} }%%
flowchart TD
    SYS["SYS-001<br/>AMR operates safely in shared spaces"] --> SUB1["SUB-010<br/>Safety subsystem"]
    SYS --> SUB2["SUB-020<br/>Motion subsystem"]
    SYS --> SUB3["SUB-030<br/>Perception subsystem"]
    SUB1 --> R1["REQ-014<br/>E-stop halts in 100 ms"]
    SUB1 --> R2["REQ-015<br/>Bumper halts in 50 ms"]
    SUB1 --> R3["REQ-016<br/>Brake releases on reset"]
    SUB2 --> R4["REQ-022<br/>Max velocity 1.5 m/s"]
    SUB3 --> R5["REQ-101<br/>Perception p99 < 50 ms"]
```

Each requirement points at its parent with `parent_id`. The root nodes have `parent_id` unset. A requirement can have any number of children; it has at most one parent.

## Declaring hierarchy in inline YAML

```yaml theme={null}
- id: SYS-001
  title: AMR operates safely in shared spaces
  type: safety
  text: ...

- id: SUB-010
  title: Safety subsystem
  type: safety
  parent_id: SYS-001
  text: ...

- id: REQ-014
  title: E-stop halts motion within 100 ms
  type: safety
  parent_id: SUB-010
  asil_pl: PLd
  text: ...
```

Order in the file doesn't matter — parents and children can appear in any order. Roboticks resolves references after the full file is parsed.

## Declaring hierarchy in ReqIF

ReqIF imports use the `<SPEC-HIERARCHY>` element. Polarion, Jama, codeBeamer, and DOORS all emit it; Roboticks reads it directly without any extra mapping. See [ReqIF](/requirements/reqif) for the field tables.

## Constraints

| Rule                                                  | Why                                                                                                                                                                                                |
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| A parent must exist in the same project               | We won't link across projects; copy or factor first.                                                                                                                                               |
| No cycles                                             | The hierarchy is a tree, not a graph. A cycle is a modelling error and is rejected on import.                                                                                                      |
| At most one parent per requirement                    | Multi-parent makes rollup ambiguous. If a requirement satisfies two parents, the second relationship is an inter-requirement link, not a parent (see [ReqIF link semantics](/requirements/reqif)). |
| Children inherit `type` from the parent only if unset | The default is conservative — children stay `functional` unless explicitly marked `safety`.                                                                                                        |

## Coverage rollup

The coverage state of a parent is computed from its descendants:

| All descendants are...                                            | Parent is... |
| ----------------------------------------------------------------- | ------------ |
| `confirmed` (or have only verification methods other than `test`) | `confirmed`  |
| At least one `gap`                                                | `gap`        |
| At least one `regression`                                         | `regression` |
| Mixed `confirmed` + `unconfirmed`, no failures                    | `partial`    |
| Any failing                                                       | `partial`    |

In short: **a parent is only as healthy as its weakest descendant**. This is what the auditor expects — "the safety subsystem is verified" must mean every requirement inside the safety subsystem is verified, not just three of five.

```mermaid theme={null}
%%{init: {"theme": "neutral", "themeVariables": {"primaryColor": "#4040ff"}} }%%
flowchart TD
    P["SUB-010 — partial"] --> A["REQ-014 — confirmed"]
    P --> B["REQ-015 — gap"]
    P --> C["REQ-016 — confirmed"]
```

In the example above, `SUB-010` rolls up to `partial` because `REQ-015` has no confirming test. Once a confirming test is added, `SUB-010` becomes `confirmed`.

## Verification methods other than test

Real audits accept four verification methods — `test`, `inspection`, `analysis`, `demonstration`. Roboticks confirms `test`; the others are recorded for the audit trail and **excluded from the rollup denominator**. So if a leaf requirement has `verification_method: inspection`, its parent's rollup ignores it. This is the auditor-friendly behaviour: don't penalise the test-coverage signal for requirements that were never going to be test-verified.

## The matrix view

The traceability matrix (see [Matrix](/traceability/matrix)) supports two display modes:

* **Flat** — one row per requirement, indented by depth. Best for filtering and sorting.
* **Tree** — collapsible groups. Best for exploring decomposition.

Both modes show the rolled-up state on parent rows. A parent showing `partial` is a hint to expand and find the weak descendant.

## Walking the tree from the SDK

```python theme={null}
from roboticks import Client

client = Client()
req = client.requirements.get("SYS-001")

for descendant in req.descendants():
    print(descendant.id, descendant.coverage_state)
```

`descendants()` is a generator that walks the tree breadth-first. Use it in custom dashboards or release scripts that need to assert *"every descendant of SYS-001 is confirmed before we ship"*.

## Aggregated coverage across projects

A single Roboticks project can host multiple top-level (root) requirements — e.g. one root per subsystem owned by a different team. The rollup is computed per root. The dashboard summary shows the count of confirmed / partial / gap per root, plus an aggregated percentage across all roots.

For multi-project rollups (across organisational scope), use the org-level **Compliance dashboard** — see [Compliance overview](/compliance/overview).

## Editing the tree

Three kinds of edit:

1. **Add a child** — appears under the parent immediately. Rollup recomputes on the next coverage update.
2. **Change a `parent_id`** — the requirement moves in the tree. Both the old and new parents get a rollup recomputation. The audit trail records both ends of the move.
3. **Delete a node with children** — rejected. Re-parent the children first, then delete. This prevents accidental orphan-trees.

Edits after a snapshot follow the [snapshotting rules](/requirements/snapshotting): the snapshot keeps the old tree shape; the live tree evolves.

## Next

<CardGroup cols={2}>
  <Card title="Snapshotting" icon="camera" href="/requirements/snapshotting">
    What the tree looks like at release time.
  </Card>

  <Card title="Coverage" icon="chart-pie" href="/traceability/coverage">
    The state machine in detail.
  </Card>

  <Card title="Matrix" icon="table-cells" href="/traceability/matrix">
    Flat vs tree display modes.
  </Card>

  <Card title="Multi-repo projects" icon="layer-group" href="/traceability/multi-repo">
    Rollup across many repos in one project.
  </Card>
</CardGroup>
