Requirements engineering
A Requirement and Its Anchor Have to Move Together
We repaired a specification, recorded four numeric conflicts as resolved, and were wrong about three of them. The requirements file was correct. The file the rule engine actually reads still held the old numbers.
The rule is easy to state
A physical quantity gets one value, at one measurement point, in one place. Everything else cites it. When a constraint table, a safety anchor and a system requirement all state the same limit, two of them are copies waiting to drift.
In such a model an anchor is a deterministic constraint that overrides anything probabilistic: a hard limit with a predicate, a severity and an action on breach. Anchors live in their own catalog because the runtime evaluates them directly. The requirements that state those same limits live in the specification, because that is what a supplier is held to. Two files, one quantity, and a reader who is almost never holding both.
What we were reviewing
This is our own work, not a client’s. We built a smart-city reference package (requirements, ontology, anchor catalog, hazard analysis, an MCP integration plan) to exercise the method on something with real physics in it: heat, dust, monsoon flooding, fire flow, grid islanding.
Version 0.1 failed a design review on four numeric conflicts and ten unverifiable statements. Version 0.2 was the repair, and it was a good one: every requirement restructured into an EARS pattern, full metadata on all fifty-six, a security section that had been missing entirely, and a Critical anti-islanding anchor that previously had no requirement of any kind. Its own reviewer note opened by saying it “resolves all four conflicts.”
Then we reviewed the repair. It raised twenty-one findings, seven of them blocking, and of the four closure claims exactly one held.
A worked case
Anchor A-004 governs traffic signal availability during a declared dust storm. Its predicate:
signal.uptime_fraction ≥ 0.9995 during declared DustStorm
Multiply that out against the window it is stated on. A dust event runs about three hours, so 0.05 % of 10,800 seconds is 5.4 seconds of permitted downtime for the entire event, including any controller reboot. That is not an event-availability target. It is an annual figure someone re-pointed at an event window, and a single reboot blows it.
Version 0.2 caught this and fixed it properly, splitting the obligation into a per-outage bound and an outage-count bound, which is what an event can actually be measured in:
| Where | What it says | Permits per event |
|---|---|---|
Requirement SR-TRF-001 | ≤ 60 s per outage, ≤ 3 outages | 180 s |
Anchor A-004 | uptime_fraction ≥ 0.9995 | 5.4 s |
The requirement’s rationale states that the defect is resolved. It was not resolved. It was relocated. The anchor catalog was never touched, and the anchor catalog is what check_anchors evaluates at runtime.
So a controller vendor reads the specification and builds to 180 seconds. Whoever wires the rule engine reads the catalog and enforces 5.4. One of them fails acceptance and neither of them is wrong.
Why both halves read correctly
This is the part that makes the defect expensive. Open the requirements file and SR-TRF-001 is a good requirement: EARS pattern, named subject, two measurable bounds, a stated measurement window, a rationale that explains the arithmetic. Open the anchor catalog and A-004 is a well-formed anchor: a predicate, an applies-to set, a severity, an action on breach.
Nothing is malformed. A per-requirement review of either file finds nothing, because the defect does not live in a requirement. It lives in the space between two documents, and that space belongs to nobody.
The case that was worse
The second one is subtler, and we think it is the more instructive failure.
A stakeholder requirement obliges the city to publish a public air-quality alert within ten minutes of a pollutant crossing an EPA threshold. Ten minutes from when, exactly? Version 0.2 measured from the timestamp of the qualifying sensor sample, and recorded the ambiguity as an open item: confirm whether the clock starts at the sample or at the physical crossing.
Anchor A-005 had already answered it. Its predicate reads alert_latency_minutes ≤ 10 from threshold breach, and its evaluation method is the timestamp delta between threshold breach and alert publish. The question the specification deferred to a future meeting was settled, in writing, in a companion document it cited by name three paragraphs earlier.
And the reading it adopted was the favourable one. On the sample reading, the ten-minute budget closed with 55 seconds of reserve. On the anchor’s reading it did not close at all. The note estimated the over-run at 5 seconds, but the particulate sensors sample at one per minute, so a crossing can precede its qualifying sample by up to 60 seconds. The estimate was wrong by an order of magnitude, in the direction that let the budget close.
We do not think anyone did this deliberately. It is what happens when you are repairing a long document under time pressure: you take the reading that lets the arithmetic work, write an open item so you have been honest about it, and move on. The open item is what makes it feel handled.
What no tool catches
We checked the repaired document mechanically before reviewing it by hand. Every one of the fifty-six requirements carried a type, a priority, a status, a verification method, a rationale and an allocation. No identifier was referenced that was not defined. The completeness pass was clean.
It would be. Traceability tooling checks that a link resolves, never that the two ends agree about a number. Schema validation checks that a field is present, not that its value matches the same value somewhere else. Neither has any notion of a quantity, so neither can notice that one is stated twice.
The checks that do find it are cheap, and all of them are arithmetic rather than judgement:
- The conflict sweep. Extract every quantitative claim in the document set into one table (one row per statement, not per quantity), then sort by quantity and look for groups with more than one distinct row. The measurement point is a column, because the same number at two measurement points is also a conflict. Ours had three temperature limits at three different points, all of them nominally the same limit.
- Window arithmetic. Multiply every percentage and rate out against its stated window and read the result aloud. “5.4 seconds including reboot” is a sentence nobody says twice.
- Budget closure. Sum the children against the parent and state the reserve. Then check the harder thing: whether the parent’s measurement window is the same one the budget measures from. That is where our ten-minute alert budget was hiding.
The rule behind the fix
The repair itself was ordinary. Amend the anchor, correct the requirement’s rationale so it stops claiming a resolution it did not achieve, adopt the anchor’s measurement point, re-apportion the budget against a 60-second sampling lag.
The rule is the part worth keeping: a conflict is not resolved until every file that states the quantity has moved. The specification is not the system of record for a limit the runtime evaluates from somewhere else. If a repair touches one file, the repair is half done, and the half that is missing is the half a machine will act on.
The version that closed all twenty-one findings amends the requirements, the anchor catalog, the hazard analysis, the integration plan, the ontology, the brief and both architecture diagrams, in one change. Not because that is tidy, but because anything less reproduces the defect it is fixing.
What to take from it
- Amend the companion documents in the same change, or you have not amended anything. A quantity stated in two files is two systems, and the one you did not edit is usually the one that executes.
- Multiply every percentage out against its window before you accept it. A figure written for a year and re-pointed at an event survives review because nobody does the arithmetic aloud.
- Read your open items back against the documents you cited. An open item that defers a question its own source already answered is not honesty, it is a deferral wearing honesty’s clothes, and it will usually be deferring in the direction that makes your numbers work.
- A clean mechanical pass proves the fields are populated and nothing else. Completeness tooling cannot see a quantity, so it cannot see the same quantity stated twice.
Requirements work earns its keep by making somebody accountable for every number. That guarantee ends at the first document that restates a number without citing it.
U.S. Provisional Patent App. No. 64/073,689. Patent Pending.