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