@pmelab/gtd 35.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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +45 -29
- package/claude/hooks/context.ts +66 -0
- package/claude/hooks/drive.ts +3 -0
- package/claude/hooks/register.tsx +69 -6
- package/dist/chunks/{reserved-C8Fm3s72.mjs → reserved-zAfoJtR7.mjs} +62 -24
- package/dist/chunks/{tail-oaYewBN_.mjs → tail-BRC4DGdV.mjs} +1 -1
- package/dist/chunks/{wire-D3Rc5r22.mjs → wire-CSsa3Wxd.mjs} +117 -2
- package/dist/flows.mjs +15 -10
- package/dist/gtd.bundle.mjs +511 -64
- package/dist/testkit.mjs +158 -26
- package/dist/wire.mjs +2 -2
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +138 -38
- 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 +208 -66
- package/package.json +1 -1
- package/skills/authoring/SKILL.md +3 -3
- package/src/cli/Cli.test.ts +10 -3
- package/src/cli/Cli.ts +76 -1
- package/src/cli/Program.test.ts +1 -0
- package/src/cli/Program.ts +28 -0
- package/src/cli/ToolCommands.ts +3 -0
- package/src/edge/Edge.ts +11 -0
- package/src/flows/runtime.ts +11 -0
- package/src/flows/scripts.ts +31 -9
- package/src/replay/Replay.ts +51 -2
- package/src/testkit/FlowScripts.test.ts +58 -0
- package/src/testkit/FlowScripts.ts +61 -24
- package/src/testkit/Layers.ts +1 -0
- package/src/wire/BeatDocument.ts +12 -2
- package/src/wire/BeatStatus.ts +3 -0
- package/src/wire/ContextLimit.fixture.ts +30 -0
- package/src/wire/ContextLimit.test.ts +96 -0
- package/src/wire/ContextLimit.ts +169 -0
- package/src/wire/index.ts +1 -0
- package/src/wire/types.ts +7 -0
- package/src/workflow/Workflow.ts +9 -1
- package/src/workflows/feature/acceptance-criteria.feature +5 -1
- package/src/workflows/feature/context-limit.feature +37 -0
- package/src/workflows/feature/coverage.feature +6 -3
- package/src/workflows/feature/coverage.ts +8 -2
- package/src/workflows/feature/fast-path.feature +11 -19
- package/src/workflows/feature/feature.test.ts +24 -4
- package/src/workflows/feature/feature.ts +14 -10
- 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/refactor-absorbs.feature +12 -5
- package/src/workflows/feature/refactor-first.feature +11 -2
- package/src/workflows/feature/refactor-lens-models.feature +4 -0
- package/src/workflows/feature/refactor-rebuild.feature +269 -0
- package/src/workflows/feature/refactor-review.feature +45 -1
- package/src/workflows/feature/refactor.ts +9 -3
- 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 +117 -6
- package/src/workflows/feature/seam.ts +158 -39
- package/src/workflows/review/review-door.feature +12 -4
- package/src/workflows/shared/options.test.ts +11 -2
- package/src/workflows/shared/options.ts +18 -4
- package/src/workflows/shared/steps.ts +1 -0
- package/src/workflows/shared/text.test.ts +8 -0
- package/src/workflows/shared/text.ts +91 -22
|
@@ -17,14 +17,15 @@ import * as t from "../shared/text.js"
|
|
|
17
17
|
import { skipDesign, statedCriteria, type StatedCriteria } from "./criteria.js"
|
|
18
18
|
import { design } from "./planning.js"
|
|
19
19
|
import { buildTail, isCode, reUnwind, type ReviewOutcome } from "./review.js"
|
|
20
|
-
import { implement, seamBuild
|
|
20
|
+
import { implement, seamBuild } from "./seam.js"
|
|
21
21
|
import { recordRisk, riskTierOn } from "./tier.js"
|
|
22
22
|
|
|
23
23
|
// The ordinary start. Any change to the tree starts a process: `idle` →
|
|
24
24
|
// `unwind` reverts the sketch (its intent survives in history) → a
|
|
25
|
-
// green-baseline gate → design → a
|
|
26
|
-
//
|
|
27
|
-
//
|
|
25
|
+
// green-baseline gate → design → a naive pass, judged with `flagMode` on: it
|
|
26
|
+
// lands as the implementation, or (refactoring first when the judge asks) is
|
|
27
|
+
// built fresh from → the health loop → the quality lap, and the episode ends
|
|
28
|
+
// back at `idle`. Human review is the `review` door's.
|
|
28
29
|
|
|
29
30
|
/** Revert the sketch that started the process out of the working tree; its intent survives in history. */
|
|
30
31
|
export const unwind = (): Promise<void> => {
|
|
@@ -36,21 +37,24 @@ export const unwind = (): Promise<void> => {
|
|
|
36
37
|
|
|
37
38
|
export { reUnwind }
|
|
38
39
|
|
|
39
|
-
/** A
|
|
40
|
+
/** A naive pass (judged only with `flagMode` on), then implement and the quality lap; design must have run since `firstBase`. */
|
|
40
41
|
export const buildFeature = async (firstBase: string): Promise<void> => {
|
|
41
42
|
const tier = riskTierOn() ? recordRisk() : undefined
|
|
42
|
-
const seam
|
|
43
|
-
? await seamBuild(firstBase)
|
|
44
|
-
: { base: firstBase }
|
|
43
|
+
const seam = await seamBuild(firstBase)
|
|
45
44
|
await slice("feature", async () => {
|
|
46
45
|
await implement({
|
|
47
46
|
reference: seam.reference,
|
|
48
47
|
tier,
|
|
49
48
|
afterRefactor: seam.refactored,
|
|
49
|
+
naive: seam.land === true ? seam.reference : undefined,
|
|
50
|
+
rebuild: seam.rebuild,
|
|
50
51
|
})
|
|
51
|
-
//
|
|
52
|
+
// A feature slice with no code beyond the carried-over tests: the refactor carried the
|
|
53
|
+
// feature, so the lap covers the whole change.
|
|
54
|
+
const carried = seam.rebuild?.acceptance?.tests ?? []
|
|
52
55
|
const base =
|
|
53
|
-
seam.refactored === true &&
|
|
56
|
+
seam.refactored === true &&
|
|
57
|
+
!changesSince(seam.base).some((c) => isCode(c.path) && !carried.includes(c.path))
|
|
54
58
|
? firstBase
|
|
55
59
|
: seam.base
|
|
56
60
|
await tail(settleQuality, { green: true, lensBase: base })
|
|
@@ -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:
|
|
@@ -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
|
|
8
|
-
|
|
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:
|
|
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"
|
|
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 →
|
|
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 →
|
|
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
|
+
"""
|