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,576 @@
1
+ {
2
+ "ok": false,
3
+ "spec": "examples/plan-to-problems/generated.ears.md",
4
+ "conflicts": [
5
+ {
6
+ "ids": [
7
+ "R1",
8
+ "R2"
9
+ ],
10
+ "minimal": true,
11
+ "everySituation": false,
12
+ "summary": "R1 and R2 cannot both hold.",
13
+ "clash": "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.",
14
+ "requirements": [
15
+ {
16
+ "id": "R1",
17
+ "line": 16,
18
+ "sentence": "While the copy is in the building, when a member asks to take a copy home, the Fall Desk shall allow the loan."
19
+ },
20
+ {
21
+ "id": "R2",
22
+ "line": 17,
23
+ "sentence": "While the copy is out on loan, when a member asks to take a copy home, the Fall Desk shall not allow the loan."
24
+ }
25
+ ],
26
+ "situation": [
27
+ {
28
+ "name": "borrow_requested",
29
+ "value": "true"
30
+ },
31
+ {
32
+ "name": "renew_requested",
33
+ "value": "false"
34
+ },
35
+ {
36
+ "name": "copy_returned",
37
+ "value": "false"
38
+ },
39
+ {
40
+ "name": "copy_in_building",
41
+ "value": "true"
42
+ },
43
+ {
44
+ "name": "copy_on_loan",
45
+ "value": "true"
46
+ },
47
+ {
48
+ "name": "fine_owed",
49
+ "value": "false"
50
+ },
51
+ {
52
+ "name": "loan_overdue",
53
+ "value": "false"
54
+ },
55
+ {
56
+ "name": "loan_days",
57
+ "value": "1"
58
+ }
59
+ ],
60
+ "requires": [
61
+ {
62
+ "id": "R1",
63
+ "text": "that the loan was allowed"
64
+ },
65
+ {
66
+ "id": "R2",
67
+ "text": "that the loan was not allowed"
68
+ }
69
+ ],
70
+ "example": [
71
+ {
72
+ "name": "borrow_requested",
73
+ "value": "true"
74
+ },
75
+ {
76
+ "name": "renew_requested",
77
+ "value": "false"
78
+ },
79
+ {
80
+ "name": "copy_returned",
81
+ "value": "false"
82
+ },
83
+ {
84
+ "name": "copy_in_building",
85
+ "value": "true"
86
+ },
87
+ {
88
+ "name": "copy_on_loan",
89
+ "value": "true"
90
+ },
91
+ {
92
+ "name": "fine_owed",
93
+ "value": "false"
94
+ },
95
+ {
96
+ "name": "loan_overdue",
97
+ "value": "false"
98
+ },
99
+ {
100
+ "name": "loan_days",
101
+ "value": "1"
102
+ },
103
+ {
104
+ "name": "loan_allowed",
105
+ "value": "true"
106
+ },
107
+ {
108
+ "name": "renewal_allowed",
109
+ "value": "false"
110
+ },
111
+ {
112
+ "name": "reminder_sent",
113
+ "value": "false"
114
+ },
115
+ {
116
+ "name": "loan_closed",
117
+ "value": "false"
118
+ }
119
+ ],
120
+ "keeps": [
121
+ "R1"
122
+ ],
123
+ "breaks": "R2",
124
+ "fix": "Suggested fix: ask which requirement should win. To keep R1, remove R2. To keep R2, remove R1.",
125
+ "repairs": [
126
+ {
127
+ "kind": "drop",
128
+ "id": "R1",
129
+ "text": "One repair is to drop R1 and keep R2 as written.",
130
+ "sentence": "While the copy is out on loan, when a member asks to take a copy home, the Fall Desk shall not allow the loan."
131
+ },
132
+ {
133
+ "kind": "drop",
134
+ "id": "R2",
135
+ "text": "One repair is to drop R2 and keep R1 as written.",
136
+ "sentence": "While the copy is in the building, when a member asks to take a copy home, the Fall Desk shall allow the loan."
137
+ },
138
+ {
139
+ "kind": "strengthen",
140
+ "id": "R1",
141
+ "text": "One repair is to add \"and the copy is not out on loan\" to the condition of R1. R2 stays as written.",
142
+ "sentence": "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."
143
+ },
144
+ {
145
+ "kind": "strengthen",
146
+ "id": "R2",
147
+ "text": "One repair is to add \"and the copy is not in the building\" to the condition of R2. R1 stays as written.",
148
+ "sentence": "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."
149
+ }
150
+ ]
151
+ },
152
+ {
153
+ "ids": [
154
+ "R2",
155
+ "R3"
156
+ ],
157
+ "minimal": true,
158
+ "everySituation": false,
159
+ "summary": "R2 and R3 cannot both hold.",
160
+ "clash": "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.",
161
+ "requirements": [
162
+ {
163
+ "id": "R2",
164
+ "line": 17,
165
+ "sentence": "While the copy is out on loan, when a member asks to take a copy home, the Fall Desk shall not allow the loan."
166
+ },
167
+ {
168
+ "id": "R3",
169
+ "line": 18,
170
+ "sentence": "While the copy is out on loan, when a member asks to take a copy home, the Fall Desk shall allow the loan."
171
+ }
172
+ ],
173
+ "situation": [
174
+ {
175
+ "name": "borrow_requested",
176
+ "value": "true"
177
+ },
178
+ {
179
+ "name": "renew_requested",
180
+ "value": "false"
181
+ },
182
+ {
183
+ "name": "copy_returned",
184
+ "value": "false"
185
+ },
186
+ {
187
+ "name": "copy_in_building",
188
+ "value": "false"
189
+ },
190
+ {
191
+ "name": "copy_on_loan",
192
+ "value": "true"
193
+ },
194
+ {
195
+ "name": "fine_owed",
196
+ "value": "false"
197
+ },
198
+ {
199
+ "name": "loan_overdue",
200
+ "value": "false"
201
+ },
202
+ {
203
+ "name": "loan_days",
204
+ "value": "1"
205
+ }
206
+ ],
207
+ "requires": [
208
+ {
209
+ "id": "R2",
210
+ "text": "that the loan was not allowed"
211
+ },
212
+ {
213
+ "id": "R3",
214
+ "text": "that the loan was allowed"
215
+ }
216
+ ],
217
+ "example": [
218
+ {
219
+ "name": "borrow_requested",
220
+ "value": "true"
221
+ },
222
+ {
223
+ "name": "renew_requested",
224
+ "value": "false"
225
+ },
226
+ {
227
+ "name": "copy_returned",
228
+ "value": "false"
229
+ },
230
+ {
231
+ "name": "copy_in_building",
232
+ "value": "false"
233
+ },
234
+ {
235
+ "name": "copy_on_loan",
236
+ "value": "true"
237
+ },
238
+ {
239
+ "name": "fine_owed",
240
+ "value": "false"
241
+ },
242
+ {
243
+ "name": "loan_overdue",
244
+ "value": "false"
245
+ },
246
+ {
247
+ "name": "loan_days",
248
+ "value": "1"
249
+ },
250
+ {
251
+ "name": "loan_allowed",
252
+ "value": "false"
253
+ },
254
+ {
255
+ "name": "renewal_allowed",
256
+ "value": "false"
257
+ },
258
+ {
259
+ "name": "reminder_sent",
260
+ "value": "false"
261
+ },
262
+ {
263
+ "name": "loan_closed",
264
+ "value": "false"
265
+ }
266
+ ],
267
+ "keeps": [
268
+ "R2"
269
+ ],
270
+ "breaks": "R3",
271
+ "fix": "Suggested fix: ask which requirement should win. To keep R2, remove R3. To keep R3, remove R2.",
272
+ "repairs": [
273
+ {
274
+ "kind": "drop",
275
+ "id": "R2",
276
+ "text": "One repair is to drop R2 and keep R3 as written.",
277
+ "sentence": "While the copy is out on loan, when a member asks to take a copy home, the Fall Desk shall allow the loan."
278
+ },
279
+ {
280
+ "kind": "drop",
281
+ "id": "R3",
282
+ "text": "One repair is to drop R3 and keep R2 as written.",
283
+ "sentence": "While the copy is out on loan, when a member asks to take a copy home, the Fall Desk shall not allow the loan."
284
+ }
285
+ ]
286
+ },
287
+ {
288
+ "ids": [
289
+ "R5",
290
+ "R6"
291
+ ],
292
+ "minimal": true,
293
+ "everySituation": false,
294
+ "summary": "R5 and R6 cannot both hold.",
295
+ "clash": "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.",
296
+ "requirements": [
297
+ {
298
+ "id": "R5",
299
+ "line": 20,
300
+ "sentence": "While a fine is owed, when a member asks to renew a copy, the Fall Desk shall not allow the renewal."
301
+ },
302
+ {
303
+ "id": "R6",
304
+ "line": 21,
305
+ "sentence": "While a fine is owed, when a member asks to renew a copy, the Fall Desk shall allow the renewal."
306
+ }
307
+ ],
308
+ "situation": [
309
+ {
310
+ "name": "borrow_requested",
311
+ "value": "false"
312
+ },
313
+ {
314
+ "name": "renew_requested",
315
+ "value": "true"
316
+ },
317
+ {
318
+ "name": "copy_returned",
319
+ "value": "false"
320
+ },
321
+ {
322
+ "name": "copy_in_building",
323
+ "value": "false"
324
+ },
325
+ {
326
+ "name": "copy_on_loan",
327
+ "value": "false"
328
+ },
329
+ {
330
+ "name": "fine_owed",
331
+ "value": "true"
332
+ },
333
+ {
334
+ "name": "loan_overdue",
335
+ "value": "false"
336
+ },
337
+ {
338
+ "name": "loan_days",
339
+ "value": "1"
340
+ }
341
+ ],
342
+ "requires": [
343
+ {
344
+ "id": "R5",
345
+ "text": "that the renewal was not allowed"
346
+ },
347
+ {
348
+ "id": "R6",
349
+ "text": "that the renewal was allowed"
350
+ }
351
+ ],
352
+ "example": [
353
+ {
354
+ "name": "borrow_requested",
355
+ "value": "false"
356
+ },
357
+ {
358
+ "name": "renew_requested",
359
+ "value": "true"
360
+ },
361
+ {
362
+ "name": "copy_returned",
363
+ "value": "false"
364
+ },
365
+ {
366
+ "name": "copy_in_building",
367
+ "value": "false"
368
+ },
369
+ {
370
+ "name": "copy_on_loan",
371
+ "value": "false"
372
+ },
373
+ {
374
+ "name": "fine_owed",
375
+ "value": "true"
376
+ },
377
+ {
378
+ "name": "loan_overdue",
379
+ "value": "false"
380
+ },
381
+ {
382
+ "name": "loan_days",
383
+ "value": "1"
384
+ },
385
+ {
386
+ "name": "loan_allowed",
387
+ "value": "false"
388
+ },
389
+ {
390
+ "name": "renewal_allowed",
391
+ "value": "false"
392
+ },
393
+ {
394
+ "name": "reminder_sent",
395
+ "value": "false"
396
+ },
397
+ {
398
+ "name": "loan_closed",
399
+ "value": "false"
400
+ }
401
+ ],
402
+ "keeps": [
403
+ "R5"
404
+ ],
405
+ "breaks": "R6",
406
+ "fix": "Suggested fix: ask which requirement should win. To keep R5, remove R6. To keep R6, remove R5.",
407
+ "repairs": [
408
+ {
409
+ "kind": "drop",
410
+ "id": "R5",
411
+ "text": "One repair is to drop R5 and keep R6 as written.",
412
+ "sentence": "While a fine is owed, when a member asks to renew a copy, the Fall Desk shall allow the renewal."
413
+ },
414
+ {
415
+ "kind": "drop",
416
+ "id": "R6",
417
+ "text": "One repair is to drop R6 and keep R5 as written.",
418
+ "sentence": "While a fine is owed, when a member asks to renew a copy, the Fall Desk shall not allow the renewal."
419
+ }
420
+ ]
421
+ }
422
+ ],
423
+ "gaps": [
424
+ {
425
+ "output": "loan_allowed",
426
+ "situation": [
427
+ {
428
+ "name": "borrow_requested",
429
+ "value": "true"
430
+ },
431
+ {
432
+ "name": "renew_requested",
433
+ "value": "true"
434
+ },
435
+ {
436
+ "name": "copy_returned",
437
+ "value": "true"
438
+ },
439
+ {
440
+ "name": "copy_in_building",
441
+ "value": "false"
442
+ },
443
+ {
444
+ "name": "copy_on_loan",
445
+ "value": "false"
446
+ },
447
+ {
448
+ "name": "fine_owed",
449
+ "value": "true"
450
+ },
451
+ {
452
+ "name": "loan_overdue",
453
+ "value": "true"
454
+ },
455
+ {
456
+ "name": "loan_days",
457
+ "value": "21"
458
+ }
459
+ ],
460
+ "text": "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."
461
+ },
462
+ {
463
+ "output": "renewal_allowed",
464
+ "situation": [
465
+ {
466
+ "name": "borrow_requested",
467
+ "value": "true"
468
+ },
469
+ {
470
+ "name": "renew_requested",
471
+ "value": "false"
472
+ },
473
+ {
474
+ "name": "copy_returned",
475
+ "value": "true"
476
+ },
477
+ {
478
+ "name": "copy_in_building",
479
+ "value": "true"
480
+ },
481
+ {
482
+ "name": "copy_on_loan",
483
+ "value": "true"
484
+ },
485
+ {
486
+ "name": "fine_owed",
487
+ "value": "true"
488
+ },
489
+ {
490
+ "name": "loan_overdue",
491
+ "value": "true"
492
+ },
493
+ {
494
+ "name": "loan_days",
495
+ "value": "21"
496
+ }
497
+ ],
498
+ "text": "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."
499
+ },
500
+ {
501
+ "output": "reminder_sent",
502
+ "situation": [
503
+ {
504
+ "name": "borrow_requested",
505
+ "value": "true"
506
+ },
507
+ {
508
+ "name": "renew_requested",
509
+ "value": "true"
510
+ },
511
+ {
512
+ "name": "copy_returned",
513
+ "value": "true"
514
+ },
515
+ {
516
+ "name": "copy_in_building",
517
+ "value": "true"
518
+ },
519
+ {
520
+ "name": "copy_on_loan",
521
+ "value": "true"
522
+ },
523
+ {
524
+ "name": "fine_owed",
525
+ "value": "true"
526
+ },
527
+ {
528
+ "name": "loan_overdue",
529
+ "value": "false"
530
+ },
531
+ {
532
+ "name": "loan_days",
533
+ "value": "21"
534
+ }
535
+ ],
536
+ "text": "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."
537
+ }
538
+ ],
539
+ "misuse": [],
540
+ "wording": [
541
+ {
542
+ "line": 19,
543
+ "word": "not",
544
+ "kind": "negation",
545
+ "text": "The trigger is a bare negation. Write a positive condition, such as \"When a non-member asks\", or declare a positive event.",
546
+ "context": "When not a member asks to take a copy home, the Fall Desk shall not allow the loan."
547
+ },
548
+ {
549
+ "line": 27,
550
+ "word": "not",
551
+ "kind": "negation",
552
+ "text": "The trigger is a bare negation. Write a positive condition, such as \"When a non-member asks\", or declare a positive event.",
553
+ "context": "When not a member brings a copy back, the Fall Desk shall not close the loan."
554
+ }
555
+ ],
556
+ "redundant": [
557
+ {
558
+ "id": "R11",
559
+ "line": 26,
560
+ "sentence": "When a member brings a copy back and the loan is overdue, the Fall Desk shall close the loan.",
561
+ "text": "R11 adds nothing. Whenever the requirements outside a conflict hold, R11 holds too.",
562
+ "fix": "Suggested fix: remove R11, or ask whether it was meant to say something the others do not already require. Do not delete it until the PM agrees."
563
+ }
564
+ ],
565
+ "duplicate": [],
566
+ "vacuous": [
567
+ {
568
+ "id": "R9",
569
+ "line": 24,
570
+ "sentence": "When a member asks to take a copy home and the loan length is at least 40, the Fall Desk shall allow the loan.",
571
+ "text": "R9 can never trigger. Its condition is impossible for every allowed value.",
572
+ "fix": "Suggested fix: rewrite the condition in R9 so a real situation can make it true, or remove the requirement. Do not invent a response."
573
+ }
574
+ ],
575
+ "unused": []
576
+ }
@@ -0,0 +1,5 @@
1
+ The fall desk rules for the public library need to be settled before we build anything. A member can ask to take a copy home, renew a copy they already have, or bring a copy back. Loans are only 1 to 21 days, and those are the only lengths the desk will set. If a member asks for a copy and that copy is still in the building, let them take it home. If they ask for a copy that is already out on loan, do not lend it. I changed my mind on that last point: if they ask for a copy that is already out on loan, lend it anyway. If nobody is asking to borrow, do not open a loan.
2
+
3
+ If a member still owes a fine, do not let them renew. Also, if a member still owes a fine, let them renew. If they do not owe a fine, renewals are allowed. After a loan passes its due date, send the member a reminder. If someone asks to keep a copy for 40 days or longer, allow that loan. If a member brings a copy back, close the loan. If a member brings a copy back and the loan is already overdue, close the loan as well. If the copy has not come back, leave the loan open.
4
+
5
+ Staff need a clear answer on whether the loan was allowed, whether the renewal was allowed, whether a reminder went out, and whether the loan was closed. This quarter covers only those desk decisions. We are not adding holds, extra fees, or a new catalog.
@@ -0,0 +1,5 @@
1
+ conflict R2 R3
2
+ conflict R5 R6
3
+ gap reminder_sent
4
+ vacuous R9
5
+ redundant R11