@pmelab/gtd 38.0.0 → 38.1.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 +6 -0
- package/dist/chunks/{reserved-sfMdy7v7.mjs → reserved-CfQ8eurM.mjs} +55 -11
- package/dist/chunks/{tail-Cgca5Gp-.mjs → tail-C7yaWvaS.mjs} +1 -1
- package/dist/chunks/{wire-CSsa3Wxd.mjs → wire-BJuZWR3C.mjs} +1 -1
- package/dist/gtd.bundle.mjs +1 -1
- package/dist/testkit.mjs +1 -1
- package/dist/wire.mjs +1 -1
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +158 -42
- package/dist/workflows/fix.mjs +2 -2
- package/dist/workflows/review.mjs +2 -2
- package/dist/workflows/rollout.mjs +1 -1
- package/dist/workflows.mjs +230 -70
- package/package.json +1 -1
- package/src/testkit/steps.ts +4 -0
- package/src/wire/ContextLimit.ts +1 -1
- package/src/workflows/feature/continuation-cap.feature +65 -0
- package/src/workflows/feature/continuation-rebuild.feature +88 -0
- package/src/workflows/feature/continuation-slices.feature +64 -0
- package/src/workflows/feature/continuation.feature +205 -0
- package/src/workflows/feature/criteria.ts +11 -0
- package/src/workflows/feature/refactor.ts +64 -30
- package/src/workflows/feature/seam.ts +102 -30
- package/src/workflows/shared/continuation.ts +92 -0
- package/src/workflows/shared/options.ts +2 -0
- package/src/workflows/shared/steps.ts +1 -0
- package/src/workflows/shared/text.test.ts +6 -2
- package/src/workflows/shared/text.ts +104 -8
- package/src/workflows/shared/vars.ts +5 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
@inmem
|
|
2
|
+
Feature: a rebuild stopped at its context limit is continued on the branch
|
|
3
|
+
|
|
4
|
+
The rebuild's snapshot is its own commit: the continuation finds the work
|
|
5
|
+
in the tree, and the health loop greens the whole build afterwards.
|
|
6
|
+
`seamAllow` is empty here, so the naive pass's change to an existing test
|
|
7
|
+
file is the judge's only stray and refactor-first rebuilds with no refactor.
|
|
8
|
+
|
|
9
|
+
Background:
|
|
10
|
+
Given the shipped workflows, as gtd install scaffolds them
|
|
11
|
+
And an environment variable "GTD_SEAMALLOW" set to ""
|
|
12
|
+
And an environment variable "GTD_QUALITYREVIEWS" set to ""
|
|
13
|
+
And an environment variable "GTD_REFACTORQUALITYREVIEWS" set to ""
|
|
14
|
+
And an environment variable "GTD_FLAG_RISK_TIER" set to "0"
|
|
15
|
+
And a file "src/calc.ts" with:
|
|
16
|
+
"""
|
|
17
|
+
export const add = (a: number, b: number) => a + b
|
|
18
|
+
"""
|
|
19
|
+
And a file "tests/calc.test.ts" with:
|
|
20
|
+
"""
|
|
21
|
+
it("adds", () => {})
|
|
22
|
+
"""
|
|
23
|
+
And the working tree is committed as "feat: add calculator"
|
|
24
|
+
And a file "TODO.md" with:
|
|
25
|
+
"""
|
|
26
|
+
# Add subtract and multiply
|
|
27
|
+
"""
|
|
28
|
+
And step "unwind" changes nothing
|
|
29
|
+
And step "start-gate.check" changes nothing
|
|
30
|
+
And step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
|
|
31
|
+
"""
|
|
32
|
+
# Requirements
|
|
33
|
+
|
|
34
|
+
## Acceptance Criteria
|
|
35
|
+
|
|
36
|
+
- AC-1: `subtract(5, 3)` returns 2.
|
|
37
|
+
- AC-2: `multiply(2, 3)` returns 6.
|
|
38
|
+
"""
|
|
39
|
+
And step "design.gate.answer" changes nothing
|
|
40
|
+
And step "seam.check" changes nothing
|
|
41
|
+
|
|
42
|
+
Scenario: a rebuild stopped at its context limit lands its snapshot and is continued on the branch
|
|
43
|
+
Given step "seam.naive" writes "src/ops.ts" with:
|
|
44
|
+
"""
|
|
45
|
+
export const subtract = (a: number, b: number) => a - b
|
|
46
|
+
export const multiply = (a: number, b: number) => a * b
|
|
47
|
+
"""
|
|
48
|
+
And step "seam.naive" writes "tests/calc.test.ts" with:
|
|
49
|
+
"""
|
|
50
|
+
it("adds", () => {})
|
|
51
|
+
it("[AC-1] subtracts", () => {})
|
|
52
|
+
it("[AC-2] multiplies", () => {})
|
|
53
|
+
"""
|
|
54
|
+
And judge step "seam.judge" answers:
|
|
55
|
+
"""
|
|
56
|
+
[{"id":"shape","answer":"refactor-first","p":0.9}]
|
|
57
|
+
"""
|
|
58
|
+
And step "implement.building" writes "tests/calc.test.ts" with:
|
|
59
|
+
"""
|
|
60
|
+
it("adds", () => {})
|
|
61
|
+
it("[AC-1] subtracts", () => {})
|
|
62
|
+
"""
|
|
63
|
+
And step "implement.building" writes ".gtd/HANDOFF.md" with:
|
|
64
|
+
"""
|
|
65
|
+
Rebuilt subtract behind the flag; multiply was next.
|
|
66
|
+
"""
|
|
67
|
+
When the process is driven
|
|
68
|
+
Then the process rests at "implement.continue.building"
|
|
69
|
+
And the resting step's content contains "Still open: AC-2."
|
|
70
|
+
And the resting step's content contains "Its work is already in the tree."
|
|
71
|
+
And the resting step's content contains "Delete `.gtd/HANDOFF.md` once you are done"
|
|
72
|
+
And "tests/calc.test.ts" contains "[AC-1] subtracts"
|
|
73
|
+
|
|
74
|
+
Given step "implement.continue.building" writes "tests/calc.test.ts" with:
|
|
75
|
+
"""
|
|
76
|
+
it("adds", () => {})
|
|
77
|
+
it("[AC-1] subtracts", () => {})
|
|
78
|
+
it("[AC-2] multiplies", () => {})
|
|
79
|
+
"""
|
|
80
|
+
And step "implement.continue.building" deletes ".gtd/HANDOFF.md"
|
|
81
|
+
When the process is driven
|
|
82
|
+
Then the process rests at "implement.health.check"
|
|
83
|
+
And the commit subjects end with:
|
|
84
|
+
"""
|
|
85
|
+
gtd(agent): implement.building → implement.continue.building
|
|
86
|
+
gtd(agent): implement.continue.building → implement.health.check
|
|
87
|
+
"""
|
|
88
|
+
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
@inmem
|
|
2
|
+
Feature: a continuation stays inside its coarse slice
|
|
3
|
+
|
|
4
|
+
A naive pass of one coarse slice that stops at its context limit is
|
|
5
|
+
continued with that slice's criteria still open, never another slice's.
|
|
6
|
+
|
|
7
|
+
Background:
|
|
8
|
+
Given the shipped workflows, as gtd install scaffolds them
|
|
9
|
+
And an environment variable "GTD_QUALITYREVIEWS" set to ""
|
|
10
|
+
And an environment variable "GTD_FLAGMODE" set to "false"
|
|
11
|
+
And an environment variable "GTD_SLICEMAXCRITERIA" set to "2"
|
|
12
|
+
And a file "src/calc.ts" with:
|
|
13
|
+
"""
|
|
14
|
+
export const add = (a: number, b: number) => a + b
|
|
15
|
+
"""
|
|
16
|
+
And the working tree is committed as "feat: add calculator"
|
|
17
|
+
And a file "TODO.md" with:
|
|
18
|
+
"""
|
|
19
|
+
# More arithmetic
|
|
20
|
+
|
|
21
|
+
subtract, multiply and divide.
|
|
22
|
+
"""
|
|
23
|
+
And step "unwind" changes nothing
|
|
24
|
+
And step "start-gate.check" changes nothing
|
|
25
|
+
And step "design.gate.answer" changes nothing
|
|
26
|
+
And step "seam.check" changes nothing
|
|
27
|
+
|
|
28
|
+
Scenario: the first slice's continuation is told only that slice's open criterion
|
|
29
|
+
Given step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
|
|
30
|
+
"""
|
|
31
|
+
# Requirements
|
|
32
|
+
|
|
33
|
+
## Acceptance Criteria
|
|
34
|
+
|
|
35
|
+
- AC-1: subtract(a, b) returns a - b.
|
|
36
|
+
- AC-2: multiply(a, b) returns a * b.
|
|
37
|
+
- AC-3: divide(a, b) returns a / b.
|
|
38
|
+
"""
|
|
39
|
+
And step "design.slicing" writes ".gtd/SLICES.md" with:
|
|
40
|
+
"""
|
|
41
|
+
# Slices
|
|
42
|
+
|
|
43
|
+
## Subtract and divide
|
|
44
|
+
|
|
45
|
+
- AC-1
|
|
46
|
+
- AC-3
|
|
47
|
+
|
|
48
|
+
## Multiply
|
|
49
|
+
|
|
50
|
+
- AC-2
|
|
51
|
+
"""
|
|
52
|
+
And step "seam.naive" writes "src/sub.test.ts" with:
|
|
53
|
+
"""
|
|
54
|
+
it("[AC-1] subtracts", () => {})
|
|
55
|
+
"""
|
|
56
|
+
And step "seam.naive" writes ".gtd/HANDOFF.md" with:
|
|
57
|
+
"""
|
|
58
|
+
Subtract is tested; divide was next.
|
|
59
|
+
"""
|
|
60
|
+
When the process is driven
|
|
61
|
+
Then the process rests at "seam.continue.naive"
|
|
62
|
+
And the resting step's content contains "This slice builds AC-1, AC-3 only"
|
|
63
|
+
And the resting step's content contains "Still open: AC-3."
|
|
64
|
+
And the resting step's content does not contain "AC-2"
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
@inmem
|
|
2
|
+
Feature: a turn stopped at its context limit is snapshotted and continued
|
|
3
|
+
|
|
4
|
+
A naive pass, refactor or build turn told to stop at its context limit
|
|
5
|
+
writes its handoff note to `.gtd/HANDOFF.md`. That turn is the snapshot —
|
|
6
|
+
the naive pass's kept tip, or the refactor's or build's own commit — and a
|
|
7
|
+
continuation in a fresh conversation (`<phase>.continue.<step>`) picks up
|
|
8
|
+
from it with the note and what is still open. After `continuations` (3) of
|
|
9
|
+
them, or `continuationStall` (2) in a row that close nothing still open, the
|
|
10
|
+
phase rests at `<phase>.oversized`.
|
|
11
|
+
|
|
12
|
+
Background:
|
|
13
|
+
Given the shipped workflows, as gtd install scaffolds them
|
|
14
|
+
And an environment variable "GTD_QUALITYREVIEWS" set to ""
|
|
15
|
+
And an environment variable "GTD_REFACTORQUALITYREVIEWS" set to ""
|
|
16
|
+
And an environment variable "GTD_FLAG_RISK_TIER" set to "0"
|
|
17
|
+
And a file "src/calc.ts" with:
|
|
18
|
+
"""
|
|
19
|
+
export const add = (a: number, b: number) => a + b
|
|
20
|
+
"""
|
|
21
|
+
And the working tree is committed as "feat: add calculator"
|
|
22
|
+
And a file "TODO.md" with:
|
|
23
|
+
"""
|
|
24
|
+
# Add subtract and multiply
|
|
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
|
+
## Acceptance Criteria
|
|
33
|
+
|
|
34
|
+
- AC-1: `subtract(5, 3)` returns 2.
|
|
35
|
+
- AC-2: `multiply(2, 3)` returns 6.
|
|
36
|
+
"""
|
|
37
|
+
And step "design.gate.answer" changes nothing
|
|
38
|
+
And step "seam.check" changes nothing
|
|
39
|
+
|
|
40
|
+
Scenario: a naive pass stopped at its context limit is continued from its kept tip with the open IDs
|
|
41
|
+
Given step "seam.naive" writes "tests/calc.test.ts" with:
|
|
42
|
+
"""
|
|
43
|
+
it("[AC-1] subtracts", () => {})
|
|
44
|
+
"""
|
|
45
|
+
And step "seam.naive" writes ".gtd/HANDOFF.md" with:
|
|
46
|
+
"""
|
|
47
|
+
Wrote subtract and its test; multiply was next.
|
|
48
|
+
"""
|
|
49
|
+
When the process is driven
|
|
50
|
+
Then the process rests at "seam.continue.naive"
|
|
51
|
+
And the resting step's content contains "Your previous turn stopped at its context limit"
|
|
52
|
+
And the resting step's content contains "Still open: AC-2."
|
|
53
|
+
And the resting step's content contains "Wrote subtract and its test; multiply was next."
|
|
54
|
+
And the resting step's content contains "':(top)' ':(top,exclude).gtd/HANDOFF.md' | git apply"
|
|
55
|
+
And ".gtd/HANDOFF.md" does not exist
|
|
56
|
+
And the output of gtd "next --json" contains "\"resume\":false"
|
|
57
|
+
And the output of gtd "next --json" contains "\"memory\":\"seam.continue#"
|
|
58
|
+
|
|
59
|
+
Given step "seam.continue.naive" writes "tests/calc.test.ts" with:
|
|
60
|
+
"""
|
|
61
|
+
it("[AC-1] subtracts", () => {})
|
|
62
|
+
it("[AC-2] multiplies", () => {})
|
|
63
|
+
"""
|
|
64
|
+
When the process is driven
|
|
65
|
+
Then the process rests at "implement.applying"
|
|
66
|
+
And the commit subjects end with:
|
|
67
|
+
"""
|
|
68
|
+
gtd(human): design.gate.answer → seam.naive
|
|
69
|
+
gtd(agent): seam.naive → seam.continue.naive
|
|
70
|
+
gtd(agent): seam.continue.naive → seam.check
|
|
71
|
+
gtd(check): seam.check → implement.applying
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
Scenario: two continuations in a row that close nothing rest for a person
|
|
75
|
+
Given step "seam.naive" writes "tests/calc.test.ts" with:
|
|
76
|
+
"""
|
|
77
|
+
it("[AC-1] subtracts", () => {})
|
|
78
|
+
"""
|
|
79
|
+
And step "seam.naive" writes ".gtd/HANDOFF.md" with:
|
|
80
|
+
"""
|
|
81
|
+
Subtract is tested; multiply was next.
|
|
82
|
+
"""
|
|
83
|
+
And step "seam.continue.naive" writes ".gtd/HANDOFF.md" with:
|
|
84
|
+
"""
|
|
85
|
+
Still on multiply.
|
|
86
|
+
"""
|
|
87
|
+
When the process is driven
|
|
88
|
+
Then the process rests at "seam.oversized"
|
|
89
|
+
And the resting step's content contains "the last 2 continuations closed nothing still open"
|
|
90
|
+
And the commit subjects end with:
|
|
91
|
+
"""
|
|
92
|
+
gtd(human): design.gate.answer → seam.naive
|
|
93
|
+
gtd(agent): seam.naive → seam.continue.naive
|
|
94
|
+
gtd(agent): seam.continue.naive
|
|
95
|
+
gtd(agent): seam.continue.naive → seam.oversized
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
Scenario: a refactor stopped at its context limit is run green before its continuation
|
|
99
|
+
Given step "seam.naive" writes "tests/calc.test.ts" with:
|
|
100
|
+
"""
|
|
101
|
+
it("[AC-1] subtracts", () => {})
|
|
102
|
+
it("[AC-2] multiplies", () => {})
|
|
103
|
+
"""
|
|
104
|
+
And step "seam.naive" writes "src/calc.ts" with:
|
|
105
|
+
"""
|
|
106
|
+
export const add = (a: number, b: number) => a + b
|
|
107
|
+
export const subtract = (a: number, b: number) => a - b
|
|
108
|
+
export const multiply = (a: number, b: number) => a * b
|
|
109
|
+
"""
|
|
110
|
+
And judge step "seam.judge" answers:
|
|
111
|
+
"""
|
|
112
|
+
[{"id":"shape","answer":"refactor-first","p":0.9}]
|
|
113
|
+
"""
|
|
114
|
+
And step "seam.note" writes ".gtd/REFACTOR-NOTE.md" with:
|
|
115
|
+
"""
|
|
116
|
+
Seam: a shared `op` in src/calc.ts.
|
|
117
|
+
"""
|
|
118
|
+
And step "refactor.deriving" runs its script
|
|
119
|
+
And step "refactor.building" writes ".gtd/HANDOFF.md" with:
|
|
120
|
+
"""
|
|
121
|
+
Started on op; nothing changed yet.
|
|
122
|
+
"""
|
|
123
|
+
And step "refactor.health.check" changes nothing
|
|
124
|
+
When the process is driven
|
|
125
|
+
Then the process rests at "refactor.continue.building"
|
|
126
|
+
And the resting step's content contains "Still open: src/calc.ts."
|
|
127
|
+
And the resting step's content contains "Started on op; nothing changed yet."
|
|
128
|
+
And the commit subjects end with:
|
|
129
|
+
"""
|
|
130
|
+
gtd(agent): refactor.building → refactor.health.check
|
|
131
|
+
gtd(check): refactor.health.check → refactor.continue.building
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
Scenario: a refactor continuation that leaves the note in place stopped again
|
|
135
|
+
Given step "seam.naive" writes "tests/calc.test.ts" with:
|
|
136
|
+
"""
|
|
137
|
+
it("[AC-1] subtracts", () => {})
|
|
138
|
+
it("[AC-2] multiplies", () => {})
|
|
139
|
+
"""
|
|
140
|
+
And step "seam.naive" writes "src/calc.ts" with:
|
|
141
|
+
"""
|
|
142
|
+
export const add = (a: number, b: number) => a + b
|
|
143
|
+
export const subtract = (a: number, b: number) => a - b
|
|
144
|
+
"""
|
|
145
|
+
And judge step "seam.judge" answers:
|
|
146
|
+
"""
|
|
147
|
+
[{"id":"shape","answer":"refactor-first","p":0.9}]
|
|
148
|
+
"""
|
|
149
|
+
And step "seam.note" writes ".gtd/REFACTOR-NOTE.md" with:
|
|
150
|
+
"""
|
|
151
|
+
Seam: a shared `op` in src/calc.ts.
|
|
152
|
+
"""
|
|
153
|
+
And step "refactor.deriving" runs its script
|
|
154
|
+
And step "refactor.building" writes "src/calc.ts" with:
|
|
155
|
+
"""
|
|
156
|
+
export const op = (a: number, b: number, sign: number) => a + sign * b
|
|
157
|
+
export const add = (a: number, b: number) => a + b
|
|
158
|
+
"""
|
|
159
|
+
And step "refactor.building" writes ".gtd/HANDOFF.md" with:
|
|
160
|
+
"""
|
|
161
|
+
op is in; routing add through it was next.
|
|
162
|
+
"""
|
|
163
|
+
And step "refactor.health.check" changes nothing
|
|
164
|
+
And step "refactor.continue.building" writes "src/calc.ts" with:
|
|
165
|
+
"""
|
|
166
|
+
export const op = (a: number, b: number, sign: number) => a + sign * b
|
|
167
|
+
export const add = (a: number, b: number) => op(a, b, 1)
|
|
168
|
+
"""
|
|
169
|
+
When the process is driven
|
|
170
|
+
Then the process rests at "refactor.continue.building"
|
|
171
|
+
And the commit subjects end with:
|
|
172
|
+
"""
|
|
173
|
+
gtd(agent): refactor.building → refactor.health.check
|
|
174
|
+
gtd(check): refactor.health.check → refactor.continue.building
|
|
175
|
+
gtd(agent): refactor.continue.building → refactor.health.check
|
|
176
|
+
gtd(check): refactor.health.check → refactor.continue.building
|
|
177
|
+
gtd(agent): refactor.continue.building
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
Scenario: once a naive pass has continued, its retry runs in a fresh conversation, not as a continuation
|
|
181
|
+
Given step "seam.naive" writes "tests/calc.test.ts" with:
|
|
182
|
+
"""
|
|
183
|
+
it("[AC-1] subtracts", () => {})
|
|
184
|
+
"""
|
|
185
|
+
And step "seam.naive" writes ".gtd/HANDOFF.md" with:
|
|
186
|
+
"""
|
|
187
|
+
Multiply was next.
|
|
188
|
+
"""
|
|
189
|
+
And step "seam.continue.naive" writes "tests/calc.test.ts" with:
|
|
190
|
+
"""
|
|
191
|
+
it("[AC-1] subtracts", () => {})
|
|
192
|
+
it("[AC-2] multiplies", () => {})
|
|
193
|
+
"""
|
|
194
|
+
And step "seam.continue.naive" writes "src/calc.ts" with:
|
|
195
|
+
"""
|
|
196
|
+
export const add = (a: number, b: number) => a - b
|
|
197
|
+
"""
|
|
198
|
+
And judge step "seam.judge" answers:
|
|
199
|
+
"""
|
|
200
|
+
[{"id":"shape","answer":"reject","p":0.9}]
|
|
201
|
+
"""
|
|
202
|
+
When the process is driven
|
|
203
|
+
Then the process rests at "seam.fresh.naive"
|
|
204
|
+
And the resting step's content contains "rejected"
|
|
205
|
+
And the output of gtd "next --json" contains "\"resume\":false"
|
|
@@ -189,3 +189,14 @@ export const skipDesign = (stated: StatedCriteria | undefined): Promise<boolean>
|
|
|
189
189
|
})
|
|
190
190
|
return true
|
|
191
191
|
})
|
|
192
|
+
|
|
193
|
+
/** After a gate that invites narrowing the requirements: an edit must leave valid criteria. */
|
|
194
|
+
export const requireCriteriaIfEdited = (): void => {
|
|
195
|
+
const change = changes().get(REQUIREMENTS)
|
|
196
|
+
if (change === undefined) return
|
|
197
|
+
if (change.status === "deleted")
|
|
198
|
+
refuse(
|
|
199
|
+
`gtd land: criteria-missing: ${REQUIREMENTS} must stay — narrow its criteria, never delete it`,
|
|
200
|
+
)
|
|
201
|
+
requireCriteria(REQUIREMENTS)
|
|
202
|
+
}
|
|
@@ -15,8 +15,9 @@ import {
|
|
|
15
15
|
} from "../../flows/index.js"
|
|
16
16
|
import { lensEntriesOf } from "../../workflow-kit/index.js"
|
|
17
17
|
import { markFenced } from "../shared/fences.js"
|
|
18
|
+
import { continuing, phase, type Phase } from "../shared/continuation.js"
|
|
18
19
|
import { healthy, type Guard } from "../shared/health.js"
|
|
19
|
-
import { coder, FEEDBACK, REFACTOR } from "../shared/steps.js"
|
|
20
|
+
import { coder, FEEDBACK, HANDOFF, REFACTOR } from "../shared/steps.js"
|
|
20
21
|
import * as t from "../shared/text.js"
|
|
21
22
|
import { defaults, testFileGlobs } from "../shared/vars.js"
|
|
22
23
|
import { buildTail, reUnwind, type FeedbackTarget } from "./review.js"
|
|
@@ -263,7 +264,12 @@ const guardOf =
|
|
|
263
264
|
}
|
|
264
265
|
|
|
265
266
|
/** Derive, build, check and close one refactor lap; `feedback` is the collected review text of the lap before. */
|
|
266
|
-
const refactorLap = async (
|
|
267
|
+
const refactorLap = async (
|
|
268
|
+
p: Plan,
|
|
269
|
+
guard: Guard,
|
|
270
|
+
refactor: Phase,
|
|
271
|
+
feedback?: string,
|
|
272
|
+
): Promise<void> => {
|
|
267
273
|
const { pattern, strays, reference, note } = p
|
|
268
274
|
await run(
|
|
269
275
|
"deriving",
|
|
@@ -276,32 +282,59 @@ const refactorLap = async (p: Plan, guard: Guard, feedback?: string): Promise<vo
|
|
|
276
282
|
]),
|
|
277
283
|
{ label: "Deriving the refactor package", file: REFACTOR_PACKAGE },
|
|
278
284
|
)
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
285
|
+
const reached = new Set<string>()
|
|
286
|
+
// Each snapshot is greened before its continuation: every sub-step keeps the old suite green.
|
|
287
|
+
await continuing(refactor, async (from, fresh) => {
|
|
288
|
+
await fresh(() =>
|
|
289
|
+
t.agentWithSkills(
|
|
290
|
+
"building",
|
|
291
|
+
t.refactorBuildingPrompt({
|
|
292
|
+
pkg: REFACTOR_PACKAGE,
|
|
293
|
+
strays,
|
|
294
|
+
pattern,
|
|
295
|
+
reference,
|
|
296
|
+
feedback: feedback !== undefined,
|
|
297
|
+
note: note !== undefined,
|
|
298
|
+
from,
|
|
299
|
+
}),
|
|
300
|
+
{
|
|
301
|
+
label: from === undefined ? "Refactoring" : "Continuing the refactor",
|
|
302
|
+
model: coder(),
|
|
303
|
+
system: t.builderSystem(),
|
|
304
|
+
},
|
|
305
|
+
),
|
|
306
|
+
)
|
|
307
|
+
// Committed by the snapshot, so a turn stopped again may leave it as it was: present means stopped.
|
|
308
|
+
const left = read(HANDOFF)
|
|
309
|
+
guard()
|
|
310
|
+
const snapshot = head()
|
|
311
|
+
// The building turns' own changes only: a fix turn reaching a path is not the refactor reaching it.
|
|
312
|
+
for (const c of changes()) reached.add(c.path)
|
|
313
|
+
await healthy(
|
|
314
|
+
async () => {
|
|
315
|
+
await t.agentWithSkills("fix.suite.fixing", t.refactorFixPrompt(strays, pattern), {
|
|
316
|
+
label: "Fixing the check",
|
|
317
|
+
file: FEEDBACK,
|
|
318
|
+
model: coder(),
|
|
319
|
+
system: t.builderSystem(),
|
|
320
|
+
})
|
|
321
|
+
guard()
|
|
322
|
+
},
|
|
323
|
+
{ describeGuard: guard, rules: t.refactorRules(strays, pattern) },
|
|
324
|
+
)
|
|
325
|
+
if (left === undefined) return { result: undefined }
|
|
326
|
+
return {
|
|
327
|
+
result: undefined,
|
|
328
|
+
handoff: {
|
|
329
|
+
snapshot,
|
|
330
|
+
note: left.trim(),
|
|
331
|
+
open: strays.filter((path) => !reached.has(path)),
|
|
332
|
+
},
|
|
333
|
+
}
|
|
334
|
+
})
|
|
335
|
+
await run("closing", removeScript([REFACTOR_PACKAGE, HANDOFF]), {
|
|
336
|
+
label: "Closing out the refactor",
|
|
337
|
+
})
|
|
305
338
|
}
|
|
306
339
|
|
|
307
340
|
const strayAccess = (strays: readonly string[]) => ({
|
|
@@ -315,7 +348,7 @@ export const refactorFirst = async (
|
|
|
315
348
|
const p = plan(shape, options)
|
|
316
349
|
if (p === undefined) return false
|
|
317
350
|
return scope({ name: "refactor", access: strayAccess(p.strays) }, async () => {
|
|
318
|
-
await refactorLap(p, guardOf(p, head()))
|
|
351
|
+
await refactorLap(p, guardOf(p, head()), phase("refactor"))
|
|
319
352
|
return true
|
|
320
353
|
})
|
|
321
354
|
}
|
|
@@ -342,9 +375,10 @@ export const reviewedRefactor = async (
|
|
|
342
375
|
"gtd land: refactor-feedback: the review feedback changed files but wrote no .gtd/REFACTOR.md — a refactor cannot act on it; revert the change, or `gtd abandon` and restart",
|
|
343
376
|
dropped: t.refactorDroppedRefusal,
|
|
344
377
|
}
|
|
378
|
+
const refactor = phase("refactor")
|
|
345
379
|
let collected: string | undefined
|
|
346
380
|
for (;;) {
|
|
347
|
-
await refactorLap(p, guard, collected)
|
|
381
|
+
await refactorLap(p, guard, refactor, collected)
|
|
348
382
|
// A person's edit at an escalation rest is never refused, as in the lap.
|
|
349
383
|
const outcome = await buildTail(entry, {
|
|
350
384
|
green: true,
|