@pmelab/gtd 35.0.0 → 36.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 +31 -27
- package/dist/chunks/{reserved-C8Fm3s72.mjs → reserved-BAv71I4i.mjs} +10 -9
- package/dist/chunks/{tail-oaYewBN_.mjs → tail-kar1zhOb.mjs} +1 -1
- package/dist/flows.mjs +15 -10
- package/dist/gtd.bundle.mjs +14 -9
- package/dist/testkit.mjs +43 -23
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +18 -11
- 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 +37 -25
- package/package.json +1 -1
- package/skills/authoring/SKILL.md +3 -3
- package/src/flows/scripts.ts +31 -9
- package/src/testkit/FlowScripts.test.ts +58 -0
- package/src/testkit/FlowScripts.ts +61 -24
- package/src/workflows/feature/acceptance-criteria.feature +5 -1
- package/src/workflows/feature/coverage.feature +6 -3
- package/src/workflows/feature/fast-path.feature +11 -19
- package/src/workflows/feature/feature.test.ts +24 -4
- package/src/workflows/feature/feature.ts +8 -8
- package/src/workflows/feature/flag-mode.feature +17 -9
- package/src/workflows/feature/lens-models.feature +7 -1
- package/src/workflows/feature/naive-acceptance.feature +2 -2
- package/src/workflows/feature/one-step-land.feature +10 -12
- package/src/workflows/feature/quality-unresolved.feature +7 -1
- package/src/workflows/feature/risk-tier.feature +12 -4
- package/src/workflows/feature/seam-rejection.feature +4 -9
- package/src/workflows/feature/seam.feature +29 -26
- package/src/workflows/feature/seam.test.ts +9 -3
- package/src/workflows/feature/seam.ts +40 -22
- package/src/workflows/review/review-door.feature +12 -4
- package/src/workflows/shared/text.test.ts +8 -0
- package/src/workflows/shared/text.ts +11 -8
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
@inmem
|
|
2
|
-
Feature: flag mode off
|
|
2
|
+
Feature: flag mode off lands the naive pass with no release flag, seam judge or refactor
|
|
3
3
|
|
|
4
4
|
`flagMode` is a factory option of the shipped workflows, on by default. Off,
|
|
5
5
|
the project ships without feature flags: no seam judge and no refactor run,
|
|
6
|
-
and the naive
|
|
6
|
+
and the naive pass itself lands, straight through the health loop and the
|
|
7
7
|
quality lap.
|
|
8
8
|
|
|
9
9
|
Background:
|
|
@@ -43,17 +43,22 @@ Feature: flag mode off builds a feature with no release flag, seam judge or refa
|
|
|
43
43
|
"""
|
|
44
44
|
And step "design.gate.answer" changes nothing
|
|
45
45
|
|
|
46
|
-
Scenario: flag mode off
|
|
46
|
+
Scenario: flag mode off lands the naive pass unjudged, and asks for no flag
|
|
47
47
|
When the process is driven
|
|
48
|
-
Then the process rests at "
|
|
48
|
+
Then the process rests at "seam.naive"
|
|
49
49
|
And the resting step's content contains "the quickest way that works"
|
|
50
|
-
And the resting step's content
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
And the resting step's content does not contain "release flag"
|
|
51
|
+
Given step "seam.naive" writes "src/calc.test.ts" with:
|
|
52
|
+
"""
|
|
53
|
+
it("[AC-1] subtracts", () => {})
|
|
54
|
+
"""
|
|
55
|
+
And step "seam.naive" writes "src/calc.ts" with:
|
|
53
56
|
"""
|
|
54
57
|
export const add = (a: number, b: number) => a + b
|
|
55
58
|
export const subtract = (a: number, b: number) => a - b
|
|
56
59
|
"""
|
|
60
|
+
And step "seam.check" changes nothing
|
|
61
|
+
And step "implement.applying" runs its script
|
|
57
62
|
And step "implement.health.check" changes nothing
|
|
58
63
|
And step "implement.closing" runs its script
|
|
59
64
|
And judge step "implement.coverage" answers:
|
|
@@ -64,6 +69,7 @@ Feature: flag mode off builds a feature with no release flag, seam judge or refa
|
|
|
64
69
|
When the process is driven
|
|
65
70
|
Then the process rests at "idle"
|
|
66
71
|
And "src/calc.ts" contains "subtract"
|
|
72
|
+
And "src/calc.test.ts" contains "[AC-1] subtracts"
|
|
67
73
|
And the commit subjects end with:
|
|
68
74
|
"""
|
|
69
75
|
chore: initial commit
|
|
@@ -72,8 +78,10 @@ Feature: flag mode off builds a feature with no release flag, seam judge or refa
|
|
|
72
78
|
gtd(check): unwind → start-gate.check
|
|
73
79
|
gtd(check): start-gate.check → design.triage
|
|
74
80
|
gtd(agent): design.triage → design.gate.answer
|
|
75
|
-
gtd(human): design.gate.answer →
|
|
76
|
-
gtd(agent):
|
|
81
|
+
gtd(human): design.gate.answer → seam.naive
|
|
82
|
+
gtd(agent): seam.naive → seam.check
|
|
83
|
+
gtd(check): seam.check → implement.applying
|
|
84
|
+
gtd(check): implement.applying → implement.health.check
|
|
77
85
|
gtd(check): implement.health.check → implement.coverage
|
|
78
86
|
gtd(judge): implement.coverage → implement.closing
|
|
79
87
|
gtd(check): implement.closing → build.quality.correctness.reviewing
|
|
@@ -37,11 +37,17 @@ Feature: each quality lens runs on its own model hint
|
|
|
37
37
|
- AC-1: `subtract(a, b)` returns `a - b`.
|
|
38
38
|
"""
|
|
39
39
|
And step "design.gate.answer" changes nothing
|
|
40
|
-
And step "
|
|
40
|
+
And step "seam.naive" writes "src/calc.test.ts" with:
|
|
41
|
+
"""
|
|
42
|
+
it("[AC-1] subtracts", () => {})
|
|
43
|
+
"""
|
|
44
|
+
And step "seam.naive" writes "src/calc.ts" with:
|
|
41
45
|
"""
|
|
42
46
|
export const add = (a: number, b: number) => a + b
|
|
43
47
|
export const subtract = (a: number, b: number) => a - b
|
|
44
48
|
"""
|
|
49
|
+
And step "seam.check" changes nothing
|
|
50
|
+
And step "implement.applying" runs its script
|
|
45
51
|
And step "implement.health.check" changes nothing
|
|
46
52
|
And step "implement.closing" runs its script
|
|
47
53
|
And judge step "implement.coverage" answers:
|
|
@@ -129,12 +129,12 @@ Feature: a naive pass ends only when a test names every criterion and the suite
|
|
|
129
129
|
"""
|
|
130
130
|
And step "seam.check" from now on changes nothing
|
|
131
131
|
When the process is driven
|
|
132
|
-
Then the process rests at "implement.
|
|
132
|
+
Then the process rests at "implement.applying"
|
|
133
133
|
And the commit subjects end with:
|
|
134
134
|
"""
|
|
135
135
|
gtd(human): design.gate.answer → seam.naive
|
|
136
136
|
gtd(agent): seam.naive → seam.check
|
|
137
137
|
gtd(check): seam.check → seam.naive
|
|
138
138
|
gtd(agent): seam.naive → seam.check
|
|
139
|
-
gtd(check): seam.check → implement.
|
|
139
|
+
gtd(check): seam.check → implement.applying
|
|
140
140
|
"""
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# One SDLC path per file, so a changed path reds exactly one fixture — do not merge.
|
|
2
2
|
@inmem
|
|
3
|
-
Feature: a naive pass the judge rules land
|
|
3
|
+
Feature: a naive pass the judge rules land is itself the implementation
|
|
4
4
|
|
|
5
5
|
With GTD_FLAG_RISK_TIER off, a naive pass that
|
|
6
6
|
edits an existing source file is judged land: the process skips the approval
|
|
7
|
-
gate and any refactor,
|
|
8
|
-
back to idle, with no review. The flag is pinned here so the per-flag test runs never
|
|
7
|
+
gate and any refactor, applies the kept pass as it is — no build turn — and
|
|
8
|
+
runs the health loop and quality back to idle, with no review. The flag is pinned here so the per-flag test runs never
|
|
9
9
|
move it off this path.
|
|
10
10
|
|
|
11
11
|
Background:
|
|
@@ -53,10 +53,7 @@ Feature: a naive pass the judge rules land goes straight to implement with no se
|
|
|
53
53
|
"""
|
|
54
54
|
[{"id":"shape","answer":"land","p":0.9}]
|
|
55
55
|
"""
|
|
56
|
-
And step "implement.
|
|
57
|
-
"""
|
|
58
|
-
export const greeting = "Hello, world!"
|
|
59
|
-
"""
|
|
56
|
+
And step "implement.applying" runs its script
|
|
60
57
|
And step "implement.health.check" changes nothing
|
|
61
58
|
And step "implement.closing" runs its script
|
|
62
59
|
And judge step "implement.coverage" answers:
|
|
@@ -65,10 +62,11 @@ Feature: a naive pass the judge rules land goes straight to implement with no se
|
|
|
65
62
|
"""
|
|
66
63
|
And step "build.quality.correctness.reviewing" changes nothing
|
|
67
64
|
|
|
68
|
-
Scenario: a land judgment runs idle to idle with no
|
|
65
|
+
Scenario: a land judgment applies the naive pass and runs idle to idle with no rebuild
|
|
69
66
|
When the process is driven
|
|
70
67
|
Then the process rests at "idle"
|
|
71
|
-
And "src/greeting.ts" contains "Hello, world
|
|
68
|
+
And "src/greeting.ts" contains "Hello, world"
|
|
69
|
+
And "src/acceptance.test.ts" contains "[AC-1] holds"
|
|
72
70
|
And the commit subjects end with:
|
|
73
71
|
"""
|
|
74
72
|
gtd(human): idle → unwind
|
|
@@ -78,8 +76,8 @@ Feature: a naive pass the judge rules land goes straight to implement with no se
|
|
|
78
76
|
gtd(human): design.gate.answer → seam.naive
|
|
79
77
|
gtd(agent): seam.naive → seam.check
|
|
80
78
|
gtd(check): seam.check → seam.judge
|
|
81
|
-
gtd(judge): seam.judge → implement.
|
|
82
|
-
gtd(
|
|
79
|
+
gtd(judge): seam.judge → implement.applying
|
|
80
|
+
gtd(check): implement.applying → implement.health.check
|
|
83
81
|
gtd(check): implement.health.check → implement.coverage
|
|
84
82
|
gtd(judge): implement.coverage → implement.closing
|
|
85
83
|
gtd(check): implement.closing → build.quality.correctness.reviewing
|
|
@@ -87,7 +85,7 @@ Feature: a naive pass the judge rules land goes straight to implement with no se
|
|
|
87
85
|
"""
|
|
88
86
|
And the commit "gtd(human): idle → unwind" has a body containing "Gtd-Var: flag_risk_tier=0"
|
|
89
87
|
And the commit "gtd(agent): seam.naive → seam.check" has a body containing "Gtd-Kept:"
|
|
90
|
-
And the commit "gtd(judge): seam.judge → implement.
|
|
88
|
+
And the commit "gtd(judge): seam.judge → implement.applying" has a body containing "\"answer\":\"land\""
|
|
91
89
|
And no commit body contains "Gtd-Approved:"
|
|
92
90
|
And the output lines of gtd "slices" start with:
|
|
93
91
|
"""
|
|
@@ -37,11 +37,17 @@ Feature: quality findings left unresolved hold a feature process short of idle
|
|
|
37
37
|
- AC-1: subtract(a, b) returns a - b.
|
|
38
38
|
"""
|
|
39
39
|
And step "design.gate.answer" changes nothing
|
|
40
|
-
And step "
|
|
40
|
+
And step "seam.naive" writes "src/calc.test.ts" with:
|
|
41
|
+
"""
|
|
42
|
+
it("[AC-1] subtracts", () => {})
|
|
43
|
+
"""
|
|
44
|
+
And step "seam.naive" writes "src/calc.ts" with:
|
|
41
45
|
"""
|
|
42
46
|
export const add = (a: number, b: number) => a + b
|
|
43
47
|
export const subtract = (a: number, b: number) => a - b
|
|
44
48
|
"""
|
|
49
|
+
And step "seam.check" changes nothing
|
|
50
|
+
And step "implement.applying" runs its script
|
|
45
51
|
And step "implement.health.check" changes nothing
|
|
46
52
|
And step "implement.closing" runs its script
|
|
47
53
|
And judge step "implement.coverage" answers:
|
|
@@ -133,7 +133,7 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
|
|
|
133
133
|
| low |
|
|
134
134
|
| high |
|
|
135
135
|
|
|
136
|
-
Scenario: the tier comes from the requirements the final design gate lap accepted
|
|
136
|
+
Scenario: the tier a rebuilt pass is told comes from the requirements the final design gate lap accepted
|
|
137
137
|
Given step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
|
|
138
138
|
"""
|
|
139
139
|
# Requirements
|
|
@@ -146,7 +146,11 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
|
|
|
146
146
|
|
|
147
147
|
- AC-1: the change behaves as described.
|
|
148
148
|
"""
|
|
149
|
-
And step "seam.check"
|
|
149
|
+
And step "seam.check" writes "src/acceptance.test.ts" with:
|
|
150
|
+
"""
|
|
151
|
+
it("[AC-1] holds elsewhere", () => {})
|
|
152
|
+
"""
|
|
153
|
+
And step "implement.applying" runs its script
|
|
150
154
|
And step "seam.naive" writes "src/acceptance.test.ts" with:
|
|
151
155
|
"""
|
|
152
156
|
it("[AC-1] holds", () => {})
|
|
@@ -245,7 +249,7 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
|
|
|
245
249
|
And the resting step's content does not contain "## Production Signals"
|
|
246
250
|
|
|
247
251
|
|
|
248
|
-
Scenario: the flag-off twin keeps today's flag demand
|
|
252
|
+
Scenario: the flag-off twin keeps today's flag demand on a rebuilt pass
|
|
249
253
|
Given step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
|
|
250
254
|
"""
|
|
251
255
|
# Requirements
|
|
@@ -259,7 +263,11 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
|
|
|
259
263
|
- AC-1: the change behaves as described.
|
|
260
264
|
"""
|
|
261
265
|
And step "design.gate.answer" changes nothing
|
|
262
|
-
And step "seam.check"
|
|
266
|
+
And step "seam.check" writes "src/acceptance.test.ts" with:
|
|
267
|
+
"""
|
|
268
|
+
it("[AC-1] holds elsewhere", () => {})
|
|
269
|
+
"""
|
|
270
|
+
And step "implement.applying" runs its script
|
|
263
271
|
And step "seam.naive" writes "src/acceptance.test.ts" with:
|
|
264
272
|
"""
|
|
265
273
|
it("[AC-1] holds", () => {})
|
|
@@ -97,12 +97,7 @@ Feature: rejected naive passes walk one fixed path through the steer gate to idl
|
|
|
97
97
|
"""
|
|
98
98
|
[{"id":"shape","answer":"land","p":0.9}]
|
|
99
99
|
"""
|
|
100
|
-
And step "implement.
|
|
101
|
-
"""
|
|
102
|
-
export const add = (a: number, b: number) => a + b
|
|
103
|
-
export const subtract = (a: number, b: number) => a - b
|
|
104
|
-
export const SUBTRACT = true
|
|
105
|
-
"""
|
|
100
|
+
And step "implement.applying" runs its script
|
|
106
101
|
When gtd lands
|
|
107
102
|
And the process is driven
|
|
108
103
|
Then the process rests at "idle"
|
|
@@ -125,8 +120,8 @@ Feature: rejected naive passes walk one fixed path through the steer gate to idl
|
|
|
125
120
|
gtd(human): seam.steer → seam.naive
|
|
126
121
|
gtd(agent): seam.naive → seam.check
|
|
127
122
|
gtd(check): seam.check → seam.judge
|
|
128
|
-
gtd(judge): seam.judge → implement.
|
|
129
|
-
gtd(
|
|
123
|
+
gtd(judge): seam.judge → implement.applying
|
|
124
|
+
gtd(check): implement.applying → implement.health.check
|
|
130
125
|
gtd(check): implement.health.check → implement.coverage
|
|
131
126
|
gtd(judge): implement.coverage → implement.closing
|
|
132
127
|
gtd(check): implement.closing → idle
|
|
@@ -135,7 +130,7 @@ Feature: rejected naive passes walk one fixed path through the steer gate to idl
|
|
|
135
130
|
And the commit "gtd(agent): seam.naive → seam.check" has a body containing "Gtd-Kept:"
|
|
136
131
|
And the commit "gtd(judge): seam.judge → seam.naive" has a body containing "\"answer\":\"reject\""
|
|
137
132
|
And the commit "gtd(judge): seam.judge → seam.steer" has a body containing "\"answer\":\"reject\""
|
|
138
|
-
And the commit "gtd(judge): seam.judge → implement.
|
|
133
|
+
And the commit "gtd(judge): seam.judge → implement.applying" has a body containing "\"answer\":\"land\""
|
|
139
134
|
And no commit body contains "Gtd-Approved:"
|
|
140
135
|
And the output lines of gtd "slices" start with:
|
|
141
136
|
"""
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Feature: the naive pass and seam judge decide how a feature is built
|
|
3
3
|
|
|
4
4
|
After design a naive pass is set aside under a kept ref, the judge reads its
|
|
5
|
-
diff, and the process either
|
|
6
|
-
first behind a human approval, or retries the naive pass.
|
|
5
|
+
diff, and the process either applies the pass as the implementation,
|
|
6
|
+
refactors the seam first behind a human approval, or retries the naive pass.
|
|
7
7
|
|
|
8
8
|
Background:
|
|
9
9
|
Given the shipped workflows, as gtd install scaffolds them
|
|
@@ -50,7 +50,7 @@ Feature: the naive pass and seam judge decide how a feature is built
|
|
|
50
50
|
"""
|
|
51
51
|
And step "design.gate.answer" changes nothing
|
|
52
52
|
|
|
53
|
-
Scenario: a land judgment
|
|
53
|
+
Scenario: a land judgment applies the naive pass with no seam gate, refactor or build turn
|
|
54
54
|
Given step "seam.check" changes nothing
|
|
55
55
|
And step "seam.naive" writes "src/acceptance.test.ts" with:
|
|
56
56
|
"""
|
|
@@ -66,7 +66,7 @@ Feature: the naive pass and seam judge decide how a feature is built
|
|
|
66
66
|
[{"id":"shape","answer":"land","p":0.9}]
|
|
67
67
|
"""
|
|
68
68
|
When the process is driven
|
|
69
|
-
Then the process rests at "implement.
|
|
69
|
+
Then the process rests at "implement.applying"
|
|
70
70
|
And the last commit body contains "Gtd-Judge:"
|
|
71
71
|
And the commit subjects end with:
|
|
72
72
|
"""
|
|
@@ -79,27 +79,34 @@ Feature: the naive pass and seam judge decide how a feature is built
|
|
|
79
79
|
gtd(human): design.gate.answer → seam.naive
|
|
80
80
|
gtd(agent): seam.naive → seam.check
|
|
81
81
|
gtd(check): seam.check → seam.judge
|
|
82
|
-
gtd(judge): seam.judge → implement.
|
|
82
|
+
gtd(judge): seam.judge → implement.applying
|
|
83
83
|
"""
|
|
84
84
|
|
|
85
|
-
Scenario:
|
|
86
|
-
Given step "seam.
|
|
87
|
-
And step "seam.naive" writes "src/acceptance.test.ts" with:
|
|
85
|
+
Scenario: a naive pass that no longer applies is built fresh, with the implement scope's skills
|
|
86
|
+
Given step "seam.naive" writes "src/acceptance.test.ts" with:
|
|
88
87
|
"""
|
|
89
88
|
it("[AC-1] holds", () => {})
|
|
90
89
|
"""
|
|
91
|
-
And step "seam.naive" writes "src/
|
|
90
|
+
And step "seam.naive" writes "src/subtract.ts" with:
|
|
92
91
|
"""
|
|
93
|
-
export const add = (a: number, b: number) => a + b
|
|
94
92
|
export const subtract = (a: number, b: number) => a - b
|
|
95
93
|
"""
|
|
96
|
-
And
|
|
94
|
+
And step "seam.check" writes "src/subtract.ts" with:
|
|
97
95
|
"""
|
|
98
|
-
|
|
96
|
+
export const subtract = (a: number, b: number) => b - a
|
|
99
97
|
"""
|
|
98
|
+
And step "implement.applying" runs its script
|
|
100
99
|
When the process is driven
|
|
101
100
|
Then the process rests at "implement.building"
|
|
102
101
|
And the resting step's content contains "my-tdd, my-runbook"
|
|
102
|
+
And the resting step's content contains "git show"
|
|
103
|
+
And ".gtd/FEEDBACK.md" contains "could not apply the naive pass"
|
|
104
|
+
And "src/subtract.ts" contains "b - a"
|
|
105
|
+
And the commit subjects end with:
|
|
106
|
+
"""
|
|
107
|
+
gtd(check): seam.check → implement.applying
|
|
108
|
+
gtd(check): implement.applying → implement.building
|
|
109
|
+
"""
|
|
103
110
|
|
|
104
111
|
Scenario: the naive pass lands as a marker commit and its turn stays off the branch
|
|
105
112
|
Given step "seam.check" changes nothing
|
|
@@ -122,7 +129,7 @@ Feature: the naive pass and seam judge decide how a feature is built
|
|
|
122
129
|
And the commit "gtd(agent): seam.naive → seam.check" has a body containing "Gtd-Kept:"
|
|
123
130
|
And "src/calc.ts" does not contain "subtract"
|
|
124
131
|
|
|
125
|
-
Scenario: the implementation is
|
|
132
|
+
Scenario: the implementation is the applied naive pass, a green check, then the requirements are closed out
|
|
126
133
|
Given step "seam.check" changes nothing
|
|
127
134
|
And step "seam.naive" writes "src/acceptance.test.ts" with:
|
|
128
135
|
"""
|
|
@@ -137,12 +144,7 @@ Feature: the naive pass and seam judge decide how a feature is built
|
|
|
137
144
|
"""
|
|
138
145
|
[{"id":"shape","answer":"land","p":0.9}]
|
|
139
146
|
"""
|
|
140
|
-
And step "implement.
|
|
141
|
-
"""
|
|
142
|
-
export const add = (a: number, b: number) => a + b
|
|
143
|
-
export const subtract = (a: number, b: number) => a - b
|
|
144
|
-
export const SUBTRACT_ENABLED = process.env.GTD_FLAG_SUBTRACT === "1"
|
|
145
|
-
"""
|
|
147
|
+
And step "implement.applying" runs its script
|
|
146
148
|
And step "implement.health.check" changes nothing
|
|
147
149
|
And step "implement.closing" runs its script
|
|
148
150
|
And judge step "implement.coverage" answers:
|
|
@@ -151,10 +153,11 @@ Feature: the naive pass and seam judge decide how a feature is built
|
|
|
151
153
|
"""
|
|
152
154
|
When the process is driven
|
|
153
155
|
Then the process rests at "idle"
|
|
156
|
+
And "src/calc.ts" contains "subtract"
|
|
154
157
|
And the commit subjects end with:
|
|
155
158
|
"""
|
|
156
|
-
gtd(judge): seam.judge → implement.
|
|
157
|
-
gtd(
|
|
159
|
+
gtd(judge): seam.judge → implement.applying
|
|
160
|
+
gtd(check): implement.applying → implement.health.check
|
|
158
161
|
gtd(check): implement.health.check → implement.coverage
|
|
159
162
|
gtd(judge): implement.coverage → implement.closing
|
|
160
163
|
gtd(check): implement.closing → idle
|
|
@@ -177,12 +180,12 @@ Feature: the naive pass and seam judge decide how a feature is built
|
|
|
177
180
|
it("adds", () => expect(2 + 2).toBe(4))
|
|
178
181
|
"""
|
|
179
182
|
When the process is driven
|
|
180
|
-
Then the process rests at "implement.
|
|
183
|
+
Then the process rests at "implement.applying"
|
|
181
184
|
And the commit subjects end with:
|
|
182
185
|
"""
|
|
183
186
|
gtd(human): design.gate.answer → seam.naive
|
|
184
187
|
gtd(agent): seam.naive → seam.check
|
|
185
|
-
gtd(check): seam.check → implement.
|
|
188
|
+
gtd(check): seam.check → implement.applying
|
|
186
189
|
"""
|
|
187
190
|
|
|
188
191
|
Scenario: a naive pass adding a glob-named file does not hide a modified sibling
|
|
@@ -258,7 +261,7 @@ Feature: the naive pass and seam judge decide how a feature is built
|
|
|
258
261
|
gtd(judge): seam.judge → seam.steer
|
|
259
262
|
"""
|
|
260
263
|
|
|
261
|
-
Scenario: a clean landing at the steer gate overrules, with no seam gate and no refactor
|
|
264
|
+
Scenario: a clean landing at the steer gate overrules and applies the pass, with no seam gate and no refactor
|
|
262
265
|
Given step "seam.check" changes nothing
|
|
263
266
|
And step "seam.naive" writes "src/acceptance.test.ts" with:
|
|
264
267
|
"""
|
|
@@ -275,9 +278,9 @@ Feature: the naive pass and seam judge decide how a feature is built
|
|
|
275
278
|
When the process is driven
|
|
276
279
|
Then the process rests at "seam.steer"
|
|
277
280
|
When gtd lands
|
|
278
|
-
Then the process rests at "implement.
|
|
281
|
+
Then the process rests at "implement.applying"
|
|
279
282
|
And the commit subjects end with:
|
|
280
283
|
"""
|
|
281
284
|
gtd(judge): seam.judge → seam.steer
|
|
282
|
-
gtd(human): seam.steer → implement.
|
|
285
|
+
gtd(human): seam.steer → implement.applying
|
|
283
286
|
"""
|
|
@@ -321,13 +321,19 @@ const driveSeam = async (
|
|
|
321
321
|
}
|
|
322
322
|
|
|
323
323
|
describe("seamBuild", () => {
|
|
324
|
-
it("land resolves the kept tip with the caller's base and enters no slice", async () => {
|
|
324
|
+
it("land resolves the kept tip to land, with the caller's base, and enters no slice", async () => {
|
|
325
325
|
const h = await driveSeam("land")
|
|
326
|
-
expect(h.outcome).toEqual({ reference: "abc", base: "c-base" })
|
|
326
|
+
expect(h.outcome).toEqual({ reference: "abc", base: "c-base", land: true })
|
|
327
327
|
expect(h.slices).toEqual([])
|
|
328
328
|
expect(h.names).toEqual(["seam.naive", "seam.check", "seam.judge"])
|
|
329
329
|
})
|
|
330
330
|
|
|
331
|
+
it("with flagMode off resolves a vetted pass to land with no judge", async () => {
|
|
332
|
+
const h = await driveSeam("reject", undefined, { flagMode: "false" })
|
|
333
|
+
expect(h.outcome).toEqual({ reference: "abc", base: "c-base", land: true })
|
|
334
|
+
expect(h.names).toEqual(["seam.naive", "seam.check"])
|
|
335
|
+
})
|
|
336
|
+
|
|
331
337
|
it("a red check retries the pass, and the judge sees it only once green", async () => {
|
|
332
338
|
const h = await driveSeam("land", undefined, {}, 1)
|
|
333
339
|
expect(h.names).toEqual(["seam.naive", "seam.check", "seam.naive", "seam.check", "seam.judge"])
|
|
@@ -355,7 +361,7 @@ describe("seamBuild", () => {
|
|
|
355
361
|
"seam.clear",
|
|
356
362
|
"seam.judge",
|
|
357
363
|
])
|
|
358
|
-
expect(h.outcome).toEqual({ reference: "abc", base: "c-base" })
|
|
364
|
+
expect(h.outcome).toEqual({ reference: "abc", base: "c-base", land: true })
|
|
359
365
|
})
|
|
360
366
|
|
|
361
367
|
it("refactor-first runs the reviewed refactor in slice refactor and resolves HEAD after it", async () => {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { posix } from "node:path"
|
|
2
2
|
import {
|
|
3
|
+
applyScript,
|
|
3
4
|
changes,
|
|
4
5
|
check,
|
|
6
|
+
codeChanges,
|
|
5
7
|
env,
|
|
6
8
|
head,
|
|
7
9
|
human,
|
|
@@ -14,6 +16,7 @@ import {
|
|
|
14
16
|
scope,
|
|
15
17
|
slice,
|
|
16
18
|
vars,
|
|
19
|
+
wrote,
|
|
17
20
|
type Change,
|
|
18
21
|
type DiffShapeResult,
|
|
19
22
|
type KeptTip,
|
|
@@ -144,7 +147,7 @@ const naiveGreen = (tip: KeptTip): Promise<boolean> => {
|
|
|
144
147
|
[
|
|
145
148
|
...paths,
|
|
146
149
|
"dirty=$(git -c core.quotePath=false ls-files -m -d -o --exclude-standard)",
|
|
147
|
-
`if ! git diff --binary ${quote(`${tip.hash}^`)} ${quote(tip.hash)} -- | git apply; then`,
|
|
150
|
+
`if ! git diff --binary --no-color --no-ext-diff --no-textconv --src-prefix=a/ --dst-prefix=b/ ${quote(`${tip.hash}^`)} ${quote(tip.hash)} -- | git apply --whitespace=nowarn; then`,
|
|
148
151
|
" echo 'gtd: the kept naive pass no longer applies to HEAD; no test ran'",
|
|
149
152
|
" exit 1",
|
|
150
153
|
"fi",
|
|
@@ -169,6 +172,16 @@ export interface ImplementOptions {
|
|
|
169
172
|
readonly amend?: boolean | undefined
|
|
170
173
|
/** After a refactor slice, which may already carry the feature: an empty turn completes the step. */
|
|
171
174
|
readonly afterRefactor?: boolean | undefined
|
|
175
|
+
/** A kept naive tip to apply as the implementation; built fresh only when it no longer applies. */
|
|
176
|
+
readonly naive?: string | undefined
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Resolves `false` when the tip no longer applies (the failure is in `.gtd/FEEDBACK.md`) or has nothing outside `.gtd/` to apply. */
|
|
180
|
+
const applied = async (tip: string): Promise<boolean> => {
|
|
181
|
+
await run("applying", applyScript(tip, FEEDBACK, t.naiveApplyFailure(tip), [".gtd"]), {
|
|
182
|
+
label: "Applying the naive pass",
|
|
183
|
+
})
|
|
184
|
+
return !wrote(FEEDBACK) && codeChanges().length > 0
|
|
172
185
|
}
|
|
173
186
|
|
|
174
187
|
export function implement(options?: ImplementOptions): Promise<void>
|
|
@@ -186,22 +199,23 @@ export function implement(
|
|
|
186
199
|
const options: ImplementOptions =
|
|
187
200
|
typeof first === "object" && first !== null ? first : { reference: first, tier, amend }
|
|
188
201
|
return scope("implement", async () => {
|
|
189
|
-
await
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
202
|
+
if (options.naive === undefined || !(await applied(options.naive)))
|
|
203
|
+
await t.agentWithSkills(
|
|
204
|
+
"building",
|
|
205
|
+
t.implementBuildingPrompt(
|
|
206
|
+
options.reference,
|
|
207
|
+
options.tier,
|
|
208
|
+
options.amend,
|
|
209
|
+
options.afterRefactor,
|
|
210
|
+
flagModeOn(),
|
|
211
|
+
),
|
|
212
|
+
{
|
|
213
|
+
label: "Implementing",
|
|
214
|
+
model: coder(),
|
|
215
|
+
system: t.builderSystem(),
|
|
216
|
+
...(options.afterRefactor === true ? { allowEmpty: true } : {}),
|
|
217
|
+
},
|
|
218
|
+
)
|
|
205
219
|
requireCriteriaFrozen(REQUIREMENTS)
|
|
206
220
|
await green()
|
|
207
221
|
await requireCoverage()
|
|
@@ -211,14 +225,14 @@ export function implement(
|
|
|
211
225
|
|
|
212
226
|
interface Decision {
|
|
213
227
|
readonly reference: string
|
|
214
|
-
/**
|
|
228
|
+
/** Absent on `land` (or with `flagMode` off, unjudged): the pass itself is the implementation. */
|
|
215
229
|
readonly shape?: DiffShapeResult
|
|
216
230
|
}
|
|
217
231
|
|
|
218
232
|
/** Resolves `true` when the human steered (edited), `false` on a clean overrule. */
|
|
219
233
|
const steer = async (retry: t.NaiveRetry): Promise<boolean> => {
|
|
220
234
|
await human("steer", {
|
|
221
|
-
message: t.seamSteerMessage(retry),
|
|
235
|
+
message: t.seamSteerMessage(retry, flagModeOn()),
|
|
222
236
|
label: "Steer or overrule the naive pass",
|
|
223
237
|
file: REQUIREMENTS,
|
|
224
238
|
acceptClean: true,
|
|
@@ -285,15 +299,19 @@ const decide = (): Promise<Decision> =>
|
|
|
285
299
|
for (;;) {
|
|
286
300
|
await naivePass(retry)
|
|
287
301
|
const tip = kept("naive")!
|
|
288
|
-
const outcome =
|
|
302
|
+
const outcome =
|
|
303
|
+
(await vetted(tip, vetting)) ??
|
|
304
|
+
(flagModeOn() ? await ruled(tip, vetting.again) : { reference: tip.hash })
|
|
289
305
|
if (!("kind" in outcome)) return outcome
|
|
290
306
|
retry = outcome.kind === "retry" ? outcome.retry : undefined
|
|
291
307
|
}
|
|
292
308
|
})
|
|
293
309
|
|
|
294
310
|
export interface SeamOutcome {
|
|
295
|
-
/** The naive pass's kept tip: the first implement turn's reading material. */
|
|
311
|
+
/** The naive pass's kept tip: applied as the implementation when `land`, else the first implement turn's reading material. */
|
|
296
312
|
readonly reference: string
|
|
313
|
+
/** The naive pass lands as it is: no build turn. */
|
|
314
|
+
readonly land?: boolean
|
|
297
315
|
/** The feature slice's base: HEAD after the refactor on refactor-first, else the caller's base. */
|
|
298
316
|
readonly base: string
|
|
299
317
|
readonly refactored?: boolean
|
|
@@ -301,7 +319,7 @@ export interface SeamOutcome {
|
|
|
301
319
|
|
|
302
320
|
export const seamBuild = async (base: string): Promise<SeamOutcome> => {
|
|
303
321
|
const { reference, shape } = await decide()
|
|
304
|
-
if (shape === undefined) return { reference, base }
|
|
322
|
+
if (shape === undefined) return { reference, base, land: true }
|
|
305
323
|
if (shape.unreadable.length > 0) {
|
|
306
324
|
await scope("seam", () =>
|
|
307
325
|
human("by-hand", {
|
|
@@ -37,11 +37,17 @@ Feature: human review is the review door's, never the feature workflow's
|
|
|
37
37
|
- AC-1: subtract(a, b) returns a - b.
|
|
38
38
|
"""
|
|
39
39
|
And step "design.gate.answer" changes nothing
|
|
40
|
-
And step "
|
|
40
|
+
And step "seam.naive" writes "src/calc.test.ts" with:
|
|
41
|
+
"""
|
|
42
|
+
it("[AC-1] subtracts", () => {})
|
|
43
|
+
"""
|
|
44
|
+
And step "seam.naive" writes "src/calc.ts" with:
|
|
41
45
|
"""
|
|
42
46
|
export const add = (a: number, b: number) => a + b
|
|
43
47
|
export const subtract = (a: number, b: number) => a - b
|
|
44
48
|
"""
|
|
49
|
+
And step "seam.check" changes nothing
|
|
50
|
+
And step "implement.applying" runs its script
|
|
45
51
|
And step "implement.health.check" changes nothing
|
|
46
52
|
And step "implement.closing" runs its script
|
|
47
53
|
And judge step "implement.coverage" answers:
|
|
@@ -56,8 +62,10 @@ Feature: human review is the review door's, never the feature workflow's
|
|
|
56
62
|
And ".gtd/REVIEW.md" does not exist
|
|
57
63
|
And the commit subjects end with:
|
|
58
64
|
"""
|
|
59
|
-
gtd(human): design.gate.answer →
|
|
60
|
-
gtd(agent):
|
|
65
|
+
gtd(human): design.gate.answer → seam.naive
|
|
66
|
+
gtd(agent): seam.naive → seam.check
|
|
67
|
+
gtd(check): seam.check → implement.applying
|
|
68
|
+
gtd(check): implement.applying → implement.health.check
|
|
61
69
|
gtd(check): implement.health.check → implement.coverage
|
|
62
70
|
gtd(judge): implement.coverage → implement.closing
|
|
63
71
|
gtd(check): implement.closing → idle
|
|
@@ -80,7 +88,7 @@ Feature: human review is the review door's, never the feature workflow's
|
|
|
80
88
|
- [ ] ./src/calc.ts#2-2
|
|
81
89
|
new subtract function
|
|
82
90
|
"""
|
|
83
|
-
When gtd opens door "review" with "HEAD~
|
|
91
|
+
When gtd opens door "review" with "HEAD~11"
|
|
84
92
|
And the process is driven
|
|
85
93
|
Then the process rests at "build.review.await-review"
|
|
86
94
|
And the resting step's content contains the hash of the commit "feat: add calculator"
|
|
@@ -1059,6 +1059,14 @@ describe("the seam phase's texts", () => {
|
|
|
1059
1059
|
expect(message).not.toContain("outside the expected set")
|
|
1060
1060
|
})
|
|
1061
1061
|
|
|
1062
|
+
it("promises no judge on an overrule with flagMode off", () => {
|
|
1063
|
+
for (const retry of [{ red: "abc123" }, { fault: "acceptance-tests: x", hash: "abc123" }]) {
|
|
1064
|
+
const message = renderText(() => seamSteerMessage(retry, false))
|
|
1065
|
+
expect(message).not.toContain("seam judge")
|
|
1066
|
+
expect(message).toContain("it lands as the implementation")
|
|
1067
|
+
}
|
|
1068
|
+
})
|
|
1069
|
+
|
|
1062
1070
|
it("names every unreadable path for a by-hand landing", () => {
|
|
1063
1071
|
const message = renderText(() =>
|
|
1064
1072
|
seamByHandMessage(["vendor/a.bin", "src/flows/helpers.ts"], ["vendor/a.bin"], "abc123"),
|