@pmelab/gtd 39.0.0 → 40.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 (32) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +54 -53
  3. package/dist/chunks/{reserved-B6tTYjeb.mjs → reserved-Qt5FM6Ia.mjs} +7 -5
  4. package/dist/chunks/{tail-_CZTP7Hu.mjs → tail-tAHx_AHe.mjs} +1 -1
  5. package/dist/workflows/cleanup.mjs +2 -2
  6. package/dist/workflows/feature.mjs +98 -37
  7. package/dist/workflows/fix.mjs +2 -2
  8. package/dist/workflows/review.mjs +2 -2
  9. package/dist/workflows/rollout.mjs +1 -1
  10. package/dist/workflows.mjs +103 -40
  11. package/package.json +1 -1
  12. package/skills/authoring/SKILL.md +3 -2
  13. package/src/workflows/feature/coverage.feature +5 -5
  14. package/src/workflows/feature/criteria.test.ts +20 -1
  15. package/src/workflows/feature/criteria.ts +24 -0
  16. package/src/workflows/feature/fast-path.feature +4 -4
  17. package/src/workflows/feature/feature.ts +82 -22
  18. package/src/workflows/feature/flag-mode.feature +2 -2
  19. package/src/workflows/feature/one-step-land.feature +4 -4
  20. package/src/workflows/feature/planning.ts +3 -2
  21. package/src/workflows/feature/quality-unresolved.feature +6 -6
  22. package/src/workflows/feature/refactor-absorbs.feature +3 -3
  23. package/src/workflows/feature/refactor-first.feature +5 -5
  24. package/src/workflows/feature/refactor-rebuild.feature +2 -2
  25. package/src/workflows/feature/refactor-review.feature +8 -8
  26. package/src/workflows/feature/review-gate.feature +271 -0
  27. package/src/workflows/feature/seam-rejection.feature +4 -4
  28. package/src/workflows/feature/seam.feature +2 -2
  29. package/src/workflows/feature/seam.ts +17 -7
  30. package/src/workflows/feature/slicing.feature +63 -4
  31. package/src/workflows/shared/text.ts +7 -5
  32. package/src/workflows/review/review-door.feature +0 -121
@@ -1,9 +1,9 @@
1
1
  @inmem
2
- Feature: quality findings left unresolved hold a feature process short of idle
2
+ Feature: quality findings left unresolved hold a feature process short of its review
3
3
 
4
- The quality lap is the last look `feature` takes. When the fix cap leaves
5
- `.gtd/QUALITY.md` behind, the process rests for a person instead of ending
6
- with findings nobody acted on.
4
+ When the quality lap's fix cap leaves `.gtd/QUALITY.md` behind, the process
5
+ rests for a person instead of handing findings nobody acted on to the
6
+ human review.
7
7
 
8
8
  Background:
9
9
  Given the shipped workflows, as gtd install scaffolds them
@@ -97,10 +97,10 @@ Feature: quality findings left unresolved hold a feature process short of idle
97
97
  And step "build.health.check" changes nothing
98
98
  When gtd lands
99
99
  And the process is driven
100
- Then the process rests at "idle"
100
+ Then the process rests at "build.review.reviewing"
101
101
  And the commit subjects end with:
102
102
  """
103
103
  gtd(check): build.health.check → build.quality-unresolved
104
104
  gtd(human): build.quality-unresolved → build.health.check
105
- gtd(check): build.health.check → idle
105
+ gtd(check): build.health.check → build.review.reviewing
106
106
  """
@@ -42,7 +42,7 @@ Feature: a refactor that carried the whole feature still gets a quality lap over
42
42
  """
43
43
  And step "design.gate.answer" changes nothing
44
44
 
45
- Scenario: a tests-only implement turn after a refactor that did it all reaches the quality lap and idle
45
+ Scenario: a tests-only implement turn after a refactor that did it all reaches the quality lap and the review
46
46
  Given step "seam.check" changes nothing
47
47
  And step "seam.naive" writes "src/acceptance.test.ts" with:
48
48
  """
@@ -113,7 +113,7 @@ Feature: a refactor that carried the whole feature still gets a quality lap over
113
113
 
114
114
  Given step "build.quality.correctness.reviewing" changes nothing
115
115
  When the process is driven
116
- Then the process rests at "idle"
116
+ Then the process rests at "build.review.reviewing"
117
117
  And "src/calc.ts" contains "subtract"
118
118
  And the commit subjects end with:
119
119
  """
@@ -123,7 +123,7 @@ Feature: a refactor that carried the whole feature still gets a quality lap over
123
123
  gtd(check): implement.health.check → implement.coverage
124
124
  gtd(judge): implement.coverage → implement.closing
125
125
  gtd(check): implement.closing → build.quality.correctness.reviewing
126
- gtd(agent): build.quality.correctness.reviewing → idle
126
+ gtd(agent): build.quality.correctness.reviewing → build.review.reviewing
127
127
  """
128
128
  And the output lines of gtd "slices" start with:
129
129
  """
@@ -1,10 +1,10 @@
1
1
  # One SDLC path per file, so a changed path reds exactly one fixture — do not merge.
2
2
  @inmem
3
- Feature: a refactor-first judgment walks one fixed path to idle
3
+ Feature: a refactor-first judgment walks one fixed path to the review gate
4
4
 
5
5
  With GTD_FLAG_RISK_TIER off, a naive pass touching
6
6
  an existing non-allowlisted file is ruled refactor-first, is refactored and
7
- that refactor reviewed, and only then implemented, with no feature review. The flags are
7
+ that refactor reviewed, and only then implemented and brought to the feature review. The flags are
8
8
  pinned so the per-flag test runs never move it off its path.
9
9
 
10
10
  Background:
@@ -41,7 +41,7 @@ Feature: a refactor-first judgment walks one fixed path to idle
41
41
  """
42
42
  And step "design.gate.answer" changes nothing
43
43
 
44
- Scenario: a refactor-first judgment runs refactor, its review, implement and review to idle
44
+ Scenario: a refactor-first judgment runs refactor, its review, implement and the quality lap to the feature review
45
45
  Given step "seam.check" changes nothing
46
46
  And step "seam.naive" writes "src/acceptance.test.ts" with:
47
47
  """
@@ -121,7 +121,7 @@ Feature: a refactor-first judgment walks one fixed path to idle
121
121
  [{"id":"AC-1","answer":"yes","p":0.9}]
122
122
  """
123
123
  When the process is driven
124
- Then the process rests at "idle"
124
+ Then the process rests at "build.review.reviewing"
125
125
  And "src/calc.ts" contains "op(a, b, 1)"
126
126
  And "src/calc.ts" contains "subtract"
127
127
  And the commit subjects end with:
@@ -145,7 +145,7 @@ Feature: a refactor-first judgment walks one fixed path to idle
145
145
  gtd(agent): implement.building → implement.health.check
146
146
  gtd(check): implement.health.check → implement.coverage
147
147
  gtd(judge): implement.coverage → implement.closing
148
- gtd(check): implement.closing → idle
148
+ gtd(check): implement.closing → build.review.reviewing
149
149
  """
150
150
  And the commit "gtd(human): idle → unwind" has a body containing "Gtd-Var: flag_risk_tier=0"
151
151
  And the commit "gtd(agent): seam.naive → seam.check" has a body containing "Gtd-Kept:"
@@ -242,7 +242,7 @@ Feature: refactor-first writes a refactor note, refactors on its own, then rebui
242
242
  [{"id":"AC-1","answer":"yes","p":0.9}]
243
243
  """
244
244
  When the process is driven
245
- Then the process rests at "idle"
245
+ Then the process rests at "build.review.reviewing"
246
246
  And "src/calc.ts" contains "op(a, b, -1)"
247
247
  And "src/acceptance.test.ts" contains "[AC-1]"
248
248
  And ".gtd/REFACTOR-NOTE.md" does not exist
@@ -260,7 +260,7 @@ Feature: refactor-first writes a refactor note, refactors on its own, then rebui
260
260
  gtd(agent): implement.building → implement.health.check
261
261
  gtd(check): implement.health.check → implement.coverage
262
262
  gtd(judge): implement.coverage → implement.closing
263
- gtd(check): implement.closing → idle
263
+ gtd(check): implement.closing → build.review.reviewing
264
264
  """
265
265
  And the output lines of gtd "slices" start with:
266
266
  """
@@ -4,7 +4,7 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
4
4
  After a refactor-first judgment the refactor is built, quality-lapped and
5
5
  reviewed in its own slice. Review edits re-run the refactor, never the design;
6
6
  the feature slice that follows is quality-lapped from the refactor's
7
- sign-off, with no feature review. The flags are pinned so the per-flag test runs never move it off
7
+ sign-off, then reviewed. The flags are pinned so the per-flag test runs never move it off
8
8
  its path.
9
9
 
10
10
  Background:
@@ -428,7 +428,7 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
428
428
  Then the process rests at "refactor.build.review.await-review"
429
429
  When gtd lands
430
430
  And the process is driven
431
- Then the process rests at "idle"
431
+ Then the process rests at "build.review.reviewing"
432
432
 
433
433
  Scenario: a question answered at the refactor review reaches the re-derived package as ### Answered Questions
434
434
  When the process is driven
@@ -727,7 +727,7 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
727
727
  """
728
728
  When gtd lands
729
729
  And the process is driven
730
- Then the process rests at "idle"
730
+ Then the process rests at "build.review.reviewing"
731
731
  And "src/calc.ts" contains "subtract"
732
732
 
733
733
  Scenario: a praise-only one-shot footnote signs off and the feature slice follows
@@ -769,7 +769,7 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
769
769
  """
770
770
  When gtd lands
771
771
  And the process is driven
772
- Then the process rests at "idle"
772
+ Then the process rests at "build.review.reviewing"
773
773
  And "src/calc.ts" contains "subtract"
774
774
 
775
775
  Scenario: a one-shot footnote judged edit that collecting leaves out refuses naming its anchor
@@ -1084,7 +1084,7 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
1084
1084
  """
1085
1085
  When gtd lands
1086
1086
  And the process is driven
1087
- Then the process rests at "idle"
1087
+ Then the process rests at "build.review.reviewing"
1088
1088
  And "src/calc.ts" contains "subtract"
1089
1089
 
1090
1090
  Scenario: a praise-only refactor review still signs off and the feature slice follows
@@ -1119,7 +1119,7 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
1119
1119
  """
1120
1120
  When gtd lands
1121
1121
  And the process is driven
1122
- Then the process rests at "idle"
1122
+ Then the process rests at "build.review.reviewing"
1123
1123
  And "src/calc.ts" contains "subtract"
1124
1124
 
1125
1125
  Scenario: a nit fix in the refactor slice that changes a non-stray is refused, though a glob stray widens the write access
@@ -1309,11 +1309,11 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
1309
1309
  And the resting step's content contains the hash of the commit "gtd(check): refactor.build.review.closing → implement.building"
1310
1310
  Given step "build.quality.correctness.reviewing" changes nothing
1311
1311
  When the process is driven
1312
- Then the process rests at "idle"
1312
+ Then the process rests at "build.review.reviewing"
1313
1313
  And the commit subjects end with:
1314
1314
  """
1315
1315
  gtd(check): implement.closing → build.quality.correctness.reviewing
1316
- gtd(agent): build.quality.correctness.reviewing → idle
1316
+ gtd(agent): build.quality.correctness.reviewing → build.review.reviewing
1317
1317
  """
1318
1318
 
1319
1319
  Rule: a refactor package never carries a test file, and demoted headings stop at ######
@@ -0,0 +1,271 @@
1
+ @inmem
2
+ Feature: a feature process ends at the human review
3
+
4
+ After the last slice's quality lap the process rests at the review gate,
5
+ `.gtd/REVIEW.md`. A sign-off ends the process. A note asking for a change in
6
+ behaviour starts a feedback lap — design, then implement with no new naive
7
+ pass or seam judge, the coverage judge over every criterion, the quality lap
8
+ — and the review again. Its design keeps a built criterion's ID and numbers
9
+ new behaviour above every ID handed out.
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_FLAGMODE" set to "false"
15
+ And a file "src/calc.ts" with:
16
+ """
17
+ export const add = (a: number, b: number) => a + b
18
+ """
19
+ And the working tree is committed as "feat: add calculator"
20
+ And a file "TODO.md" with:
21
+ """
22
+ # Add subtract
23
+
24
+ subtract(a, b) returns the difference.
25
+ """
26
+ And step "unwind" changes nothing
27
+ And step "start-gate.check" changes nothing
28
+ And step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
29
+ """
30
+ # Requirements
31
+
32
+ ## Concerns
33
+
34
+ ### Subtract
35
+
36
+ - `subtract(a, b)` returns `a - b`.
37
+
38
+ ## Acceptance Criteria
39
+
40
+ - AC-1: subtract(a, b) returns a - b.
41
+ """
42
+ And step "design.gate.answer" changes nothing
43
+ And step "seam.naive" writes "src/calc.test.ts" with:
44
+ """
45
+ it("[AC-1] subtracts", () => {})
46
+ """
47
+ And step "seam.naive" writes "src/calc.ts" with:
48
+ """
49
+ export const add = (a: number, b: number) => a + b
50
+ export const subtract = (a: number, b: number) => a - b
51
+ """
52
+ And step "seam.check" changes nothing
53
+ And step "implement.applying" runs its script
54
+ And step "implement.health.check" changes nothing
55
+ And step "implement.closing" runs its script
56
+ And judge step "implement.coverage" answers:
57
+ """
58
+ [{"id":"AC-1","answer":"yes","p":0.9}]
59
+ """
60
+ And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
61
+ """
62
+ # Review: abc1234
63
+
64
+ <!-- base: 0000000 -->
65
+
66
+ ## calc
67
+
68
+ - [ ] ./src/calc.ts#2-2
69
+ new subtract function
70
+ """
71
+ And step "build.review.closing" runs its script
72
+
73
+ Scenario: a feature process ends at the review gate
74
+ When the process is driven
75
+ Then the process rests at "build.review.await-review"
76
+ And ".gtd/REQUIREMENTS.md" does not exist
77
+ And the resting step's content contains the hash of the commit "feat: add calculator"
78
+ And the commit subjects end with:
79
+ """
80
+ gtd(judge): implement.coverage → implement.closing
81
+ gtd(check): implement.closing → build.review.reviewing
82
+ gtd(agent): build.review.reviewing → build.review.await-review
83
+ """
84
+
85
+ Scenario: a sign-off ends the process at idle
86
+ When the process is driven
87
+ Then the process rests at "build.review.await-review"
88
+ Given a file ".gtd/REVIEW.md" with:
89
+ """
90
+ # Review: abc1234
91
+
92
+ <!-- base: 0000000 -->
93
+
94
+ ## calc
95
+
96
+ - [x] ./src/calc.ts#2-2
97
+ new subtract function
98
+ """
99
+ When gtd lands
100
+ And the process is driven
101
+ Then the process rests at "idle"
102
+ And ".gtd/REVIEW.md" does not exist
103
+ And the commit subjects end with:
104
+ """
105
+ gtd(agent): build.review.reviewing → build.review.await-review
106
+ gtd(human): build.review.await-review → build.review.closing
107
+ gtd(check): build.review.closing → idle
108
+ """
109
+
110
+ Rule: a note asking for new behaviour runs the feedback lap
111
+
112
+ Background:
113
+ Given step "build.review.collecting" writes ".gtd/REQUIREMENTS.md" with:
114
+ """
115
+ # Requirements
116
+
117
+ ## Concerns
118
+
119
+ ### Multiply
120
+
121
+ - Export `multiply()`.
122
+ """
123
+ And step "build.review.collecting" writes ".gtd/REVIEW.md" with:
124
+ """
125
+ # Review: abc1234
126
+
127
+ <!-- base: 0000000 -->
128
+
129
+ ## calc
130
+
131
+ - [ ] ./src/calc.ts#2-2
132
+ new subtract function[^why]
133
+
134
+ [^why]:
135
+ - H: also export multiply
136
+ - A: noted in the requirements.
137
+ """
138
+ And step "re-unwind" runs its script
139
+ When the process is driven
140
+ Then the process rests at "build.review.await-review"
141
+ Given a file ".gtd/REVIEW.md" with:
142
+ """
143
+ # Review: abc1234
144
+
145
+ <!-- base: 0000000 -->
146
+
147
+ ## calc
148
+
149
+ - [ ] ./src/calc.ts#2-2
150
+ new subtract function[^why]
151
+
152
+ [^why]:
153
+ - H: also export multiply
154
+ """
155
+ When gtd lands
156
+ And the process is driven
157
+ Then the process rests at "build.review.await-review"
158
+ And ".gtd/REQUIREMENTS.md" contains "multiply"
159
+ Given a file ".gtd/REVIEW.md" with:
160
+ """
161
+ # Review: abc1234
162
+
163
+ <!-- base: 0000000 -->
164
+
165
+ ## calc
166
+
167
+ - [ ] ./src/calc.ts#2-2
168
+ new subtract function
169
+ """
170
+
171
+ Scenario: the lap keeps AC-1, adds AC-2, and covers both before the next review
172
+ Given step "design.triage" from now on changes nothing
173
+ And step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
174
+ """
175
+ # Requirements
176
+
177
+ ## Concerns
178
+
179
+ ### Multiply
180
+
181
+ - `multiply(a, b)` returns `a * b`.
182
+
183
+ ## Acceptance Criteria
184
+
185
+ - AC-1: subtract(a, b) returns a - b.
186
+ - AC-2: multiply(a, b) returns a * b.
187
+ """
188
+ And step "implement.building" writes "src/calc.ts" with:
189
+ """
190
+ export const add = (a: number, b: number) => a + b
191
+ export const subtract = (a: number, b: number) => a - b
192
+ export const multiply = (a: number, b: number) => a * b
193
+ """
194
+ And step "implement.building" writes "src/multiply.test.ts" with:
195
+ """
196
+ it("[AC-2] multiplies", () => {})
197
+ """
198
+ And judge step "implement.coverage" answers:
199
+ """
200
+ [{"id":"AC-1","answer":"yes","p":0.9},{"id":"AC-2","answer":"yes","p":0.9}]
201
+ """
202
+ When gtd lands
203
+ And the process is driven
204
+ Then the process rests at "build.review.await-review"
205
+ And ".gtd/REQUIREMENTS.md" does not exist
206
+ And the commit "gtd(judge): implement.coverage → implement.closing" has a body containing "Gtd-Mark: covers=AC-1"
207
+ And the commit subjects end with:
208
+ """
209
+ gtd(human): build.review.await-review → build.review.closing
210
+ gtd(check): build.review.closing → re-unwind
211
+ gtd(check): re-unwind → design.triage
212
+ gtd(agent): design.triage → design.gate.answer
213
+ gtd(human): design.gate.answer → implement.building
214
+ gtd(agent): implement.building → implement.health.check
215
+ gtd(check): implement.health.check → implement.coverage
216
+ gtd(judge): implement.coverage → implement.closing
217
+ gtd(check): implement.closing → build.review.reviewing
218
+ gtd(agent): build.review.reviewing → build.review.await-review
219
+ """
220
+
221
+ Scenario: a design numbering new behaviour at or below an earlier ID is refused
222
+ Given step "design.triage" from now on changes nothing
223
+ And step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
224
+ """
225
+ # Requirements
226
+
227
+ ## Concerns
228
+
229
+ ### Multiply
230
+
231
+ - `multiply(a, b)` returns `a * b`.
232
+
233
+ ## Acceptance Criteria
234
+
235
+ - AC-0: multiply(a, b) returns a * b.
236
+ - AC-1: subtract(a, b) returns a - b.
237
+ """
238
+ When gtd lands
239
+ Then driving the process is refused with "criteria-ids"
240
+
241
+ Scenario: the lap holds a new criterion to coverage
242
+ Given step "design.triage" from now on changes nothing
243
+ And step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
244
+ """
245
+ # Requirements
246
+
247
+ ## Concerns
248
+
249
+ ### Multiply
250
+
251
+ - `multiply(a, b)` returns `a * b`.
252
+
253
+ ## Acceptance Criteria
254
+
255
+ - AC-1: subtract(a, b) returns a - b.
256
+ - AC-2: multiply(a, b) returns a * b.
257
+ """
258
+ And step "implement.building" writes "src/calc.ts" with:
259
+ """
260
+ export const add = (a: number, b: number) => a + b
261
+ export const subtract = (a: number, b: number) => a - b
262
+ export const multiply = (a: number, b: number) => a * b
263
+ """
264
+ And judge step "implement.coverage" answers:
265
+ """
266
+ [{"id":"AC-1","answer":"yes","p":0.9},{"id":"AC-2","answer":"no","p":0.9}]
267
+ """
268
+ When gtd lands
269
+ And the process is driven
270
+ Then the process rests at "implement.covering"
271
+ And the resting step's content contains "AC-2"
@@ -1,6 +1,6 @@
1
1
  # One SDLC path per file, so a changed path reds exactly one fixture — do not merge.
2
2
  @inmem
3
- Feature: rejected naive passes walk one fixed path through the steer gate to idle
3
+ Feature: rejected naive passes walk one fixed path through the steer gate to the review gate
4
4
 
5
5
  With GTD_FLAG_RISK_TIER off, three rejected naive
6
6
  passes rest at the steer gate, where a human edit restarts the seam and the
@@ -40,7 +40,7 @@ Feature: rejected naive passes walk one fixed path through the steer gate to idl
40
40
  """
41
41
  And step "design.gate.answer" changes nothing
42
42
 
43
- Scenario: three rejects rest at the steer gate, a steered rerun lands and runs to idle
43
+ Scenario: three rejects rest at the steer gate, a steered rerun lands and runs to the review gate
44
44
  Given step "seam.check" changes nothing
45
45
  And step "seam.naive" writes "src/acceptance.test.ts" with:
46
46
  """
@@ -100,7 +100,7 @@ Feature: rejected naive passes walk one fixed path through the steer gate to idl
100
100
  And step "implement.applying" runs its script
101
101
  When gtd lands
102
102
  And the process is driven
103
- Then the process rests at "idle"
103
+ Then the process rests at "build.review.reviewing"
104
104
  And the commit subjects end with:
105
105
  """
106
106
  gtd(human): idle → unwind
@@ -124,7 +124,7 @@ Feature: rejected naive passes walk one fixed path through the steer gate to idl
124
124
  gtd(check): implement.applying → implement.health.check
125
125
  gtd(check): implement.health.check → implement.coverage
126
126
  gtd(judge): implement.coverage → implement.closing
127
- gtd(check): implement.closing → idle
127
+ gtd(check): implement.closing → build.review.reviewing
128
128
  """
129
129
  And the commit "gtd(human): idle → unwind" has a body containing "Gtd-Var: flag_risk_tier=0"
130
130
  And the commit "gtd(agent): seam.naive → seam.check" has a body containing "Gtd-Kept:"
@@ -152,7 +152,7 @@ Feature: the naive pass and seam judge decide how a feature is built
152
152
  [{"id":"AC-1","answer":"yes","p":0.9}]
153
153
  """
154
154
  When the process is driven
155
- Then the process rests at "idle"
155
+ Then the process rests at "build.review.reviewing"
156
156
  And "src/calc.ts" contains "subtract"
157
157
  And the commit subjects end with:
158
158
  """
@@ -160,7 +160,7 @@ Feature: the naive pass and seam judge decide how a feature is built
160
160
  gtd(check): implement.applying → implement.health.check
161
161
  gtd(check): implement.health.check → implement.coverage
162
162
  gtd(judge): implement.coverage → implement.closing
163
- gtd(check): implement.closing → idle
163
+ gtd(check): implement.closing → build.review.reviewing
164
164
  """
165
165
  And ".gtd/REQUIREMENTS.md" does not exist
166
166
 
@@ -283,7 +283,7 @@ export interface ImplementOptions {
283
283
  /** Where the slice began: the coverage judge reads only the tests changed since. */
284
284
  readonly base?: string | undefined
285
285
  /** Not the last slice: `.gtd/REQUIREMENTS.md` and `.gtd/SLICES.md` stay for the next one. */
286
- readonly more?: boolean | undefined
286
+ readonly more?: boolean | (() => boolean) | undefined
287
287
  }
288
288
 
289
289
  /** Resolves `false` when the tip no longer applies (the failure is in `.gtd/FEEDBACK.md`) or has nothing outside `.gtd/` to apply. */
@@ -351,14 +351,19 @@ export function implement(
351
351
  tier?: RiskTier,
352
352
  amend?: boolean,
353
353
  ): Promise<void>
354
- export function implement(
354
+ export async function implement(
355
355
  first?: string | ImplementOptions,
356
356
  tier?: RiskTier,
357
357
  amend?: boolean,
358
358
  ): Promise<void> {
359
- const options: ImplementOptions =
360
- typeof first === "object" && first !== null ? first : { reference: first, tier, amend }
361
- return scope("implement", async () => {
359
+ await implementing(
360
+ typeof first === "object" && first !== null ? first : { reference: first, tier, amend },
361
+ )
362
+ }
363
+
364
+ /** `implement`, resolving with the criterion IDs the requirements listed when it closed. */
365
+ export const implementing = (options: ImplementOptions): Promise<readonly string[]> =>
366
+ scope("implement", async () => {
362
367
  const since = head()
363
368
  const acceptance = options.rebuild?.acceptance
364
369
  const built = options.naive === undefined || !(await applied(options.naive))
@@ -372,9 +377,14 @@ export function implement(
372
377
  built && acceptance !== undefined ? () => requireAcceptanceKept(acceptance) : undefined,
373
378
  )
374
379
  await requireCoverage(options.base, options.slice)
375
- await closing(options.more === true)
380
+ const ids = criterionIds(read(REQUIREMENTS) ?? "")
381
+ await closing(moreToBuild(options.more))
382
+ return ids
376
383
  })
377
- }
384
+
385
+ // Read at closing: a narrowing at `implement.oversized` may have emptied every later slice.
386
+ const moreToBuild = (more: ImplementOptions["more"]): boolean =>
387
+ typeof more === "function" ? more() : more === true
378
388
 
379
389
  /** The last slice's closing, for a last slice a steer left with no criterion to build. */
380
390
  export const closeOut = (): Promise<void> => scope("implement", () => closing(false))
@@ -51,7 +51,7 @@ Feature: a change with more criteria than the cap is built in coarse slices
51
51
  [{"id":"AC-1","answer":"yes","p":0.9},{"id":"AC-2","answer":"yes","p":0.9}]
52
52
  """
53
53
  When the process is driven
54
- Then the process rests at "idle"
54
+ Then the process rests at "build.review.reviewing"
55
55
  And the commit subjects end with:
56
56
  """
57
57
  gtd(human): design.gate.answer → seam.naive
@@ -60,7 +60,7 @@ Feature: a change with more criteria than the cap is built in coarse slices
60
60
  gtd(check): implement.applying → implement.health.check
61
61
  gtd(check): implement.health.check → implement.coverage
62
62
  gtd(judge): implement.coverage → implement.closing
63
- gtd(check): implement.closing → idle
63
+ gtd(check): implement.closing → build.review.reviewing
64
64
  """
65
65
  And the output lines of gtd "slices" start with:
66
66
  """
@@ -112,7 +112,7 @@ Feature: a change with more criteria than the cap is built in coarse slices
112
112
  [{"id":"AC-2","answer":"yes","p":0.9}]
113
113
  """
114
114
  When the process is driven
115
- Then the process rests at "idle"
115
+ Then the process rests at "build.review.reviewing"
116
116
  And the commit subjects end with:
117
117
  """
118
118
  gtd(agent): design.triage → design.gate.answer
@@ -129,7 +129,7 @@ Feature: a change with more criteria than the cap is built in coarse slices
129
129
  gtd(check): implement.applying → implement.health.check
130
130
  gtd(check): implement.health.check → implement.coverage
131
131
  gtd(judge): implement.coverage → implement.closing
132
- gtd(check): implement.closing → idle
132
+ gtd(check): implement.closing → build.review.reviewing
133
133
  """
134
134
  And the output lines of gtd "slices" start with:
135
135
  """
@@ -223,3 +223,62 @@ Feature: a change with more criteria than the cap is built in coarse slices
223
223
  - AC-2
224
224
  """
225
225
  Then driving the process is refused with "wrote outside its write access"
226
+
227
+ Scenario: a steer that empties the last slice still ends at the review of the whole change
228
+ Given step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
229
+ """
230
+ # Requirements
231
+
232
+ ## Acceptance Criteria
233
+
234
+ - AC-1: subtract(a, b) returns a - b.
235
+ - AC-2: multiply(a, b) returns a * b.
236
+ - AC-3: divide(a, b) returns a / b.
237
+ """
238
+ And step "design.slicing" writes ".gtd/SLICES.md" with:
239
+ """
240
+ # Slices
241
+
242
+ ## Subtract and divide
243
+
244
+ - AC-1
245
+ - AC-3
246
+
247
+ ## Multiply
248
+
249
+ - AC-2
250
+ """
251
+ And step "seam.naive" writes "src/sub.test.ts" with:
252
+ """
253
+ it("[AC-1] subtracts", () => {})
254
+ """
255
+ And judge step "implement.coverage" answers:
256
+ """
257
+ [{"id":"AC-1","answer":"yes","p":0.9}]
258
+ """
259
+ When the process is driven
260
+ Then the process rests at "seam.steer"
261
+ Given a file ".gtd/REQUIREMENTS.md" with:
262
+ """
263
+ # Requirements
264
+
265
+ ## Acceptance Criteria
266
+
267
+ - AC-1: subtract(a, b) returns a - b.
268
+ """
269
+ When gtd lands
270
+ And the process is driven
271
+ Then the process rests at "build.review.reviewing"
272
+ And the commit subjects end with:
273
+ """
274
+ gtd(human): seam.steer → seam.naive
275
+ gtd(agent): seam.naive → seam.check
276
+ gtd(check): seam.check → implement.applying
277
+ gtd(check): implement.applying → implement.health.check
278
+ gtd(check): implement.health.check → implement.coverage
279
+ gtd(judge): implement.coverage → implement.closing
280
+ gtd(check): implement.closing → build.review.reviewing
281
+ """
282
+ And ".gtd/REQUIREMENTS.md" does not exist
283
+ And ".gtd/SLICES.md" does not exist
284
+ And the output of gtd "slices" does not contain "feature-2"