@pmelab/gtd 58.2.0 → 59.0.1

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 (150) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +120 -395
  3. package/dist/chunks/{Schema-B3o6eRNU.mjs → testkit-07Z_ENch.mjs} +13503 -3460
  4. package/dist/chunks/{wire-C12dahrH.mjs → wire-BTUhRGXj.mjs} +1 -1
  5. package/dist/flows.mjs +1 -1
  6. package/dist/gtd.bundle.mjs +484 -497
  7. package/dist/testkit/gherkin.mjs +145 -65
  8. package/dist/testkit.mjs +1 -1
  9. package/dist/wire.mjs +1 -1
  10. package/package.json +3 -42
  11. package/skills/authoring/SKILL.md +34 -27
  12. package/src/cli/Cli.test.ts +148 -71
  13. package/src/cli/Cli.ts +384 -463
  14. package/src/cli/Help.test.ts +47 -0
  15. package/src/cli/Help.ts +70 -0
  16. package/src/cli/Program.test.ts +127 -33
  17. package/src/cli/Program.ts +70 -25
  18. package/src/edge/Edge.ts +4 -4
  19. package/src/emit/Emit.test.ts +1 -1
  20. package/src/emit/Emit.ts +1 -1
  21. package/src/flags/index.test.ts +0 -13
  22. package/src/flags/index.ts +1 -3
  23. package/src/flows/factory.ts +3 -1
  24. package/src/gherkin/index.ts +135 -71
  25. package/src/gherkin/messages.test.ts +90 -0
  26. package/src/gherkin/messages.ts +84 -0
  27. package/src/install/Install.test.ts +14 -11
  28. package/src/install/Install.ts +38 -15
  29. package/src/install/index.ts +1 -2
  30. package/src/replay/Replay.test.ts +1 -1
  31. package/src/replay/index.ts +1 -1
  32. package/src/step/planStart.ts +6 -1
  33. package/src/testkit/TestRepo.test.ts +47 -2
  34. package/src/testkit/TestRepo.ts +51 -6
  35. package/src/twins/EmittedScriptRecognizer.ts +5 -8
  36. package/src/twins/FlowScripts.ts +1 -1
  37. package/src/wire/Land.ts +1 -1
  38. package/src/workflow/index.ts +1 -0
  39. package/src/workflow/load.test.ts +138 -113
  40. package/src/workflow/load.ts +7 -7
  41. package/src/workflow/skills.test.ts +5 -5
  42. package/src/workflow/skills.ts +1 -1
  43. package/src/workflow/vars.test.ts +30 -3
  44. package/src/workflow/vars.ts +34 -2
  45. package/src/workflow-kit/index.ts +1 -1
  46. package/dist/chunks/reserved-lMYCaapY.mjs +0 -1990
  47. package/dist/chunks/review-Bfaror38.mjs +0 -427
  48. package/dist/chunks/tail-Bd7L940p.mjs +0 -355
  49. package/dist/chunks/testkit-JDtpNKyi.mjs +0 -10410
  50. package/dist/workflows/cleanup.mjs +0 -36
  51. package/dist/workflows/feature.mjs +0 -1018
  52. package/dist/workflows/fix.mjs +0 -41
  53. package/dist/workflows/review.mjs +0 -45
  54. package/dist/workflows/rollout.mjs +0 -48
  55. package/dist/workflows.mjs +0 -40651
  56. package/src/install/ConfigScaffold.ts +0 -22
  57. package/src/workflows/cleanup/cleanup-path.feature +0 -94
  58. package/src/workflows/cleanup/cleanup.feature +0 -315
  59. package/src/workflows/cleanup/cleanup.test.ts +0 -276
  60. package/src/workflows/cleanup/cleanup.ts +0 -52
  61. package/src/workflows/cleanup/index.ts +0 -6
  62. package/src/workflows/feature/acceptance-criteria-tier.feature +0 -25
  63. package/src/workflows/feature/acceptance-criteria.feature +0 -389
  64. package/src/workflows/feature/criteria-coverage.feature +0 -102
  65. package/src/workflows/feature/criteria.test.ts +0 -147
  66. package/src/workflows/feature/criteria.ts +0 -267
  67. package/src/workflows/feature/fast-path.feature +0 -93
  68. package/src/workflows/feature/feature.test.ts +0 -338
  69. package/src/workflows/feature/feature.ts +0 -156
  70. package/src/workflows/feature/green.ts +0 -19
  71. package/src/workflows/feature/index.ts +0 -18
  72. package/src/workflows/feature/lens-models.feature +0 -69
  73. package/src/workflows/feature/lens-review.feature +0 -566
  74. package/src/workflows/feature/naive-acceptance.feature +0 -147
  75. package/src/workflows/feature/naive-test.feature +0 -263
  76. package/src/workflows/feature/one-step-land.feature +0 -93
  77. package/src/workflows/feature/planning.ts +0 -41
  78. package/src/workflows/feature/refactor-absorbs.feature +0 -131
  79. package/src/workflows/feature/refactor-first.feature +0 -158
  80. package/src/workflows/feature/refactor-lens-models.feature +0 -79
  81. package/src/workflows/feature/refactor-rebuild.feature +0 -273
  82. package/src/workflows/feature/refactor-review.feature +0 -1528
  83. package/src/workflows/feature/refactor.test.ts +0 -1220
  84. package/src/workflows/feature/refactor.ts +0 -368
  85. package/src/workflows/feature/release-flag.feature +0 -167
  86. package/src/workflows/feature/release-flag.test.ts +0 -49
  87. package/src/workflows/feature/release-flag.ts +0 -46
  88. package/src/workflows/feature/review-gate.feature +0 -241
  89. package/src/workflows/feature/reviewer-walkthrough.feature +0 -142
  90. package/src/workflows/feature/risk-tier.feature +0 -351
  91. package/src/workflows/feature/seam-rejection.feature +0 -146
  92. package/src/workflows/feature/seam.feature +0 -297
  93. package/src/workflows/feature/seam.test.ts +0 -527
  94. package/src/workflows/feature/seam.ts +0 -426
  95. package/src/workflows/feature/standards.feature +0 -354
  96. package/src/workflows/feature/steps.test.ts +0 -65
  97. package/src/workflows/feature/steps.ts +0 -45
  98. package/src/workflows/feature/tier-pin.feature +0 -66
  99. package/src/workflows/feature/tier.test.ts +0 -166
  100. package/src/workflows/feature/tier.ts +0 -56
  101. package/src/workflows/fix/fix-lens-origin.feature +0 -114
  102. package/src/workflows/fix/fix-quality.feature +0 -212
  103. package/src/workflows/fix/fix.feature +0 -79
  104. package/src/workflows/fix/fix.test.ts +0 -74
  105. package/src/workflows/fix/fix.ts +0 -52
  106. package/src/workflows/fix/index.ts +0 -6
  107. package/src/workflows/index.ts +0 -47
  108. package/src/workflows/review/index.ts +0 -6
  109. package/src/workflows/review/review-door.feature +0 -199
  110. package/src/workflows/review/review.test.ts +0 -27
  111. package/src/workflows/review/review.ts +0 -60
  112. package/src/workflows/rollout/index.ts +0 -6
  113. package/src/workflows/rollout/rollout.feature +0 -510
  114. package/src/workflows/rollout/rollout.test.ts +0 -263
  115. package/src/workflows/rollout/rollout.ts +0 -82
  116. package/src/workflows/shared/__fixtures__/feature-collecting-prompt.txt +0 -98
  117. package/src/workflows/shared/access.test.ts +0 -62
  118. package/src/workflows/shared/access.ts +0 -24
  119. package/src/workflows/shared/config.fixture.ts +0 -12
  120. package/src/workflows/shared/fences.ts +0 -24
  121. package/src/workflows/shared/health.test.ts +0 -148
  122. package/src/workflows/shared/health.ts +0 -191
  123. package/src/workflows/shared/learn.test.ts +0 -140
  124. package/src/workflows/shared/learn.ts +0 -201
  125. package/src/workflows/shared/lenses.test.ts +0 -39
  126. package/src/workflows/shared/lenses.ts +0 -294
  127. package/src/workflows/shared/options.test.ts +0 -157
  128. package/src/workflows/shared/options.ts +0 -92
  129. package/src/workflows/shared/progress.feature +0 -124
  130. package/src/workflows/shared/prose.ts +0 -263
  131. package/src/workflows/shared/recheck.feature +0 -60
  132. package/src/workflows/shared/registry.test.ts +0 -345
  133. package/src/workflows/shared/registry.ts +0 -183
  134. package/src/workflows/shared/reserved.ts +0 -11
  135. package/src/workflows/shared/review.test.ts +0 -1062
  136. package/src/workflows/shared/review.ts +0 -446
  137. package/src/workflows/shared/skills.test.ts +0 -121
  138. package/src/workflows/shared/skills.ts +0 -49
  139. package/src/workflows/shared/standards.test.ts +0 -90
  140. package/src/workflows/shared/standards.ts +0 -156
  141. package/src/workflows/shared/steps.test.ts +0 -219
  142. package/src/workflows/shared/steps.ts +0 -201
  143. package/src/workflows/shared/tail.test.ts +0 -454
  144. package/src/workflows/shared/tail.ts +0 -264
  145. package/src/workflows/shared/text.fixture.ts +0 -128
  146. package/src/workflows/shared/text.test.ts +0 -1430
  147. package/src/workflows/shared/text.ts +0 -1700
  148. package/src/workflows/shared/threads.test.ts +0 -129
  149. package/src/workflows/shared/vars.ts +0 -47
  150. package/src/workflows/shared/workflows.steps.ts +0 -54
@@ -1,389 +0,0 @@
1
- @inmem
2
- Feature: acceptance criteria carry stable IDs, and a sketch that states clear ones skips design
3
-
4
- `.gtd/REQUIREMENTS.md` lists the change's acceptance criteria as
5
- `- AC-<n>: …` under `## Acceptance Criteria`. On the full path
6
- `design.triage` writes them and the design gate settles them; a sketch
7
- that already states its criteria under an `Acceptance criteria` heading
8
- is judged at `design.clear`, and a `yes` records them as they are — no
9
- triage, no design gate. After design they are frozen.
10
-
11
- Background:
12
- Given the shipped workflows, as gtd install scaffolds them
13
- And an environment variable "GTD_QUALITYREVIEWS" set to ""
14
- And an environment variable "GTD_FLAG_RISK_TIER" set to "0"
15
- And a file "src/greeting.ts" with:
16
- """
17
- export const greeting = "Helo, world"
18
- """
19
- And the working tree is committed as "feat: add greeting"
20
- And step "unwind" changes nothing
21
- And step "start-gate.check" changes nothing
22
-
23
- Rule: the full path
24
-
25
- Background:
26
- Given a file "TODO.md" with:
27
- """
28
- # Fix the typo
29
-
30
- greeting says "Helo"; make it "Hello".
31
- """
32
-
33
- Scenario: [AC-6] triage writes ID'd criteria that reach the naive pass through the design gate
34
- Given step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
35
- """
36
- # Requirements
37
-
38
- ## Impact
39
-
40
- - the greeting module
41
-
42
- ## Release Flag
43
-
44
- new
45
-
46
- ## Acceptance Criteria
47
-
48
- - AC-1: `greeting` reads "Hello, world".
49
- - AC-2: no other export changes.
50
- """
51
- And step "design.gate.answer" changes nothing
52
- When the process is driven
53
- Then the process rests at "seam.naive"
54
- And ".gtd/REQUIREMENTS.md" contains "- AC-1: `greeting` reads \"Hello, world\"."
55
- And ".gtd/REQUIREMENTS.md" contains "- AC-2: no other export changes."
56
- And the resting step's content contains "maps where the change lands"
57
- And the resting step's content contains "Acceptance Criteria"
58
- And the commit subjects end with:
59
- """
60
- gtd(check): start-gate.check → design.triage
61
- gtd(agent): design.triage → design.gate.answer
62
- gtd(human): design.gate.answer → seam.naive
63
- """
64
-
65
- Scenario: a triage turn whose criteria carry no ID is refused
66
- Given step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
67
- """
68
- # Requirements
69
-
70
- ## Impact
71
-
72
- - the greeting module
73
-
74
- ## Release Flag
75
-
76
- new
77
-
78
- ## Acceptance Criteria
79
-
80
- - `greeting` reads "Hello, world".
81
- """
82
- Then driving the process is refused with "acceptance-criteria: .gtd/REQUIREMENTS.md has 1 criterion without an ID"
83
-
84
- Scenario: a triage turn reusing an ID is refused
85
- Given step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
86
- """
87
- # Requirements
88
-
89
- ## Impact
90
-
91
- - the greeting module
92
-
93
- ## Release Flag
94
-
95
- new
96
-
97
- ## Acceptance Criteria
98
-
99
- - AC-1: `greeting` reads "Hello, world".
100
- - AC-1: no other export changes.
101
- """
102
- Then driving the process is refused with "duplicate IDs: AC-1"
103
-
104
- Scenario: an implementation turn that edits the settled criteria is refused
105
- Given step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
106
- """
107
- # Requirements
108
-
109
- ## Impact
110
-
111
- - the greeting module
112
-
113
- ## Release Flag
114
-
115
- new
116
-
117
- ## Acceptance Criteria
118
-
119
- - AC-1: `greeting` reads "Hello, world".
120
- """
121
- And step "design.gate.answer" changes nothing
122
- And step "seam.naive" writes "src/acceptance.test.ts" with:
123
- """
124
- it("[AC-1] holds", () => {})
125
- it("[AC-2] holds", () => {})
126
- it("[AC-3] holds", () => {})
127
- it("[AC-7] holds", () => {})
128
- """
129
- And step "seam.naive" writes "src/greeting.ts" with:
130
- """
131
- export const greeting = "Hello, world"
132
- """
133
- And step "seam.check" writes "src/acceptance.test.ts" with:
134
- """
135
- it("[AC-1] holds elsewhere", () => {})
136
- """
137
- And judge step "seam.judge" answers:
138
- """
139
- [{"id":"shape","answer":"land","p":0.9}]
140
- """
141
- And step "implement.applying" runs its script
142
- And step "implement.building" writes ".gtd/REQUIREMENTS.md" with:
143
- """
144
- # Requirements
145
-
146
- ## Impact
147
-
148
- - the greeting module
149
-
150
- ## Release Flag
151
-
152
- new
153
-
154
- ## Acceptance Criteria
155
-
156
- - AC-1: `greeting` reads "Hi".
157
- """
158
- Then driving the process is refused with "criteria-frozen"
159
-
160
- Rule: impact
161
-
162
- Scenario: [AC-1] a triage turn with no Impact section is refused, naming it
163
- Given a file "TODO.md" with:
164
- """
165
- # Fix the typo
166
-
167
- greeting says "Helo"; make it "Hello".
168
- """
169
- And step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
170
- """
171
- # Requirements
172
-
173
- ## Release Flag
174
-
175
- new
176
-
177
- ## Acceptance Criteria
178
-
179
- - AC-1: `greeting` reads "Hello, world".
180
- """
181
- Then driving the process is refused with "impact: .gtd/REQUIREMENTS.md has no \"## Impact\" section"
182
-
183
- Scenario: [AC-9] a design.impact turn that changes the criteria is refused
184
- Given a file "TODO.md" with:
185
- """
186
- # Fix the typo
187
-
188
- ## Acceptance criteria
189
-
190
- - `greeting` reads "Hello, world".
191
- """
192
- And judge step "design.clear" answers:
193
- """
194
- [{"id":"clear","answer":"yes","p":0.9}]
195
- """
196
- And step "design.normalise" runs its script
197
- And step "design.impact" writes ".gtd/REQUIREMENTS.md" with:
198
- """
199
- # Requirements
200
-
201
- ## Impact
202
-
203
- - the greeting module
204
-
205
- ## Release Flag
206
-
207
- new
208
-
209
- ## Acceptance Criteria
210
-
211
- - AC-1: `greeting` reads "Hi".
212
- """
213
- Then driving the process is refused with "criteria-frozen"
214
-
215
- Scenario: [AC-9] a design.impact turn that adds no Impact section is refused
216
- Given a file "TODO.md" with:
217
- """
218
- # Fix the typo
219
-
220
- ## Acceptance criteria
221
-
222
- - `greeting` reads "Hello, world".
223
- """
224
- And judge step "design.clear" answers:
225
- """
226
- [{"id":"clear","answer":"yes","p":0.9}]
227
- """
228
- And step "design.normalise" runs its script
229
- And step "design.impact" appends to ".gtd/REQUIREMENTS.md":
230
- """
231
- Touches the greeting module.
232
- """
233
- Then driving the process is refused with "impact: .gtd/REQUIREMENTS.md has no \"## Impact\" section"
234
-
235
- Scenario: [AC-9] a design.impact turn that drops the sketch pointer is refused
236
- Given a file "TODO.md" with:
237
- """
238
- # Fix the typo
239
-
240
- ## Acceptance criteria
241
-
242
- - `greeting` reads "Hello, world".
243
- """
244
- And judge step "design.clear" answers:
245
- """
246
- [{"id":"clear","answer":"yes","p":0.9}]
247
- """
248
- And step "design.normalise" runs its script
249
- And step "design.impact" writes ".gtd/REQUIREMENTS.md" with:
250
- """
251
- # Requirements
252
-
253
- ## Impact
254
-
255
- - the greeting module
256
-
257
- ## Release Flag
258
-
259
- new
260
-
261
- ## Acceptance Criteria
262
-
263
- - AC-1: `greeting` reads "Hello, world".
264
- """
265
- Then driving the process is refused with "impact-only"
266
-
267
- Rule: the skip path
268
-
269
- Scenario: [AC-9] [AC-10] clear stated criteria are normalised into ID'd requirements with no triage and no design gate
270
- Given a file "TODO.md" with:
271
- """
272
- # Fix the typo
273
-
274
- ## Acceptance criteria
275
-
276
- - [ ] `greeting` reads "Hello, world".
277
- - [ ] no other export changes.
278
- """
279
- And judge step "design.clear" answers:
280
- """
281
- [{"id":"clear","answer":"yes","p":0.9}]
282
- """
283
- And step "design.normalise" runs its script
284
- And step "design.impact" appends to ".gtd/REQUIREMENTS.md":
285
- """
286
- ## Impact
287
-
288
- - the greeting module
289
- """
290
- When the process is driven
291
- Then the process rests at "seam.naive"
292
- And ".gtd/REQUIREMENTS.md" contains "- AC-1: `greeting` reads \"Hello, world\"."
293
- And ".gtd/REQUIREMENTS.md" contains "- AC-2: no other export changes."
294
- And ".gtd/REQUIREMENTS.md" contains "Stated in `TODO.md` by the sketch"
295
- And the resting step's content contains "maps where the change lands"
296
- And the commit subjects end with:
297
- """
298
- gtd(human): idle → unwind
299
- gtd(check): unwind → start-gate.check
300
- gtd(check): start-gate.check → design.clear
301
- gtd(judge): design.clear → design.normalise
302
- gtd(check): design.normalise → design.impact
303
- gtd(agent): design.impact → seam.naive
304
- """
305
-
306
- Scenario: a fast-path process recorded before design.impact existed fails replay
307
- Given a file "TODO.md" with:
308
- """
309
- # Fix the typo
310
-
311
- ## Acceptance criteria
312
-
313
- - `greeting` reads "Hello, world".
314
- """
315
- And judge step "design.clear" answers:
316
- """
317
- [{"id":"clear","answer":"yes","p":0.9}]
318
- """
319
- And step "design.normalise" runs its script
320
- When the process is driven
321
- Then the process rests at "design.impact"
322
- Given the working tree is committed with the message:
323
- """
324
- gtd(agent): seam.naive → seam.check
325
-
326
- Gtd-Step: seam.naive#1
327
- Gtd-Format: 1
328
- """
329
- Then driving the process is refused with "the workflow changed under this process"
330
-
331
- Scenario: IDs the sketch already gave its criteria are kept
332
- Given a file "TODO.md" with:
333
- """
334
- ## Acceptance criteria
335
-
336
- 1. AC-3: `greeting` reads "Hello, world".
337
- 2. AC-7: no other export changes.
338
- """
339
- And judge step "design.clear" answers:
340
- """
341
- [{"id":"clear","answer":"yes","p":0.9}]
342
- """
343
- And step "design.normalise" runs its script
344
- And step "design.impact" appends to ".gtd/REQUIREMENTS.md":
345
- """
346
- ## Impact
347
-
348
- - the greeting module
349
- """
350
- When the process is driven
351
- Then the process rests at "seam.naive"
352
- And ".gtd/REQUIREMENTS.md" contains "- AC-3: `greeting` reads \"Hello, world\"."
353
- And ".gtd/REQUIREMENTS.md" contains "- AC-7: no other export changes."
354
-
355
- Scenario: one criterion the judge finds uncheckable falls back to the full path
356
- Given a file "TODO.md" with:
357
- """
358
- # Fix the typo
359
-
360
- ## Acceptance criteria
361
-
362
- - `greeting` reads "Hello, world".
363
- - the greeting feels friendlier.
364
- """
365
- And judge step "design.clear" answers:
366
- """
367
- [{"id":"clear","answer":"no","p":0.9}]
368
- """
369
- When the process is driven
370
- Then the process rests at "design.triage"
371
- And the commit subjects end with:
372
- """
373
- gtd(check): start-gate.check → design.clear
374
- gtd(judge): design.clear → design.triage
375
- """
376
-
377
- Scenario: a sketch with no criteria heading goes straight to triage, unjudged
378
- Given a file "TODO.md" with:
379
- """
380
- # Fix the typo
381
-
382
- - `greeting` reads "Hello, world".
383
- """
384
- When the process is driven
385
- Then the process rests at "design.triage"
386
- And the commit subjects end with:
387
- """
388
- gtd(check): start-gate.check → design.triage
389
- """
@@ -1,102 +0,0 @@
1
- @inmem
2
- Feature: the criteria-coverage lens checks the change's tests against its criteria
3
-
4
- The `criteria-coverage` quality lens gets the requirements the change was
5
- built to, quoted in its prompt, and writes one finding per acceptance
6
- criterion no test exercises. Its findings are triaged like any other lens's.
7
-
8
- Background:
9
- Given the shipped workflows, as gtd install scaffolds them
10
- And an environment variable "GTD_QUALITYREVIEWS" set to "criteria-coverage"
11
- And a file "src/calc.ts" with:
12
- """
13
- export const add = (a: number, b: number) => a + b
14
- """
15
- And the working tree is committed as "feat: add calculator"
16
- And a file "TODO.md" with:
17
- """
18
- # Add subtract
19
-
20
- subtract(a, b) returns the difference.
21
- """
22
- And step "unwind" changes nothing
23
- And step "start-gate.check" changes nothing
24
- And step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
25
- """
26
- # Requirements
27
-
28
- ## Impact
29
-
30
- - the calculator module
31
-
32
- ## Acceptance Criteria
33
-
34
- - AC-1: `subtract(5, 3)` returns 2.
35
- - AC-2: `subtract(1, 3)` returns -2.
36
- """
37
- And step "design.gate.answer" changes nothing
38
- And step "seam.naive" writes "src/calc.test.ts" with:
39
- """
40
- it("subtracts", () => expect(subtract(5, 3)).toBe(2))
41
- """
42
- And step "seam.naive" writes "src/calc.ts" with:
43
- """
44
- export const add = (a: number, b: number) => a + b
45
- export const subtract = (a: number, b: number) => a - b
46
- """
47
- And step "seam.check" changes nothing
48
- And step "implement.applying" runs its script
49
- And step "implement.health.check" changes nothing
50
- And step "implement.closing" runs its script
51
-
52
- Scenario: the lens gets the closed requirements and its finding per uncovered criterion is fixed
53
- When the process is driven
54
- Then the process rests at "build.quality.criteria-coverage.reviewing"
55
- And ".gtd/REQUIREMENTS.md" does not exist
56
- And the resting step's content contains " > - AC-1: `subtract(5, 3)` returns 2."
57
- And the resting step's content contains " > - AC-2: `subtract(1, 3)` returns -2."
58
- And the resting step's content contains "one finding per criterion no test exercises"
59
-
60
- Given step "build.quality.criteria-coverage.reviewing" writes ".gtd/QUALITY.md" with:
61
- """
62
- # Quality
63
-
64
- ## AC-2 is not tested
65
-
66
- No test subtracts a larger number.
67
- """
68
- And judge step "build.quality.triage" answers:
69
- """
70
- [{"id":"Q1","answer":"mechanical","p":0.9}]
71
- """
72
- And step "build.fix.quality.fixing" writes "src/calc.test.ts" with:
73
- """
74
- it("subtracts", () => expect(subtract(5, 3)).toBe(2))
75
- it("subtracts a larger number", () => expect(subtract(1, 3)).toBe(-2))
76
- """
77
- And step "build.fix.quality.fixing" deletes ".gtd/QUALITY.md"
78
- And step "build.health.check" changes nothing
79
- And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
80
- """
81
- # Review: abc1234
82
-
83
- <!-- base: 0000000 -->
84
-
85
- ## Subtract
86
-
87
- - [ ] ./src/calc.ts#2-2 — subtract(a, b)
88
- """
89
- And step "build.review.carrying" runs its script
90
- When the process is driven
91
- Then the process rests at "build.review.await-review"
92
- And ".gtd/REVIEW.md" contains "- AC-2 is not tested ("
93
- And the commit subjects end with:
94
- """
95
- gtd(check): implement.closing → build.quality.criteria-coverage.reviewing
96
- gtd(agent): build.quality.criteria-coverage.reviewing → build.quality.triage
97
- gtd(judge): build.quality.triage → build.fix.quality.fixing
98
- gtd(agent): build.fix.quality.fixing → build.health.check
99
- gtd(check): build.health.check → build.review.reviewing
100
- gtd(agent): build.review.reviewing → build.review.carrying
101
- gtd(check): build.review.carrying → build.review.await-review
102
- """
@@ -1,147 +0,0 @@
1
- import { describe, expect, it } from "vitest"
2
- import type { Change } from "../../flows/index.js"
3
- import { criteriaOf, criterionIds, hasImpact, statedCriteria, unstableIds } from "./criteria.js"
4
-
5
- const added = (path: string, after: string): Change => ({
6
- path,
7
- status: "added",
8
- before: undefined,
9
- after,
10
- })
11
-
12
- describe("criteriaOf", () => {
13
- it("reads ID'd items, joining wrapped lines, and stops at the next section", () => {
14
- expect(
15
- criteriaOf(
16
- "# R\n\n## Acceptance Criteria\n\n- AC-1: one\n continued\n- two\n\n## Notes\n\n- AC-9: not one\n",
17
- ),
18
- ).toEqual([
19
- { id: "AC-1", text: "one continued" },
20
- { id: "", text: "two" },
21
- ])
22
- })
23
-
24
- it("skips fenced lines, reads an indented item, and stops at a subheading", () => {
25
- expect(
26
- criteriaOf(
27
- "## Acceptance Criteria\n\n- AC-1: runs\n\n```sh\n# setup\n- key: v\n```\n - AC-2: two\n\n### Notes\n\n- note\n",
28
- ),
29
- ).toEqual([
30
- { id: "AC-1", text: "runs" },
31
- { id: "AC-2", text: "two" },
32
- ])
33
- })
34
-
35
- it("reads only the level-2 section and joins an unindented wrapped line", () => {
36
- expect(
37
- criteriaOf(
38
- "### Acceptance criteria\n\n- x\n\n## Acceptance Criteria\n\n- AC-1: exits 2 when the config\nfile is missing\n",
39
- ),
40
- ).toEqual([{ id: "AC-1", text: "exits 2 when the config file is missing" }])
41
- })
42
-
43
- it("folds a nested sub-bullet into its criterion", () => {
44
- expect(
45
- criteriaOf(
46
- "## Acceptance Criteria\n\n- AC-1: exits 2 when config is bad\n - missing file\n",
47
- ),
48
- ).toEqual([{ id: "AC-1", text: "exits 2 when config is bad - missing file" }])
49
- })
50
-
51
- it("is undefined without the section", () => {
52
- expect(criteriaOf("# R\n\n- AC-1: x\n")).toBeUndefined()
53
- })
54
- })
55
-
56
- describe("statedCriteria", () => {
57
- it("numbers a stated checklist from AC-1", () => {
58
- const sketch = [added("TODO.md", "# T\n\n### Acceptance criteria:\n\n- [ ] a\n* b\n\nprose\n")]
59
- expect(statedCriteria(sketch, "abc")).toMatchObject({
60
- path: "TODO.md",
61
- commit: "abc",
62
- criteria: [
63
- { id: "AC-1", text: "a" },
64
- { id: "AC-2", text: "b" },
65
- ],
66
- })
67
- })
68
-
69
- it("renumbers when the stated IDs are partial or clash", () => {
70
- const sketch = [added("TODO.md", "## Acceptance criteria\n\n- AC-4: a\n- b\n")]
71
- expect(statedCriteria(sketch, "abc")?.criteria).toEqual([
72
- { id: "AC-1", text: "a" },
73
- { id: "AC-2", text: "b" },
74
- ])
75
- })
76
-
77
- it("ignores a list the sketch left unchanged, a file other than TODO.md, a sketch with more in it, and an ambiguous pair", () => {
78
- const list = "## Acceptance criteria\n\n- a\n"
79
- expect(
80
- statedCriteria(
81
- [{ path: "TODO.md", status: "modified", before: list, after: `${list}\nmore\n` }],
82
- "abc",
83
- ),
84
- ).toBeUndefined()
85
- expect(
86
- statedCriteria(
87
- [
88
- {
89
- path: "TODO.md",
90
- status: "modified",
91
- before: `${list}- \n`,
92
- after: `${list}- \n\nmore\n`,
93
- },
94
- ],
95
- "abc",
96
- ),
97
- ).toBeUndefined()
98
- expect(statedCriteria([added("docs/guide.md", list)], "abc")).toBeUndefined()
99
- expect(
100
- statedCriteria([added("TODO.md", list), added("src/a.ts", "x\n")], "abc"),
101
- ).toBeUndefined()
102
- expect(
103
- statedCriteria([added("TODO.md", list), added(".gtd/TODO.md", list)], "abc"),
104
- ).toBeUndefined()
105
- })
106
- })
107
-
108
- describe("criterionIds", () => {
109
- const requirements = "## Acceptance Criteria\n\n- AC-1: a\n- AC-2: b\n- AC-4: added\n- unnamed\n"
110
-
111
- it("lists every ID'd criterion", () => {
112
- expect(criterionIds(requirements)).toEqual(["AC-1", "AC-2", "AC-4"])
113
- })
114
- })
115
-
116
- describe("unstableIds", () => {
117
- const ids = { closed: ["AC-1", "AC-3"], used: ["AC-1", "AC-2", "AC-3"] }
118
-
119
- it("accepts built IDs kept or dropped and new ones numbered above all handed out", () => {
120
- expect(unstableIds(["AC-3", "AC-4", "AC-7"], ids)).toBeUndefined()
121
- expect(unstableIds([], ids)).toBeUndefined()
122
- })
123
-
124
- it("names a new ID that reuses a number handed out before, even a narrowed-away one", () => {
125
- expect(unstableIds(["AC-1", "AC-2", "AC-3"], ids)).toMatch(
126
- /^numbers AC-2 at or below AC-3, .* from AC-4$/,
127
- )
128
- })
129
-
130
- it("names every reused ID", () => {
131
- expect(unstableIds(["AC-0", "AC-2"], ids)).toMatch(/^numbers AC-0, AC-2 at or below AC-3/)
132
- })
133
- })
134
-
135
- describe("hasImpact", () => {
136
- it.each([
137
- ["a level-2 heading", "# R\n\n## Impact\n\nx\n", true],
138
- ["a mixed-case heading", "## impact\n", true],
139
- ["a level-3 heading", "### Impact\n", false],
140
- ["a level-1 heading", "# Impact\n", false],
141
- ["a longer heading", "## Impactful\n", false],
142
- ["a fenced heading", "```md\n## Impact\n```\n", false],
143
- ["no heading", "# R\n\nImpact\n", false],
144
- ])("%s", (_name, text, expected) => {
145
- expect(hasImpact(text)).toBe(expected)
146
- })
147
- })