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.
Files changed (222) hide show
  1. package/.github/workflows/ci.yml +48 -0
  2. package/README.md +1431 -2
  3. package/assets/icon.png +0 -0
  4. package/assets/logo-mark.png +0 -0
  5. package/assets/logo.png +0 -0
  6. package/examples/checkout.ears.md +74 -0
  7. package/examples/order-always.ears.md +3 -0
  8. package/examples/order-awkward.ears.md +8 -0
  9. package/examples/order-bad.json +1 -0
  10. package/examples/order-chain.ears.md +9 -0
  11. package/examples/order-chosen.ears.md +7 -0
  12. package/examples/order-clear.ears.md +8 -0
  13. package/examples/order-combined.ears.md +10 -0
  14. package/examples/order-conflict.ears.md +8 -0
  15. package/examples/order-covered.ears.md +9 -0
  16. package/examples/order-duplicate.ears.md +8 -0
  17. package/examples/order-fail.json +5 -0
  18. package/examples/order-misuse.ears.md +7 -0
  19. package/examples/order-negation.ears.md +7 -0
  20. package/examples/order-pass.json +5 -0
  21. package/examples/order-quantity.ears.md +8 -0
  22. package/examples/order-redundant.ears.md +9 -0
  23. package/examples/order-repair.ears.md +9 -0
  24. package/examples/order-repeat.ears.md +13 -0
  25. package/examples/order-same.ears.md +3 -0
  26. package/examples/order-status.ears.md +7 -0
  27. package/examples/order-unanswered.ears.md +8 -0
  28. package/examples/order-unhandled.ears.md +8 -0
  29. package/examples/order-unmeasured.ears.md +10 -0
  30. package/examples/order-unused.ears.md +13 -0
  31. package/examples/order-vacuous.ears.md +9 -0
  32. package/examples/order-vague.ears.md +7 -0
  33. package/examples/order-wide.ears.md +5 -0
  34. package/examples/order-wording.ears.md +8 -0
  35. package/examples/order.ears.md +7 -0
  36. package/examples/payment-if.ears.md +7 -0
  37. package/examples/plan-to-problems/README.md +54 -0
  38. package/examples/plan-to-problems/expected-problems.md +25 -0
  39. package/examples/plan-to-problems/generate.mjs +186 -0
  40. package/examples/plan-to-problems/generated.ears.md +27 -0
  41. package/examples/plan-to-problems/lint.json +576 -0
  42. package/examples/plan-to-problems/plan.md +5 -0
  43. package/examples/plan-to-problems/planted.txt +5 -0
  44. package/examples/plan-to-problems/problems.md +112 -0
  45. package/examples/status-placed.ears.md +3 -0
  46. package/package.json +25 -4
  47. package/scripts/install-scriptc.sh +16 -0
  48. package/skills/plan-verify/SKILL.md +1515 -0
  49. package/src/check.ts +250 -0
  50. package/src/decide.ts +1324 -0
  51. package/src/ears.ts +1520 -0
  52. package/src/jsonvalue.ts +228 -0
  53. package/src/lint.ts +1245 -0
  54. package/src/main.ts +273 -0
  55. package/src/policy.ts +148 -0
  56. package/src/repair.ts +567 -0
  57. package/src/report.ts +329 -0
  58. package/src/scenarios.ts +149 -0
  59. package/src/smt.ts +97 -0
  60. package/src/solver.ts +868 -0
  61. package/src/spec.ts +307 -0
  62. package/src/term.ts +331 -0
  63. package/src/text.ts +95 -0
  64. package/test/fixtures/malformed.ears.md +3 -0
  65. package/test/fixtures/unknown-phrase.ears.md +5 -0
  66. package/test/golden/check-fail-json.exit +1 -0
  67. package/test/golden/check-fail-json.stderr +0 -0
  68. package/test/golden/check-fail-json.stdout +13 -0
  69. package/test/golden/check-fail.exit +1 -0
  70. package/test/golden/check-fail.stderr +0 -0
  71. package/test/golden/check-fail.stdout +7 -0
  72. package/test/golden/check-pass-json.exit +1 -0
  73. package/test/golden/check-pass-json.stderr +0 -0
  74. package/test/golden/check-pass-json.stdout +6 -0
  75. package/test/golden/check-pass.exit +1 -0
  76. package/test/golden/check-pass.stderr +0 -0
  77. package/test/golden/check-pass.stdout +3 -0
  78. package/test/golden/emit-smt-json-usage.exit +1 -0
  79. package/test/golden/emit-smt-json-usage.stderr +0 -0
  80. package/test/golden/emit-smt-json-usage.stdout +4 -0
  81. package/test/golden/emit-smt-json.exit +1 -0
  82. package/test/golden/emit-smt-json.stderr +0 -0
  83. package/test/golden/emit-smt-json.stdout +5 -0
  84. package/test/golden/emit-smt.exit +1 -0
  85. package/test/golden/emit-smt.stderr +0 -0
  86. package/test/golden/emit-smt.stdout +10 -0
  87. package/test/golden/help.exit +1 -0
  88. package/test/golden/help.stderr +0 -0
  89. package/test/golden/help.stdout +8 -0
  90. package/test/golden/lint-awkward.exit +1 -0
  91. package/test/golden/lint-awkward.stderr +0 -0
  92. package/test/golden/lint-awkward.stdout +29 -0
  93. package/test/golden/lint-chain.exit +1 -0
  94. package/test/golden/lint-chain.stderr +0 -0
  95. package/test/golden/lint-chain.stdout +56 -0
  96. package/test/golden/lint-checkout.exit +1 -0
  97. package/test/golden/lint-checkout.stderr +0 -0
  98. package/test/golden/lint-checkout.stdout +301 -0
  99. package/test/golden/lint-clean.exit +1 -0
  100. package/test/golden/lint-clean.stderr +0 -0
  101. package/test/golden/lint-clean.stdout +9 -0
  102. package/test/golden/lint-clear.exit +1 -0
  103. package/test/golden/lint-clear.stderr +0 -0
  104. package/test/golden/lint-clear.stdout +9 -0
  105. package/test/golden/lint-conflict-json.exit +1 -0
  106. package/test/golden/lint-conflict-json.stderr +0 -0
  107. package/test/golden/lint-conflict-json.stdout +80 -0
  108. package/test/golden/lint-conflict.exit +1 -0
  109. package/test/golden/lint-conflict.stderr +0 -0
  110. package/test/golden/lint-conflict.stdout +41 -0
  111. package/test/golden/lint-duplicate.exit +1 -0
  112. package/test/golden/lint-duplicate.stderr +0 -0
  113. package/test/golden/lint-duplicate.stdout +14 -0
  114. package/test/golden/lint-gap.exit +1 -0
  115. package/test/golden/lint-gap.stderr +0 -0
  116. package/test/golden/lint-gap.stdout +12 -0
  117. package/test/golden/lint-misuse.exit +1 -0
  118. package/test/golden/lint-misuse.stderr +0 -0
  119. package/test/golden/lint-misuse.stdout +18 -0
  120. package/test/golden/lint-negation.exit +1 -0
  121. package/test/golden/lint-negation.stderr +0 -0
  122. package/test/golden/lint-negation.stdout +13 -0
  123. package/test/golden/lint-quantity.exit +1 -0
  124. package/test/golden/lint-quantity.stderr +0 -0
  125. package/test/golden/lint-quantity.stdout +41 -0
  126. package/test/golden/lint-redundant.exit +1 -0
  127. package/test/golden/lint-redundant.stderr +0 -0
  128. package/test/golden/lint-redundant.stdout +14 -0
  129. package/test/golden/lint-repair-json.exit +1 -0
  130. package/test/golden/lint-repair-json.stderr +0 -0
  131. package/test/golden/lint-repair-json.stdout +127 -0
  132. package/test/golden/lint-repair.exit +1 -0
  133. package/test/golden/lint-repair.stderr +0 -0
  134. package/test/golden/lint-repair.stdout +54 -0
  135. package/test/golden/lint-repeat.exit +1 -0
  136. package/test/golden/lint-repeat.stderr +0 -0
  137. package/test/golden/lint-repeat.stdout +48 -0
  138. package/test/golden/lint-status.exit +1 -0
  139. package/test/golden/lint-status.stderr +0 -0
  140. package/test/golden/lint-status.stdout +44 -0
  141. package/test/golden/lint-unanswered.exit +1 -0
  142. package/test/golden/lint-unanswered.stderr +0 -0
  143. package/test/golden/lint-unanswered.stdout +12 -0
  144. package/test/golden/lint-unhandled.exit +1 -0
  145. package/test/golden/lint-unhandled.stderr +0 -0
  146. package/test/golden/lint-unhandled.stdout +12 -0
  147. package/test/golden/lint-unmeasured.exit +1 -0
  148. package/test/golden/lint-unmeasured.stderr +0 -0
  149. package/test/golden/lint-unmeasured.stdout +13 -0
  150. package/test/golden/lint-unused-json.exit +1 -0
  151. package/test/golden/lint-unused-json.stderr +0 -0
  152. package/test/golden/lint-unused-json.stdout +37 -0
  153. package/test/golden/lint-unused.exit +1 -0
  154. package/test/golden/lint-unused.stderr +0 -0
  155. package/test/golden/lint-unused.stdout +25 -0
  156. package/test/golden/lint-vacuous-json.exit +1 -0
  157. package/test/golden/lint-vacuous-json.stderr +0 -0
  158. package/test/golden/lint-vacuous-json.stdout +27 -0
  159. package/test/golden/lint-vacuous.exit +1 -0
  160. package/test/golden/lint-vacuous.stderr +0 -0
  161. package/test/golden/lint-vacuous.stdout +19 -0
  162. package/test/golden/lint-vague.exit +1 -0
  163. package/test/golden/lint-vague.stderr +0 -0
  164. package/test/golden/lint-vague.stdout +21 -0
  165. package/test/golden/lint-wide.exit +1 -0
  166. package/test/golden/lint-wide.stderr +0 -0
  167. package/test/golden/lint-wide.stdout +9 -0
  168. package/test/golden/lint-wording.exit +1 -0
  169. package/test/golden/lint-wording.stderr +0 -0
  170. package/test/golden/lint-wording.stdout +21 -0
  171. package/test/golden/malformed-scenario.exit +1 -0
  172. package/test/golden/malformed-scenario.stderr +1 -0
  173. package/test/golden/malformed-scenario.stdout +0 -0
  174. package/test/golden/malformed-spec.exit +1 -0
  175. package/test/golden/malformed-spec.stderr +1 -0
  176. package/test/golden/malformed-spec.stdout +0 -0
  177. package/test/golden/policy-counterexample.exit +1 -0
  178. package/test/golden/policy-counterexample.stderr +0 -0
  179. package/test/golden/policy-counterexample.stdout +13 -0
  180. package/test/golden/policy-covered.exit +1 -0
  181. package/test/golden/policy-covered.stderr +0 -0
  182. package/test/golden/policy-covered.stdout +19 -0
  183. package/test/golden/policy-holds.exit +1 -0
  184. package/test/golden/policy-holds.stderr +0 -0
  185. package/test/golden/policy-holds.stdout +3 -0
  186. package/test/golden/policy-json.exit +1 -0
  187. package/test/golden/policy-json.stderr +0 -0
  188. package/test/golden/policy-json.stdout +34 -0
  189. package/test/golden/policy-status.exit +1 -0
  190. package/test/golden/policy-status.stderr +0 -0
  191. package/test/golden/policy-status.stdout +16 -0
  192. package/test/golden/policy-vacuous.exit +1 -0
  193. package/test/golden/policy-vacuous.stderr +0 -0
  194. package/test/golden/policy-vacuous.stdout +3 -0
  195. package/test/golden/policy-wide.exit +1 -0
  196. package/test/golden/policy-wide.stderr +0 -0
  197. package/test/golden/policy-wide.stdout +15 -0
  198. package/test/golden/policy.exit +1 -0
  199. package/test/golden/policy.stderr +0 -0
  200. package/test/golden/policy.stdout +19 -0
  201. package/test/golden/scenarios-json.exit +1 -0
  202. package/test/golden/scenarios-json.stderr +0 -0
  203. package/test/golden/scenarios-json.stdout +75 -0
  204. package/test/golden/scenarios-status.exit +1 -0
  205. package/test/golden/scenarios-status.stderr +0 -0
  206. package/test/golden/scenarios-status.stdout +24 -0
  207. package/test/golden/scenarios.exit +1 -0
  208. package/test/golden/scenarios.stderr +0 -0
  209. package/test/golden/scenarios.stdout +26 -0
  210. package/test/golden/unit.exit +1 -0
  211. package/test/golden/unit.stderr +0 -0
  212. package/test/golden/unit.stdout +2 -0
  213. package/test/golden/unknown-phrase.exit +1 -0
  214. package/test/golden/unknown-phrase.stderr +1 -0
  215. package/test/golden/unknown-phrase.stdout +0 -0
  216. package/test/golden/usage.exit +1 -0
  217. package/test/golden/usage.stderr +9 -0
  218. package/test/golden/usage.stdout +0 -0
  219. package/test/run-all.ts +499 -0
  220. package/test/soundness.ts +375 -0
  221. package/test/unit.ts +1352 -0
  222. 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.
@@ -0,0 +1,3 @@
1
+ # Property: the status is placed.
2
+
3
+ P1 The Order System shall mark the order placed.
package/package.json CHANGED
@@ -1,6 +1,27 @@
1
1
  {
2
2
  "name": "plan-verify",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
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