@pmelab/gtd 58.1.0 → 59.0.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 (142) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +115 -390
  3. package/dist/chunks/{Schema-B3o6eRNU.mjs → testkit-CMBAAyRK.mjs} +13409 -3351
  4. package/dist/gtd.bundle.mjs +76 -59
  5. package/dist/testkit/gherkin.mjs +30 -2
  6. package/dist/testkit.mjs +1 -1
  7. package/package.json +3 -42
  8. package/skills/authoring/SKILL.md +31 -25
  9. package/src/cli/Cli.test.ts +15 -19
  10. package/src/cli/Cli.ts +2 -4
  11. package/src/cli/Program.test.ts +60 -29
  12. package/src/cli/Program.ts +5 -7
  13. package/src/edge/Edge.ts +3 -3
  14. package/src/flags/index.test.ts +0 -13
  15. package/src/flags/index.ts +1 -3
  16. package/src/gherkin/git-control.feature +78 -0
  17. package/src/gherkin/index.ts +35 -1
  18. package/src/install/Install.test.ts +14 -11
  19. package/src/install/Install.ts +38 -15
  20. package/src/install/index.ts +1 -2
  21. package/src/replay/Replay.test.ts +1 -1
  22. package/src/replay/index.ts +1 -1
  23. package/src/step/planStart.ts +6 -1
  24. package/src/testkit/TestRepo.test.ts +118 -2
  25. package/src/testkit/TestRepo.ts +57 -6
  26. package/src/testkit/index.ts +1 -0
  27. package/src/twins/EmittedScriptRecognizer.ts +12 -14
  28. package/src/twins/FlowScripts.ts +10 -9
  29. package/src/twins/InMemRepo.ts +26 -1
  30. package/src/twins/Layers.ts +12 -1
  31. package/src/twins/index.ts +6 -1
  32. package/src/workflow/index.ts +1 -0
  33. package/src/workflow/load.test.ts +138 -113
  34. package/src/workflow/load.ts +1 -1
  35. package/src/workflow/vars.test.ts +30 -3
  36. package/src/workflow/vars.ts +34 -2
  37. package/src/workflow-kit/index.ts +1 -1
  38. package/dist/chunks/reserved-lMYCaapY.mjs +0 -1990
  39. package/dist/chunks/review-Bfaror38.mjs +0 -427
  40. package/dist/chunks/tail-Bd7L940p.mjs +0 -355
  41. package/dist/chunks/testkit-D1-auYau.mjs +0 -10362
  42. package/dist/workflows/cleanup.mjs +0 -36
  43. package/dist/workflows/feature.mjs +0 -1018
  44. package/dist/workflows/fix.mjs +0 -41
  45. package/dist/workflows/review.mjs +0 -45
  46. package/dist/workflows/rollout.mjs +0 -48
  47. package/dist/workflows.mjs +0 -40651
  48. package/src/install/ConfigScaffold.ts +0 -22
  49. package/src/workflows/cleanup/cleanup-path.feature +0 -94
  50. package/src/workflows/cleanup/cleanup.feature +0 -315
  51. package/src/workflows/cleanup/cleanup.test.ts +0 -276
  52. package/src/workflows/cleanup/cleanup.ts +0 -52
  53. package/src/workflows/cleanup/index.ts +0 -6
  54. package/src/workflows/feature/acceptance-criteria-tier.feature +0 -25
  55. package/src/workflows/feature/acceptance-criteria.feature +0 -389
  56. package/src/workflows/feature/criteria-coverage.feature +0 -102
  57. package/src/workflows/feature/criteria.test.ts +0 -147
  58. package/src/workflows/feature/criteria.ts +0 -267
  59. package/src/workflows/feature/fast-path.feature +0 -93
  60. package/src/workflows/feature/feature.test.ts +0 -338
  61. package/src/workflows/feature/feature.ts +0 -156
  62. package/src/workflows/feature/green.ts +0 -19
  63. package/src/workflows/feature/index.ts +0 -18
  64. package/src/workflows/feature/lens-models.feature +0 -69
  65. package/src/workflows/feature/lens-review.feature +0 -566
  66. package/src/workflows/feature/naive-acceptance.feature +0 -147
  67. package/src/workflows/feature/naive-test.feature +0 -263
  68. package/src/workflows/feature/one-step-land.feature +0 -93
  69. package/src/workflows/feature/planning.ts +0 -41
  70. package/src/workflows/feature/refactor-absorbs.feature +0 -131
  71. package/src/workflows/feature/refactor-first.feature +0 -158
  72. package/src/workflows/feature/refactor-lens-models.feature +0 -79
  73. package/src/workflows/feature/refactor-rebuild.feature +0 -273
  74. package/src/workflows/feature/refactor-review.feature +0 -1528
  75. package/src/workflows/feature/refactor.test.ts +0 -1220
  76. package/src/workflows/feature/refactor.ts +0 -368
  77. package/src/workflows/feature/release-flag.feature +0 -167
  78. package/src/workflows/feature/release-flag.test.ts +0 -49
  79. package/src/workflows/feature/release-flag.ts +0 -46
  80. package/src/workflows/feature/review-gate.feature +0 -241
  81. package/src/workflows/feature/reviewer-walkthrough.feature +0 -142
  82. package/src/workflows/feature/risk-tier.feature +0 -351
  83. package/src/workflows/feature/seam-rejection.feature +0 -146
  84. package/src/workflows/feature/seam.feature +0 -297
  85. package/src/workflows/feature/seam.test.ts +0 -527
  86. package/src/workflows/feature/seam.ts +0 -426
  87. package/src/workflows/feature/standards.feature +0 -354
  88. package/src/workflows/feature/steps.test.ts +0 -65
  89. package/src/workflows/feature/steps.ts +0 -45
  90. package/src/workflows/feature/tier-pin.feature +0 -66
  91. package/src/workflows/feature/tier.test.ts +0 -166
  92. package/src/workflows/feature/tier.ts +0 -56
  93. package/src/workflows/fix/fix-lens-origin.feature +0 -114
  94. package/src/workflows/fix/fix-quality.feature +0 -212
  95. package/src/workflows/fix/fix.feature +0 -79
  96. package/src/workflows/fix/fix.test.ts +0 -74
  97. package/src/workflows/fix/fix.ts +0 -52
  98. package/src/workflows/fix/index.ts +0 -6
  99. package/src/workflows/index.ts +0 -47
  100. package/src/workflows/review/index.ts +0 -6
  101. package/src/workflows/review/review-door.feature +0 -199
  102. package/src/workflows/review/review.test.ts +0 -27
  103. package/src/workflows/review/review.ts +0 -60
  104. package/src/workflows/rollout/index.ts +0 -6
  105. package/src/workflows/rollout/rollout.feature +0 -510
  106. package/src/workflows/rollout/rollout.test.ts +0 -263
  107. package/src/workflows/rollout/rollout.ts +0 -82
  108. package/src/workflows/shared/__fixtures__/feature-collecting-prompt.txt +0 -98
  109. package/src/workflows/shared/access.test.ts +0 -62
  110. package/src/workflows/shared/access.ts +0 -24
  111. package/src/workflows/shared/config.fixture.ts +0 -12
  112. package/src/workflows/shared/fences.ts +0 -24
  113. package/src/workflows/shared/health.test.ts +0 -148
  114. package/src/workflows/shared/health.ts +0 -191
  115. package/src/workflows/shared/learn.test.ts +0 -140
  116. package/src/workflows/shared/learn.ts +0 -201
  117. package/src/workflows/shared/lenses.test.ts +0 -39
  118. package/src/workflows/shared/lenses.ts +0 -294
  119. package/src/workflows/shared/options.test.ts +0 -157
  120. package/src/workflows/shared/options.ts +0 -92
  121. package/src/workflows/shared/progress.feature +0 -124
  122. package/src/workflows/shared/prose.ts +0 -263
  123. package/src/workflows/shared/recheck.feature +0 -60
  124. package/src/workflows/shared/registry.test.ts +0 -345
  125. package/src/workflows/shared/registry.ts +0 -183
  126. package/src/workflows/shared/reserved.ts +0 -11
  127. package/src/workflows/shared/review.test.ts +0 -1062
  128. package/src/workflows/shared/review.ts +0 -446
  129. package/src/workflows/shared/skills.test.ts +0 -121
  130. package/src/workflows/shared/skills.ts +0 -49
  131. package/src/workflows/shared/standards.test.ts +0 -90
  132. package/src/workflows/shared/standards.ts +0 -156
  133. package/src/workflows/shared/steps.test.ts +0 -219
  134. package/src/workflows/shared/steps.ts +0 -201
  135. package/src/workflows/shared/tail.test.ts +0 -454
  136. package/src/workflows/shared/tail.ts +0 -264
  137. package/src/workflows/shared/text.fixture.ts +0 -128
  138. package/src/workflows/shared/text.test.ts +0 -1430
  139. package/src/workflows/shared/text.ts +0 -1700
  140. package/src/workflows/shared/threads.test.ts +0 -129
  141. package/src/workflows/shared/vars.ts +0 -47
  142. 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
- })