Requirements rarely stand alone. Some break a larger requirement into parts,
some need context from elsewhere in the spec, and some use terms defined
elsewhere. SLCR records each relationship as an explicit link that tools can
follow. One kind of link, refines, also determines how individual check
results combine into a status for each requirement.
Four ways nodes connectLink to this section
| Link | From → To | Meaning | Affects satisfaction? |
|---|---|---|---|
| Nesting | Section → section or requirement; requirement → requirement or criterion | "I'm placed here" | No |
refines | Requirement → requirement | "I help satisfy that" | Yes, and it's the only one |
dependsOn | Requirement → requirement or section | "You need that to understand or implement me" | No |
usesTerm | Section or requirement → term | "I use this word" | No |
All four determine what goes into a slice.
refines and dependsOn links can't form cycles, and every link must point
to an ID that exists. slcr vet reports either problem as an error.
Nesting provides structureLink to this section
Nesting determines where a node appears in the rendered document and which section bodies it inherits as background. It says nothing about whether a child helps satisfy its parent.
A child requirement counts toward its parent only when it lists the parent in
refines, even when the parent is the node it's nested in. In the todo-api
spec, "POST creates an item" sits inside "TodoListItem endpoint" and also
refines it explicitly:
- kind: requirement
id: REQ-001
title: TodoListItem endpoint
body: There MUST be an endpoint for the TodoListItem resource.
modality: MUST
refinement: AND
children:
- kind: requirement
id: REQ-002
title: POST creates an item
body: The endpoint MUST accept POST to create an item.
modality: MUST
refinement: AND # REQ-004 refines it (see below)
refines: [REQ-001]A child nested without refines elaborates on its parent without
contributing to its status. It is judged only by its own validators.
One home, many parentsLink to this section
Some requirements serve several parents. In todo-api, both POST and GET
require the request to be authenticated. That requirement is written once
and lists every parent it serves:
- kind: requirement
id: REQ-004
title: Request is authenticated
body: The request MUST be authenticated.
modality: MUST
refines: [REQ-002, REQ-003]Two copies, one under each parent, could drift apart over time. One copy
with two refines links means one statement, one set of validators, and one
result that counts toward both parents.
Dependencies are contextLink to this section
dependsOn records what an implementer needs to know. The TodoListItem
controller, REQ-005, depends on the endpoint it serves (REQ-001) and the
table it reads and writes (REQ-007). The controller isn't satisfied just
because the endpoint is, so this isn't refinement. But the controller can't
be built without knowing the endpoint's shapes and the table's columns. A
dependsOn link brings that context into the controller's slice and has no
effect on satisfaction.
TermsLink to this section
usesTerm links a section or requirement to the glossary entries it relies
on, so a slice can include their definitions.
SatisfactionLink to this section
slcr check runs every validator, then assigns each requirement one of three
statuses:
- Satisfied: its validators and refinements show that it holds.
- Unsatisfied: they show that it doesn't.
- Unknown: nothing decides it yet.
LeavesLink to this section
A requirement that nothing refines is a leaf. Its status comes from its own
validators: satisfied when all of them pass, unsatisfied when any fails. A
leaf with no validators is unknown, which check reports as a coverage gap.
A coverage gap shows where the spec isn't verified yet. It never counts as a
failure, even with --deny-warnings.
Combining refinementsLink to this section
A requirement that others refine states how to combine them in its
refinement field:
| Refinement | Satisfied when |
|---|---|
AND | Every refiner is satisfied. MAY refiners are left out, so an optional part can't block its parent. |
OR | At least one refiner is satisfied. |
XOR | Exactly one refiner is satisfied, and the rest are unsatisfied. |
If every refiner of an AND is a MAY, all of them are left out and nothing
remains to evaluate. The parent is then vacuously satisfied: no counted
refiner is unsatisfied, because there are none. slcr vet reports this as a
vacuous-and warning. Using OR, or giving at least one refiner a MUST or
SHOULD modality, gives the parent something to evaluate.
If a requirement has its own validators as well as refiners, both must hold:
the result of its validators is combined with the result of its refiners
using AND.
The operator is part of what the spec means. The todo-api spec says the API
accepts OAuth tokens or API keys:
- kind: requirement
id: REQ-008
title: Accepts authentication tokens
body: The API MUST accept OAuth tokens or API keys.
modality: MUST
refinement: OR
children:
- kind: requirement
id: REQ-009
title: Accepts OAuth tokens
body: The API MAY accept OAuth tokens.
modality: MAY
refines: [REQ-008]
- kind: requirement
id: REQ-010
title: Accepts API keys
body: The API MAY accept API keys.
modality: MAY
refines: [REQ-008]If the API had to accept both, the parent would use AND over two MUST
children. The two versions read almost the same in prose, but the difference
is recorded in the data, so check evaluates each one correctly.
UnknownsLink to this section
Unknowns combine using strong Kleene logic, a three-valued logic in which a result is decided only when every possible outcome of the unknowns would give the same answer:
- An
ANDwith one unsatisfied refiner is unsatisfied, whatever the unknowns turn out to be. - An
ORwith one satisfied refiner is satisfied, whatever the unknowns turn out to be. - An
ANDwhose refiners are all satisfied except one unknown is unknown, because the answer depends on the unknown.
A spec that is only partly covered by validators still gets every result that can already be decided, without guessing at the rest.
Modality sets severityLink to this section
Whether a requirement is satisfied and how much a failure matters are separate questions. Validators and refinements answer the first. The requirement's modality answers the second:
| Modality | When unsatisfied |
|---|---|
MUST, MUST NOT | A violation, which fails check |
SHOULD, SHOULD NOT | A warning, which fails check only with --deny-warnings |
MAY | Informational, which never fails check |
MUST NOT and SHOULD NOT need no special structure. Their validators check
that the forbidden behavior is absent.
A failure is absorbed when every requirement it refines is satisfied
anyway, such as one alternative of an OR failing while another passes. An
absorbed failure is reported as a warning, or as informational if the
requirement is a MAY.