Decision record

AD-0008 Quantity, comparison and limit read from the constraint

Status: accepted. Date: 2026-08-27.

Context

The README’s projection promises that a requirement is an identifier, a name, some text, a limit value and a set of things that satisfy it. It does not say how an adapter that contains nothing specific to any one model finds the limit among a requirement’s attributes, or how a service that knows only a subject and a number tells one requirement on that subject from another. Until gate 2 neither question had an answer in any document.

The example carries a latency requirement with a verification case beside the throughput requirement, and both have the pipeline as their subject. The reading at gate 2 found the consequence: a verdict rule keyed on the subject could not tell PIPE-R1 from PIPE-R2 and would have passed the latency requirement at a capacity of 1200 against a limit of 200. A second gap in the same place was that no document said how a generic adapter knows which of a requirement’s attributes is its limit. Both came back to planning as design findings rather than implementation defects.

The language supplies the shape. A requirement definition or usage carries a require constraint whose body is a comparison, verified from the OMG training files as require constraint { massActual <= massReqd } with the limit bound on the usage as attribute :>> massReqd = 2000[kg], and an attribute or subject may be bound to a feature chain such as vehicle.dryMass + vehicle.fuelMass. It was already settled that the model never carries the rollup arithmetic: an abstract part definition declares capacity without a value, the pipeline declares latency without a value, and every requirement is a constraint over one of them. The Systems Library’s VerdictKind has four literals, pass, fail, inconclusive and error, which gives a requirement the service cannot evaluate a word of its own.

Decision

We will have the adapter read each requirement’s require constraint as a comparison between a feature chain rooted at the requirement’s subject and either an attribute of the requirement or a literal, and project the chain’s last segment as the constrained quantity, the operator as the comparison and the other operand’s evaluated value as the limit, with the unit as written in the source beside it. A constraint of any other shape is refused at start with its file, line and column, as for any construct outside the subset (SR-18, SR-19). The capacity service evaluates only requirements whose quantity is the name it is configured to compute, returns PASS or FAIL by the projected comparison for those, and returns INCONCLUSIVE for every other quantity before it looks at anything else (SR-30).

Alternatives considered

A verdict keyed on the subject. The rule before gate 2 compared the subject’s capacity with the requirement’s limit and nothing more. It lost on the latency finding: two requirements with the same subject and different quantities are indistinguishable to it, and the wrong one passes.

A naming convention on the requirement’s attributes. The adapter could have looked for an attribute with an agreed name, requiredRate in the example, and taken its value as the limit. It was rejected because the convention would have been an assumption taken from the example, and the README’s claim is that the adapter is specific to no example.

Examining child values before the quantity check. The first verdict precedence put ERROR ahead of every INCONCLUSIVE case, so the latency requirement would have reported a bad server value it never uses. The other-quantity check moved to the front.

Consequences

One rule evaluates all seven requirements of the example. The six throughput requirements constrain <subject>.capacity, the service computes capacity, and each gets PASS or FAIL by its own operator against its own limit, the derived ones on single servers through the leaf rule with no second code path. PIPE-R2 constrains latency, no service computes it, and it reports INCONCLUSIVE with the reason that PIPE-VC1 is declared and no service runs it. The comparison is the constraint’s own, so a requirement written the other way round is evaluated the other way round, and the limit’s unit reaches the document as written, which is how it shows “200 ms”. The adapter still contains no word of the example, and the capacity service’s contract with it stays at the entity key, the field set in its @requires, which now carries quantity, comparison, limit, the verification case’s short name and the names of the subject and its children, and two configured names (SR-31).

The rule reaches back into the model. For a leaf’s constraint to read <server>.capacity, every server had to declare capacity as well as throughput, which is why the example shares an abstract part definition between the pipeline and the servers, and the pipeline had to declare latency for the latency constraint to resolve at all. A requirement whose constraint has another shape, two feature chains, a compound expression, or no operand rooted at the subject, refuses the whole model at start rather than projecting without a quantity. That is the honest behaviour for a subset and it narrows what “any conforming model” can mean until the shape is widened.

Two spikes settle the literals the other operand may take: the plain numeric binding on a Real attribute and the duration literal for the latency limit, both confirmed by the validation run with the OMG pilot and reported in Five spikes before the first line.

Requirements affected

SR-19, SR-30, SR-31

Sources

The repository README, “Nobody outside systems engineering should need to know what SysML is”. The SysML 2.0 language specification on the body of a requirement definition and usage, the OMG training material for require constraint and the bound limit, and the Systems Library’s VerdictKind. From use cases to requirements for the verdict rules and their precedence.