@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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +115 -390
- package/dist/chunks/{Schema-B3o6eRNU.mjs → testkit-CMBAAyRK.mjs} +13409 -3351
- package/dist/gtd.bundle.mjs +76 -59
- package/dist/testkit/gherkin.mjs +1 -1
- package/dist/testkit.mjs +1 -1
- package/package.json +3 -42
- package/skills/authoring/SKILL.md +31 -25
- package/src/cli/Cli.test.ts +15 -19
- package/src/cli/Cli.ts +2 -4
- package/src/cli/Program.test.ts +60 -29
- package/src/cli/Program.ts +5 -7
- package/src/edge/Edge.ts +3 -3
- package/src/flags/index.test.ts +0 -13
- package/src/flags/index.ts +1 -3
- package/src/install/Install.test.ts +14 -11
- package/src/install/Install.ts +38 -15
- package/src/install/index.ts +1 -2
- package/src/replay/Replay.test.ts +1 -1
- package/src/replay/index.ts +1 -1
- package/src/step/planStart.ts +6 -1
- package/src/twins/FlowScripts.ts +1 -1
- package/src/workflow/index.ts +1 -0
- package/src/workflow/load.test.ts +138 -113
- package/src/workflow/load.ts +1 -1
- package/src/workflow/vars.test.ts +30 -3
- package/src/workflow/vars.ts +34 -2
- package/src/workflow-kit/index.ts +1 -1
- package/dist/chunks/reserved-lMYCaapY.mjs +0 -1990
- package/dist/chunks/review-Bfaror38.mjs +0 -427
- package/dist/chunks/tail-Bd7L940p.mjs +0 -355
- package/dist/chunks/testkit-JDtpNKyi.mjs +0 -10410
- package/dist/workflows/cleanup.mjs +0 -36
- package/dist/workflows/feature.mjs +0 -1018
- package/dist/workflows/fix.mjs +0 -41
- package/dist/workflows/review.mjs +0 -45
- package/dist/workflows/rollout.mjs +0 -48
- package/dist/workflows.mjs +0 -40651
- package/src/install/ConfigScaffold.ts +0 -22
- package/src/workflows/cleanup/cleanup-path.feature +0 -94
- package/src/workflows/cleanup/cleanup.feature +0 -315
- package/src/workflows/cleanup/cleanup.test.ts +0 -276
- package/src/workflows/cleanup/cleanup.ts +0 -52
- package/src/workflows/cleanup/index.ts +0 -6
- package/src/workflows/feature/acceptance-criteria-tier.feature +0 -25
- package/src/workflows/feature/acceptance-criteria.feature +0 -389
- package/src/workflows/feature/criteria-coverage.feature +0 -102
- package/src/workflows/feature/criteria.test.ts +0 -147
- package/src/workflows/feature/criteria.ts +0 -267
- package/src/workflows/feature/fast-path.feature +0 -93
- package/src/workflows/feature/feature.test.ts +0 -338
- package/src/workflows/feature/feature.ts +0 -156
- package/src/workflows/feature/green.ts +0 -19
- package/src/workflows/feature/index.ts +0 -18
- package/src/workflows/feature/lens-models.feature +0 -69
- package/src/workflows/feature/lens-review.feature +0 -566
- package/src/workflows/feature/naive-acceptance.feature +0 -147
- package/src/workflows/feature/naive-test.feature +0 -263
- package/src/workflows/feature/one-step-land.feature +0 -93
- package/src/workflows/feature/planning.ts +0 -41
- package/src/workflows/feature/refactor-absorbs.feature +0 -131
- package/src/workflows/feature/refactor-first.feature +0 -158
- package/src/workflows/feature/refactor-lens-models.feature +0 -79
- package/src/workflows/feature/refactor-rebuild.feature +0 -273
- package/src/workflows/feature/refactor-review.feature +0 -1528
- package/src/workflows/feature/refactor.test.ts +0 -1220
- package/src/workflows/feature/refactor.ts +0 -368
- package/src/workflows/feature/release-flag.feature +0 -167
- package/src/workflows/feature/release-flag.test.ts +0 -49
- package/src/workflows/feature/release-flag.ts +0 -46
- package/src/workflows/feature/review-gate.feature +0 -241
- package/src/workflows/feature/reviewer-walkthrough.feature +0 -142
- package/src/workflows/feature/risk-tier.feature +0 -351
- package/src/workflows/feature/seam-rejection.feature +0 -146
- package/src/workflows/feature/seam.feature +0 -297
- package/src/workflows/feature/seam.test.ts +0 -527
- package/src/workflows/feature/seam.ts +0 -426
- package/src/workflows/feature/standards.feature +0 -354
- package/src/workflows/feature/steps.test.ts +0 -65
- package/src/workflows/feature/steps.ts +0 -45
- package/src/workflows/feature/tier-pin.feature +0 -66
- package/src/workflows/feature/tier.test.ts +0 -166
- package/src/workflows/feature/tier.ts +0 -56
- package/src/workflows/fix/fix-lens-origin.feature +0 -114
- package/src/workflows/fix/fix-quality.feature +0 -212
- package/src/workflows/fix/fix.feature +0 -79
- package/src/workflows/fix/fix.test.ts +0 -74
- package/src/workflows/fix/fix.ts +0 -52
- package/src/workflows/fix/index.ts +0 -6
- package/src/workflows/index.ts +0 -47
- package/src/workflows/review/index.ts +0 -6
- package/src/workflows/review/review-door.feature +0 -199
- package/src/workflows/review/review.test.ts +0 -27
- package/src/workflows/review/review.ts +0 -60
- package/src/workflows/rollout/index.ts +0 -6
- package/src/workflows/rollout/rollout.feature +0 -510
- package/src/workflows/rollout/rollout.test.ts +0 -263
- package/src/workflows/rollout/rollout.ts +0 -82
- package/src/workflows/shared/__fixtures__/feature-collecting-prompt.txt +0 -98
- package/src/workflows/shared/access.test.ts +0 -62
- package/src/workflows/shared/access.ts +0 -24
- package/src/workflows/shared/config.fixture.ts +0 -12
- package/src/workflows/shared/fences.ts +0 -24
- package/src/workflows/shared/health.test.ts +0 -148
- package/src/workflows/shared/health.ts +0 -191
- package/src/workflows/shared/learn.test.ts +0 -140
- package/src/workflows/shared/learn.ts +0 -201
- package/src/workflows/shared/lenses.test.ts +0 -39
- package/src/workflows/shared/lenses.ts +0 -294
- package/src/workflows/shared/options.test.ts +0 -157
- package/src/workflows/shared/options.ts +0 -92
- package/src/workflows/shared/progress.feature +0 -124
- package/src/workflows/shared/prose.ts +0 -263
- package/src/workflows/shared/recheck.feature +0 -60
- package/src/workflows/shared/registry.test.ts +0 -345
- package/src/workflows/shared/registry.ts +0 -183
- package/src/workflows/shared/reserved.ts +0 -11
- package/src/workflows/shared/review.test.ts +0 -1062
- package/src/workflows/shared/review.ts +0 -446
- package/src/workflows/shared/skills.test.ts +0 -121
- package/src/workflows/shared/skills.ts +0 -49
- package/src/workflows/shared/standards.test.ts +0 -90
- package/src/workflows/shared/standards.ts +0 -156
- package/src/workflows/shared/steps.test.ts +0 -219
- package/src/workflows/shared/steps.ts +0 -201
- package/src/workflows/shared/tail.test.ts +0 -454
- package/src/workflows/shared/tail.ts +0 -264
- package/src/workflows/shared/text.fixture.ts +0 -128
- package/src/workflows/shared/text.test.ts +0 -1430
- package/src/workflows/shared/text.ts +0 -1700
- package/src/workflows/shared/threads.test.ts +0 -129
- package/src/workflows/shared/vars.ts +0 -47
- 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
|
-
})
|