@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.
Files changed (69) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +45 -29
  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-C8Fm3s72.mjs → reserved-zAfoJtR7.mjs} +62 -24
  7. package/dist/chunks/{tail-oaYewBN_.mjs → tail-BRC4DGdV.mjs} +1 -1
  8. package/dist/chunks/{wire-D3Rc5r22.mjs → wire-CSsa3Wxd.mjs} +117 -2
  9. package/dist/flows.mjs +15 -10
  10. package/dist/gtd.bundle.mjs +511 -64
  11. package/dist/testkit.mjs +158 -26
  12. package/dist/wire.mjs +2 -2
  13. package/dist/workflows/cleanup.mjs +2 -2
  14. package/dist/workflows/feature.mjs +138 -38
  15. package/dist/workflows/fix.mjs +2 -2
  16. package/dist/workflows/review.mjs +2 -2
  17. package/dist/workflows/rollout.mjs +1 -1
  18. package/dist/workflows.mjs +208 -66
  19. package/package.json +1 -1
  20. package/skills/authoring/SKILL.md +3 -3
  21. package/src/cli/Cli.test.ts +10 -3
  22. package/src/cli/Cli.ts +76 -1
  23. package/src/cli/Program.test.ts +1 -0
  24. package/src/cli/Program.ts +28 -0
  25. package/src/cli/ToolCommands.ts +3 -0
  26. package/src/edge/Edge.ts +11 -0
  27. package/src/flows/runtime.ts +11 -0
  28. package/src/flows/scripts.ts +31 -9
  29. package/src/replay/Replay.ts +51 -2
  30. package/src/testkit/FlowScripts.test.ts +58 -0
  31. package/src/testkit/FlowScripts.ts +61 -24
  32. package/src/testkit/Layers.ts +1 -0
  33. package/src/wire/BeatDocument.ts +12 -2
  34. package/src/wire/BeatStatus.ts +3 -0
  35. package/src/wire/ContextLimit.fixture.ts +30 -0
  36. package/src/wire/ContextLimit.test.ts +96 -0
  37. package/src/wire/ContextLimit.ts +169 -0
  38. package/src/wire/index.ts +1 -0
  39. package/src/wire/types.ts +7 -0
  40. package/src/workflow/Workflow.ts +9 -1
  41. package/src/workflows/feature/acceptance-criteria.feature +5 -1
  42. package/src/workflows/feature/context-limit.feature +37 -0
  43. package/src/workflows/feature/coverage.feature +6 -3
  44. package/src/workflows/feature/coverage.ts +8 -2
  45. package/src/workflows/feature/fast-path.feature +11 -19
  46. package/src/workflows/feature/feature.test.ts +24 -4
  47. package/src/workflows/feature/feature.ts +14 -10
  48. package/src/workflows/feature/flag-mode.feature +17 -9
  49. package/src/workflows/feature/lens-models.feature +7 -1
  50. package/src/workflows/feature/naive-acceptance.feature +2 -2
  51. package/src/workflows/feature/one-step-land.feature +10 -12
  52. package/src/workflows/feature/quality-unresolved.feature +7 -1
  53. package/src/workflows/feature/refactor-absorbs.feature +12 -5
  54. package/src/workflows/feature/refactor-first.feature +11 -2
  55. package/src/workflows/feature/refactor-lens-models.feature +4 -0
  56. package/src/workflows/feature/refactor-rebuild.feature +269 -0
  57. package/src/workflows/feature/refactor-review.feature +45 -1
  58. package/src/workflows/feature/refactor.ts +9 -3
  59. package/src/workflows/feature/risk-tier.feature +12 -4
  60. package/src/workflows/feature/seam-rejection.feature +4 -9
  61. package/src/workflows/feature/seam.feature +29 -26
  62. package/src/workflows/feature/seam.test.ts +117 -6
  63. package/src/workflows/feature/seam.ts +158 -39
  64. package/src/workflows/review/review-door.feature +12 -4
  65. package/src/workflows/shared/options.test.ts +11 -2
  66. package/src/workflows/shared/options.ts +18 -4
  67. package/src/workflows/shared/steps.ts +1 -0
  68. package/src/workflows/shared/text.test.ts +8 -0
  69. 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, type SeamOutcome } from "./seam.js"
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 judged naive pass (refactoring first when
26
- // the judge asks; with `flagMode` off, neither) → implement → the quality lap,
27
- // and the episode ends back at `idle`. Human review is the `review` door's.
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 judged naive pass (none with `flagMode` off), then implement and the quality lap; design must have run since `firstBase`. */
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: Omit<SeamOutcome, "reference"> & { reference?: string } = flagModeOn()
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
- // An empty feature slice: the refactor carried the feature, so the lap covers the whole change.
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 && !changesSince(seam.base).some((c) => isCode(c.path))
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 builds a feature with no release flag, seam judge or refactor
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 implementation goes straight through the health loop and the
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 skips the seam judge and the refactor, and asks for no flag
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 "implement.building"
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 contains "Add no release flag — this project ships without release flags"
51
- And the resting step's content does not contain "new release flag"
52
- Given step "implement.building" writes "src/calc.ts" with:
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 → implement.building
76
- gtd(agent): implement.building → implement.health.check
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 "implement.building" writes "src/calc.ts" with:
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.building"
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.building
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 goes straight to implement with no seam gate
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, and runs implement and quality
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.building" writes "src/greeting.ts" with:
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 seam gate or refactor
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.building
82
- gtd(agent): implement.building → implement.health.check
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.building" has a body containing "\"answer\":\"land\""
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 "implement.building" writes "src/calc.ts" with:
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 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
+ """