@pmelab/gtd 36.0.0 → 37.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 (50) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +21 -9
  3. package/claude/hooks/context.ts +66 -0
  4. package/claude/hooks/drive.ts +3 -0
  5. package/claude/hooks/register.tsx +69 -6
  6. package/dist/chunks/{reserved-BAv71I4i.mjs → reserved-zAfoJtR7.mjs} +53 -16
  7. package/dist/chunks/{tail-kar1zhOb.mjs → tail-BRC4DGdV.mjs} +1 -1
  8. package/dist/chunks/{wire-D3Rc5r22.mjs → wire-CSsa3Wxd.mjs} +117 -2
  9. package/dist/gtd.bundle.mjs +498 -56
  10. package/dist/testkit.mjs +115 -3
  11. package/dist/wire.mjs +2 -2
  12. package/dist/workflows/cleanup.mjs +2 -2
  13. package/dist/workflows/feature.mjs +126 -33
  14. package/dist/workflows/fix.mjs +2 -2
  15. package/dist/workflows/review.mjs +2 -2
  16. package/dist/workflows/rollout.mjs +1 -1
  17. package/dist/workflows.mjs +174 -44
  18. package/package.json +1 -1
  19. package/src/cli/Cli.test.ts +10 -3
  20. package/src/cli/Cli.ts +76 -1
  21. package/src/cli/Program.test.ts +1 -0
  22. package/src/cli/Program.ts +28 -0
  23. package/src/cli/ToolCommands.ts +3 -0
  24. package/src/edge/Edge.ts +11 -0
  25. package/src/flows/runtime.ts +11 -0
  26. package/src/replay/Replay.ts +51 -2
  27. package/src/testkit/Layers.ts +1 -0
  28. package/src/wire/BeatDocument.ts +12 -2
  29. package/src/wire/BeatStatus.ts +3 -0
  30. package/src/wire/ContextLimit.fixture.ts +30 -0
  31. package/src/wire/ContextLimit.test.ts +96 -0
  32. package/src/wire/ContextLimit.ts +169 -0
  33. package/src/wire/index.ts +1 -0
  34. package/src/wire/types.ts +7 -0
  35. package/src/workflow/Workflow.ts +9 -1
  36. package/src/workflows/feature/context-limit.feature +37 -0
  37. package/src/workflows/feature/coverage.ts +8 -2
  38. package/src/workflows/feature/feature.ts +6 -2
  39. package/src/workflows/feature/refactor-absorbs.feature +12 -5
  40. package/src/workflows/feature/refactor-first.feature +11 -2
  41. package/src/workflows/feature/refactor-lens-models.feature +4 -0
  42. package/src/workflows/feature/refactor-rebuild.feature +269 -0
  43. package/src/workflows/feature/refactor-review.feature +45 -1
  44. package/src/workflows/feature/refactor.ts +9 -3
  45. package/src/workflows/feature/seam.test.ts +108 -3
  46. package/src/workflows/feature/seam.ts +120 -19
  47. package/src/workflows/shared/options.test.ts +11 -2
  48. package/src/workflows/shared/options.ts +18 -4
  49. package/src/workflows/shared/steps.ts +1 -0
  50. package/src/workflows/shared/text.ts +80 -14
@@ -4,9 +4,9 @@ Feature: a refactor that carried the whole feature still gets a quality lap over
4
4
 
5
5
  With GTD_FLAG_RISK_TIER off, a refactor-first
6
6
  refactor can land everything the requirements ask for. The feature slice's
7
- implement turn then has nothing to author: its empty turn completes the
8
- step, and with no code changed since the refactor the feature slice's
9
- quality lap covers the whole change, refactor included.
7
+ implement turn then has only the naive pass's acceptance tests to carry
8
+ over, and with no code but tests changed since the refactor the feature
9
+ slice's quality lap covers the whole change, refactor included.
10
10
 
11
11
  Background:
12
12
  Given the shipped workflows, as gtd install scaffolds them
@@ -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: an empty 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 idle
46
46
  Given step "seam.check" changes nothing
47
47
  And step "seam.naive" writes "src/acceptance.test.ts" with:
48
48
  """
@@ -57,6 +57,10 @@ Feature: a refactor that carried the whole feature still gets a quality lap over
57
57
  """
58
58
  [{"id":"shape","answer":"refactor-first","p":0.9}]
59
59
  """
60
+ And step "seam.note" writes ".gtd/REFACTOR-NOTE.md" with:
61
+ """
62
+ Seam: the existing code the strays touch.
63
+ """
60
64
  And step "refactor.deriving" runs its script
61
65
  And step "refactor.building" writes "src/calc.ts" with:
62
66
  """
@@ -91,7 +95,10 @@ Feature: a refactor that carried the whole feature still gets a quality lap over
91
95
  - [x] ./src/calc.ts#1-2
92
96
  subtract next to add
93
97
  """
94
- And step "implement.building" changes nothing
98
+ And step "implement.building" writes "src/acceptance.test.ts" with:
99
+ """
100
+ it("[AC-1] holds", () => {})
101
+ """
95
102
  And step "implement.health.check" changes nothing
96
103
  And step "implement.closing" runs its script
97
104
  And judge step "implement.coverage" answers:
@@ -56,6 +56,10 @@ Feature: a refactor-first judgment walks one fixed path to idle
56
56
  """
57
57
  [{"id":"shape","answer":"refactor-first","p":0.9}]
58
58
  """
59
+ And step "seam.note" writes ".gtd/REFACTOR-NOTE.md" with:
60
+ """
61
+ Seam: the existing code the strays touch.
62
+ """
59
63
  And step "refactor.deriving" runs its script
60
64
  When the process is driven
61
65
  Then the process rests at "refactor.building"
@@ -106,6 +110,10 @@ Feature: a refactor-first judgment walks one fixed path to idle
106
110
  export const add = (a: number, b: number) => op(a, b, 1)
107
111
  export const subtract = (a: number, b: number) => op(a, b, -1)
108
112
  """
113
+ And step "implement.building" writes "src/acceptance.test.ts" with:
114
+ """
115
+ it("[AC-1] holds", () => {})
116
+ """
109
117
  And step "implement.health.check" changes nothing
110
118
  And step "implement.closing" runs its script
111
119
  And judge step "implement.coverage" answers:
@@ -125,7 +133,8 @@ Feature: a refactor-first judgment walks one fixed path to idle
125
133
  gtd(human): design.gate.answer → seam.naive
126
134
  gtd(agent): seam.naive → seam.check
127
135
  gtd(check): seam.check → seam.judge
128
- gtd(judge): seam.judge → refactor.deriving
136
+ gtd(judge): seam.judge → seam.note
137
+ gtd(agent): seam.note → refactor.deriving
129
138
  gtd(check): refactor.deriving → refactor.building
130
139
  gtd(agent): refactor.building → refactor.health.check
131
140
  gtd(check): refactor.health.check → refactor.closing
@@ -140,7 +149,7 @@ Feature: a refactor-first judgment walks one fixed path to idle
140
149
  """
141
150
  And the commit "gtd(human): idle → unwind" has a body containing "Gtd-Var: flag_risk_tier=0"
142
151
  And the commit "gtd(agent): seam.naive → seam.check" has a body containing "Gtd-Kept:"
143
- And the commit "gtd(judge): seam.judge → refactor.deriving" has a body containing "\"answer\":\"refactor-first\""
152
+ And the commit "gtd(judge): seam.judge → seam.note" has a body containing "\"answer\":\"refactor-first\""
144
153
  And no commit body contains "Gtd-Approved:"
145
154
  And the output lines of gtd "slices" start with:
146
155
  """
@@ -50,6 +50,10 @@ Feature: the refactor review's lenses take their model hints from refactorQualit
50
50
  """
51
51
  [{"id":"shape","answer":"refactor-first","p":0.9}]
52
52
  """
53
+ And step "seam.note" writes ".gtd/REFACTOR-NOTE.md" with:
54
+ """
55
+ Seam: the existing code the strays touch.
56
+ """
53
57
  And step "refactor.deriving" runs its script
54
58
  And step "refactor.building" writes "src/calc.ts" with:
55
59
  """
@@ -0,0 +1,269 @@
1
+ # One SDLC path per file, so a changed path reds exactly one fixture — do not merge.
2
+ @inmem
3
+ Feature: refactor-first writes a refactor note, refactors on its own, then rebuilds
4
+
5
+ On a refactor-first judgment the naive pass is never applied. `seam.note`
6
+ reads the whole naive diff and writes `.gtd/REFACTOR-NOTE.md`; the refactor
7
+ lands on its own with the existing suite green; the rebuild re-implements the
8
+ change on the refactored base and lands only once it carries the naive pass's
9
+ acceptance tests and the suite is green. The note reaches both turns.
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_REFACTORQUALITYREVIEWS" set to ""
15
+ And an environment variable "GTD_FLAG_RISK_TIER" set to "0"
16
+ And a file "src/calc.ts" with:
17
+ """
18
+ export const add = (a: number, b: number) => a + b
19
+ """
20
+ And the working tree is committed as "feat: add calculator"
21
+ And a file "TODO.md" with:
22
+ """
23
+ # Add subtract
24
+
25
+ subtract(a, b) returns the difference.
26
+ """
27
+ And step "unwind" changes nothing
28
+ And step "start-gate.check" changes nothing
29
+ And step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
30
+ """
31
+ # Requirements
32
+
33
+ ## Concerns
34
+
35
+ ### Subtract
36
+
37
+ - `subtract(a, b)` returns `a - b`.
38
+
39
+ ## Acceptance Criteria
40
+
41
+ - AC-1: subtract returns the difference.
42
+ """
43
+ And step "design.gate.answer" changes nothing
44
+ And step "seam.check" changes nothing
45
+ And step "seam.naive" writes "src/acceptance.test.ts" with:
46
+ """
47
+ it("[AC-1] subtracts", () => {})
48
+ """
49
+ And step "seam.naive" writes "src/calc.ts" with:
50
+ """
51
+ export const add = (a: number, b: number) => a + b
52
+ export const subtract = (a: number, b: number) => a - b
53
+ """
54
+ And judge step "seam.judge" answers:
55
+ """
56
+ [{"id":"shape","answer":"refactor-first","p":0.9}]
57
+ """
58
+
59
+ Scenario: the refactor note reaches the refactor turn and the rebuild turn
60
+ When the process is driven
61
+ Then the process rests at "seam.note"
62
+ And the resting step's content contains "git show"
63
+ And the resting step's content contains ".gtd/REFACTOR-NOTE.md"
64
+ And "src/calc.ts" does not contain "subtract"
65
+
66
+ Given step "seam.note" writes ".gtd/REFACTOR-NOTE.md" with:
67
+ """
68
+ # Refactor note
69
+
70
+ Seam: route add through a shared `op(a, b, sign)` in src/calc.ts.
71
+ """
72
+ And step "refactor.deriving" runs its script
73
+ When the process is driven
74
+ Then the process rests at "refactor.building"
75
+ And ".gtd/REFACTOR.md" contains "## Refactor note"
76
+ And ".gtd/REFACTOR.md" contains "shared `op(a, b, sign)`"
77
+ And the resting step's content contains "Refactor note"
78
+
79
+ Given step "refactor.building" writes "src/calc.ts" with:
80
+ """
81
+ export const op = (a: number, b: number, sign: number) => a + sign * b
82
+ export const add = (a: number, b: number) => op(a, b, 1)
83
+ """
84
+ And step "refactor.health.check" changes nothing
85
+ And step "refactor.closing" runs its script
86
+ And step "refactor.build.review.reviewing" writes ".gtd/REVIEW.md" with:
87
+ """
88
+ # Review: abc1234
89
+
90
+ <!-- base: 0000000 -->
91
+
92
+ ## calc
93
+
94
+ - [ ] ./src/calc.ts#1-2
95
+ add built on the shared op
96
+ """
97
+ And step "refactor.build.review.closing" runs its script
98
+ When the process is driven
99
+ Then the process rests at "refactor.build.review.await-review"
100
+
101
+ Given a file ".gtd/REVIEW.md" with:
102
+ """
103
+ # Review: abc1234
104
+
105
+ <!-- base: 0000000 -->
106
+
107
+ ## calc
108
+
109
+ - [x] ./src/calc.ts#1-2
110
+ add built on the shared op
111
+ """
112
+ When gtd lands
113
+ And the process is driven
114
+ Then the process rests at "implement.building"
115
+ And the resting step's content contains "shared `op(a, b, sign)`"
116
+ And the resting step's content contains "acceptance tests for AC-1"
117
+
118
+ Scenario: a note turn that changes anything but the note is refused
119
+ Given step "seam.note" writes "src/calc.ts" with:
120
+ """
121
+ export const sub = 1
122
+ """
123
+ Then driving the process is refused with "refactor-note"
124
+
125
+ Scenario: the refactor lands on its own only with the existing suite green
126
+ Given step "seam.note" writes ".gtd/REFACTOR-NOTE.md" with:
127
+ """
128
+ Seam: a shared `op` in src/calc.ts.
129
+ """
130
+ And step "refactor.deriving" runs its script
131
+ And step "refactor.building" writes "src/calc.ts" with:
132
+ """
133
+ export const op = (a: number, b: number, sign: number) => a + sign * b
134
+ export const add = (a: number, b: number) => op(a, b, -1)
135
+ """
136
+ And step "refactor.health.check" writes ".gtd/FEEDBACK.md" with:
137
+ """
138
+ FAIL add(1, 2) returned -1
139
+ """
140
+ When the process is driven
141
+ Then the process rests at "refactor.fix.suite.fixing"
142
+
143
+ Given step "refactor.fix.suite.fixing" writes "src/calc.ts" with:
144
+ """
145
+ export const op = (a: number, b: number, sign: number) => a + sign * b
146
+ export const add = (a: number, b: number) => op(a, b, 1)
147
+ """
148
+ And step "refactor.health.check" changes nothing
149
+ And step "refactor.closing" runs its script
150
+ And step "refactor.build.review.reviewing" writes ".gtd/REVIEW.md" with:
151
+ """
152
+ # Review: abc1234
153
+
154
+ <!-- base: 0000000 -->
155
+
156
+ ## calc
157
+
158
+ - [ ] ./src/calc.ts#1-2
159
+ add built on the shared op
160
+ """
161
+ And step "refactor.build.review.closing" runs its script
162
+ When the process is driven
163
+ Then the process rests at "refactor.build.review.await-review"
164
+
165
+ Given a file ".gtd/REVIEW.md" with:
166
+ """
167
+ # Review: abc1234
168
+
169
+ <!-- base: 0000000 -->
170
+
171
+ ## calc
172
+
173
+ - [x] ./src/calc.ts#1-2
174
+ add built on the shared op
175
+ """
176
+ When gtd lands
177
+ And the process is driven
178
+ Then the process rests at "implement.building"
179
+ And "src/calc.ts" contains "op(a, b, 1)"
180
+ And "src/calc.ts" does not contain "subtract"
181
+ And the output of gtd "slices" contains "refactor "
182
+
183
+ Scenario: the rebuild lands only once it carries the naive pass's acceptance tests, green
184
+ Given step "seam.note" writes ".gtd/REFACTOR-NOTE.md" with:
185
+ """
186
+ Seam: a shared `op` in src/calc.ts.
187
+ """
188
+ And step "refactor.deriving" runs its script
189
+ And step "refactor.building" writes "src/calc.ts" with:
190
+ """
191
+ export const op = (a: number, b: number, sign: number) => a + sign * b
192
+ export const add = (a: number, b: number) => op(a, b, 1)
193
+ """
194
+ And step "refactor.health.check" changes nothing
195
+ And step "refactor.closing" runs its script
196
+ And step "refactor.build.review.reviewing" writes ".gtd/REVIEW.md" with:
197
+ """
198
+ # Review: abc1234
199
+
200
+ <!-- base: 0000000 -->
201
+
202
+ ## calc
203
+
204
+ - [ ] ./src/calc.ts#1-2
205
+ add built on the shared op
206
+ """
207
+ And step "refactor.build.review.closing" runs its script
208
+ When the process is driven
209
+ Then the process rests at "refactor.build.review.await-review"
210
+
211
+ Given a file ".gtd/REVIEW.md" with:
212
+ """
213
+ # Review: abc1234
214
+
215
+ <!-- base: 0000000 -->
216
+
217
+ ## calc
218
+
219
+ - [x] ./src/calc.ts#1-2
220
+ add built on the shared op
221
+ """
222
+ When gtd lands
223
+ And the process is driven
224
+ Then the process rests at "implement.building"
225
+
226
+ Given step "implement.building" writes "src/calc.ts" with:
227
+ """
228
+ export const op = (a: number, b: number, sign: number) => a + sign * b
229
+ export const add = (a: number, b: number) => op(a, b, 1)
230
+ export const subtract = (a: number, b: number) => op(a, b, -1)
231
+ """
232
+ Then driving the process is refused with "rebuild-acceptance"
233
+
234
+ Given step "implement.building" writes "src/acceptance.test.ts" with:
235
+ """
236
+ it("[AC-1] subtracts", () => {})
237
+ """
238
+ And step "implement.health.check" changes nothing
239
+ And step "implement.closing" runs its script
240
+ And judge step "implement.coverage" answers:
241
+ """
242
+ [{"id":"AC-1","answer":"yes","p":0.9}]
243
+ """
244
+ When the process is driven
245
+ Then the process rests at "idle"
246
+ And "src/calc.ts" contains "op(a, b, -1)"
247
+ And "src/acceptance.test.ts" contains "[AC-1]"
248
+ And ".gtd/REFACTOR-NOTE.md" does not exist
249
+ And the commit subjects end with:
250
+ """
251
+ gtd(judge): seam.judge → seam.note
252
+ gtd(agent): seam.note → refactor.deriving
253
+ gtd(check): refactor.deriving → refactor.building
254
+ gtd(agent): refactor.building → refactor.health.check
255
+ gtd(check): refactor.health.check → refactor.closing
256
+ gtd(check): refactor.closing → refactor.build.review.reviewing
257
+ gtd(agent): refactor.build.review.reviewing → refactor.build.review.await-review
258
+ gtd(human): refactor.build.review.await-review → refactor.build.review.closing
259
+ gtd(check): refactor.build.review.closing → implement.building
260
+ gtd(agent): implement.building → implement.health.check
261
+ gtd(check): implement.health.check → implement.coverage
262
+ gtd(judge): implement.coverage → implement.closing
263
+ gtd(check): implement.closing → idle
264
+ """
265
+ And the output lines of gtd "slices" start with:
266
+ """
267
+ refactor
268
+ feature
269
+ """
@@ -66,6 +66,10 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
66
66
  """
67
67
  [{"id":"shape","answer":"refactor-first","p":0.9}]
68
68
  """
69
+ And step "seam.note" writes ".gtd/REFACTOR-NOTE.md" with:
70
+ """
71
+ Seam: the existing code the strays touch.
72
+ """
69
73
  And step "refactor.deriving" runs its script
70
74
  And step "refactor.building" writes "src/calc.ts" with:
71
75
  """
@@ -155,6 +159,10 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
155
159
  """
156
160
  [{"id":"shape","answer":"refactor-first","p":0.9}]
157
161
  """
162
+ And step "seam.note" writes ".gtd/REFACTOR-NOTE.md" with:
163
+ """
164
+ Seam: the existing code the strays touch.
165
+ """
158
166
  And step "refactor.deriving" runs its script
159
167
  And step "refactor.building" writes "src/calc.ts" with:
160
168
  """
@@ -243,6 +251,10 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
243
251
  """
244
252
  [{"id":"shape","answer":"refactor-first","p":0.9}]
245
253
  """
254
+ And step "seam.note" writes ".gtd/REFACTOR-NOTE.md" with:
255
+ """
256
+ Seam: the existing code the strays touch.
257
+ """
246
258
  And step "refactor.deriving" runs its script
247
259
  And step "refactor.building" writes "src/calc.ts" with:
248
260
  """
@@ -345,6 +357,10 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
345
357
  export const add = (a: number, b: number) => op(a, b, 1)
346
358
  export const subtract = (a: number, b: number) => op(a, b, -1)
347
359
  """
360
+ And step "implement.building" writes "src/acceptance.test.ts" with:
361
+ """
362
+ it("[AC-1] holds", () => {})
363
+ """
348
364
  And step "implement.health.check" changes nothing
349
365
  And step "implement.closing" runs its script
350
366
  And judge step "implement.coverage" answers:
@@ -638,6 +654,10 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
638
654
  export const add = (a: number, b: number) => op(a, b, 1)
639
655
  export const subtract = (a: number, b: number) => op(a, b, -1)
640
656
  """
657
+ And step "implement.building" writes "src/acceptance.test.ts" with:
658
+ """
659
+ it("[AC-1] holds", () => {})
660
+ """
641
661
  And step "implement.health.check" changes nothing
642
662
  And step "implement.closing" runs its script
643
663
  And judge step "implement.coverage" answers:
@@ -717,6 +737,10 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
717
737
  export const add = (a: number, b: number) => op(a, b, 1)
718
738
  export const subtract = (a: number, b: number) => op(a, b, -1)
719
739
  """
740
+ And step "implement.building" writes "src/acceptance.test.ts" with:
741
+ """
742
+ it("[AC-1] holds", () => {})
743
+ """
720
744
  And step "implement.health.check" changes nothing
721
745
  And step "implement.closing" runs its script
722
746
  And judge step "implement.coverage" answers:
@@ -983,6 +1007,10 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
983
1007
  export const add = (a: number, b: number) => op(a, b, 1)
984
1008
  export const subtract = (a: number, b: number) => op(a, b, -1)
985
1009
  """
1010
+ And step "implement.building" writes "src/acceptance.test.ts" with:
1011
+ """
1012
+ it("[AC-1] holds", () => {})
1013
+ """
986
1014
  And step "implement.health.check" changes nothing
987
1015
  And step "implement.closing" runs its script
988
1016
  And judge step "implement.coverage" answers:
@@ -1066,6 +1094,10 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
1066
1094
  export const add = (a: number, b: number) => op(a, b, 1)
1067
1095
  export const subtract = (a: number, b: number) => op(a, b, -1)
1068
1096
  """
1097
+ And step "implement.building" writes "src/acceptance.test.ts" with:
1098
+ """
1099
+ it("[AC-1] holds", () => {})
1100
+ """
1069
1101
  And step "implement.health.check" changes nothing
1070
1102
  And step "implement.closing" runs its script
1071
1103
  And judge step "implement.coverage" answers:
@@ -1212,6 +1244,10 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
1212
1244
  """
1213
1245
  [{"id":"shape","answer":"refactor-first","p":0.9}]
1214
1246
  """
1247
+ And step "seam.note" writes ".gtd/REFACTOR-NOTE.md" with:
1248
+ """
1249
+ Seam: the existing code the strays touch.
1250
+ """
1215
1251
  And step "refactor.deriving" runs its script
1216
1252
  And step "refactor.building" writes "src/calc.ts" with:
1217
1253
  """
@@ -1236,7 +1272,7 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
1236
1272
  Scenario: each slice's quality lap reviews from that slice's base
1237
1273
  When the process is driven
1238
1274
  Then the process rests at "refactor.build.quality.correctness.reviewing"
1239
- And the resting step's content contains the hash of the commit "gtd(judge): seam.judge → refactor.deriving"
1275
+ And the resting step's content contains the hash of the commit "gtd(agent): seam.note → refactor.deriving"
1240
1276
  Given step "refactor.build.quality.correctness.reviewing" changes nothing
1241
1277
  And step "implement.building" writes "src/calc.ts" with:
1242
1278
  """
@@ -1244,6 +1280,10 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
1244
1280
  export const add = (a: number, b: number) => op(a, b, 1)
1245
1281
  export const subtract = (a: number, b: number) => op(a, b, -1)
1246
1282
  """
1283
+ And step "implement.building" writes "src/acceptance.test.ts" with:
1284
+ """
1285
+ it("[AC-1] holds", () => {})
1286
+ """
1247
1287
  And step "implement.health.check" changes nothing
1248
1288
  And step "implement.closing" runs its script
1249
1289
  And judge step "implement.coverage" answers:
@@ -1318,6 +1358,10 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
1318
1358
  """
1319
1359
  [{"id":"shape","answer":"refactor-first","p":0.9}]
1320
1360
  """
1361
+ And step "seam.note" writes ".gtd/REFACTOR-NOTE.md" with:
1362
+ """
1363
+ Seam: the existing code the strays touch.
1364
+ """
1321
1365
 
1322
1366
  Scenario: a test-file stray is dropped from the derived refactor package
1323
1367
  Given step "seam.check" changes nothing
@@ -27,6 +27,8 @@ export const REFACTOR_PACKAGE = REFACTOR
27
27
  export interface RefactorOptions {
28
28
  /** Blank counts as absent: `kept()` reports `""` while the keeping turn is pending. */
29
29
  readonly reference?: string | undefined
30
+ /** The refactor note `seam.note` wrote; rides the package as `## Refactor note`. */
31
+ readonly note?: string | undefined
30
32
  }
31
33
 
32
34
  const isCode = (path: string): boolean => path !== ".gtd" && !path.startsWith(".gtd/")
@@ -203,7 +205,7 @@ const packageLines = (
203
205
  "",
204
206
  ]
205
207
 
206
- // Nested under `## Review feedback`, so every heading of the collected text drops a level.
208
+ // Nested under a `##` section (review feedback, the refactor note), so every heading drops a level.
207
209
  const demoteHeadings = (text: string): string[] => {
208
210
  const marked = markFenced(text.trimEnd())
209
211
  const lines = marked.map((m) => m.line)
@@ -230,6 +232,7 @@ interface Plan {
230
232
  readonly pattern: string
231
233
  readonly strays: readonly string[]
232
234
  readonly reference: string | undefined
235
+ readonly note: string | undefined
233
236
  }
234
237
 
235
238
  /** Undefined for every shape that is not a readable `refactor-first`; throws on a bad flag pattern. */
@@ -245,7 +248,8 @@ const plan = (shape: DiffShapeResult, options: RefactorOptions): Plan | undefine
245
248
  `gtd: refactorFlagPattern (${pattern}) produces a zero-length match in:\n${listPaths(zeroLength)}`,
246
249
  )
247
250
  const reference = options.reference?.trim() ? options.reference : undefined
248
- return { pattern, strays, reference }
251
+ const note = options.note?.trim() ? options.note : undefined
252
+ return { pattern, strays, reference, note }
249
253
  }
250
254
 
251
255
  // The in-memory `strays`, never the package file: a hand edit of its list changes nothing.
@@ -260,11 +264,12 @@ const guardOf =
260
264
 
261
265
  /** Derive, build, check and close one refactor lap; `feedback` is the collected review text of the lap before. */
262
266
  const refactorLap = async (p: Plan, guard: Guard, feedback?: string): Promise<void> => {
263
- const { pattern, strays, reference } = p
267
+ const { pattern, strays, reference, note } = p
264
268
  await run(
265
269
  "deriving",
266
270
  deriveScript([
267
271
  ...packageLines(strays, pattern, reference),
272
+ ...(note === undefined ? [] : ["## Refactor note", "", ...demoteHeadings(note), ""]),
268
273
  ...(feedback === undefined
269
274
  ? []
270
275
  : ["## Review feedback", "", ...demoteHeadings(feedback), ""]),
@@ -279,6 +284,7 @@ const refactorLap = async (p: Plan, guard: Guard, feedback?: string): Promise<vo
279
284
  pattern,
280
285
  reference,
281
286
  feedback: feedback !== undefined,
287
+ note: note !== undefined,
282
288
  }),
283
289
  { label: "Refactoring", model: coder(), system: t.builderSystem() },
284
290
  )