plan-verify 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +48 -0
- package/README.md +1431 -2
- package/assets/icon.png +0 -0
- package/assets/logo-mark.png +0 -0
- package/assets/logo.png +0 -0
- package/examples/checkout.ears.md +74 -0
- package/examples/order-always.ears.md +3 -0
- package/examples/order-awkward.ears.md +8 -0
- package/examples/order-bad.json +1 -0
- package/examples/order-chain.ears.md +9 -0
- package/examples/order-chosen.ears.md +7 -0
- package/examples/order-clear.ears.md +8 -0
- package/examples/order-combined.ears.md +10 -0
- package/examples/order-conflict.ears.md +8 -0
- package/examples/order-covered.ears.md +9 -0
- package/examples/order-duplicate.ears.md +8 -0
- package/examples/order-fail.json +5 -0
- package/examples/order-misuse.ears.md +7 -0
- package/examples/order-negation.ears.md +7 -0
- package/examples/order-pass.json +5 -0
- package/examples/order-quantity.ears.md +8 -0
- package/examples/order-redundant.ears.md +9 -0
- package/examples/order-repair.ears.md +9 -0
- package/examples/order-repeat.ears.md +13 -0
- package/examples/order-same.ears.md +3 -0
- package/examples/order-status.ears.md +7 -0
- package/examples/order-unanswered.ears.md +8 -0
- package/examples/order-unhandled.ears.md +8 -0
- package/examples/order-unmeasured.ears.md +10 -0
- package/examples/order-unused.ears.md +13 -0
- package/examples/order-vacuous.ears.md +9 -0
- package/examples/order-vague.ears.md +7 -0
- package/examples/order-wide.ears.md +5 -0
- package/examples/order-wording.ears.md +8 -0
- package/examples/order.ears.md +7 -0
- package/examples/payment-if.ears.md +7 -0
- package/examples/plan-to-problems/README.md +54 -0
- package/examples/plan-to-problems/expected-problems.md +25 -0
- package/examples/plan-to-problems/generate.mjs +186 -0
- package/examples/plan-to-problems/generated.ears.md +27 -0
- package/examples/plan-to-problems/lint.json +576 -0
- package/examples/plan-to-problems/plan.md +5 -0
- package/examples/plan-to-problems/planted.txt +5 -0
- package/examples/plan-to-problems/problems.md +112 -0
- package/examples/status-placed.ears.md +3 -0
- package/package.json +25 -4
- package/scripts/install-scriptc.sh +16 -0
- package/skills/plan-verify/SKILL.md +1515 -0
- package/src/check.ts +250 -0
- package/src/decide.ts +1324 -0
- package/src/ears.ts +1520 -0
- package/src/jsonvalue.ts +228 -0
- package/src/lint.ts +1245 -0
- package/src/main.ts +273 -0
- package/src/policy.ts +148 -0
- package/src/repair.ts +567 -0
- package/src/report.ts +329 -0
- package/src/scenarios.ts +149 -0
- package/src/smt.ts +97 -0
- package/src/solver.ts +868 -0
- package/src/spec.ts +307 -0
- package/src/term.ts +331 -0
- package/src/text.ts +95 -0
- package/test/fixtures/malformed.ears.md +3 -0
- package/test/fixtures/unknown-phrase.ears.md +5 -0
- package/test/golden/check-fail-json.exit +1 -0
- package/test/golden/check-fail-json.stderr +0 -0
- package/test/golden/check-fail-json.stdout +13 -0
- package/test/golden/check-fail.exit +1 -0
- package/test/golden/check-fail.stderr +0 -0
- package/test/golden/check-fail.stdout +7 -0
- package/test/golden/check-pass-json.exit +1 -0
- package/test/golden/check-pass-json.stderr +0 -0
- package/test/golden/check-pass-json.stdout +6 -0
- package/test/golden/check-pass.exit +1 -0
- package/test/golden/check-pass.stderr +0 -0
- package/test/golden/check-pass.stdout +3 -0
- package/test/golden/emit-smt-json-usage.exit +1 -0
- package/test/golden/emit-smt-json-usage.stderr +0 -0
- package/test/golden/emit-smt-json-usage.stdout +4 -0
- package/test/golden/emit-smt-json.exit +1 -0
- package/test/golden/emit-smt-json.stderr +0 -0
- package/test/golden/emit-smt-json.stdout +5 -0
- package/test/golden/emit-smt.exit +1 -0
- package/test/golden/emit-smt.stderr +0 -0
- package/test/golden/emit-smt.stdout +10 -0
- package/test/golden/help.exit +1 -0
- package/test/golden/help.stderr +0 -0
- package/test/golden/help.stdout +8 -0
- package/test/golden/lint-awkward.exit +1 -0
- package/test/golden/lint-awkward.stderr +0 -0
- package/test/golden/lint-awkward.stdout +29 -0
- package/test/golden/lint-chain.exit +1 -0
- package/test/golden/lint-chain.stderr +0 -0
- package/test/golden/lint-chain.stdout +56 -0
- package/test/golden/lint-checkout.exit +1 -0
- package/test/golden/lint-checkout.stderr +0 -0
- package/test/golden/lint-checkout.stdout +301 -0
- package/test/golden/lint-clean.exit +1 -0
- package/test/golden/lint-clean.stderr +0 -0
- package/test/golden/lint-clean.stdout +9 -0
- package/test/golden/lint-clear.exit +1 -0
- package/test/golden/lint-clear.stderr +0 -0
- package/test/golden/lint-clear.stdout +9 -0
- package/test/golden/lint-conflict-json.exit +1 -0
- package/test/golden/lint-conflict-json.stderr +0 -0
- package/test/golden/lint-conflict-json.stdout +80 -0
- package/test/golden/lint-conflict.exit +1 -0
- package/test/golden/lint-conflict.stderr +0 -0
- package/test/golden/lint-conflict.stdout +41 -0
- package/test/golden/lint-duplicate.exit +1 -0
- package/test/golden/lint-duplicate.stderr +0 -0
- package/test/golden/lint-duplicate.stdout +14 -0
- package/test/golden/lint-gap.exit +1 -0
- package/test/golden/lint-gap.stderr +0 -0
- package/test/golden/lint-gap.stdout +12 -0
- package/test/golden/lint-misuse.exit +1 -0
- package/test/golden/lint-misuse.stderr +0 -0
- package/test/golden/lint-misuse.stdout +18 -0
- package/test/golden/lint-negation.exit +1 -0
- package/test/golden/lint-negation.stderr +0 -0
- package/test/golden/lint-negation.stdout +13 -0
- package/test/golden/lint-quantity.exit +1 -0
- package/test/golden/lint-quantity.stderr +0 -0
- package/test/golden/lint-quantity.stdout +41 -0
- package/test/golden/lint-redundant.exit +1 -0
- package/test/golden/lint-redundant.stderr +0 -0
- package/test/golden/lint-redundant.stdout +14 -0
- package/test/golden/lint-repair-json.exit +1 -0
- package/test/golden/lint-repair-json.stderr +0 -0
- package/test/golden/lint-repair-json.stdout +127 -0
- package/test/golden/lint-repair.exit +1 -0
- package/test/golden/lint-repair.stderr +0 -0
- package/test/golden/lint-repair.stdout +54 -0
- package/test/golden/lint-repeat.exit +1 -0
- package/test/golden/lint-repeat.stderr +0 -0
- package/test/golden/lint-repeat.stdout +48 -0
- package/test/golden/lint-status.exit +1 -0
- package/test/golden/lint-status.stderr +0 -0
- package/test/golden/lint-status.stdout +44 -0
- package/test/golden/lint-unanswered.exit +1 -0
- package/test/golden/lint-unanswered.stderr +0 -0
- package/test/golden/lint-unanswered.stdout +12 -0
- package/test/golden/lint-unhandled.exit +1 -0
- package/test/golden/lint-unhandled.stderr +0 -0
- package/test/golden/lint-unhandled.stdout +12 -0
- package/test/golden/lint-unmeasured.exit +1 -0
- package/test/golden/lint-unmeasured.stderr +0 -0
- package/test/golden/lint-unmeasured.stdout +13 -0
- package/test/golden/lint-unused-json.exit +1 -0
- package/test/golden/lint-unused-json.stderr +0 -0
- package/test/golden/lint-unused-json.stdout +37 -0
- package/test/golden/lint-unused.exit +1 -0
- package/test/golden/lint-unused.stderr +0 -0
- package/test/golden/lint-unused.stdout +25 -0
- package/test/golden/lint-vacuous-json.exit +1 -0
- package/test/golden/lint-vacuous-json.stderr +0 -0
- package/test/golden/lint-vacuous-json.stdout +27 -0
- package/test/golden/lint-vacuous.exit +1 -0
- package/test/golden/lint-vacuous.stderr +0 -0
- package/test/golden/lint-vacuous.stdout +19 -0
- package/test/golden/lint-vague.exit +1 -0
- package/test/golden/lint-vague.stderr +0 -0
- package/test/golden/lint-vague.stdout +21 -0
- package/test/golden/lint-wide.exit +1 -0
- package/test/golden/lint-wide.stderr +0 -0
- package/test/golden/lint-wide.stdout +9 -0
- package/test/golden/lint-wording.exit +1 -0
- package/test/golden/lint-wording.stderr +0 -0
- package/test/golden/lint-wording.stdout +21 -0
- package/test/golden/malformed-scenario.exit +1 -0
- package/test/golden/malformed-scenario.stderr +1 -0
- package/test/golden/malformed-scenario.stdout +0 -0
- package/test/golden/malformed-spec.exit +1 -0
- package/test/golden/malformed-spec.stderr +1 -0
- package/test/golden/malformed-spec.stdout +0 -0
- package/test/golden/policy-counterexample.exit +1 -0
- package/test/golden/policy-counterexample.stderr +0 -0
- package/test/golden/policy-counterexample.stdout +13 -0
- package/test/golden/policy-covered.exit +1 -0
- package/test/golden/policy-covered.stderr +0 -0
- package/test/golden/policy-covered.stdout +19 -0
- package/test/golden/policy-holds.exit +1 -0
- package/test/golden/policy-holds.stderr +0 -0
- package/test/golden/policy-holds.stdout +3 -0
- package/test/golden/policy-json.exit +1 -0
- package/test/golden/policy-json.stderr +0 -0
- package/test/golden/policy-json.stdout +34 -0
- package/test/golden/policy-status.exit +1 -0
- package/test/golden/policy-status.stderr +0 -0
- package/test/golden/policy-status.stdout +16 -0
- package/test/golden/policy-vacuous.exit +1 -0
- package/test/golden/policy-vacuous.stderr +0 -0
- package/test/golden/policy-vacuous.stdout +3 -0
- package/test/golden/policy-wide.exit +1 -0
- package/test/golden/policy-wide.stderr +0 -0
- package/test/golden/policy-wide.stdout +15 -0
- package/test/golden/policy.exit +1 -0
- package/test/golden/policy.stderr +0 -0
- package/test/golden/policy.stdout +19 -0
- package/test/golden/scenarios-json.exit +1 -0
- package/test/golden/scenarios-json.stderr +0 -0
- package/test/golden/scenarios-json.stdout +75 -0
- package/test/golden/scenarios-status.exit +1 -0
- package/test/golden/scenarios-status.stderr +0 -0
- package/test/golden/scenarios-status.stdout +24 -0
- package/test/golden/scenarios.exit +1 -0
- package/test/golden/scenarios.stderr +0 -0
- package/test/golden/scenarios.stdout +26 -0
- package/test/golden/unit.exit +1 -0
- package/test/golden/unit.stderr +0 -0
- package/test/golden/unit.stdout +2 -0
- package/test/golden/unknown-phrase.exit +1 -0
- package/test/golden/unknown-phrase.stderr +1 -0
- package/test/golden/unknown-phrase.stdout +0 -0
- package/test/golden/usage.exit +1 -0
- package/test/golden/usage.stderr +9 -0
- package/test/golden/usage.stdout +0 -0
- package/test/run-all.ts +499 -0
- package/test/soundness.ts +375 -0
- package/test/unit.ts +1352 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,1515 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plan-verify
|
|
3
|
+
description: Use when a product manager is stating or revising requirements. Turn their words into EARS with this grammar, run plan-verify lint, explain any conflict, and ask which requirement should win before anyone builds the system.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# plan-verify
|
|
7
|
+
|
|
8
|
+
You are in a conversation with a product manager. They are telling you what a system should do, often in a few passes, sometimes contradicting what they said earlier. Turn what they say into EARS requirements with the grammar below, then run plan-verify.
|
|
9
|
+
|
|
10
|
+
Lint is implemented, and it is the check that matters. When it reports a conflict, explain it in plain words: which requirements clash, under what situation, the example scenario, and the suggested fix. Ask which requirement should win. Do not choose one, and do not paper over the clash with a new sentence they did not ask for. A requirement that adds nothing stays until the PM agrees to remove it. `scenarios` prints accepted and rejected examples so you can check that you heard them correctly.
|
|
11
|
+
|
|
12
|
+
`help` lists every command:
|
|
13
|
+
|
|
14
|
+
<!-- verify stdout 0
|
|
15
|
+
build/plan-verify
|
|
16
|
+
help
|
|
17
|
+
-->
|
|
18
|
+
```text
|
|
19
|
+
usage:
|
|
20
|
+
plan-verify check [--json] <spec> --input <scenario>
|
|
21
|
+
plan-verify emit-smt [--json] <spec>
|
|
22
|
+
plan-verify lint [--json] <spec>
|
|
23
|
+
plan-verify report <spec>
|
|
24
|
+
plan-verify scenarios [--json] <spec>
|
|
25
|
+
plan-verify policy [--json] <spec> [--property <file>]
|
|
26
|
+
plan-verify help
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`report <spec>` prints the logical problems in plain language: contradictions, uncovered situations, conditions that can never be true, requirements that add nothing, and exact duplicates. For a contradiction it asks which requirement should win and does not choose. Lint itself names a repeat that sits outside every conflict. The report still explains a stricter repeat by naming the wider requirement.
|
|
30
|
+
|
|
31
|
+
`policy` searches the same finite domain as lint. With no property it prints one scenario the requirements allow and one they forbid. With a property file it looks for a scenario the requirements allow and the property forbids. Do not treat either scenario as the behavior the PM agreed to, and do not pick a winning requirement.
|
|
32
|
+
|
|
33
|
+
The samples below are the built CLI. `node --experimental-strip-types src/main.ts` prints the same bytes when `build/plan-verify` is not there yet. The same spec always produces the same SMT-LIB.
|
|
34
|
+
|
|
35
|
+
## Vocabulary
|
|
36
|
+
|
|
37
|
+
Declare every name before the requirement that uses it. One name per idea, reused every time the PM comes back to that idea. Bind that name to the phrase they actually say. The requirement line uses the phrase, not the name. The name is what a JSON scenario and the SMT-LIB use.
|
|
38
|
+
|
|
39
|
+
```ears
|
|
40
|
+
enum Region { us, eu }
|
|
41
|
+
event order_submitted: Bool "The customer submits an order." phrase "an order is submitted"
|
|
42
|
+
state inventory_available: Bool "Enough stock is on hand to fill the order." phrase "inventory is available"
|
|
43
|
+
input quantity: Int 1..10 "How many units the customer asked for." phrase "the quantity"
|
|
44
|
+
input region: Region "The region the order ships to." phrase "the region"
|
|
45
|
+
output order_fulfilled: Bool "The order is fulfilled." phrase "fulfill the order" outcome "the order was fulfilled" / "the order was not fulfilled"
|
|
46
|
+
unwanted event payment_declined: Bool "The payment was declined." phrase "payment fails"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Keywords are case-insensitive. Names keep the case you wrote. A phrase match is case-insensitive too.
|
|
50
|
+
|
|
51
|
+
| Declaration | Form the parser accepts |
|
|
52
|
+
| --- | --- |
|
|
53
|
+
| Enum | `enum Name { variant, variant }` |
|
|
54
|
+
| Event | `event name: Bool "description" phrase "words"` |
|
|
55
|
+
| State | `state name: Bool "description" phrase "words"` |
|
|
56
|
+
| Input | `input name: Type "description" phrase "words"` |
|
|
57
|
+
| Output | `output name: Type "description" phrase "words"` |
|
|
58
|
+
| Unwanted event | `unwanted event name: Bool "description" phrase "words"` |
|
|
59
|
+
|
|
60
|
+
`phrase` is optional. Leave it off only when you are still naming the idea. Once it is there, write requirements with that phrase.
|
|
61
|
+
|
|
62
|
+
A Bool may also declare how a report states that fact. Put `outcome` after the phrase:
|
|
63
|
+
|
|
64
|
+
`output order_fulfilled: Bool "The order is fulfilled." phrase "fulfill the order" outcome "the order was fulfilled" / "the order was not fulfilled"`
|
|
65
|
+
|
|
66
|
+
One quoted string is enough. The false wording inserts `not` after the first `was`, `were`, or `is`, and prefixes `not` when none of those words is there. Two strings separated by `/` are used exactly as written. `outcome` is only for Bool. Its words follow the same punctuation rules as a phrase, and the word `not` is allowed. When no outcome is declared, a phrase that contains `is` is reported as `was` when the symbol is true and `was not` when it is false, so `an order is submitted` becomes `an order was submitted` or `an order was not submitted`. A violation then reads `An order was submitted and inventory was available, but the order was not fulfilled.`
|
|
67
|
+
|
|
68
|
+
`Type` is `Bool`, `Int min..max`, or an enum name declared above that line. Events and states are `Bool` only. An unwanted marker applies only to an event. Ranges are inclusive and both ends sit inside `-1000000..1000000`. At least one variant is required. Variant names are unique across the whole spec and cannot match a symbol name.
|
|
69
|
+
|
|
70
|
+
A description and a phrase are double-quoted strings. `\"`, `\\`, `\/`, and `\t` are the escapes. A newline is rejected, and an empty description or phrase is rejected. Extra spaces inside a phrase collapse to one space. A phrase is words: a hyphen may sit inside a word (`user-friendly`), and `+`, `=`, and other punctuation may not. A phrase cannot start with `not`, `and`, or `or`. A one-word phrase cannot be a reserved word. Two phrases that match ignoring case are an error, and a phrase cannot repeat another symbol, enum, or variant.
|
|
71
|
+
|
|
72
|
+
These words are reserved and cannot be names: `not`, `and`, `or`, `true`, `false`, `while`, `when`, `if`, `where`, `then`, `the`, `shall`, `should`, `must`, `will`, `event`, `state`, `input`, `output`, `unwanted`, `enum`, `bool`, `int`, `phrase`, `outcome`, `action`, `says`, `as`.
|
|
73
|
+
|
|
74
|
+
Blank lines, `#` headings, `//` comments, and a full-line `<!-- ...` are ignored. A requirement may continue onto the next lines until a blank line, a comment, a declaration, or the next id. The requirement's line number is the line of its id.
|
|
75
|
+
|
|
76
|
+
## Phrases
|
|
77
|
+
|
|
78
|
+
Matching is exact. The words in the sentence have to be a declared phrase, compared without regard to case. The longest phrase wins, so `an order` does not steal `an order is submitted` when both are declared. There is no fuzzy match and no stemming.
|
|
79
|
+
|
|
80
|
+
`not` in front of a phrase is the negation: `not fulfill the order`. Write that. Do not declare a second phrase for the false case. When the phrase itself contains `is`, write `is not` inside it: `the customer is not cancelling`, not `not the customer is cancelling`. Both parse as the same negation. Lint flags the second shape.
|
|
81
|
+
|
|
82
|
+
A Bool may also declare `action "start the subscription"` beside `phrase "the subscription is started"`. The action is a second phrase for the same fact, so a response can say `shall start the subscription` and a condition can say `when the subscription is started`.
|
|
83
|
+
|
|
84
|
+
An Int or enum may declare `as` and `says` after the phrase. `as` is the verb phrase a requirement uses. `{value}` is the number or the variant. `{op}` is optional and matches `at most`, `at least`, `less than`, `greater than`, `not`, or nothing when the value is exact. A pattern word with a trailing `s` also matches the word without that `s`, so `seats` matches `seat`. `says` is how a report states the result, and `{value}` is filled in: `says "{value} seats were billed"` turns 3 into `3 seats were billed` and `at most` into `at most 2 seats were billed`. `{name}` and `{symbolName}` are the same hole. `says` is skipped when the right side is another phrase, so `the number shipped was the quantity` stays a phrase. With no `says`, a singular integer reads `the quantity was 12`, and a plural or an enum reads `were set to` or `was set to`.
|
|
85
|
+
|
|
86
|
+
An unknown run of words is an error. The message names the line and up to three closest phrases, nearest edit distance first:
|
|
87
|
+
|
|
88
|
+
<!-- verify stderr 2
|
|
89
|
+
build/plan-verify
|
|
90
|
+
lint
|
|
91
|
+
test/fixtures/unknown-phrase.ears.md
|
|
92
|
+
-->
|
|
93
|
+
```text
|
|
94
|
+
error: test/fixtures/unknown-phrase.ears.md:5: unknown phrase "an order was submitted". Closest: "an order is submitted", "inventory is available", "fulfill the order".
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
When you see that, show the PM the sentence and the suggestions. Ask which declared phrase they meant, or add a phrase they just introduced. Do not silently pick one.
|
|
98
|
+
|
|
99
|
+
A name with no phrase still parses as that name. Prefer the phrase in anything the PM will read.
|
|
100
|
+
|
|
101
|
+
## Operations
|
|
102
|
+
|
|
103
|
+
A condition or a response is a boolean expression. This is the whole language. The phrase column is what you write once `phrase` is declared. The name column is the same term with no phrase bound.
|
|
104
|
+
|
|
105
|
+
| With a phrase | With only the name | What it means |
|
|
106
|
+
| --- | --- | --- |
|
|
107
|
+
| `an order is submitted` | `order_submitted` | That Bool symbol is true. |
|
|
108
|
+
| `not fulfill the order` | `not order_fulfilled` | That Bool symbol is false. |
|
|
109
|
+
| `not the quantity is 1` | `not quantity = 1` | `not` applies to the comparison: the quantity is something other than 1. |
|
|
110
|
+
| `a and b` | `a and b` | Both sides are true. `and` binds tighter than `or`. |
|
|
111
|
+
| `a or b` | `a or b` | Either side is true. |
|
|
112
|
+
| `true`, `false` | `true`, `false` | Boolean literals, any case. |
|
|
113
|
+
| `the quantity is 1`, `the quantity is not 1` | `quantity = 1`, `quantity != 1` | Equality. Both sides must be Bool, both Int, or the same enum. |
|
|
114
|
+
| `the quantity is less than 2`, `is at most`, `is greater than`, `is at least` | `quantity < 2`, `<=`, `>`, `>=` | Integer comparisons only. |
|
|
115
|
+
| `the number shipped is the quantity + 1` | `shipped = quantity + 1`, and `quantity - 1` | Integer addition and subtraction. |
|
|
116
|
+
| `the offset is -1` | `offset = -1` | A negative integer literal. No leading zeros. |
|
|
117
|
+
| `the region is us` | `region = us` | The symbol equals that enum variant. `us` is the variant name. |
|
|
118
|
+
| `(a or b) and c` | `(a or b) and c` | Parentheses group the expression. |
|
|
119
|
+
|
|
120
|
+
`is`, `is not`, `is at most`, `is at least`, `is less than`, and `is greater than` are the same comparisons as `=`, `!=`, `<=`, `>=`, `<`, and `>`. When the left side has a phrase, reports print the English form.
|
|
121
|
+
|
|
122
|
+
`shall the quantity` is rejected, because the response has to be a boolean. `shall the quantity is 1` still parses, and lint flags it. Bind `as "accept {value} units"` and write `shall accept 1 unit`. `+` and `-` expect integers.
|
|
123
|
+
|
|
124
|
+
`examples/order-awkward.ears.md` is the shape to stop writing. The trigger is a bare verb, and the response is `shall the lane is fulfill`. `When not start the subscription` is also a bare negation. Lint names each of those. It does not pick a lane.
|
|
125
|
+
|
|
126
|
+
<!-- verify stdout 1
|
|
127
|
+
build/plan-verify
|
|
128
|
+
lint
|
|
129
|
+
examples/order-awkward.ears.md
|
|
130
|
+
-->
|
|
131
|
+
```text
|
|
132
|
+
spec: examples/order-awkward.ears.md
|
|
133
|
+
conflicts: 0
|
|
134
|
+
gaps: 0
|
|
135
|
+
misuse: 0
|
|
136
|
+
wording: 5
|
|
137
|
+
redundant: 0
|
|
138
|
+
duplicate: 0
|
|
139
|
+
vacuous: 0
|
|
140
|
+
unused: 0
|
|
141
|
+
|
|
142
|
+
wording line 7
|
|
143
|
+
"shall the ... is ..." is awkward. Write the response as a verb phrase, such as "bill at most 2 seats", and bind it with as.
|
|
144
|
+
When start the subscription, the Checkout shall the lane is fulfill.
|
|
145
|
+
|
|
146
|
+
wording line 7
|
|
147
|
+
"When" needs a condition with "is", not a bare verb. "When the subscription is started" is the form.
|
|
148
|
+
When start the subscription, the Checkout shall the lane is fulfill.
|
|
149
|
+
|
|
150
|
+
wording line 8
|
|
151
|
+
"shall the ... is ..." is awkward. Write the response as a verb phrase, such as "bill at most 2 seats", and bind it with as.
|
|
152
|
+
When not start the subscription, the Checkout shall the lane is hold.
|
|
153
|
+
|
|
154
|
+
wording line 8
|
|
155
|
+
"When" needs a condition with "is", not a bare verb. "When the subscription is started" is the form.
|
|
156
|
+
When not start the subscription, the Checkout shall the lane is hold.
|
|
157
|
+
|
|
158
|
+
wording line 8
|
|
159
|
+
The trigger is a bare negation. Write a positive condition, such as "When a non-member asks", or declare a positive event.
|
|
160
|
+
When not start the subscription, the Checkout shall the lane is hold.
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`examples/order-clear.ears.md` is the same lane, written so the sentence can be read aloud. `as "route the order to the {value} lane"` binds the verb phrase, and `says` is what a later explanation uses. Lint is clean.
|
|
164
|
+
|
|
165
|
+
<!-- verify stdout 0
|
|
166
|
+
build/plan-verify
|
|
167
|
+
lint
|
|
168
|
+
examples/order-clear.ears.md
|
|
169
|
+
-->
|
|
170
|
+
```text
|
|
171
|
+
spec: examples/order-clear.ears.md
|
|
172
|
+
conflicts: 0
|
|
173
|
+
gaps: 0
|
|
174
|
+
misuse: 0
|
|
175
|
+
wording: 0
|
|
176
|
+
redundant: 0
|
|
177
|
+
duplicate: 0
|
|
178
|
+
vacuous: 0
|
|
179
|
+
unused: 0
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## EARS patterns
|
|
183
|
+
|
|
184
|
+
Clauses that appear go in this order, separated by commas: `while`, `when`, `if`, `where`. `then` is required when `if` is present, and rejected when `if` is absent. The line ends with a period. The id is `R`, digits, then zero or more `.digits` groups, such as `R1` or `R1.2`. One requirement per line.
|
|
185
|
+
|
|
186
|
+
```ears
|
|
187
|
+
While <state>, when <trigger>, if <condition>, where <feature>, then the <system> shall <response>.
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
| Pattern | Use it for | Shape the parser accepts |
|
|
191
|
+
| --- | --- | --- |
|
|
192
|
+
| Ubiquitous | A response that is always required | `The Order System shall fulfill the order.` |
|
|
193
|
+
| When | The normal trigger | `When an order is submitted, the Order System shall fulfill the order.` |
|
|
194
|
+
| If, then | An unwanted or exceptional case | `If payment fails, then the Order System shall reject the order.` |
|
|
195
|
+
| While | A state that already holds | `While inventory is available, the Order System shall fulfill the order.` |
|
|
196
|
+
| Where | A variant, such as a region | `Where the region is us, the Order System shall fulfill the order.` |
|
|
197
|
+
| Combined | Any of the above, in the order above | `While inventory is available, when an order is submitted, if the quantity is at least 1, where the region is us, then the Warehouse shall ship the quantity.` |
|
|
198
|
+
|
|
199
|
+
`should`, `must`, and `will` are not `shall`. The parser stops and asks for `shall`.
|
|
200
|
+
|
|
201
|
+
Each requirement with at least one clause becomes one implication. The clauses are joined with `and` in the order while, when, if, where, and that antecedent implies the response. A ubiquitous requirement is asserted as the response itself. Assertion names are the id, `_L`, and the line, such as `R1_L7`. The SMT-LIB uses the symbol names. The comment above each assertion repeats the original sentence.
|
|
202
|
+
|
|
203
|
+
An unwanted event is written with `if` ... `then`. `when` is the normal trigger. Lint flags an unwanted event used in `when` without an `if`.
|
|
204
|
+
|
|
205
|
+
The line has to be something a scenario can make true or false. Lint rejects these whole words as too vague to test: `as needed`, `fast`, `slow`, `large`, `small`, `appropriate`, `quickly`, `easy`, `robust`, `flexible`, `user-friendly`, `efficient`. It also rejects solution words: `database`, `api`, `algorithm`, `microservice`, `server`, `class`, `function`. A word that sits inside a declared phrase or action is already bound to a schema item, so that use is not a wording finding. Put the PM's phrasing in the description only after those words are gone, or say the testable fact in the requirement. Lint also flags an awkward response (`shall the lane is fulfill`), a `When` that is a bare verb (`When start the subscription`), and a `not` that sits in front of a phrase containing `is` (`not the customer is cancelling`). Those findings have kind `form`. A trigger or state that starts with bare `not`, such as `When not a member asks` or `While not a member asks`, has kind `negation`. Ask for a positive condition, such as `When a non-member asks`, or a positive event. Show the sentence and the wording line.
|
|
206
|
+
|
|
207
|
+
## Workflow
|
|
208
|
+
|
|
209
|
+
1. Listen. Write down the PM's words before you translate them.
|
|
210
|
+
2. Add or reuse declarations. If they describe something you already named, use that name and its phrase. Bind a new phrase to the words they keep using.
|
|
211
|
+
3. Write one EARS line per requirement, using the phrases. Normal triggers use `when`. Unwanted conditions use `if` ... `then`.
|
|
212
|
+
4. Run `build/plan-verify lint <spec>`. Run `build/plan-verify report <spec>` for the logical problems in one plain-language list. Run `build/plan-verify scenarios <spec>` when you want an accepted and a rejected example of each trigger.
|
|
213
|
+
5. Fix what lint reports. For a conflict, tell the PM which requirements clash, the situation, and the example, then ask which requirement should win. Also show the candidate repairs: dropping one requirement in the set, and any rewritten line that adds one Bool, enum equality, or integer bound to one condition. Stop until they answer. Do not choose a requirement or a repair. If a requirement adds nothing, including a repeat that sits outside a conflict, ask whether they meant something the others do not already require. Do not delete it. If two requirements are the same condition and the same response, show the duplicate and ask whether the later line was meant to differ. Do not delete it. If a trigger starts with bare `not`, ask for a positive condition or a positive event. If a condition can never be true, show the suggested fix and ask them to rewrite the condition. Do not invent a response. If a state, an input, an enum variant, or an output is unused, ask whether to use it or remove it. For a gap, read the suggested fix and ask what the system shall do in that situation. Do not choose the response.
|
|
214
|
+
6. When they describe a concrete story, write a flat JSON scenario with every declared name and run `build/plan-verify check <spec> --input <scenario>`. When they hope something is always true, write that hope as a property file of `P` requirements and run `build/plan-verify policy <spec> --property <file>`. A counterexample is a scenario the requirements allow and the property forbids. If you have no property yet, `policy <spec>` prints one allowed scenario and one forbidden scenario. The search tries false before true, so the first hit is not a recommendation.
|
|
215
|
+
7. Report every check violation by requirement ID, line, and sentence, then the values. A passing check can still leave a gap. If lint reported one, say which output is uncovered and in what situation.
|
|
216
|
+
|
|
217
|
+
`--json` is the same report in a stable shape, for you. Show the PM the text. Stderr stays empty. A bad file or a bad argument is exit 2 and `{ "ok": false, "error": "..." }`.
|
|
218
|
+
|
|
219
|
+
`check` returns `ok`, `spec`, `scenario`, and `violations` of `id`, `line`, `sentence`, `because`.
|
|
220
|
+
|
|
221
|
+
<!-- verify stdout 1
|
|
222
|
+
build/plan-verify
|
|
223
|
+
check
|
|
224
|
+
--json
|
|
225
|
+
examples/order.ears.md
|
|
226
|
+
--input
|
|
227
|
+
examples/order-fail.json
|
|
228
|
+
-->
|
|
229
|
+
```text
|
|
230
|
+
{
|
|
231
|
+
"ok": false,
|
|
232
|
+
"spec": "examples/order.ears.md",
|
|
233
|
+
"scenario": "examples/order-fail.json",
|
|
234
|
+
"violations": [
|
|
235
|
+
{
|
|
236
|
+
"id": "R1",
|
|
237
|
+
"line": 7,
|
|
238
|
+
"sentence": "WHEN an order is submitted AND inventory is available, THE Order System SHALL fulfill the order.",
|
|
239
|
+
"because": "An order was submitted and inventory was available, but the order was not fulfilled."
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
`lint` returns `ok`, `spec`, `conflicts`, `gaps`, `misuse`, `wording`, `redundant`, `duplicate`, `vacuous`, and `unused`. A conflict carries `ids`, `minimal`, `everySituation`, `summary`, `clash`, `requirements`, `situation`, `requires`, `example`, `keeps`, `breaks`, `fix`, and `repairs`. A repair is `kind`, `id`, `text`, and `sentence`. `kind` is `drop` or `strengthen`. Situation and example entries are `name` and `value`. Requirements are `id`, `line`, and `sentence`. Requires entries are `id` and `text`. A gap is `output`, `situation`, and `text`. Misuse is `id`, `line`, `symbols`, `sentence`, and `text`. Wording is `line`, `word`, `kind`, `text`, and `context`. Wording `kind` is `vague`, `solution`, `form`, or `negation`. A redundant requirement is `id`, `line`, `sentence`, `text`, and `fix`. An exact duplicate is `id`, `line`, `sameAs`, `sentence`, `text`, and `fix`. A vacuous requirement has the same fields as a redundant requirement. An unused entry is `name`, `kind`, `text`, and `fix`. Unused `kind` is `state`, `input`, `output`, or `variant`. Do not delete a redundant requirement, or an exact duplicate, until the PM agrees. Do not invent a response for a condition that can never be true.
|
|
246
|
+
|
|
247
|
+
<!-- verify stdout 1
|
|
248
|
+
build/plan-verify
|
|
249
|
+
lint
|
|
250
|
+
--json
|
|
251
|
+
examples/order-conflict.ears.md
|
|
252
|
+
-->
|
|
253
|
+
```text
|
|
254
|
+
{
|
|
255
|
+
"ok": false,
|
|
256
|
+
"spec": "examples/order-conflict.ears.md",
|
|
257
|
+
"conflicts": [
|
|
258
|
+
{
|
|
259
|
+
"ids": [
|
|
260
|
+
"R2",
|
|
261
|
+
"R3"
|
|
262
|
+
],
|
|
263
|
+
"minimal": true,
|
|
264
|
+
"everySituation": false,
|
|
265
|
+
"summary": "R2 and R3 cannot both hold.",
|
|
266
|
+
"clash": "They clash when an order was submitted.",
|
|
267
|
+
"requirements": [
|
|
268
|
+
{
|
|
269
|
+
"id": "R2",
|
|
270
|
+
"line": 7,
|
|
271
|
+
"sentence": "When an order is submitted, the Order System shall fulfill the order."
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"id": "R3",
|
|
275
|
+
"line": 8,
|
|
276
|
+
"sentence": "When an order is submitted, the Order System shall not fulfill the order."
|
|
277
|
+
}
|
|
278
|
+
],
|
|
279
|
+
"situation": [
|
|
280
|
+
{
|
|
281
|
+
"name": "order_submitted",
|
|
282
|
+
"value": "true"
|
|
283
|
+
}
|
|
284
|
+
],
|
|
285
|
+
"requires": [
|
|
286
|
+
{
|
|
287
|
+
"id": "R2",
|
|
288
|
+
"text": "that the order was fulfilled"
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
"id": "R3",
|
|
292
|
+
"text": "that the order was not fulfilled"
|
|
293
|
+
}
|
|
294
|
+
],
|
|
295
|
+
"example": [
|
|
296
|
+
{
|
|
297
|
+
"name": "order_submitted",
|
|
298
|
+
"value": "true"
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
"name": "order_fulfilled",
|
|
302
|
+
"value": "true"
|
|
303
|
+
}
|
|
304
|
+
],
|
|
305
|
+
"keeps": [
|
|
306
|
+
"R2"
|
|
307
|
+
],
|
|
308
|
+
"breaks": "R3",
|
|
309
|
+
"fix": "Suggested fix: ask which requirement should win. To keep R2, remove R3. To keep R3, remove R2.",
|
|
310
|
+
"repairs": [
|
|
311
|
+
{
|
|
312
|
+
"kind": "drop",
|
|
313
|
+
"id": "R2",
|
|
314
|
+
"text": "One repair is to drop R2 and keep R3 as written.",
|
|
315
|
+
"sentence": "When an order is submitted, the Order System shall not fulfill the order."
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
"kind": "drop",
|
|
319
|
+
"id": "R3",
|
|
320
|
+
"text": "One repair is to drop R3 and keep R2 as written.",
|
|
321
|
+
"sentence": "When an order is submitted, the Order System shall fulfill the order."
|
|
322
|
+
}
|
|
323
|
+
]
|
|
324
|
+
}
|
|
325
|
+
],
|
|
326
|
+
"gaps": [],
|
|
327
|
+
"misuse": [],
|
|
328
|
+
"wording": [],
|
|
329
|
+
"redundant": [],
|
|
330
|
+
"duplicate": [],
|
|
331
|
+
"vacuous": [],
|
|
332
|
+
"unused": []
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
`policy` returns `ok`, `spec`, `property`, `holds`, `note`, `acceptedFound`, `accepted`, `rejectedFound`, `rejected`, `counterexampleFound`, `counterexample`, and `breaks`. A scenario list is empty when its `Found` flag is false. Each entry is `name` and `value`. A break is `id`, `line`, `sentence`, and `because`.
|
|
337
|
+
|
|
338
|
+
<!-- verify stdout 1
|
|
339
|
+
build/plan-verify
|
|
340
|
+
policy
|
|
341
|
+
--json
|
|
342
|
+
examples/order.ears.md
|
|
343
|
+
--property
|
|
344
|
+
examples/order-always.ears.md
|
|
345
|
+
-->
|
|
346
|
+
```text
|
|
347
|
+
{
|
|
348
|
+
"ok": false,
|
|
349
|
+
"spec": "examples/order.ears.md",
|
|
350
|
+
"property": "examples/order-always.ears.md",
|
|
351
|
+
"holds": false,
|
|
352
|
+
"note": "",
|
|
353
|
+
"acceptedFound": false,
|
|
354
|
+
"accepted": [],
|
|
355
|
+
"rejectedFound": false,
|
|
356
|
+
"rejected": [],
|
|
357
|
+
"counterexampleFound": true,
|
|
358
|
+
"counterexample": [
|
|
359
|
+
{
|
|
360
|
+
"name": "order_submitted",
|
|
361
|
+
"value": "false"
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
"name": "inventory_available",
|
|
365
|
+
"value": "false"
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
"name": "order_fulfilled",
|
|
369
|
+
"value": "false"
|
|
370
|
+
}
|
|
371
|
+
],
|
|
372
|
+
"breaks": [
|
|
373
|
+
{
|
|
374
|
+
"id": "P1",
|
|
375
|
+
"line": 3,
|
|
376
|
+
"sentence": "The Order System shall fulfill the order.",
|
|
377
|
+
"because": "The order was not fulfilled."
|
|
378
|
+
}
|
|
379
|
+
]
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
`emit-smt --json` returns `ok`, `spec`, and `smt`. `smt` is the plain SMT-LIB text, newlines between lines, none after the last line.
|
|
384
|
+
|
|
385
|
+
<!-- verify stdout 0
|
|
386
|
+
build/plan-verify
|
|
387
|
+
emit-smt
|
|
388
|
+
--json
|
|
389
|
+
examples/order.ears.md
|
|
390
|
+
-->
|
|
391
|
+
```text
|
|
392
|
+
{
|
|
393
|
+
"ok": true,
|
|
394
|
+
"spec": "examples/order.ears.md",
|
|
395
|
+
"smt": "; spec: examples/order.ears.md\n(set-logic ALL)\n; event order_submitted: Bool \"The customer submits an order.\" phrase \"an order is submitted\"\n(declare-fun order_submitted () Bool)\n; state inventory_available: Bool \"Enough stock is on hand to fill the order.\" phrase \"inventory is available\"\n(declare-fun inventory_available () Bool)\n; output order_fulfilled: Bool \"The order is fulfilled.\" phrase \"fulfill the order\" outcome \"the order was fulfilled\" / \"the order was not fulfilled\"\n(declare-fun order_fulfilled () Bool)\n; R1_L7 line 7: WHEN an order is submitted AND inventory is available, THE Order System SHALL fulfill the order.\n(assert (! (=> (and order_submitted inventory_available) order_fulfilled) :named R1_L7))"
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
A scenario value is `true` or `false`, an integer inside the declared range, or an enum variant string. Every declared name is required. Extra names are rejected. Leading zeros are rejected.
|
|
400
|
+
|
|
401
|
+
## Milestone 1: one requirement, one scenario
|
|
402
|
+
|
|
403
|
+
`examples/order.ears.md` says: when an order is submitted and inventory is available, the order is fulfilled. R1 is line 7.
|
|
404
|
+
|
|
405
|
+
This scenario breaks it. Report `R1`, line 7, and the sentence below:
|
|
406
|
+
|
|
407
|
+
<!-- verify stdout 1
|
|
408
|
+
build/plan-verify
|
|
409
|
+
check
|
|
410
|
+
examples/order.ears.md
|
|
411
|
+
--input
|
|
412
|
+
examples/order-fail.json
|
|
413
|
+
-->
|
|
414
|
+
```text
|
|
415
|
+
spec: examples/order.ears.md
|
|
416
|
+
scenario: examples/order-fail.json
|
|
417
|
+
violations: 1
|
|
418
|
+
|
|
419
|
+
R1 line 7
|
|
420
|
+
WHEN an order is submitted AND inventory is available, THE Order System SHALL fulfill the order.
|
|
421
|
+
An order was submitted and inventory was available, but the order was not fulfilled.
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
This scenario fits R1:
|
|
425
|
+
|
|
426
|
+
<!-- verify stdout 0
|
|
427
|
+
build/plan-verify
|
|
428
|
+
check
|
|
429
|
+
examples/order.ears.md
|
|
430
|
+
--input
|
|
431
|
+
examples/order-pass.json
|
|
432
|
+
-->
|
|
433
|
+
```text
|
|
434
|
+
spec: examples/order.ears.md
|
|
435
|
+
scenario: examples/order-pass.json
|
|
436
|
+
violations: 0
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
`emit-smt` prints the implication. The comment maps the assertion back to line 7 and the sentence:
|
|
440
|
+
|
|
441
|
+
<!-- verify stdout 0
|
|
442
|
+
build/plan-verify
|
|
443
|
+
emit-smt
|
|
444
|
+
examples/order.ears.md
|
|
445
|
+
-->
|
|
446
|
+
```text
|
|
447
|
+
; spec: examples/order.ears.md
|
|
448
|
+
(set-logic ALL)
|
|
449
|
+
; event order_submitted: Bool "The customer submits an order." phrase "an order is submitted"
|
|
450
|
+
(declare-fun order_submitted () Bool)
|
|
451
|
+
; state inventory_available: Bool "Enough stock is on hand to fill the order." phrase "inventory is available"
|
|
452
|
+
(declare-fun inventory_available () Bool)
|
|
453
|
+
; output order_fulfilled: Bool "The order is fulfilled." phrase "fulfill the order" outcome "the order was fulfilled" / "the order was not fulfilled"
|
|
454
|
+
(declare-fun order_fulfilled () Bool)
|
|
455
|
+
; R1_L7 line 7: WHEN an order is submitted AND inventory is available, THE Order System SHALL fulfill the order.
|
|
456
|
+
(assert (! (=> (and order_submitted inventory_available) order_fulfilled) :named R1_L7))
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
Lint on that same spec is already useful. The check can pass while a situation is still uncovered:
|
|
460
|
+
|
|
461
|
+
<!-- verify stdout 1
|
|
462
|
+
build/plan-verify
|
|
463
|
+
lint
|
|
464
|
+
examples/order.ears.md
|
|
465
|
+
-->
|
|
466
|
+
```text
|
|
467
|
+
spec: examples/order.ears.md
|
|
468
|
+
conflicts: 0
|
|
469
|
+
gaps: 1
|
|
470
|
+
misuse: 0
|
|
471
|
+
wording: 0
|
|
472
|
+
redundant: 0
|
|
473
|
+
duplicate: 0
|
|
474
|
+
vacuous: 0
|
|
475
|
+
unused: 0
|
|
476
|
+
|
|
477
|
+
gap order_fulfilled
|
|
478
|
+
Nothing says whether the order was fulfilled when an order was submitted and inventory was not available. Suggested fix: add a requirement for this situation, and ask what the system shall do. Do not choose the response.
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
Ask what should happen in that situation. `examples/order-covered.ears.md` answers every situation, and lint is clean:
|
|
482
|
+
|
|
483
|
+
<!-- verify stdout 0
|
|
484
|
+
build/plan-verify
|
|
485
|
+
lint
|
|
486
|
+
examples/order-covered.ears.md
|
|
487
|
+
-->
|
|
488
|
+
```text
|
|
489
|
+
spec: examples/order-covered.ears.md
|
|
490
|
+
conflicts: 0
|
|
491
|
+
gaps: 0
|
|
492
|
+
misuse: 0
|
|
493
|
+
wording: 0
|
|
494
|
+
redundant: 0
|
|
495
|
+
duplicate: 0
|
|
496
|
+
vacuous: 0
|
|
497
|
+
unused: 0
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
## Combined clauses
|
|
501
|
+
|
|
502
|
+
`examples/order-combined.ears.md` uses while, when, if, and where. The id is `R1.2`. The printed implication nests `and` in clause order, and the integer range is its own unnamed assertion:
|
|
503
|
+
|
|
504
|
+
<!-- verify stdout 0
|
|
505
|
+
build/plan-verify
|
|
506
|
+
emit-smt
|
|
507
|
+
examples/order-combined.ears.md
|
|
508
|
+
-->
|
|
509
|
+
```text
|
|
510
|
+
; spec: examples/order-combined.ears.md
|
|
511
|
+
(set-logic ALL)
|
|
512
|
+
; enum Region { us, eu }
|
|
513
|
+
(declare-datatypes ((Region 0)) (((us) (eu))))
|
|
514
|
+
; state inventory_available: Bool "Enough stock is on hand to fill the order." phrase "inventory is available"
|
|
515
|
+
(declare-fun inventory_available () Bool)
|
|
516
|
+
; event order_submitted: Bool "The customer submits an order." phrase "an order is submitted"
|
|
517
|
+
(declare-fun order_submitted () Bool)
|
|
518
|
+
; input quantity: Int 1..5 "How many units the customer asked for." phrase "the quantity"
|
|
519
|
+
(declare-fun quantity () Int)
|
|
520
|
+
(assert (and (>= quantity 1) (<= quantity 5)))
|
|
521
|
+
; input region: Region "The region the order ships to." phrase "the region"
|
|
522
|
+
(declare-fun region () Region)
|
|
523
|
+
; output shipped: Int 1..5 "How many units are shipped." phrase "the number shipped" as "ship {value}"
|
|
524
|
+
(declare-fun shipped () Int)
|
|
525
|
+
(assert (and (>= shipped 1) (<= shipped 5)))
|
|
526
|
+
; R1.2_L10 line 10: While inventory is available, when an order is submitted, if the quantity is at least 1, where the region is us, then the Warehouse shall ship the quantity.
|
|
527
|
+
(assert (! (=> (and (and (and inventory_available order_submitted) (>= quantity 1)) (= region us)) (= shipped quantity)) :named R1.2_L10))
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
## Bad: the PM has said both things
|
|
531
|
+
|
|
532
|
+
`examples/order-conflict.ears.md` records two answers for a submitted order. Lint is implemented; this is its real report:
|
|
533
|
+
|
|
534
|
+
<!-- verify stdout 1
|
|
535
|
+
build/plan-verify
|
|
536
|
+
lint
|
|
537
|
+
examples/order-conflict.ears.md
|
|
538
|
+
-->
|
|
539
|
+
```text
|
|
540
|
+
spec: examples/order-conflict.ears.md
|
|
541
|
+
conflicts: 1
|
|
542
|
+
gaps: 0
|
|
543
|
+
misuse: 0
|
|
544
|
+
wording: 0
|
|
545
|
+
redundant: 0
|
|
546
|
+
duplicate: 0
|
|
547
|
+
vacuous: 0
|
|
548
|
+
unused: 0
|
|
549
|
+
|
|
550
|
+
conflict {R2, R3}
|
|
551
|
+
R2 and R3 cannot both hold.
|
|
552
|
+
|
|
553
|
+
R2 line 7
|
|
554
|
+
When an order is submitted, the Order System shall fulfill the order.
|
|
555
|
+
|
|
556
|
+
R3 line 8
|
|
557
|
+
When an order is submitted, the Order System shall not fulfill the order.
|
|
558
|
+
|
|
559
|
+
They clash when an order was submitted.
|
|
560
|
+
R2 requires that the order was fulfilled.
|
|
561
|
+
R3 requires that the order was not fulfilled.
|
|
562
|
+
|
|
563
|
+
Example scenario:
|
|
564
|
+
{
|
|
565
|
+
"order_submitted": true,
|
|
566
|
+
"order_fulfilled": true
|
|
567
|
+
}
|
|
568
|
+
This scenario keeps R2 and breaks R3.
|
|
569
|
+
|
|
570
|
+
Suggested fix: ask which requirement should win. To keep R2, remove R3. To keep R3, remove R2.
|
|
571
|
+
|
|
572
|
+
Candidate repairs follow. This report does not choose one.
|
|
573
|
+
|
|
574
|
+
repair drop R2
|
|
575
|
+
One repair is to drop R2 and keep R3 as written.
|
|
576
|
+
When an order is submitted, the Order System shall not fulfill the order.
|
|
577
|
+
|
|
578
|
+
repair drop R3
|
|
579
|
+
One repair is to drop R3 and keep R2 as written.
|
|
580
|
+
When an order is submitted, the Order System shall fulfill the order.
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
Tell the PM: when an order was submitted, R2 says the order was fulfilled and R3 says the order was not fulfilled. The example keeps R2 and breaks R3 only because R3 is the last requirement in the set. It is evidence, not a vote. Ask which requirement should win, and wait. The repairs under the clash are candidates. For this pair the only candidates are to drop R2 or to drop R3, because both lines have the same condition.
|
|
584
|
+
|
|
585
|
+
`examples/order-repair.ears.md` is a clash that a single extra condition can separate. R1 allows the loan while the copy is in the building. R2 refuses it while the copy is out on loan. Lint checks each candidate on the patched spec and keeps it only when that conflict is gone and no new conflict appears. It still asks which requirement should win.
|
|
586
|
+
|
|
587
|
+
<!-- verify stdout 1
|
|
588
|
+
build/plan-verify
|
|
589
|
+
lint
|
|
590
|
+
examples/order-repair.ears.md
|
|
591
|
+
-->
|
|
592
|
+
```text
|
|
593
|
+
spec: examples/order-repair.ears.md
|
|
594
|
+
conflicts: 1
|
|
595
|
+
gaps: 1
|
|
596
|
+
misuse: 0
|
|
597
|
+
wording: 0
|
|
598
|
+
redundant: 0
|
|
599
|
+
duplicate: 0
|
|
600
|
+
vacuous: 0
|
|
601
|
+
unused: 0
|
|
602
|
+
|
|
603
|
+
conflict {R1, R2}
|
|
604
|
+
R1 and R2 cannot both hold.
|
|
605
|
+
|
|
606
|
+
R1 line 8
|
|
607
|
+
While the copy is in the building, when a member asks to take a copy home, the Desk shall allow the loan.
|
|
608
|
+
|
|
609
|
+
R2 line 9
|
|
610
|
+
While the copy is out on loan, when a member asks to take a copy home, the Desk shall not allow the loan.
|
|
611
|
+
|
|
612
|
+
They clash when the copy was in the building, the copy was out on loan, and a member asks to take a copy home.
|
|
613
|
+
R1 requires that the loan was allowed.
|
|
614
|
+
R2 requires that the loan was not allowed.
|
|
615
|
+
|
|
616
|
+
Example scenario:
|
|
617
|
+
{
|
|
618
|
+
"copy_in_building": true,
|
|
619
|
+
"copy_on_loan": true,
|
|
620
|
+
"borrow_requested": true,
|
|
621
|
+
"loan_allowed": true
|
|
622
|
+
}
|
|
623
|
+
This scenario keeps R1 and breaks R2.
|
|
624
|
+
|
|
625
|
+
Suggested fix: ask which requirement should win. To keep R1, remove R2. To keep R2, remove R1.
|
|
626
|
+
|
|
627
|
+
Candidate repairs follow. This report does not choose one.
|
|
628
|
+
|
|
629
|
+
repair drop R1
|
|
630
|
+
One repair is to drop R1 and keep R2 as written.
|
|
631
|
+
While the copy is out on loan, when a member asks to take a copy home, the Desk shall not allow the loan.
|
|
632
|
+
|
|
633
|
+
repair drop R2
|
|
634
|
+
One repair is to drop R2 and keep R1 as written.
|
|
635
|
+
While the copy is in the building, when a member asks to take a copy home, the Desk shall allow the loan.
|
|
636
|
+
|
|
637
|
+
repair strengthen R1
|
|
638
|
+
One repair is to add "and the copy is not out on loan" to the condition of R1. R2 stays as written.
|
|
639
|
+
While the copy is in the building, when a member asks to take a copy home and the copy is not out on loan, the Desk shall allow the loan.
|
|
640
|
+
|
|
641
|
+
repair strengthen R2
|
|
642
|
+
One repair is to add "and the copy is not in the building" to the condition of R2. R1 stays as written.
|
|
643
|
+
While the copy is out on loan, when a member asks to take a copy home and the copy is not in the building, the Desk shall not allow the loan.
|
|
644
|
+
|
|
645
|
+
gap loan_allowed
|
|
646
|
+
Nothing says whether the loan was allowed when the copy was in the building, the copy was out on loan, and not a member asks to take a copy home. Suggested fix: add a requirement for this situation, and ask what the system shall do. Do not choose the response.
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
## Unwanted events use if, then
|
|
650
|
+
|
|
651
|
+
`examples/payment-if.ears.md` writes a declined payment with `if` ... `then`. That shape is right for an unwanted event. `If not payment fails` is still a bare negation, so lint asks for a positive condition or a positive event.
|
|
652
|
+
|
|
653
|
+
<!-- verify stdout 1
|
|
654
|
+
build/plan-verify
|
|
655
|
+
lint
|
|
656
|
+
examples/payment-if.ears.md
|
|
657
|
+
-->
|
|
658
|
+
```text
|
|
659
|
+
spec: examples/payment-if.ears.md
|
|
660
|
+
conflicts: 0
|
|
661
|
+
gaps: 0
|
|
662
|
+
misuse: 0
|
|
663
|
+
wording: 1
|
|
664
|
+
redundant: 0
|
|
665
|
+
duplicate: 0
|
|
666
|
+
vacuous: 0
|
|
667
|
+
unused: 0
|
|
668
|
+
|
|
669
|
+
wording line 7
|
|
670
|
+
The trigger is a bare negation. Write a positive condition, such as "When a non-member asks", or declare a positive event.
|
|
671
|
+
If not payment fails, then the Order System shall not reject the order.
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
`examples/order-misuse.ears.md` uses `when` for that unwanted event. Show the finding and rewrite it with the PM as `If payment fails, then the Order System shall reject the order.`
|
|
675
|
+
|
|
676
|
+
<!-- verify stdout 1
|
|
677
|
+
build/plan-verify
|
|
678
|
+
lint
|
|
679
|
+
examples/order-misuse.ears.md
|
|
680
|
+
-->
|
|
681
|
+
```text
|
|
682
|
+
spec: examples/order-misuse.ears.md
|
|
683
|
+
conflicts: 0
|
|
684
|
+
gaps: 0
|
|
685
|
+
misuse: 1
|
|
686
|
+
wording: 0
|
|
687
|
+
redundant: 1
|
|
688
|
+
duplicate: 0
|
|
689
|
+
vacuous: 0
|
|
690
|
+
unused: 0
|
|
691
|
+
|
|
692
|
+
misuse R4 line 6
|
|
693
|
+
"payment fails" is an unwanted event. Write if ... then, not when.
|
|
694
|
+
When payment fails, the Order System shall reject the order.
|
|
695
|
+
|
|
696
|
+
redundant R4 line 6
|
|
697
|
+
R4 adds nothing. Whenever the other requirements hold, R4 holds too.
|
|
698
|
+
When payment fails, the Order System shall reject the order.
|
|
699
|
+
Suggested fix: remove R4, or ask whether it was meant to say something the others do not already require. Do not delete it until the PM agrees.
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
## Vague words and solution words
|
|
703
|
+
|
|
704
|
+
`examples/order-wording.ears.md` says the system "responds quickly" and stores the order "in the database". Ask what a scenario can check, and what the system must guarantee. The state `saved` is unused, because no requirement mentions it.
|
|
705
|
+
|
|
706
|
+
<!-- verify stdout 1
|
|
707
|
+
build/plan-verify
|
|
708
|
+
lint
|
|
709
|
+
examples/order-wording.ears.md
|
|
710
|
+
-->
|
|
711
|
+
```text
|
|
712
|
+
spec: examples/order-wording.ears.md
|
|
713
|
+
conflicts: 0
|
|
714
|
+
gaps: 0
|
|
715
|
+
misuse: 0
|
|
716
|
+
wording: 2
|
|
717
|
+
redundant: 0
|
|
718
|
+
duplicate: 0
|
|
719
|
+
vacuous: 0
|
|
720
|
+
unused: 1
|
|
721
|
+
|
|
722
|
+
wording line 4
|
|
723
|
+
"quickly" is too vague to test. Replace it with something a scenario can check.
|
|
724
|
+
output order_fulfilled: Bool "The system responds quickly." phrase "fulfill the order" outcome "the order was fulfilled" / "the order was not fulfilled"
|
|
725
|
+
|
|
726
|
+
wording line 5
|
|
727
|
+
"database" names a solution. Say what the system must do, not how to build it.
|
|
728
|
+
state saved: Bool "Store the order in the database." phrase "the order is saved"
|
|
729
|
+
|
|
730
|
+
unused saved
|
|
731
|
+
saved ("the order is saved") is a state and no requirement mentions it.
|
|
732
|
+
Suggested fix: use saved in a requirement, or remove it from the schema.
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
## An event nobody handles
|
|
736
|
+
|
|
737
|
+
`examples/order-unhandled.ears.md` declares "an order is cancelled" and never says what the system shall do. Tell the PM, and ask them to add the requirement or drop the event. Do not invent the response.
|
|
738
|
+
|
|
739
|
+
<!-- verify stdout 1
|
|
740
|
+
build/plan-verify
|
|
741
|
+
lint
|
|
742
|
+
examples/order-unhandled.ears.md
|
|
743
|
+
-->
|
|
744
|
+
```text
|
|
745
|
+
spec: examples/order-unhandled.ears.md
|
|
746
|
+
conflicts: 0
|
|
747
|
+
gaps: 1
|
|
748
|
+
misuse: 0
|
|
749
|
+
wording: 0
|
|
750
|
+
redundant: 0
|
|
751
|
+
duplicate: 0
|
|
752
|
+
vacuous: 0
|
|
753
|
+
unused: 0
|
|
754
|
+
|
|
755
|
+
gap order_cancelled
|
|
756
|
+
No requirement handles "an order is cancelled". Suggested fix: add a requirement that says what the system shall do, or remove the event.
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
## An error condition with no response
|
|
760
|
+
|
|
761
|
+
`examples/order-unanswered.ears.md` declares "payment fails" and no `if` ... `then` says what happens. Ask what the system shall do. Do not choose.
|
|
762
|
+
|
|
763
|
+
<!-- verify stdout 1
|
|
764
|
+
build/plan-verify
|
|
765
|
+
lint
|
|
766
|
+
examples/order-unanswered.ears.md
|
|
767
|
+
-->
|
|
768
|
+
```text
|
|
769
|
+
spec: examples/order-unanswered.ears.md
|
|
770
|
+
conflicts: 0
|
|
771
|
+
gaps: 1
|
|
772
|
+
misuse: 0
|
|
773
|
+
wording: 0
|
|
774
|
+
redundant: 0
|
|
775
|
+
duplicate: 0
|
|
776
|
+
vacuous: 0
|
|
777
|
+
unused: 0
|
|
778
|
+
|
|
779
|
+
gap payment_declined
|
|
780
|
+
No requirement states a response when "payment fails". Suggested fix: add an if ... then requirement, and ask what the system shall do. Do not choose the response.
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
## Vague: fast, appropriate, user-friendly
|
|
784
|
+
|
|
785
|
+
`examples/order-vague.ears.md` describes the checkout as fast, appropriate, and user-friendly. Each word is a separate finding. Ask what a scenario can check instead.
|
|
786
|
+
|
|
787
|
+
<!-- verify stdout 1
|
|
788
|
+
build/plan-verify
|
|
789
|
+
lint
|
|
790
|
+
examples/order-vague.ears.md
|
|
791
|
+
-->
|
|
792
|
+
```text
|
|
793
|
+
spec: examples/order-vague.ears.md
|
|
794
|
+
conflicts: 0
|
|
795
|
+
gaps: 0
|
|
796
|
+
misuse: 0
|
|
797
|
+
wording: 3
|
|
798
|
+
redundant: 0
|
|
799
|
+
duplicate: 0
|
|
800
|
+
vacuous: 0
|
|
801
|
+
unused: 0
|
|
802
|
+
|
|
803
|
+
wording line 4
|
|
804
|
+
"fast" is too vague to test. Replace it with something a scenario can check.
|
|
805
|
+
output order_fulfilled: Bool "The checkout is fast, appropriate, and user-friendly." phrase "fulfill the order" outcome "the order was fulfilled" / "the order was not fulfilled"
|
|
806
|
+
|
|
807
|
+
wording line 4
|
|
808
|
+
"appropriate" is too vague to test. Replace it with something a scenario can check.
|
|
809
|
+
output order_fulfilled: Bool "The checkout is fast, appropriate, and user-friendly." phrase "fulfill the order" outcome "the order was fulfilled" / "the order was not fulfilled"
|
|
810
|
+
|
|
811
|
+
wording line 4
|
|
812
|
+
"user-friendly" is too vague to test. Replace it with something a scenario can check.
|
|
813
|
+
output order_fulfilled: Bool "The checkout is fast, appropriate, and user-friendly." phrase "fulfill the order" outcome "the order was fulfilled" / "the order was not fulfilled"
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
## Integer clash
|
|
817
|
+
|
|
818
|
+
`examples/order-quantity.ears.md` says a submitted order accepts at most 10 units, and also that it accepts exactly 12. Show both sentences, the situation, and the example. The example quantity is 0 because that is the first value that keeps R4. Ask which requirement should win.
|
|
819
|
+
|
|
820
|
+
<!-- verify stdout 1
|
|
821
|
+
build/plan-verify
|
|
822
|
+
lint
|
|
823
|
+
examples/order-quantity.ears.md
|
|
824
|
+
-->
|
|
825
|
+
```text
|
|
826
|
+
spec: examples/order-quantity.ears.md
|
|
827
|
+
conflicts: 1
|
|
828
|
+
gaps: 0
|
|
829
|
+
misuse: 0
|
|
830
|
+
wording: 0
|
|
831
|
+
redundant: 0
|
|
832
|
+
duplicate: 0
|
|
833
|
+
vacuous: 0
|
|
834
|
+
unused: 0
|
|
835
|
+
|
|
836
|
+
conflict {R4, R5}
|
|
837
|
+
R4 and R5 cannot both hold.
|
|
838
|
+
|
|
839
|
+
R4 line 7
|
|
840
|
+
When an order is submitted, the Order System shall accept at most 10 units.
|
|
841
|
+
|
|
842
|
+
R5 line 8
|
|
843
|
+
When an order is submitted, the Order System shall accept 12 units.
|
|
844
|
+
|
|
845
|
+
They clash when an order was submitted.
|
|
846
|
+
R4 requires that at most 10 units were accepted.
|
|
847
|
+
R5 requires that 12 units were accepted.
|
|
848
|
+
|
|
849
|
+
Example scenario:
|
|
850
|
+
{
|
|
851
|
+
"order_submitted": true,
|
|
852
|
+
"quantity": 0
|
|
853
|
+
}
|
|
854
|
+
This scenario keeps R4 and breaks R5.
|
|
855
|
+
|
|
856
|
+
Suggested fix: ask which requirement should win. To keep R4, remove R5. To keep R5, remove R4.
|
|
857
|
+
|
|
858
|
+
Candidate repairs follow. This report does not choose one.
|
|
859
|
+
|
|
860
|
+
repair drop R4
|
|
861
|
+
One repair is to drop R4 and keep R5 as written.
|
|
862
|
+
When an order is submitted, the Order System shall accept 12 units.
|
|
863
|
+
|
|
864
|
+
repair drop R5
|
|
865
|
+
One repair is to drop R5 and keep R4 as written.
|
|
866
|
+
When an order is submitted, the Order System shall accept at most 10 units.
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
## Enum clash
|
|
870
|
+
|
|
871
|
+
`examples/order-status.ears.md` requires the status to be shipped and cancelled. They clash in every situation. Ask which requirement should win. `placed` is declared and no requirement mentions it, so it is unused.
|
|
872
|
+
|
|
873
|
+
<!-- verify stdout 1
|
|
874
|
+
build/plan-verify
|
|
875
|
+
lint
|
|
876
|
+
examples/order-status.ears.md
|
|
877
|
+
-->
|
|
878
|
+
```text
|
|
879
|
+
spec: examples/order-status.ears.md
|
|
880
|
+
conflicts: 1
|
|
881
|
+
gaps: 0
|
|
882
|
+
misuse: 0
|
|
883
|
+
wording: 0
|
|
884
|
+
redundant: 0
|
|
885
|
+
duplicate: 0
|
|
886
|
+
vacuous: 0
|
|
887
|
+
unused: 1
|
|
888
|
+
|
|
889
|
+
conflict {R6, R7}
|
|
890
|
+
R6 and R7 cannot both hold.
|
|
891
|
+
|
|
892
|
+
R6 line 6
|
|
893
|
+
The Order System shall mark the order shipped.
|
|
894
|
+
|
|
895
|
+
R7 line 7
|
|
896
|
+
The Order System shall mark the order cancelled.
|
|
897
|
+
|
|
898
|
+
They clash in every situation.
|
|
899
|
+
R6 requires that the order was shipped.
|
|
900
|
+
R7 requires that the order was cancelled.
|
|
901
|
+
|
|
902
|
+
Example scenario:
|
|
903
|
+
{
|
|
904
|
+
"status": "shipped"
|
|
905
|
+
}
|
|
906
|
+
This scenario keeps R6 and breaks R7.
|
|
907
|
+
|
|
908
|
+
Suggested fix: ask which requirement should win. To keep R6, remove R7. To keep R7, remove R6.
|
|
909
|
+
|
|
910
|
+
Candidate repairs follow. This report does not choose one.
|
|
911
|
+
|
|
912
|
+
repair drop R6
|
|
913
|
+
One repair is to drop R6 and keep R7 as written.
|
|
914
|
+
The Order System shall mark the order cancelled.
|
|
915
|
+
|
|
916
|
+
repair drop R7
|
|
917
|
+
One repair is to drop R7 and keep R6 as written.
|
|
918
|
+
The Order System shall mark the order shipped.
|
|
919
|
+
|
|
920
|
+
unused Status.placed
|
|
921
|
+
Status.placed is never mentioned.
|
|
922
|
+
Suggested fix: use placed in a requirement, or remove it from Status.
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
## Policy
|
|
926
|
+
|
|
927
|
+
A property file is requirement lines only. It cannot declare symbols. It uses the phrases from the spec. Ids are `P` or `R`, then digits, such as `P1`.
|
|
928
|
+
|
|
929
|
+
```ears
|
|
930
|
+
P1 The Order System shall fulfill the order.
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
No property. One scenario the order requirements allow, and one they forbid:
|
|
934
|
+
|
|
935
|
+
<!-- verify stdout 0
|
|
936
|
+
build/plan-verify
|
|
937
|
+
policy
|
|
938
|
+
examples/order.ears.md
|
|
939
|
+
-->
|
|
940
|
+
```text
|
|
941
|
+
spec: examples/order.ears.md
|
|
942
|
+
accepted:
|
|
943
|
+
{
|
|
944
|
+
"order_submitted": false,
|
|
945
|
+
"inventory_available": false,
|
|
946
|
+
"order_fulfilled": false
|
|
947
|
+
}
|
|
948
|
+
This scenario satisfies every requirement.
|
|
949
|
+
rejected:
|
|
950
|
+
{
|
|
951
|
+
"order_submitted": true,
|
|
952
|
+
"inventory_available": true,
|
|
953
|
+
"order_fulfilled": false
|
|
954
|
+
}
|
|
955
|
+
This scenario violates a requirement.
|
|
956
|
+
|
|
957
|
+
R1 line 7
|
|
958
|
+
WHEN an order is submitted AND inventory is available, THE Order System SHALL fulfill the order.
|
|
959
|
+
An order was submitted and inventory was available, but the order was not fulfilled.
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
The allowed scenario leaves the trigger false, so R1 does not apply. Say that. It is not the behavior they asked for.
|
|
963
|
+
|
|
964
|
+
`examples/order-covered.ears.md` forbids fulfilling an order that was not submitted:
|
|
965
|
+
|
|
966
|
+
<!-- verify stdout 0
|
|
967
|
+
build/plan-verify
|
|
968
|
+
policy
|
|
969
|
+
examples/order-covered.ears.md
|
|
970
|
+
-->
|
|
971
|
+
```text
|
|
972
|
+
spec: examples/order-covered.ears.md
|
|
973
|
+
accepted:
|
|
974
|
+
{
|
|
975
|
+
"order_submitted": false,
|
|
976
|
+
"inventory_available": false,
|
|
977
|
+
"order_fulfilled": false
|
|
978
|
+
}
|
|
979
|
+
This scenario satisfies every requirement.
|
|
980
|
+
rejected:
|
|
981
|
+
{
|
|
982
|
+
"order_submitted": false,
|
|
983
|
+
"inventory_available": false,
|
|
984
|
+
"order_fulfilled": true
|
|
985
|
+
}
|
|
986
|
+
This scenario violates a requirement.
|
|
987
|
+
|
|
988
|
+
R3 line 9
|
|
989
|
+
When an order is not submitted, the Order System shall not fulfill the order.
|
|
990
|
+
An order was not submitted, but the order was fulfilled.
|
|
991
|
+
```
|
|
992
|
+
|
|
993
|
+
`examples/order-always.ears.md` says the order is always fulfilled. The requirements allow a scenario where it is not:
|
|
994
|
+
|
|
995
|
+
<!-- verify stdout 1
|
|
996
|
+
build/plan-verify
|
|
997
|
+
policy
|
|
998
|
+
examples/order.ears.md
|
|
999
|
+
--property
|
|
1000
|
+
examples/order-always.ears.md
|
|
1001
|
+
-->
|
|
1002
|
+
```text
|
|
1003
|
+
spec: examples/order.ears.md
|
|
1004
|
+
property: examples/order-always.ears.md
|
|
1005
|
+
counterexample:
|
|
1006
|
+
{
|
|
1007
|
+
"order_submitted": false,
|
|
1008
|
+
"inventory_available": false,
|
|
1009
|
+
"order_fulfilled": false
|
|
1010
|
+
}
|
|
1011
|
+
This scenario satisfies every requirement and violates the property.
|
|
1012
|
+
|
|
1013
|
+
P1 line 3
|
|
1014
|
+
The Order System shall fulfill the order.
|
|
1015
|
+
The order was not fulfilled.
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
Tell the PM the requirements allow that scenario, so the property is not backed by them. Ask whether the property should become a requirement. Do not add it yourself.
|
|
1019
|
+
|
|
1020
|
+
`examples/order-same.ears.md` repeats R1, so no counterexample exists:
|
|
1021
|
+
|
|
1022
|
+
<!-- verify stdout 0
|
|
1023
|
+
build/plan-verify
|
|
1024
|
+
policy
|
|
1025
|
+
examples/order.ears.md
|
|
1026
|
+
--property
|
|
1027
|
+
examples/order-same.ears.md
|
|
1028
|
+
-->
|
|
1029
|
+
```text
|
|
1030
|
+
spec: examples/order.ears.md
|
|
1031
|
+
property: examples/order-same.ears.md
|
|
1032
|
+
No scenario satisfies every requirement and violates the property.
|
|
1033
|
+
```
|
|
1034
|
+
|
|
1035
|
+
If lint already found that the requirements cannot all hold, policy will not invent a scenario that satisfies them:
|
|
1036
|
+
|
|
1037
|
+
<!-- verify stdout 0
|
|
1038
|
+
build/plan-verify
|
|
1039
|
+
policy
|
|
1040
|
+
examples/order-status.ears.md
|
|
1041
|
+
--property
|
|
1042
|
+
examples/status-placed.ears.md
|
|
1043
|
+
-->
|
|
1044
|
+
```text
|
|
1045
|
+
spec: examples/order-status.ears.md
|
|
1046
|
+
property: examples/status-placed.ears.md
|
|
1047
|
+
No scenario satisfies every requirement, so none violates the property.
|
|
1048
|
+
```
|
|
1049
|
+
|
|
1050
|
+
Run lint for the clash, and ask which requirement should win, before you spend time on the property.
|
|
1051
|
+
|
|
1052
|
+
## Three requirements, no pair
|
|
1053
|
+
|
|
1054
|
+
`examples/order-chain.ears.md` cannot all hold, and every pair can. Tell the PM the three sentences, that removing any one of them leaves the rest consistent, and the example that keeps the first two. Ask which requirement should change. Do not pick one.
|
|
1055
|
+
|
|
1056
|
+
<!-- verify stdout 1
|
|
1057
|
+
build/plan-verify
|
|
1058
|
+
lint
|
|
1059
|
+
examples/order-chain.ears.md
|
|
1060
|
+
-->
|
|
1061
|
+
```text
|
|
1062
|
+
spec: examples/order-chain.ears.md
|
|
1063
|
+
conflicts: 1
|
|
1064
|
+
gaps: 0
|
|
1065
|
+
misuse: 0
|
|
1066
|
+
wording: 0
|
|
1067
|
+
redundant: 0
|
|
1068
|
+
duplicate: 0
|
|
1069
|
+
vacuous: 0
|
|
1070
|
+
unused: 0
|
|
1071
|
+
|
|
1072
|
+
conflict {R1, R2, R3}
|
|
1073
|
+
R1, R2, and R3 cannot all hold.
|
|
1074
|
+
|
|
1075
|
+
R1 line 7
|
|
1076
|
+
When an order is submitted, the Order System shall accept the order.
|
|
1077
|
+
|
|
1078
|
+
R2 line 8
|
|
1079
|
+
When the order is accepted, the Order System shall ship the order.
|
|
1080
|
+
|
|
1081
|
+
R3 line 9
|
|
1082
|
+
When an order is submitted, the Order System shall not ship the order.
|
|
1083
|
+
|
|
1084
|
+
They clash when an order was submitted.
|
|
1085
|
+
R1 requires that the order was accepted.
|
|
1086
|
+
R3 requires that the order was not shipped.
|
|
1087
|
+
|
|
1088
|
+
Example scenario:
|
|
1089
|
+
{
|
|
1090
|
+
"order_submitted": true,
|
|
1091
|
+
"order_accepted": true,
|
|
1092
|
+
"order_shipped": true
|
|
1093
|
+
}
|
|
1094
|
+
This scenario keeps R1 and R2 and breaks R3.
|
|
1095
|
+
|
|
1096
|
+
Suggested fix: ask which requirement should win. Removing any one of R1, R2, and R3 leaves the rest consistent.
|
|
1097
|
+
|
|
1098
|
+
Candidate repairs follow. This report does not choose one.
|
|
1099
|
+
|
|
1100
|
+
repair drop R1
|
|
1101
|
+
One repair is to drop R1 and keep R2 and R3 as written.
|
|
1102
|
+
When the order is accepted, the Order System shall ship the order.
|
|
1103
|
+
When an order is submitted, the Order System shall not ship the order.
|
|
1104
|
+
|
|
1105
|
+
repair drop R2
|
|
1106
|
+
One repair is to drop R2 and keep R1 and R3 as written.
|
|
1107
|
+
When an order is submitted, the Order System shall accept the order.
|
|
1108
|
+
When an order is submitted, the Order System shall not ship the order.
|
|
1109
|
+
|
|
1110
|
+
repair drop R3
|
|
1111
|
+
One repair is to drop R3 and keep R1 and R2 as written.
|
|
1112
|
+
When an order is submitted, the Order System shall accept the order.
|
|
1113
|
+
When the order is accepted, the Order System shall ship the order.
|
|
1114
|
+
|
|
1115
|
+
repair strengthen R2
|
|
1116
|
+
One repair is to add "and an order is not submitted" to the condition of R2. R1 and R3 stay as written.
|
|
1117
|
+
When the order is accepted and an order is not submitted, the Order System shall ship the order.
|
|
1118
|
+
```
|
|
1119
|
+
|
|
1120
|
+
## A requirement that adds nothing
|
|
1121
|
+
|
|
1122
|
+
When lint says a requirement adds nothing, show the sentence and the suggested fix. Ask whether they meant something the others do not already require. Do not delete it.
|
|
1123
|
+
|
|
1124
|
+
<!-- verify stdout 1
|
|
1125
|
+
build/plan-verify
|
|
1126
|
+
lint
|
|
1127
|
+
examples/order-redundant.ears.md
|
|
1128
|
+
-->
|
|
1129
|
+
```text
|
|
1130
|
+
spec: examples/order-redundant.ears.md
|
|
1131
|
+
conflicts: 0
|
|
1132
|
+
gaps: 0
|
|
1133
|
+
misuse: 0
|
|
1134
|
+
wording: 0
|
|
1135
|
+
redundant: 1
|
|
1136
|
+
duplicate: 0
|
|
1137
|
+
vacuous: 0
|
|
1138
|
+
unused: 0
|
|
1139
|
+
|
|
1140
|
+
redundant R2 line 8
|
|
1141
|
+
R2 adds nothing. Whenever the other requirements hold, R2 holds too.
|
|
1142
|
+
When an order is submitted and inventory is available, the Order System shall fulfill the order.
|
|
1143
|
+
Suggested fix: remove R2, or ask whether it was meant to say something the others do not already require. Do not delete it until the PM agrees.
|
|
1144
|
+
```
|
|
1145
|
+
|
|
1146
|
+
A conflict does not hide a repeat that sits outside it. Show that requirement too. An exact duplicate is a separate finding: same condition, same response. Ask whether the later line was meant to differ. A bare `not` at the start of a trigger is wording kind `negation`. Ask for a positive condition or a positive event. `as needed` is too vague to test unless those words are the declared phrase.
|
|
1147
|
+
|
|
1148
|
+
<!-- verify stdout 1
|
|
1149
|
+
build/plan-verify
|
|
1150
|
+
lint
|
|
1151
|
+
examples/order-repeat.ears.md
|
|
1152
|
+
-->
|
|
1153
|
+
```text
|
|
1154
|
+
spec: examples/order-repeat.ears.md
|
|
1155
|
+
conflicts: 1
|
|
1156
|
+
gaps: 0
|
|
1157
|
+
misuse: 0
|
|
1158
|
+
wording: 0
|
|
1159
|
+
redundant: 1
|
|
1160
|
+
duplicate: 0
|
|
1161
|
+
vacuous: 0
|
|
1162
|
+
unused: 0
|
|
1163
|
+
|
|
1164
|
+
conflict {R1, R2}
|
|
1165
|
+
R1 and R2 cannot both hold.
|
|
1166
|
+
|
|
1167
|
+
R1 line 8
|
|
1168
|
+
When an order is submitted, the Order System shall fulfill the order.
|
|
1169
|
+
|
|
1170
|
+
R2 line 9
|
|
1171
|
+
When an order is submitted, the Order System shall not fulfill the order.
|
|
1172
|
+
|
|
1173
|
+
They clash when an order was submitted and inventory was not available.
|
|
1174
|
+
R1 requires that the order was fulfilled.
|
|
1175
|
+
R2 requires that the order was not fulfilled.
|
|
1176
|
+
|
|
1177
|
+
Example scenario:
|
|
1178
|
+
{
|
|
1179
|
+
"order_submitted": true,
|
|
1180
|
+
"inventory_available": false,
|
|
1181
|
+
"order_fulfilled": true,
|
|
1182
|
+
"order_closed": false
|
|
1183
|
+
}
|
|
1184
|
+
This scenario keeps R1 and breaks R2.
|
|
1185
|
+
|
|
1186
|
+
Suggested fix: ask which requirement should win. To keep R1, remove R2. To keep R2, remove R1.
|
|
1187
|
+
|
|
1188
|
+
Candidate repairs follow. This report does not choose one.
|
|
1189
|
+
|
|
1190
|
+
repair drop R1
|
|
1191
|
+
One repair is to drop R1 and keep R2 as written.
|
|
1192
|
+
When an order is submitted, the Order System shall not fulfill the order.
|
|
1193
|
+
|
|
1194
|
+
repair drop R2
|
|
1195
|
+
One repair is to drop R2 and keep R1 as written.
|
|
1196
|
+
When an order is submitted, the Order System shall fulfill the order.
|
|
1197
|
+
|
|
1198
|
+
redundant R5 line 12
|
|
1199
|
+
R5 adds nothing. Whenever the requirements outside a conflict hold, R5 holds too.
|
|
1200
|
+
When an order is submitted and inventory is available, the Order System shall close the order.
|
|
1201
|
+
Suggested fix: remove R5, or ask whether it was meant to say something the others do not already require. Do not delete it until the PM agrees.
|
|
1202
|
+
```
|
|
1203
|
+
|
|
1204
|
+
<!-- verify stdout 1
|
|
1205
|
+
build/plan-verify
|
|
1206
|
+
lint
|
|
1207
|
+
examples/order-duplicate.ears.md
|
|
1208
|
+
-->
|
|
1209
|
+
```text
|
|
1210
|
+
spec: examples/order-duplicate.ears.md
|
|
1211
|
+
conflicts: 0
|
|
1212
|
+
gaps: 0
|
|
1213
|
+
misuse: 0
|
|
1214
|
+
wording: 0
|
|
1215
|
+
redundant: 0
|
|
1216
|
+
duplicate: 1
|
|
1217
|
+
vacuous: 0
|
|
1218
|
+
unused: 0
|
|
1219
|
+
|
|
1220
|
+
duplicate R2 line 7
|
|
1221
|
+
R2 duplicates R1. The condition and the response are the same.
|
|
1222
|
+
When an order is submitted, the Order System shall fulfill the order.
|
|
1223
|
+
Suggested fix: remove R2, or ask whether it was meant to differ from R1. Do not delete it until the PM agrees.
|
|
1224
|
+
```
|
|
1225
|
+
|
|
1226
|
+
<!-- verify stdout 1
|
|
1227
|
+
build/plan-verify
|
|
1228
|
+
lint
|
|
1229
|
+
examples/order-negation.ears.md
|
|
1230
|
+
-->
|
|
1231
|
+
```text
|
|
1232
|
+
spec: examples/order-negation.ears.md
|
|
1233
|
+
conflicts: 0
|
|
1234
|
+
gaps: 0
|
|
1235
|
+
misuse: 0
|
|
1236
|
+
wording: 1
|
|
1237
|
+
redundant: 0
|
|
1238
|
+
duplicate: 0
|
|
1239
|
+
vacuous: 0
|
|
1240
|
+
unused: 0
|
|
1241
|
+
|
|
1242
|
+
wording line 6
|
|
1243
|
+
The trigger is a bare negation. Write a positive condition, such as "When a non-member asks", or declare a positive event.
|
|
1244
|
+
When not a member asks to take a copy home, the Desk shall not allow the loan.
|
|
1245
|
+
```
|
|
1246
|
+
|
|
1247
|
+
<!-- verify stdout 1
|
|
1248
|
+
build/plan-verify
|
|
1249
|
+
lint
|
|
1250
|
+
examples/order-unmeasured.ears.md
|
|
1251
|
+
-->
|
|
1252
|
+
```text
|
|
1253
|
+
spec: examples/order-unmeasured.ears.md
|
|
1254
|
+
conflicts: 0
|
|
1255
|
+
gaps: 0
|
|
1256
|
+
misuse: 0
|
|
1257
|
+
wording: 1
|
|
1258
|
+
redundant: 0
|
|
1259
|
+
duplicate: 0
|
|
1260
|
+
vacuous: 0
|
|
1261
|
+
unused: 0
|
|
1262
|
+
|
|
1263
|
+
wording line 4
|
|
1264
|
+
"as needed" is too vague to test. Replace it with something a scenario can check.
|
|
1265
|
+
output order_fulfilled: Bool "Handle it as needed." phrase "fulfill the order" outcome "the order was fulfilled" / "the order was not fulfilled"
|
|
1266
|
+
```
|
|
1267
|
+
|
|
1268
|
+
## A condition that can never be true
|
|
1269
|
+
|
|
1270
|
+
Show the requirement and the suggested fix. A condition that contradicts itself, or that asks for a value outside the declared range, never triggers. Ask the PM to rewrite the condition. Do not invent a response. Do not also call that requirement redundant or a duplicate.
|
|
1271
|
+
|
|
1272
|
+
<!-- verify stdout 1
|
|
1273
|
+
build/plan-verify
|
|
1274
|
+
lint
|
|
1275
|
+
examples/order-vacuous.ears.md
|
|
1276
|
+
-->
|
|
1277
|
+
```text
|
|
1278
|
+
spec: examples/order-vacuous.ears.md
|
|
1279
|
+
conflicts: 0
|
|
1280
|
+
gaps: 0
|
|
1281
|
+
misuse: 0
|
|
1282
|
+
wording: 0
|
|
1283
|
+
redundant: 0
|
|
1284
|
+
duplicate: 0
|
|
1285
|
+
vacuous: 2
|
|
1286
|
+
unused: 0
|
|
1287
|
+
|
|
1288
|
+
vacuous R1 line 7
|
|
1289
|
+
R1 can never trigger. Its condition is impossible for every allowed value.
|
|
1290
|
+
When the cart is open and the cart is not open, the Checkout shall accept the order.
|
|
1291
|
+
Suggested fix: rewrite the condition in R1 so a real situation can make it true, or remove the requirement. Do not invent a response.
|
|
1292
|
+
|
|
1293
|
+
vacuous R2 line 8
|
|
1294
|
+
R2 can never trigger. Its condition is impossible for every allowed value.
|
|
1295
|
+
When the quantity is at least 9, the Checkout shall accept the order.
|
|
1296
|
+
Suggested fix: rewrite the condition in R2 so a real situation can make it true, or remove the requirement. Do not invent a response.
|
|
1297
|
+
```
|
|
1298
|
+
|
|
1299
|
+
## Schema the requirements never use
|
|
1300
|
+
|
|
1301
|
+
A state or input that no requirement mentions, an enum variant that no requirement mentions, and an output that is only read are unused. An output that nobody mentions at all is a gap. An event that nobody handles is a gap. Do not choose a value for an output nothing sets.
|
|
1302
|
+
|
|
1303
|
+
<!-- verify stdout 1
|
|
1304
|
+
build/plan-verify
|
|
1305
|
+
lint
|
|
1306
|
+
examples/order-unused.ears.md
|
|
1307
|
+
-->
|
|
1308
|
+
```text
|
|
1309
|
+
spec: examples/order-unused.ears.md
|
|
1310
|
+
conflicts: 0
|
|
1311
|
+
gaps: 0
|
|
1312
|
+
misuse: 0
|
|
1313
|
+
wording: 0
|
|
1314
|
+
redundant: 0
|
|
1315
|
+
duplicate: 0
|
|
1316
|
+
vacuous: 0
|
|
1317
|
+
unused: 4
|
|
1318
|
+
|
|
1319
|
+
unused loyalty
|
|
1320
|
+
loyalty ("the customer is loyal") is a state and no requirement mentions it.
|
|
1321
|
+
Suggested fix: use loyalty in a requirement, or remove it from the schema.
|
|
1322
|
+
|
|
1323
|
+
unused region
|
|
1324
|
+
region ("the region") is an input and no requirement mentions it.
|
|
1325
|
+
Suggested fix: use region in a requirement, or remove it from the schema.
|
|
1326
|
+
|
|
1327
|
+
unused receipt_sent
|
|
1328
|
+
Nothing sets receipt_sent ("a receipt is sent"). Requirements mention it, but no response says what it shall be.
|
|
1329
|
+
Suggested fix: add a shall requirement that sets receipt_sent, or declare it as an input if the environment provides it. Do not choose the value.
|
|
1330
|
+
|
|
1331
|
+
unused Lane.refund
|
|
1332
|
+
Lane.refund is never mentioned.
|
|
1333
|
+
Suggested fix: use refund in a requirement, or remove it from Lane.
|
|
1334
|
+
```
|
|
1335
|
+
|
|
1336
|
+
<!-- verify stdout 1
|
|
1337
|
+
build/plan-verify
|
|
1338
|
+
lint
|
|
1339
|
+
--json
|
|
1340
|
+
examples/order-unused.ears.md
|
|
1341
|
+
-->
|
|
1342
|
+
```text
|
|
1343
|
+
{
|
|
1344
|
+
"ok": false,
|
|
1345
|
+
"spec": "examples/order-unused.ears.md",
|
|
1346
|
+
"conflicts": [],
|
|
1347
|
+
"gaps": [],
|
|
1348
|
+
"misuse": [],
|
|
1349
|
+
"wording": [],
|
|
1350
|
+
"redundant": [],
|
|
1351
|
+
"duplicate": [],
|
|
1352
|
+
"vacuous": [],
|
|
1353
|
+
"unused": [
|
|
1354
|
+
{
|
|
1355
|
+
"name": "loyalty",
|
|
1356
|
+
"kind": "state",
|
|
1357
|
+
"text": "loyalty (\"the customer is loyal\") is a state and no requirement mentions it.",
|
|
1358
|
+
"fix": "Suggested fix: use loyalty in a requirement, or remove it from the schema."
|
|
1359
|
+
},
|
|
1360
|
+
{
|
|
1361
|
+
"name": "region",
|
|
1362
|
+
"kind": "input",
|
|
1363
|
+
"text": "region (\"the region\") is an input and no requirement mentions it.",
|
|
1364
|
+
"fix": "Suggested fix: use region in a requirement, or remove it from the schema."
|
|
1365
|
+
},
|
|
1366
|
+
{
|
|
1367
|
+
"name": "receipt_sent",
|
|
1368
|
+
"kind": "output",
|
|
1369
|
+
"text": "Nothing sets receipt_sent (\"a receipt is sent\"). Requirements mention it, but no response says what it shall be.",
|
|
1370
|
+
"fix": "Suggested fix: add a shall requirement that sets receipt_sent, or declare it as an input if the environment provides it. Do not choose the value."
|
|
1371
|
+
},
|
|
1372
|
+
{
|
|
1373
|
+
"name": "Lane.refund",
|
|
1374
|
+
"kind": "variant",
|
|
1375
|
+
"text": "Lane.refund is never mentioned.",
|
|
1376
|
+
"fix": "Suggested fix: use refund in a requirement, or remove it from Lane."
|
|
1377
|
+
}
|
|
1378
|
+
]
|
|
1379
|
+
}
|
|
1380
|
+
```
|
|
1381
|
+
|
|
1382
|
+
## Scenarios
|
|
1383
|
+
|
|
1384
|
+
`scenarios` prints accepted and rejected examples for each requirement, with the trigger true and the trigger false. Use them to check that you heard the PM correctly. Do not treat a generated scenario as the behavior they agreed to. The same assignment is printed once. A requirement with no trigger says so.
|
|
1385
|
+
|
|
1386
|
+
<!-- verify stdout 0
|
|
1387
|
+
build/plan-verify
|
|
1388
|
+
scenarios
|
|
1389
|
+
examples/order.ears.md
|
|
1390
|
+
-->
|
|
1391
|
+
```text
|
|
1392
|
+
spec: examples/order.ears.md
|
|
1393
|
+
scenarios: 3
|
|
1394
|
+
|
|
1395
|
+
R1 accepted, trigger true
|
|
1396
|
+
{
|
|
1397
|
+
"order_submitted": true,
|
|
1398
|
+
"inventory_available": true,
|
|
1399
|
+
"order_fulfilled": true
|
|
1400
|
+
}
|
|
1401
|
+
An order was submitted and inventory was available, and the order was fulfilled.
|
|
1402
|
+
|
|
1403
|
+
R1 accepted, trigger false
|
|
1404
|
+
{
|
|
1405
|
+
"order_submitted": false,
|
|
1406
|
+
"inventory_available": false,
|
|
1407
|
+
"order_fulfilled": false
|
|
1408
|
+
}
|
|
1409
|
+
An order was not submitted and inventory was not available, and the order was not fulfilled.
|
|
1410
|
+
|
|
1411
|
+
R1 rejected, trigger true
|
|
1412
|
+
{
|
|
1413
|
+
"order_submitted": true,
|
|
1414
|
+
"inventory_available": true,
|
|
1415
|
+
"order_fulfilled": false
|
|
1416
|
+
}
|
|
1417
|
+
An order was submitted and inventory was available, but the order was not fulfilled.
|
|
1418
|
+
```
|
|
1419
|
+
|
|
1420
|
+
`--json` rows are `id`, `line`, `kind`, `trigger`, `found`, `sameAs`, `scenario`, and `text`.
|
|
1421
|
+
|
|
1422
|
+
<!-- verify stdout 0
|
|
1423
|
+
build/plan-verify
|
|
1424
|
+
scenarios
|
|
1425
|
+
--json
|
|
1426
|
+
examples/order.ears.md
|
|
1427
|
+
-->
|
|
1428
|
+
```text
|
|
1429
|
+
{
|
|
1430
|
+
"ok": true,
|
|
1431
|
+
"spec": "examples/order.ears.md",
|
|
1432
|
+
"scenarios": [
|
|
1433
|
+
{
|
|
1434
|
+
"id": "R1",
|
|
1435
|
+
"line": 7,
|
|
1436
|
+
"kind": "accepted",
|
|
1437
|
+
"trigger": "true",
|
|
1438
|
+
"found": true,
|
|
1439
|
+
"sameAs": "",
|
|
1440
|
+
"scenario": [
|
|
1441
|
+
{
|
|
1442
|
+
"name": "order_submitted",
|
|
1443
|
+
"value": "true"
|
|
1444
|
+
},
|
|
1445
|
+
{
|
|
1446
|
+
"name": "inventory_available",
|
|
1447
|
+
"value": "true"
|
|
1448
|
+
},
|
|
1449
|
+
{
|
|
1450
|
+
"name": "order_fulfilled",
|
|
1451
|
+
"value": "true"
|
|
1452
|
+
}
|
|
1453
|
+
],
|
|
1454
|
+
"text": "An order was submitted and inventory was available, and the order was fulfilled."
|
|
1455
|
+
},
|
|
1456
|
+
{
|
|
1457
|
+
"id": "R1",
|
|
1458
|
+
"line": 7,
|
|
1459
|
+
"kind": "accepted",
|
|
1460
|
+
"trigger": "false",
|
|
1461
|
+
"found": true,
|
|
1462
|
+
"sameAs": "",
|
|
1463
|
+
"scenario": [
|
|
1464
|
+
{
|
|
1465
|
+
"name": "order_submitted",
|
|
1466
|
+
"value": "false"
|
|
1467
|
+
},
|
|
1468
|
+
{
|
|
1469
|
+
"name": "inventory_available",
|
|
1470
|
+
"value": "false"
|
|
1471
|
+
},
|
|
1472
|
+
{
|
|
1473
|
+
"name": "order_fulfilled",
|
|
1474
|
+
"value": "false"
|
|
1475
|
+
}
|
|
1476
|
+
],
|
|
1477
|
+
"text": "An order was not submitted and inventory was not available, and the order was not fulfilled."
|
|
1478
|
+
},
|
|
1479
|
+
{
|
|
1480
|
+
"id": "R1",
|
|
1481
|
+
"line": 7,
|
|
1482
|
+
"kind": "rejected",
|
|
1483
|
+
"trigger": "true",
|
|
1484
|
+
"found": true,
|
|
1485
|
+
"sameAs": "",
|
|
1486
|
+
"scenario": [
|
|
1487
|
+
{
|
|
1488
|
+
"name": "order_submitted",
|
|
1489
|
+
"value": "true"
|
|
1490
|
+
},
|
|
1491
|
+
{
|
|
1492
|
+
"name": "inventory_available",
|
|
1493
|
+
"value": "true"
|
|
1494
|
+
},
|
|
1495
|
+
{
|
|
1496
|
+
"name": "order_fulfilled",
|
|
1497
|
+
"value": "false"
|
|
1498
|
+
}
|
|
1499
|
+
],
|
|
1500
|
+
"text": "An order was submitted and inventory was available, but the order was not fulfilled."
|
|
1501
|
+
}
|
|
1502
|
+
]
|
|
1503
|
+
}
|
|
1504
|
+
```
|
|
1505
|
+
|
|
1506
|
+
## What you do not do
|
|
1507
|
+
|
|
1508
|
+
- You do not pick the winning requirement.
|
|
1509
|
+
- You do not delete a redundant requirement until the PM agrees.
|
|
1510
|
+
- You do not invent a response for a condition that can never be true.
|
|
1511
|
+
- You do not choose a value for an output nothing sets.
|
|
1512
|
+
- You do not drop a conflict and move on to a design, an API, or a schema for the build.
|
|
1513
|
+
- You do not treat the example scenario as the behavior they agreed to.
|
|
1514
|
+
- You do not introduce a second name for an idea that already has one.
|
|
1515
|
+
- You do not report a check violation without its requirement ID, line, and sentence.
|