Decision record

AD-0024 Verdict reasons built from the capacity service templates

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

Context

Every verdict the capacity service returns carries a reason string, and both apps show it, the viewer beside its red block and the document in each requirement’s row (SR-14, SR-37). The service is allowed exactly two model-specific names, the quantity it computes and the attribute it reads, and it selects from the adapter only fields of the generic projection declared in its @requires (SR-31). It never sees the words “server” or “pipeline”, and it never sees the derivation relationship between PIPE-R1 and its five derived requirements, because derivation is not in its field set. The README promises that a failing requirement is marked as failing with the stage responsible named, so the reason has to name the cut.

The wording had drifted before it was fixed. The storyboard at gate 1 carried the shipped failure reason four ways and was aligned on “parse is the bottleneck at 1200”. The first templates on the capacity model page carried “capacity” and “server”, which the second pass at gate 2 caught as words the service is not allowed to know, and the derived requirements had been given a template chosen by the requirement being derived, which the service cannot know. The board that quotes a reason was then aligned with the template.

There was also the question of which service owns the sentence a document reader sees. The document knows the derivation, its shipped prose paragraph explains why an allocated limit on a server can fail while the pipeline as a whole passes, and “allocated” is the brief’s own word for the derived limits. The reading at gate 2 settled that ownership on the document.

Decision

We will build every verdict reason in the capacity service from one of the fixed templates on the capacity model page, whose only variable parts are the configured quantity and attribute names, numbers, the names of parts, the kind of fault an ERROR reports and the short name of a verification case, so that no template carries a word of the model. The template for a subject with children reads `

against , limited by `, the leaf template reads ` against ` and is selected by the subject having no children, each INCONCLUSIVE case and the ERROR case has a template of its own, and a cut of several parts is listed in the order the router delivers the children, comma separated, in wording that avoids a verb that would have to agree in number. Words such as "allocated" belong to the document, which knows the derivation and may add them beside the reason. ## Alternatives considered Templates carrying the model's own words, "capacity" and "server", which the first draft of the capacity page used and which read naturally for the example. They lost at the second pass at gate 2 because the service is configured with two names and may know no other (SR-31), so the templates now use the configured names and the word "part". A separate template for derived requirements, chosen because the requirement is derived. It lost because the service never sees derivation. The leaf template is selected instead by the subject having no children, which yields the same sentence for the example's five derived requirements with no second code path, since a leaf's capacity is its own attribute value. The reason naming the allocation, so that a derived requirement's row would say its limit is allocated. It lost because the allocation rule is written in the model as expressions the adapter evaluates, and the service sees only the number that results. The document, which does know the derivation, is where that word goes. ## Consequences The reasons are testable in isolation. SR-30's verification is one test case per verdict kind, per precedence rule and per reason template, and the templates are a table a reader can check against the worked example, where `PIPE-R1` fails with `capacity 1200 against 1500, limited by parse` and `PIPE-R1.4` fails with `throughput 700 against 750`. Both apps show the string unchanged and compute nothing, which is the demo's claim that the verdict a reader sees comes from a service that has never parsed a model file. The cost is a plainer sentence than the example could have had. The reason does not say bottleneck, server or allocated, and a document reader who wants the word "allocated" finds it in the document's prose paragraph rather than in the reason. The order of a cut of several parts follows the router's delivery order of the children, which the service does not control, so the index pair reads `indexA, indexB` today and the service makes no promise about that order. For `PIPE-R2` the reason names `PIPE-VC1` as declared and run by no service, and the row shows no current value (SR-37). The INCONCLUSIVE reason for a requirement of another quantity depends on the verification case's short name arriving through the nested `@requires`, which is the first spike of the implementation phase. The flat `Part.wiring` fallback keeps `verifiedBy { shortName }` in the field set, so the template survives either outcome. ## Requirements affected SR-30, SR-37 ## Sources [From use cases to requirements](/articles/05-from-use-cases-to-requirements.html), which publishes the seven templates, the verdict precedence and the worked example they render for. [Twelve use cases and one moving bottleneck](/articles/04-twelve-use-cases-and-one-moving-bottleneck.html) for the reason as the storyboard first carried it. The SysML v2 Systems Library's `VerdictKind` for the four words a verdict may take.