@pmelab/gtd 41.0.0 → 42.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 (54) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +38 -28
  3. package/dist/chunks/{reserved-Ch4LLrEj.mjs → reserved-D7MNY9QJ.mjs} +58 -14
  4. package/dist/chunks/{steering-DVILanVf.mjs → steering-oCDdQlpP.mjs} +1 -1
  5. package/dist/chunks/tail-D2o2h5cc.mjs +246 -0
  6. package/dist/flows.mjs +11 -1
  7. package/dist/gtd.bundle.mjs +12 -2
  8. package/dist/steering.mjs +1 -1
  9. package/dist/testkit.mjs +15 -3
  10. package/dist/web/main.js +1 -1
  11. package/dist/workflow-kit.mjs +1 -1
  12. package/dist/workflows/cleanup.mjs +2 -2
  13. package/dist/workflows/feature.mjs +44 -24
  14. package/dist/workflows/fix.mjs +3 -3
  15. package/dist/workflows/review.mjs +2 -2
  16. package/dist/workflows/rollout.mjs +1 -1
  17. package/dist/workflows.mjs +245 -36
  18. package/package.json +1 -1
  19. package/src/flows/scripts.ts +15 -0
  20. package/src/steering/review.test.ts +2 -20
  21. package/src/steering/review.ts +2 -1
  22. package/src/testkit/FlowScripts.test.ts +17 -0
  23. package/src/testkit/FlowScripts.ts +15 -0
  24. package/src/testkit/steps.ts +27 -0
  25. package/src/workflows/feature/coverage.feature +3 -3
  26. package/src/workflows/feature/fast-path.feature +2 -2
  27. package/src/workflows/feature/feature.test.ts +2 -37
  28. package/src/workflows/feature/feature.ts +12 -14
  29. package/src/workflows/feature/flag-mode.feature +2 -2
  30. package/src/workflows/feature/lens-review.feature +234 -0
  31. package/src/workflows/feature/one-step-land.feature +2 -2
  32. package/src/workflows/feature/quality-unresolved.feature +47 -32
  33. package/src/workflows/feature/refactor-absorbs.feature +3 -3
  34. package/src/workflows/feature/refactor-first.feature +5 -5
  35. package/src/workflows/feature/refactor-rebuild.feature +7 -7
  36. package/src/workflows/feature/refactor-review.feature +19 -100
  37. package/src/workflows/feature/refactor.test.ts +12 -27
  38. package/src/workflows/feature/refactor.ts +0 -1
  39. package/src/workflows/feature/review-gate.feature +6 -6
  40. package/src/workflows/feature/review.test.ts +29 -25
  41. package/src/workflows/feature/review.ts +53 -10
  42. package/src/workflows/feature/seam-rejection.feature +2 -2
  43. package/src/workflows/feature/seam.feature +2 -2
  44. package/src/workflows/feature/seam.test.ts +1 -1
  45. package/src/workflows/feature/slicing.feature +6 -6
  46. package/src/workflows/index.ts +1 -0
  47. package/src/workflows/shared/lenses.test.ts +39 -0
  48. package/src/workflows/shared/lenses.ts +176 -0
  49. package/src/workflows/shared/options.ts +1 -0
  50. package/src/workflows/shared/tail.ts +19 -4
  51. package/src/workflows/shared/text.test.ts +68 -1
  52. package/src/workflows/shared/text.ts +126 -13
  53. package/src/workflows/shared/vars.ts +1 -0
  54. package/dist/chunks/tail-lY1UmJrU.mjs +0 -112
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from "vitest"
2
- import { runsItsScript, testRepo, type Actor } from "../../testkit/index.js"
2
+ import { runsItsScript, testRepo } from "../../testkit/index.js"
3
3
  import { CONFIG_SCAFFOLD } from "../../install/index.js"
4
4
  import { reviewDoc } from "../shared/config.fixture.js"
5
5
 
@@ -75,10 +75,7 @@ describe("afterTail feedback lap", () => {
75
75
  `}`,
76
76
  ].join("\n")
77
77
 
78
- const prompts: string[] = []
79
-
80
78
  const feedbackLap = async () => {
81
- prompts.length = 0
82
79
  const repo = testRepo({
83
80
  workflow: TAIL,
84
81
  env: {
@@ -93,10 +90,7 @@ describe("afterTail feedback lap", () => {
93
90
  await repo.drive({
94
91
  "review-gate.check": () => undefined,
95
92
  "build.health.check": () => undefined,
96
- "build.review.reviewing": (beat, r) => {
97
- prompts.push(beat.content)
98
- r.write(".gtd/REVIEW.md", review())
99
- },
93
+ "build.review.composing": (_, r) => r.write(".gtd/REVIEW.md", review()),
100
94
  })
101
95
  repo.write(".gtd/REVIEW.md", review("also export multiply"))
102
96
  await repo.land()
@@ -137,35 +131,6 @@ describe("afterTail feedback lap", () => {
137
131
  expect(rest.content).not.toContain("write the change fresh")
138
132
  expect(repo.subjects().some((subject) => subject.includes("seam."))).toBe(false)
139
133
  })
140
-
141
- it("the first review says everything since base, the one after feedback only what is new", async () => {
142
- const repo = await feedbackLap()
143
- expect(prompts[0]).toContain("it covers everything since the base")
144
- expect(prompts[0]).not.toContain("only what is new since the previous review")
145
- const writes: Record<string, () => [string, string]> = {
146
- "design.triage": () => [
147
- ".gtd/REQUIREMENTS.md",
148
- "# Requirements\n\n- multiply, designed\n\n## Acceptance Criteria\n\n- AC-1: done\n",
149
- ],
150
- "implement.building": () => ["src/calc.ts", `${CALC}// again\n`],
151
- }
152
- const actors = new Proxy({} as Record<string, Actor>, {
153
- get: (_, state: string): Actor | undefined =>
154
- state === "build.review.await-review" || state === "idle"
155
- ? undefined
156
- : (beat, r) => {
157
- if (state === "implement.coverage") return '[{"id":"AC-1","answer":"yes","p":0.9}]'
158
- if (state === "build.review.reviewing") {
159
- prompts.push(beat.content)
160
- r.write(".gtd/REVIEW.md", review())
161
- } else if (writes[state]) r.write(...writes[state]())
162
- else if (state.endsWith("closing") || state === "re-unwind") r.runScript(beat.content)
163
- },
164
- })
165
- await repo.drive(actors, { maxBeats: 80 })
166
- expect(prompts[1]).toContain("it covers only what is new since the previous review")
167
- expect(prompts[1]).not.toContain("everything since the base")
168
- })
169
134
  })
170
135
 
171
136
  describe("an in-flight 28.0.0 naive-pass process", () => {
@@ -9,7 +9,7 @@ import {
9
9
  slice,
10
10
  start,
11
11
  } from "../../flows/index.js"
12
- import { gate, healthy, type EscalationCount } from "../shared/health.js"
12
+ import { gate, healthy } from "../shared/health.js"
13
13
  import { flagModeOn, shipped } from "../shared/options.js"
14
14
  import { FEEDBACK, fixCheck, QUALITY, REQUIREMENTS } from "../shared/steps.js"
15
15
  import { refusePendingHandOff, tail } from "../shared/tail.js"
@@ -135,8 +135,8 @@ const buildSlice = async (sliceBase: string, build: SliceBuild): Promise<void> =
135
135
  })
136
136
  }
137
137
 
138
- // The lap's findings the fix cap left must not reach the review, nor the next process's lap,
139
- // which appends to the file. Empty counts as resolved, as the lap reads it.
138
+ // Between slices nothing reviews the lap's leftovers, and the next lap appends to the file.
139
+ // Empty counts as resolved, as the lap reads it.
140
140
  const settleQuality = async (): Promise<void> => {
141
141
  let rested = false
142
142
  while ((read(QUALITY) ?? "").length > 0) {
@@ -151,15 +151,13 @@ const settleQuality = async (): Promise<void> => {
151
151
  if (rested) await healthy(() => fixCheck())
152
152
  }
153
153
 
154
- /** The quality lap, then the human review since `base`. */
155
- const reviewedTail = (base: string, lensBase: string, lap?: "feedback"): Promise<ReviewOutcome> =>
156
- tail(
157
- async (escalations: EscalationCount) => {
158
- await settleQuality()
159
- return review(base, escalations, { lap })
160
- },
161
- { green: true, lensBase },
162
- )
154
+ /** The triaged quality lap, then the human review since `base`. */
155
+ const reviewedTail = (base: string, lensBase: string): Promise<ReviewOutcome> =>
156
+ tail((escalations, report) => review(base, escalations, { report }), {
157
+ green: true,
158
+ lensBase,
159
+ triage: true,
160
+ })
163
161
 
164
162
  // seam.naive / seam.judge never run on a feedback lap: its design → implement is built unjudged
165
163
  // and unrefactored, held to every criterion's coverage since the process start.
@@ -181,7 +179,7 @@ const featureFeedback = async (
181
179
  })
182
180
  const tier = riskTierOn() ? recordRisk() : undefined
183
181
  closed = await implementing({ tier, amend: true })
184
- outcome = await reviewedTail(outcome.base, lensBase, "feedback")
182
+ outcome = await reviewedTail(outcome.base, lensBase)
185
183
  }
186
184
  }
187
185
 
@@ -201,7 +199,7 @@ export const afterTail = async (first: ReviewOutcome): Promise<void> => {
201
199
  await design(outcome.base)
202
200
  const tier = riskTierOn() ? recordRisk() : undefined
203
201
  await implement({ tier, amend: true })
204
- outcome = await buildTail(outcome.base, { green: true, lap: "feedback" })
202
+ outcome = await buildTail(outcome.base, { green: true })
205
203
  }
206
204
  }
207
205
 
@@ -67,7 +67,7 @@ Feature: flag mode off lands the naive pass with no release flag, seam judge or
67
67
  """
68
68
  And step "build.quality.correctness.reviewing" changes nothing
69
69
  When the process is driven
70
- Then the process rests at "build.review.reviewing"
70
+ Then the process rests at "build.review.composing"
71
71
  And "src/calc.ts" contains "subtract"
72
72
  And "src/calc.test.ts" contains "[AC-1] subtracts"
73
73
  And the commit subjects end with:
@@ -85,5 +85,5 @@ Feature: flag mode off lands the naive pass with no release flag, seam judge or
85
85
  gtd(check): implement.health.check → implement.coverage
86
86
  gtd(judge): implement.coverage → implement.closing
87
87
  gtd(check): implement.closing → build.quality.correctness.reviewing
88
- gtd(agent): build.quality.correctness.reviewing → build.review.reviewing
88
+ gtd(agent): build.quality.correctness.reviewing → build.review.composing
89
89
  """
@@ -0,0 +1,234 @@
1
+ @inmem
2
+ Feature: the quality lenses are the only agentic review
3
+
4
+ A judge classifies each `## ` finding the lenses wrote to `.gtd/QUALITY.md`:
5
+ a mechanical one is fixed by the fixer, noise is dropped with its `p` kept in
6
+ a trailer, and a design one becomes a note in `.gtd/REVIEW.md`. gtd writes
7
+ that file itself — no reviewer turn runs — and lists, without tick boxes,
8
+ what was fixed automatically and what was dropped as noise.
9
+
10
+ Background:
11
+ Given the shipped workflows, as gtd install scaffolds them
12
+ And an environment variable "GTD_QUALITYREVIEWS" set to "correctness"
13
+ And an environment variable "GTD_FLAGMODE" set to "false"
14
+ And a file "src/calc.ts" with:
15
+ """
16
+ export const add = (a: number, b: number) => a + b
17
+ """
18
+ And the working tree is committed as "feat: add calculator"
19
+ And a file "TODO.md" with:
20
+ """
21
+ # Add subtract
22
+
23
+ subtract(a, b) returns the difference.
24
+ """
25
+ And step "unwind" changes nothing
26
+ And step "start-gate.check" changes nothing
27
+ And step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
28
+ """
29
+ # Requirements
30
+
31
+ ## Concerns
32
+
33
+ ### Subtract
34
+
35
+ - `subtract(a, b)` returns `a - b`.
36
+
37
+ ## Acceptance Criteria
38
+
39
+ - AC-1: subtract(a, b) returns a - b.
40
+ """
41
+ And step "design.gate.answer" changes nothing
42
+ And step "seam.naive" writes "src/calc.test.ts" with:
43
+ """
44
+ it("[AC-1] subtracts", () => {})
45
+ """
46
+ And step "seam.naive" writes "src/calc.ts" with:
47
+ """
48
+ export const add = (a: number, b: number) => a + b
49
+ export const subtract = (a: number, b: number) => a - b
50
+ """
51
+ And step "seam.check" changes nothing
52
+ And step "implement.applying" runs its script
53
+ And step "implement.health.check" changes nothing
54
+ And step "implement.closing" runs its script
55
+ And judge step "implement.coverage" answers:
56
+ """
57
+ [{"id":"AC-1","answer":"yes","p":0.9}]
58
+ """
59
+ And step "build.quality.correctness.reviewing" writes ".gtd/QUALITY.md" with:
60
+ """
61
+ # Quality
62
+
63
+ ## subtract has no negative test
64
+
65
+ Add a test subtracting a larger number.
66
+
67
+ ## add could be a method
68
+
69
+ A class would read better.
70
+
71
+ ## AC-1 ignores floating point
72
+
73
+ Should subtract round? The criterion does not say.
74
+ """
75
+ And step "build.quality.sorting" runs its script
76
+ And step "build.review.composing" runs its script
77
+
78
+ Scenario: a mechanical finding is fixed and listed, noise dropped and listed, a design one noted
79
+ Given judge step "build.quality.triage" answers:
80
+ """
81
+ [{"id":"Q1","answer":"mechanical","p":0.9},{"id":"Q2","answer":"noise","p":0.85},{"id":"Q3","answer":"design","p":0.8}]
82
+ """
83
+ And step "build.fix.quality.fixing" writes "src/calc.test.ts" with:
84
+ """
85
+ it("[AC-1] subtracts", () => {})
86
+ it("subtracts a larger number", () => {})
87
+ """
88
+ And step "build.fix.quality.fixing" deletes ".gtd/QUALITY.md"
89
+ And step "build.health.check" changes nothing
90
+ When the process is driven
91
+ Then the process rests at "build.review.await-review"
92
+ And ".gtd/QUALITY.md" does not exist
93
+ And the commit "gtd(judge): build.quality.triage → build.quality.sorting" has a body containing "Gtd-Mark: noise=Q2 p=0.85 add could be a method"
94
+ And ".gtd/REVIEW.md" contains:
95
+ """
96
+ ## AC-1 ignores floating point
97
+
98
+ Should subtract round? The criterion does not say.
99
+
100
+ ## Fixed automatically
101
+
102
+ - subtract has no negative test (
103
+ """
104
+ And ".gtd/REVIEW.md" contains the short hash of the commit "gtd(agent): build.fix.quality.fixing → build.health.check"
105
+ And ".gtd/REVIEW.md" contains:
106
+ """
107
+ ## Dropped as noise
108
+
109
+ - add could be a method (p=0.85)
110
+ """
111
+ And ".gtd/REVIEW.md" does not contain "[ ]"
112
+ And the commit subjects end with:
113
+ """
114
+ gtd(check): implement.closing → build.quality.correctness.reviewing
115
+ gtd(agent): build.quality.correctness.reviewing → build.quality.triage
116
+ gtd(judge): build.quality.triage → build.quality.sorting
117
+ gtd(check): build.quality.sorting → build.fix.quality.fixing
118
+ gtd(agent): build.fix.quality.fixing → build.health.check
119
+ gtd(check): build.health.check → build.review.composing
120
+ gtd(check): build.review.composing → build.review.await-review
121
+ """
122
+
123
+ Scenario: an unsure verdict makes a finding a note, never a fix or a drop
124
+ Given judge step "build.quality.triage" answers:
125
+ """
126
+ [{"id":"Q1","answer":"mechanical","p":0.5},{"id":"Q2","answer":"noise","p":0.6}]
127
+ """
128
+ When the process is driven
129
+ Then the process rests at "build.review.await-review"
130
+ And ".gtd/REVIEW.md" contains "## subtract has no negative test"
131
+ And ".gtd/REVIEW.md" contains "## add could be a method"
132
+ And ".gtd/REVIEW.md" contains "## AC-1 ignores floating point"
133
+ And ".gtd/REVIEW.md" does not contain "Fixed automatically"
134
+ And no commit body contains "Gtd-Mark: noise="
135
+ And the commit subjects end with:
136
+ """
137
+ gtd(judge): build.quality.triage → build.quality.sorting
138
+ gtd(check): build.quality.sorting → build.review.composing
139
+ gtd(check): build.review.composing → build.review.await-review
140
+ """
141
+
142
+ Scenario: a mechanical finding the fix cap leaves becomes a note
143
+ Given judge step "build.quality.triage" answers:
144
+ """
145
+ [{"id":"Q1","answer":"mechanical","p":0.9},{"id":"Q2","answer":"mechanical","p":0.9},{"id":"Q3","answer":"mechanical","p":0.9}]
146
+ """
147
+ And step "build.fix.quality.fixing" writes ".gtd/QUALITY.md" with:
148
+ """
149
+ # Quality
150
+
151
+ ## add could be a method
152
+
153
+ A class would read better.
154
+ """
155
+ And step "build.health.check" changes nothing
156
+ When the process is driven
157
+ Then the process rests at "build.review.await-review"
158
+ And ".gtd/QUALITY.md" does not exist
159
+ And ".gtd/REVIEW.md" contains:
160
+ """
161
+ ## add could be a method
162
+
163
+ _The fixer left this unresolved._
164
+
165
+ A class would read better.
166
+ """
167
+ And ".gtd/REVIEW.md" contains "- subtract has no negative test ("
168
+ And ".gtd/REVIEW.md" contains "- AC-1 ignores floating point ("
169
+ And the commit subjects end with:
170
+ """
171
+ gtd(judge): build.quality.triage → build.fix.quality.fixing
172
+ gtd(agent): build.fix.quality.fixing
173
+ gtd(agent): build.fix.quality.fixing
174
+ gtd(agent): build.fix.quality.fixing → build.health.check
175
+ gtd(check): build.health.check → build.review.composing
176
+ gtd(check): build.review.composing → build.review.await-review
177
+ """
178
+
179
+ Scenario: a fixer that renames a chunk it leaves counts nothing fixed
180
+ Given judge step "build.quality.triage" answers:
181
+ """
182
+ [{"id":"Q1","answer":"mechanical","p":0.9},{"id":"Q2","answer":"mechanical","p":0.9},{"id":"Q3","answer":"mechanical","p":0.9}]
183
+ """
184
+ And step "build.fix.quality.fixing" writes ".gtd/QUALITY.md" with:
185
+ """
186
+ # Quality
187
+
188
+ ## null check in parse()
189
+
190
+ Still open.
191
+ """
192
+ And step "build.health.check" changes nothing
193
+ When the process is driven
194
+ Then the process rests at "build.review.await-review"
195
+ And ".gtd/REVIEW.md" contains "## null check in parse()"
196
+ And ".gtd/REVIEW.md" contains "## subtract has no negative test"
197
+ And ".gtd/REVIEW.md" contains "check whether it was fixed"
198
+ And ".gtd/REVIEW.md" does not contain "Fixed automatically"
199
+
200
+ Scenario: prose the fixer leaves outside every chunk does not hide its fixes
201
+ Given judge step "build.quality.triage" answers:
202
+ """
203
+ [{"id":"Q1","answer":"mechanical","p":0.9},{"id":"Q2","answer":"mechanical","p":0.9},{"id":"Q3","answer":"mechanical","p":0.9}]
204
+ """
205
+ And step "build.fix.quality.fixing" writes ".gtd/QUALITY.md" with:
206
+ """
207
+ # Quality
208
+
209
+ All fixed.
210
+ """
211
+ And step "build.health.check" changes nothing
212
+ When the process is driven
213
+ Then the process rests at "build.review.await-review"
214
+ And ".gtd/REVIEW.md" contains "- subtract has no negative test ("
215
+ And ".gtd/REVIEW.md" does not contain "check whether it was fixed"
216
+
217
+ Scenario: signing off a composed review ends the process
218
+ Given judge step "build.quality.triage" answers:
219
+ """
220
+ [{"id":"Q1","answer":"noise","p":0.9},{"id":"Q2","answer":"noise","p":0.9},{"id":"Q3","answer":"noise","p":0.9}]
221
+ """
222
+ And step "build.review.closing" runs its script
223
+ When the process is driven
224
+ Then the process rests at "build.review.await-review"
225
+ When gtd lands
226
+ And the process is driven
227
+ Then the process rests at "idle"
228
+ And ".gtd/REVIEW.md" does not exist
229
+ And the commit subjects end with:
230
+ """
231
+ gtd(check): build.review.composing → build.review.await-review
232
+ gtd(human): build.review.await-review → build.review.closing
233
+ gtd(check): build.review.closing → idle
234
+ """
@@ -64,7 +64,7 @@ Feature: a naive pass the judge rules land is itself the implementation
64
64
 
65
65
  Scenario: a land judgment applies the naive pass and runs from idle to the review gate with no rebuild
66
66
  When the process is driven
67
- Then the process rests at "build.review.reviewing"
67
+ Then the process rests at "build.review.composing"
68
68
  And "src/greeting.ts" contains "Hello, world"
69
69
  And "src/acceptance.test.ts" contains "[AC-1] holds"
70
70
  And the commit subjects end with:
@@ -81,7 +81,7 @@ Feature: a naive pass the judge rules land is itself the implementation
81
81
  gtd(check): implement.health.check → implement.coverage
82
82
  gtd(judge): implement.coverage → implement.closing
83
83
  gtd(check): implement.closing → build.quality.correctness.reviewing
84
- gtd(agent): build.quality.correctness.reviewing → build.review.reviewing
84
+ gtd(agent): build.quality.correctness.reviewing → build.review.composing
85
85
  """
86
86
  And the commit "gtd(human): idle → unwind" has a body containing "Gtd-Var: flag_risk_tier=0"
87
87
  And the commit "gtd(agent): seam.naive → seam.check" has a body containing "Gtd-Kept:"
@@ -1,14 +1,16 @@
1
1
  @inmem
2
- Feature: quality findings left unresolved hold a feature process short of its review
2
+ Feature: quality findings left unresolved between slices hold the next slice back
3
3
 
4
- When the quality lap's fix cap leaves `.gtd/QUALITY.md` behind, the process
5
- rests for a person instead of handing findings nobody acted on to the
6
- human review.
4
+ Between slices no review reads the quality lap's leftovers, so when the fix
5
+ cap leaves `.gtd/QUALITY.md` behind the process rests for a person before
6
+ the next slice. The last slice's leftovers become review notes instead (see
7
+ `lens-review.feature`).
7
8
 
8
9
  Background:
9
10
  Given the shipped workflows, as gtd install scaffolds them
10
11
  And an environment variable "GTD_QUALITYREVIEWS" set to "correctness"
11
12
  And an environment variable "GTD_FLAGMODE" set to "false"
13
+ And an environment variable "GTD_SLICEMAXCRITERIA" set to "1"
12
14
  And a file "src/calc.ts" with:
13
15
  """
14
16
  export const add = (a: number, b: number) => a + b
@@ -16,9 +18,9 @@ Feature: quality findings left unresolved hold a feature process short of its re
16
18
  And the working tree is committed as "feat: add calculator"
17
19
  And a file "TODO.md" with:
18
20
  """
19
- # Add subtract
21
+ # More arithmetic
20
22
 
21
- subtract(a, b) returns the difference.
23
+ subtract and multiply.
22
24
  """
23
25
  And step "unwind" changes nothing
24
26
  And step "start-gate.check" changes nothing
@@ -26,41 +28,53 @@ Feature: quality findings left unresolved hold a feature process short of its re
26
28
  """
27
29
  # Requirements
28
30
 
29
- ## Concerns
30
-
31
- ### Subtract
32
-
33
- - `subtract(a, b)` returns `a - b`.
34
-
35
31
  ## Acceptance Criteria
36
32
 
37
33
  - AC-1: subtract(a, b) returns a - b.
34
+ - AC-2: multiply(a, b) returns a * b.
38
35
  """
39
36
  And step "design.gate.answer" changes nothing
40
- And step "seam.naive" writes "src/calc.test.ts" with:
37
+ And step "design.slicing" writes ".gtd/SLICES.md" with:
38
+ """
39
+ # Slices
40
+
41
+ ## Subtract
42
+
43
+ - AC-1
44
+
45
+ ## Multiply
46
+
47
+ - AC-2
48
+ """
49
+ And step "seam.naive", when prompted with "This slice builds AC-1 only", writes "src/sub.test.ts" with:
41
50
  """
42
51
  it("[AC-1] subtracts", () => {})
43
52
  """
44
- And step "seam.naive" writes "src/calc.ts" with:
53
+ And step "seam.naive", when prompted with "This slice builds AC-2 only", writes "src/mul.test.ts" with:
45
54
  """
46
- export const add = (a: number, b: number) => a + b
47
- export const subtract = (a: number, b: number) => a - b
55
+ it("[AC-2] multiplies", () => {})
48
56
  """
49
57
  And step "seam.check" changes nothing
50
58
  And step "implement.applying" runs its script
51
59
  And step "implement.health.check" changes nothing
52
60
  And step "implement.closing" runs its script
53
- And judge step "implement.coverage" answers:
61
+ And judge step "implement.coverage", when its questions contain "AC-1", answers:
54
62
  """
55
63
  [{"id":"AC-1","answer":"yes","p":0.9}]
56
64
  """
65
+ And judge step "implement.coverage", when its questions contain "AC-2", answers:
66
+ """
67
+ [{"id":"AC-2","answer":"yes","p":0.9}]
68
+ """
57
69
 
58
- Scenario: findings the fix cap left unresolved rest at build.quality-unresolved
70
+ Scenario: findings the fix cap left after a slice rest at build.quality-unresolved
59
71
  Given step "build.quality.correctness.reviewing" writes ".gtd/QUALITY.md" with:
60
72
  """
61
73
  # Quality
62
74
 
63
- - subtract has no test
75
+ ## subtract has no negative test
76
+
77
+ Add a test subtracting a larger number.
64
78
  """
65
79
  And step "build.fix.quality.fixing" from now on changes nothing
66
80
  And step "build.health.describe" writes ".gtd/ESCALATION.md" with:
@@ -74,33 +88,34 @@ Feature: quality findings left unresolved hold a feature process short of its re
74
88
  Given step "build.fix" writes "src/calc.ts" with:
75
89
  """
76
90
  export const add = (a: number, b: number) => a + b
77
- export const subtract = (a: number, b: number) => a - b
78
91
  // checked
79
92
  """
80
93
  And step "build.health.check" changes nothing
81
94
  When gtd lands
82
95
  And the process is driven
83
96
  Then the process rests at "build.quality-unresolved"
84
- And ".gtd/QUALITY.md" contains "subtract has no test"
97
+ And ".gtd/QUALITY.md" contains "subtract has no negative test"
85
98
 
86
99
  When gtd lands
87
100
  And the process is driven
88
101
  Then the process rests at "build.quality-unresolved"
89
102
 
90
- Given a file "src/calc.ts" with:
91
- """
92
- export const add = (a: number, b: number) => a + b
93
- export const subtract = (a: number, b: number) => a - b
94
- // checked by hand
95
- """
96
- And the file ".gtd/QUALITY.md" is deleted
97
- And step "build.health.check" changes nothing
103
+ Given the file ".gtd/QUALITY.md" is deleted
104
+ And step "build.quality.correctness.reviewing" from now on changes nothing
105
+ And step "build.review.composing" runs its script
98
106
  When gtd lands
99
107
  And the process is driven
100
- Then the process rests at "build.review.reviewing"
108
+ Then the process rests at "build.review.await-review"
101
109
  And the commit subjects end with:
102
110
  """
103
- gtd(check): build.health.check → build.quality-unresolved
104
111
  gtd(human): build.quality-unresolved → build.health.check
105
- gtd(check): build.health.check → build.review.reviewing
112
+ gtd(check): build.health.check → seam.naive
113
+ gtd(agent): seam.naive → seam.check
114
+ gtd(check): seam.check → implement.applying
115
+ gtd(check): implement.applying → implement.health.check
116
+ gtd(check): implement.health.check → implement.coverage
117
+ gtd(judge): implement.coverage → implement.closing
118
+ gtd(check): implement.closing → build.quality.correctness.reviewing
119
+ gtd(agent): build.quality.correctness.reviewing → build.review.composing
120
+ gtd(check): build.review.composing → build.review.await-review
106
121
  """
@@ -69,7 +69,7 @@ Feature: a refactor that carried the whole feature still gets a quality lap over
69
69
  """
70
70
  And step "refactor.health.check" changes nothing
71
71
  And step "refactor.closing" runs its script
72
- And step "refactor.build.review.reviewing" writes ".gtd/REVIEW.md" with:
72
+ And step "refactor.build.review.composing" writes ".gtd/REVIEW.md" with:
73
73
  """
74
74
  # Review: abc1234
75
75
 
@@ -113,7 +113,7 @@ Feature: a refactor that carried the whole feature still gets a quality lap over
113
113
 
114
114
  Given step "build.quality.correctness.reviewing" changes nothing
115
115
  When the process is driven
116
- Then the process rests at "build.review.reviewing"
116
+ Then the process rests at "build.review.composing"
117
117
  And "src/calc.ts" contains "subtract"
118
118
  And the commit subjects end with:
119
119
  """
@@ -123,7 +123,7 @@ Feature: a refactor that carried the whole feature still gets a quality lap over
123
123
  gtd(check): implement.health.check → implement.coverage
124
124
  gtd(judge): implement.coverage → implement.closing
125
125
  gtd(check): implement.closing → build.quality.correctness.reviewing
126
- gtd(agent): build.quality.correctness.reviewing → build.review.reviewing
126
+ gtd(agent): build.quality.correctness.reviewing → build.review.composing
127
127
  """
128
128
  And the output lines of gtd "slices" start with:
129
129
  """
@@ -72,7 +72,7 @@ Feature: a refactor-first judgment walks one fixed path to the review gate
72
72
  """
73
73
  And step "refactor.health.check" changes nothing
74
74
  And step "refactor.closing" runs its script
75
- And step "refactor.build.review.reviewing" writes ".gtd/REVIEW.md" with:
75
+ And step "refactor.build.review.composing" writes ".gtd/REVIEW.md" with:
76
76
  """
77
77
  # Review: abc1234
78
78
 
@@ -121,7 +121,7 @@ Feature: a refactor-first judgment walks one fixed path to the review gate
121
121
  [{"id":"AC-1","answer":"yes","p":0.9}]
122
122
  """
123
123
  When the process is driven
124
- Then the process rests at "build.review.reviewing"
124
+ Then the process rests at "build.review.composing"
125
125
  And "src/calc.ts" contains "op(a, b, 1)"
126
126
  And "src/calc.ts" contains "subtract"
127
127
  And the commit subjects end with:
@@ -138,14 +138,14 @@ Feature: a refactor-first judgment walks one fixed path to the review gate
138
138
  gtd(check): refactor.deriving → refactor.building
139
139
  gtd(agent): refactor.building → refactor.health.check
140
140
  gtd(check): refactor.health.check → refactor.closing
141
- gtd(check): refactor.closing → refactor.build.review.reviewing
142
- gtd(agent): refactor.build.review.reviewing → refactor.build.review.await-review
141
+ gtd(check): refactor.closing → refactor.build.review.composing
142
+ gtd(check): refactor.build.review.composing → refactor.build.review.await-review
143
143
  gtd(human): refactor.build.review.await-review → refactor.build.review.closing
144
144
  gtd(check): refactor.build.review.closing → implement.building
145
145
  gtd(agent): implement.building → implement.health.check
146
146
  gtd(check): implement.health.check → implement.coverage
147
147
  gtd(judge): implement.coverage → implement.closing
148
- gtd(check): implement.closing → build.review.reviewing
148
+ gtd(check): implement.closing → build.review.composing
149
149
  """
150
150
  And the commit "gtd(human): idle → unwind" has a body containing "Gtd-Var: flag_risk_tier=0"
151
151
  And the commit "gtd(agent): seam.naive → seam.check" has a body containing "Gtd-Kept:"
@@ -83,7 +83,7 @@ Feature: refactor-first writes a refactor note, refactors on its own, then rebui
83
83
  """
84
84
  And step "refactor.health.check" changes nothing
85
85
  And step "refactor.closing" runs its script
86
- And step "refactor.build.review.reviewing" writes ".gtd/REVIEW.md" with:
86
+ And step "refactor.build.review.composing" writes ".gtd/REVIEW.md" with:
87
87
  """
88
88
  # Review: abc1234
89
89
 
@@ -147,7 +147,7 @@ Feature: refactor-first writes a refactor note, refactors on its own, then rebui
147
147
  """
148
148
  And step "refactor.health.check" changes nothing
149
149
  And step "refactor.closing" runs its script
150
- And step "refactor.build.review.reviewing" writes ".gtd/REVIEW.md" with:
150
+ And step "refactor.build.review.composing" writes ".gtd/REVIEW.md" with:
151
151
  """
152
152
  # Review: abc1234
153
153
 
@@ -193,7 +193,7 @@ Feature: refactor-first writes a refactor note, refactors on its own, then rebui
193
193
  """
194
194
  And step "refactor.health.check" changes nothing
195
195
  And step "refactor.closing" runs its script
196
- And step "refactor.build.review.reviewing" writes ".gtd/REVIEW.md" with:
196
+ And step "refactor.build.review.composing" writes ".gtd/REVIEW.md" with:
197
197
  """
198
198
  # Review: abc1234
199
199
 
@@ -242,7 +242,7 @@ Feature: refactor-first writes a refactor note, refactors on its own, then rebui
242
242
  [{"id":"AC-1","answer":"yes","p":0.9}]
243
243
  """
244
244
  When the process is driven
245
- Then the process rests at "build.review.reviewing"
245
+ Then the process rests at "build.review.composing"
246
246
  And "src/calc.ts" contains "op(a, b, -1)"
247
247
  And "src/acceptance.test.ts" contains "[AC-1]"
248
248
  And ".gtd/REFACTOR-NOTE.md" does not exist
@@ -253,14 +253,14 @@ Feature: refactor-first writes a refactor note, refactors on its own, then rebui
253
253
  gtd(check): refactor.deriving → refactor.building
254
254
  gtd(agent): refactor.building → refactor.health.check
255
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
256
+ gtd(check): refactor.closing → refactor.build.review.composing
257
+ gtd(check): refactor.build.review.composing → refactor.build.review.await-review
258
258
  gtd(human): refactor.build.review.await-review → refactor.build.review.closing
259
259
  gtd(check): refactor.build.review.closing → implement.building
260
260
  gtd(agent): implement.building → implement.health.check
261
261
  gtd(check): implement.health.check → implement.coverage
262
262
  gtd(judge): implement.coverage → implement.closing
263
- gtd(check): implement.closing → build.review.reviewing
263
+ gtd(check): implement.closing → build.review.composing
264
264
  """
265
265
  And the output lines of gtd "slices" start with:
266
266
  """