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,112 @@
|
|
|
1
|
+
# Logical problems
|
|
2
|
+
|
|
3
|
+
Spec: examples/plan-to-problems/generated.ears.md
|
|
4
|
+
|
|
5
|
+
These are the logical problems in the requirements. For a contradiction, which requirement should win? This report does not choose.
|
|
6
|
+
|
|
7
|
+
## 1. R1 and R2 cannot both hold.
|
|
8
|
+
|
|
9
|
+
They clash when a member asks to take a copy home, not a member asks to renew a copy, not a member brings a copy back, the copy was in the building, the copy was out on loan, a fine was not owed, the loan was not overdue, and the loan length was 1.
|
|
10
|
+
|
|
11
|
+
R1 line 16
|
|
12
|
+
"While the copy is in the building, when a member asks to take a copy home, the Fall Desk shall allow the loan."
|
|
13
|
+
|
|
14
|
+
R2 line 17
|
|
15
|
+
"While the copy is out on loan, when a member asks to take a copy home, the Fall Desk shall not allow the loan."
|
|
16
|
+
|
|
17
|
+
R1 requires that the loan was allowed.
|
|
18
|
+
R2 requires that the loan was not allowed.
|
|
19
|
+
|
|
20
|
+
Which requirement should win? To keep R1, remove R2. To keep R2, remove R1. This report does not choose.
|
|
21
|
+
|
|
22
|
+
Candidate repairs follow. This report does not choose one.
|
|
23
|
+
|
|
24
|
+
One repair is to drop R1 and keep R2 as written.
|
|
25
|
+
"While the copy is out on loan, when a member asks to take a copy home, the Fall Desk shall not allow the loan."
|
|
26
|
+
|
|
27
|
+
One repair is to drop R2 and keep R1 as written.
|
|
28
|
+
"While the copy is in the building, when a member asks to take a copy home, the Fall Desk shall allow the loan."
|
|
29
|
+
|
|
30
|
+
One repair is to add "and the copy is not out on loan" to the condition of R1. R2 stays as written.
|
|
31
|
+
"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 Fall Desk shall allow the loan."
|
|
32
|
+
|
|
33
|
+
One repair is to add "and the copy is not in the building" to the condition of R2. R1 stays as written.
|
|
34
|
+
"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 Fall Desk shall not allow the loan."
|
|
35
|
+
|
|
36
|
+
## 2. R2 and R3 cannot both hold.
|
|
37
|
+
|
|
38
|
+
They clash when a member asks to take a copy home, not a member asks to renew a copy, not a member brings a copy back, the copy was not in the building, the copy was out on loan, a fine was not owed, the loan was not overdue, and the loan length was 1.
|
|
39
|
+
|
|
40
|
+
R2 line 17
|
|
41
|
+
"While the copy is out on loan, when a member asks to take a copy home, the Fall Desk shall not allow the loan."
|
|
42
|
+
|
|
43
|
+
R3 line 18
|
|
44
|
+
"While the copy is out on loan, when a member asks to take a copy home, the Fall Desk shall allow the loan."
|
|
45
|
+
|
|
46
|
+
R2 requires that the loan was not allowed.
|
|
47
|
+
R3 requires that the loan was allowed.
|
|
48
|
+
|
|
49
|
+
Which requirement should win? To keep R2, remove R3. To keep R3, remove R2. This report does not choose.
|
|
50
|
+
|
|
51
|
+
Candidate repairs follow. This report does not choose one.
|
|
52
|
+
|
|
53
|
+
One repair is to drop R2 and keep R3 as written.
|
|
54
|
+
"While the copy is out on loan, when a member asks to take a copy home, the Fall Desk shall allow the loan."
|
|
55
|
+
|
|
56
|
+
One repair is to drop R3 and keep R2 as written.
|
|
57
|
+
"While the copy is out on loan, when a member asks to take a copy home, the Fall Desk shall not allow the loan."
|
|
58
|
+
|
|
59
|
+
## 3. R5 and R6 cannot both hold.
|
|
60
|
+
|
|
61
|
+
They clash when not a member asks to take a copy home, a member asks to renew a copy, not a member brings a copy back, the copy was not in the building, the copy was not out on loan, a fine was owed, the loan was not overdue, and the loan length was 1.
|
|
62
|
+
|
|
63
|
+
R5 line 20
|
|
64
|
+
"While a fine is owed, when a member asks to renew a copy, the Fall Desk shall not allow the renewal."
|
|
65
|
+
|
|
66
|
+
R6 line 21
|
|
67
|
+
"While a fine is owed, when a member asks to renew a copy, the Fall Desk shall allow the renewal."
|
|
68
|
+
|
|
69
|
+
R5 requires that the renewal was not allowed.
|
|
70
|
+
R6 requires that the renewal was allowed.
|
|
71
|
+
|
|
72
|
+
Which requirement should win? To keep R5, remove R6. To keep R6, remove R5. This report does not choose.
|
|
73
|
+
|
|
74
|
+
Candidate repairs follow. This report does not choose one.
|
|
75
|
+
|
|
76
|
+
One repair is to drop R5 and keep R6 as written.
|
|
77
|
+
"While a fine is owed, when a member asks to renew a copy, the Fall Desk shall allow the renewal."
|
|
78
|
+
|
|
79
|
+
One repair is to drop R6 and keep R5 as written.
|
|
80
|
+
"While a fine is owed, when a member asks to renew a copy, the Fall Desk shall not allow the renewal."
|
|
81
|
+
|
|
82
|
+
## 4. Nothing covers allow the loan (loan_allowed)
|
|
83
|
+
|
|
84
|
+
Nothing says whether the loan was allowed when a member asks to take a copy home, a member asks to renew a copy, a member brings a copy back, the copy was not in the building, the copy was not out on loan, a fine was owed, the loan was overdue, and the loan length was 21. Suggested fix: add a requirement for this situation, and ask what the system shall do. Do not choose the response.
|
|
85
|
+
|
|
86
|
+
## 5. Nothing covers allow the renewal (renewal_allowed)
|
|
87
|
+
|
|
88
|
+
Nothing says whether the renewal was allowed when a member asks to take a copy home, not a member asks to renew a copy, a member brings a copy back, the copy was in the building, the copy was out on loan, a fine was owed, the loan was overdue, and the loan length was 21. Suggested fix: add a requirement for this situation, and ask what the system shall do. Do not choose the response.
|
|
89
|
+
|
|
90
|
+
## 6. Nothing covers send a reminder (reminder_sent)
|
|
91
|
+
|
|
92
|
+
Nothing says whether a reminder was sent when a member asks to take a copy home, a member asks to renew a copy, a member brings a copy back, the copy was in the building, the copy was out on loan, a fine was owed, the loan was not overdue, and the loan length was 21. Suggested fix: add a requirement for this situation, and ask what the system shall do. Do not choose the response.
|
|
93
|
+
|
|
94
|
+
## 7. R9 can never trigger
|
|
95
|
+
|
|
96
|
+
R9 line 24
|
|
97
|
+
"When a member asks to take a copy home and the loan length is at least 40, the Fall Desk shall allow the loan."
|
|
98
|
+
|
|
99
|
+
R9 can never trigger. Its condition is impossible for every allowed value.
|
|
100
|
+
Suggested fix: rewrite the condition in R9 so a real situation can make it true, or remove the requirement. Do not invent a response.
|
|
101
|
+
|
|
102
|
+
## 8. R11 adds nothing
|
|
103
|
+
|
|
104
|
+
R11 line 26
|
|
105
|
+
"When a member brings a copy back and the loan is overdue, the Fall Desk shall close the loan."
|
|
106
|
+
|
|
107
|
+
R11 adds nothing. It only repeats R10 in a stricter situation. Whenever R10 holds, R11 holds too.
|
|
108
|
+
|
|
109
|
+
R10 line 25
|
|
110
|
+
"When a member brings a copy back, the Fall Desk shall close the loan."
|
|
111
|
+
|
|
112
|
+
Ask whether R11 was meant to say something R10 does not already require. Do not delete it until the product manager agrees.
|
package/package.json
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plan-verify",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Catch contradictions in EARS requirements before anyone builds them. The same TypeScript runs on Node and compiles to a native binary with ScriptC.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=24"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"plan-verify": "./src/main.ts"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "scriptc build src/main.ts -o build/plan-verify",
|
|
15
|
+
"coverage": "scriptc coverage src/main.ts --fail-on=blockers",
|
|
16
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
17
|
+
"test": "node --experimental-strip-types test/run-all.ts"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "24.10.0",
|
|
21
|
+
"scriptc": "0.4.1",
|
|
22
|
+
"typescript": "5.9.3"
|
|
23
|
+
},
|
|
24
|
+
"allowScripts": {
|
|
25
|
+
"scriptc@0.4.1": true
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Install the pinned ScriptC compiler and check the version.
|
|
3
|
+
# Method: npm exact devDependency (scriptc@0.4.1), which pulls the
|
|
4
|
+
# platform package @scriptc/cli-<os>-<arch> at the same version and
|
|
5
|
+
# runs scripts/install-native.mjs on postinstall.
|
|
6
|
+
# Lockfile: package-lock.json (npm ci reproduces this tree).
|
|
7
|
+
set -eu
|
|
8
|
+
cd "$(dirname "$0")/.."
|
|
9
|
+
npm install --save-dev --save-exact scriptc@0.4.1
|
|
10
|
+
npm install-scripts approve scriptc@0.4.1 || true
|
|
11
|
+
version="$(npx scriptc --version | tr -d '\r')"
|
|
12
|
+
echo "scriptc ${version}"
|
|
13
|
+
if [ "$version" != "0.4.1" ]; then
|
|
14
|
+
echo "expected scriptc 0.4.1" >&2
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|