@pmelab/gtd 33.0.0 → 35.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 (32) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +65 -50
  3. package/dist/chunks/{reserved-BU5Zi-nX.mjs → reserved-C8Fm3s72.mjs} +45 -2
  4. package/dist/chunks/{tail-DuXanjIY.mjs → tail-oaYewBN_.mjs} +1 -1
  5. package/dist/workflows/cleanup.mjs +2 -2
  6. package/dist/workflows/feature.mjs +99 -19
  7. package/dist/workflows/fix.mjs +2 -2
  8. package/dist/workflows/review.mjs +2 -2
  9. package/dist/workflows/rollout.mjs +1 -1
  10. package/dist/workflows.mjs +144 -21
  11. package/package.json +1 -1
  12. package/skills/authoring/SKILL.md +2 -2
  13. package/src/testkit/steps.ts +4 -0
  14. package/src/workflows/feature/coverage.feature +161 -0
  15. package/src/workflows/feature/coverage.ts +106 -0
  16. package/src/workflows/feature/fast-path.feature +8 -33
  17. package/src/workflows/feature/feature.test.ts +1 -101
  18. package/src/workflows/feature/feature.ts +34 -17
  19. package/src/workflows/feature/flag-mode.feature +8 -92
  20. package/src/workflows/feature/lens-models.feature +4 -0
  21. package/src/workflows/feature/one-step-land.feature +8 -32
  22. package/src/workflows/feature/quality-unresolved.feature +100 -0
  23. package/src/workflows/feature/refactor-absorbs.feature +15 -23
  24. package/src/workflows/feature/refactor-first.feature +7 -32
  25. package/src/workflows/feature/refactor-review.feature +31 -345
  26. package/src/workflows/feature/seam-rejection.feature +5 -30
  27. package/src/workflows/feature/seam.feature +8 -104
  28. package/src/workflows/feature/seam.test.ts +3 -1
  29. package/src/workflows/feature/seam.ts +4 -12
  30. package/src/workflows/review/review-door.feature +113 -0
  31. package/src/workflows/shared/tail.ts +2 -2
  32. package/src/workflows/shared/text.ts +56 -1
@@ -64,107 +64,6 @@ const naivePlanned = () => {
64
64
  return repo
65
65
  }
66
66
 
67
- describe("planAndBuild", () => {
68
- it("a refactor that carried the whole feature: the empty implement turn passes and the review covers the whole change", async () => {
69
- const repo = naivePlanned()
70
- const SUBTRACT = `${CALC}export const subtract = (a: number, b: number) => a - b\n`
71
- expect(
72
- (
73
- await repo.drive({
74
- unwind: () => undefined,
75
- "start-gate.check": () => undefined,
76
- "design.triage": (_, r) =>
77
- r.write(
78
- ".gtd/REQUIREMENTS.md",
79
- "# Requirements\n\n## Concerns\n\n### S\n\n- `s`\n\n## Acceptance Criteria\n\n- AC-1: done\n",
80
- ),
81
- "design.gate.answer": () => undefined,
82
- "seam.naive": (_, r) => {
83
- r.write("src/calc.ts", SUBTRACT)
84
- r.write("src/calc.test.ts", ACCEPTANCE)
85
- },
86
- "seam.check": () => undefined,
87
- "seam.judge": () => '[{"id":"shape","answer":"refactor-first","p":0.9}]',
88
- "refactor.deriving": runsItsScript,
89
- "refactor.building": (_, r) => r.write("src/calc.ts", SUBTRACT),
90
- "refactor.health.check": () => undefined,
91
- "refactor.closing": runsItsScript,
92
- "refactor.build.review.reviewing": (_, r) => r.write(".gtd/REVIEW.md", review()),
93
- })
94
- ).state,
95
- ).toBe("refactor.build.review.await-review")
96
- await repo.land()
97
- const rest = await repo.drive({
98
- "refactor.build.review.closing": runsItsScript,
99
- "implement.building": () => undefined,
100
- "implement.health.check": () => undefined,
101
- "implement.closing": runsItsScript,
102
- "build.review.reviewing": (_, r) => r.write(".gtd/REVIEW.md", review()),
103
- })
104
- expect(rest.state).toBe("build.review.await-review")
105
- const base = (await repo.gtd("base")).stdout.trim()
106
- expect(repo.show(base, "src/calc.ts")).toBe(CALC)
107
- })
108
-
109
- it("after land, feedback rests next at design.triage, then implement.building, never seam.naive", async () => {
110
- const repo = naivePlanned()
111
- const requirements = (_: unknown, r: typeof repo) =>
112
- r.write(
113
- ".gtd/REQUIREMENTS.md",
114
- "# Requirements\n\n## Concerns\n\n### Subtract\n\n- `subtract`\n\n## Acceptance Criteria\n\n- AC-1: done\n",
115
- )
116
- const lap = {
117
- unwind: () => undefined,
118
- "start-gate.check": () => undefined,
119
- "design.triage": requirements,
120
- "design.gate.answer": () => undefined,
121
- "seam.naive": (_: unknown, r: typeof repo) => {
122
- r.write("src/calc.ts", `${CALC}// naive\n`)
123
- r.write("src/calc.test.ts", ACCEPTANCE)
124
- },
125
- "seam.check": () => undefined,
126
- "seam.judge": () => '[{"id":"shape","answer":"land","p":0.9}]',
127
- "implement.building": (_: unknown, r: typeof repo) =>
128
- r.write("src/calc.ts", `${CALC}// built\n`),
129
- "implement.health.check": () => undefined,
130
- "implement.closing": runsItsScript,
131
- "build.review.reviewing": (_: unknown, r: typeof repo) => r.write(".gtd/REVIEW.md", review()),
132
- }
133
- expect((await repo.drive(lap)).state).toBe("build.review.await-review")
134
-
135
- repo.write(".gtd/REVIEW.md", review("also export multiply"))
136
- await repo.land()
137
- const collected = await repo.drive({
138
- "build.review.collecting": (_, r) => {
139
- requirements(_, r)
140
- r.write(".gtd/REVIEW.md", `${review("also export multiply")} - A: noted.\n`)
141
- },
142
- })
143
- expect(collected.state).toBe("build.review.await-review")
144
-
145
- repo.write(".gtd/REVIEW.md", review())
146
- await repo.land()
147
- const rest = await repo.drive({
148
- "build.review.closing": runsItsScript,
149
- "re-unwind": runsItsScript,
150
- "design.triage": (_, r) =>
151
- r.write(
152
- ".gtd/REQUIREMENTS.md",
153
- "# Requirements\n\n- multiply\n\n## Acceptance Criteria\n\n- AC-1: done\n",
154
- ),
155
- "design.gate.answer": () => undefined,
156
- })
157
- expect(rest.state).toBe("implement.building")
158
- expect(rest.content).toContain("already exists")
159
- expect(rest.content).not.toContain("write the change fresh")
160
- const subjects = repo.subjects()
161
- const reUnwind = subjects.findIndex((subject) => subject.endsWith("→ re-unwind"))
162
- expect(reUnwind).toBeGreaterThan(-1)
163
- expect(subjects.slice(reUnwind + 1).some((subject) => subject.includes("seam."))).toBe(false)
164
- expect(subjects.filter((subject) => subject.includes("→ seam.naive"))).toHaveLength(1)
165
- })
166
- })
167
-
168
67
  describe("afterTail feedback lap", () => {
169
68
  const TAIL = [
170
69
  `import { start } from "@pmelab/gtd/flows"`,
@@ -255,6 +154,7 @@ describe("afterTail feedback lap", () => {
255
154
  state === "build.review.await-review" || state === "idle"
256
155
  ? undefined
257
156
  : (beat, r) => {
157
+ if (state === "implement.coverage") return '[{"id":"AC-1","answer":"yes","p":0.9}]'
258
158
  if (state === "build.review.reviewing") {
259
159
  prompts.push(beat.content)
260
160
  r.write(".gtd/REVIEW.md", review())
@@ -3,15 +3,16 @@ import {
3
3
  changesSince,
4
4
  head,
5
5
  human,
6
+ read,
6
7
  revertScript,
7
8
  run,
8
9
  slice,
9
10
  start,
10
11
  } from "../../flows/index.js"
11
- import { gate } from "../shared/health.js"
12
+ import { gate, healthy } from "../shared/health.js"
12
13
  import { flagModeOn, shipped } from "../shared/options.js"
13
- import { FEEDBACK } from "../shared/steps.js"
14
- import { refusePendingHandOff } from "../shared/tail.js"
14
+ import { FEEDBACK, fixCheck, QUALITY } from "../shared/steps.js"
15
+ import { refusePendingHandOff, tail } from "../shared/tail.js"
15
16
  import * as t from "../shared/text.js"
16
17
  import { skipDesign, statedCriteria, type StatedCriteria } from "./criteria.js"
17
18
  import { design } from "./planning.js"
@@ -22,9 +23,8 @@ import { recordRisk, riskTierOn } from "./tier.js"
22
23
  // The ordinary start. Any change to the tree starts a process: `idle` →
23
24
  // `unwind` reverts the sketch (its intent survives in history) → a
24
25
  // green-baseline gate → design → a judged naive pass (refactoring first when
25
- // the judge asks; with `flagMode` off, neither) → implement → the quality lap
26
- // → human review, which signs off (the episode ends back at `idle`) or sends a
27
- // design → implement lap.
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.
28
28
 
29
29
  /** Revert the sketch that started the process out of the working tree; its intent survives in history. */
30
30
  export const unwind = (): Promise<void> => {
@@ -36,25 +36,43 @@ export const unwind = (): Promise<void> => {
36
36
 
37
37
  export { reUnwind }
38
38
 
39
- // seam.naive / seam.judge run once: a feedback lap is design → implement on every verdict, so a
40
- // seam a re-plan turns up is built unjudged and unrefactored.
41
- /** A judged naive pass (none with `flagMode` off), then implement and review until a review round signs off; design must have run since `firstBase`. */
39
+ /** A judged naive pass (none with `flagMode` off), then implement and the quality lap; design must have run since `firstBase`. */
42
40
  export const buildFeature = async (firstBase: string): Promise<void> => {
43
41
  const tier = riskTierOn() ? recordRisk() : undefined
44
42
  const seam: Omit<SeamOutcome, "reference"> & { reference?: string } = flagModeOn()
45
43
  ? await seamBuild(firstBase)
46
44
  : { base: firstBase }
47
45
  await slice("feature", async () => {
48
- await implement({ reference: seam.reference, tier, afterRefactor: seam.refactored })
49
- // An empty feature slice: the refactor carried the feature, so review the whole change.
46
+ await implement({
47
+ reference: seam.reference,
48
+ tier,
49
+ afterRefactor: seam.refactored,
50
+ })
51
+ // An empty feature slice: the refactor carried the feature, so the lap covers the whole change.
50
52
  const base =
51
53
  seam.refactored === true && !changesSince(seam.base).some((c) => isCode(c.path))
52
54
  ? firstBase
53
55
  : seam.base
54
- await featureFeedback(await buildTail(base, { green: true, lensBase: base }), base)
56
+ await tail(settleQuality, { green: true, lensBase: base })
55
57
  })
56
58
  }
57
59
 
60
+ // The lap is feature's last look: findings the fix cap left must not end the process, nor reach
61
+ // the next one's lap, which appends to the file. Empty counts as resolved, as the lap reads it.
62
+ const settleQuality = async (): Promise<void> => {
63
+ let rested = false
64
+ while ((read(QUALITY) ?? "").length > 0) {
65
+ rested = true
66
+ await human("quality-unresolved", {
67
+ message: t.qualityUnresolvedMessage(),
68
+ label: "Quality findings left unresolved",
69
+ file: QUALITY,
70
+ })
71
+ }
72
+ // The person may have fixed code by hand there; nothing else checks it before idle.
73
+ if (rested) await healthy(() => fixCheck())
74
+ }
75
+
58
76
  /** Design — skipped when the sketch's own criteria are judged clear — then `buildFeature`. */
59
77
  export const planAndBuild = async (firstBase: string, stated?: StatedCriteria): Promise<void> => {
60
78
  // GTD_FLAG_RISK_TIER: the tier is proposed at triage and set at the design gate, so no skip.
@@ -62,20 +80,19 @@ export const planAndBuild = async (firstBase: string, stated?: StatedCriteria):
62
80
  await buildFeature(firstBase)
63
81
  }
64
82
 
65
- const featureFeedback = async (first: ReviewOutcome, lensBase?: string): Promise<void> => {
83
+ // seam.naive / seam.judge never run here: a re-plan's seam is built unjudged and unrefactored.
84
+ /** After a tail that skipped planning: stop on sign-off, re-plan on feedback. Opens no slice: slices cannot nest. */
85
+ export const afterTail = async (first: ReviewOutcome): Promise<void> => {
66
86
  let outcome = first
67
87
  while (outcome.verdict === "feedback") {
68
88
  await reUnwind(outcome)
69
89
  await design(outcome.base)
70
90
  const tier = riskTierOn() ? recordRisk() : undefined
71
91
  await implement({ tier, amend: true })
72
- outcome = await buildTail(outcome.base, { green: true, lap: "feedback", lensBase })
92
+ outcome = await buildTail(outcome.base, { green: true, lap: "feedback" })
73
93
  }
74
94
  }
75
95
 
76
- /** After a tail that skipped planning: stop on sign-off, re-plan on feedback. Opens no slice: slices cannot nest. */
77
- export const afterTail = (outcome: ReviewOutcome): Promise<void> => featureFeedback(outcome)
78
-
79
96
  /** An ordinary start: wait at `idle`, unwind the sketch, check the baseline, then plan and build. */
80
97
  export const ordinaryStart = async (): Promise<void> => {
81
98
  await human("idle", { message: t.idleMessage(), label: "Idle", file: ".gtd/TODO.md" })
@@ -56,9 +56,13 @@ Feature: flag mode off builds a feature with no release flag, seam judge or refa
56
56
  """
57
57
  And step "implement.health.check" changes nothing
58
58
  And step "implement.closing" runs its script
59
+ And judge step "implement.coverage" answers:
60
+ """
61
+ [{"id":"AC-1","answer":"yes","p":0.9}]
62
+ """
59
63
  And step "build.quality.correctness.reviewing" changes nothing
60
64
  When the process is driven
61
- Then the process rests at "build.review.reviewing"
65
+ Then the process rests at "idle"
62
66
  And "src/calc.ts" contains "subtract"
63
67
  And the commit subjects end with:
64
68
  """
@@ -70,96 +74,8 @@ Feature: flag mode off builds a feature with no release flag, seam judge or refa
70
74
  gtd(agent): design.triage → design.gate.answer
71
75
  gtd(human): design.gate.answer → implement.building
72
76
  gtd(agent): implement.building → implement.health.check
73
- gtd(check): implement.health.check → implement.closing
77
+ gtd(check): implement.health.check → implement.coverage
78
+ gtd(judge): implement.coverage → implement.closing
74
79
  gtd(check): implement.closing → build.quality.correctness.reviewing
75
- gtd(agent): build.quality.correctness.reviewing → build.review.reviewing
76
- """
77
-
78
- Scenario: flag mode off asks for no flag on a feedback lap either
79
- Given step "implement.building" writes "src/calc.ts" with:
80
- """
81
- export const add = (a: number, b: number) => a + b
82
- export const subtract = (a: number, b: number) => a - b
83
- """
84
- And step "implement.health.check" changes nothing
85
- And step "implement.closing" runs its script
86
- And step "build.quality.correctness.reviewing" changes nothing
87
- And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
88
- """
89
- # Review: abc1234
90
-
91
- <!-- base: 0000000 -->
92
-
93
- ## calc
94
-
95
- - [ ] ./src/calc.ts#2-2
96
- new subtract function
97
- """
98
- When the process is driven
99
- Then the process rests at "build.review.await-review"
100
- Given a file ".gtd/REVIEW.md" with:
101
- """
102
- # Review: abc1234
103
-
104
- <!-- base: 0000000 -->
105
-
106
- ## calc
107
-
108
- - [ ] ./src/calc.ts#2-2
109
- new subtract function[^why]
110
-
111
- [^why]:
112
- - H: also export multiply
113
- """
114
- And step "build.review.collecting" writes ".gtd/REQUIREMENTS.md" with:
80
+ gtd(agent): build.quality.correctness.reviewing → idle
115
81
  """
116
- # Requirements
117
-
118
- ## Concerns
119
-
120
- ### Multiply
121
-
122
- - Export `multiply()`.
123
-
124
- ## Acceptance Criteria
125
-
126
- - AC-1: the change behaves as described.
127
- """
128
- And step "build.review.collecting" writes ".gtd/REVIEW.md" with:
129
- """
130
- # Review: abc1234
131
-
132
- <!-- base: 0000000 -->
133
-
134
- ## calc
135
-
136
- - [ ] ./src/calc.ts#2-2
137
- new subtract function[^why]
138
-
139
- [^why]:
140
- - H: also export multiply
141
- - A: noted in the requirements.
142
- """
143
- When gtd lands
144
- And the process is driven
145
- Then the process rests at "build.review.await-review"
146
- Given a file ".gtd/REVIEW.md" with:
147
- """
148
- # Review: abc1234
149
-
150
- <!-- base: 0000000 -->
151
-
152
- ## calc
153
-
154
- - [ ] ./src/calc.ts#2-2
155
- new subtract function
156
- """
157
- And step "build.review.closing" runs its script
158
- And step "re-unwind" runs its script
159
- And step "implement.building" is no longer scripted
160
- When gtd lands
161
- And the process is driven
162
- Then the process rests at "implement.building"
163
- And the resting step's content contains "Add no release flag — this project ships without release flags"
164
- And the resting step's content does not contain "release flag the earlier lap added"
165
- And no commit body contains "Gtd-Kept:"
@@ -44,6 +44,10 @@ Feature: each quality lens runs on its own model hint
44
44
  """
45
45
  And step "implement.health.check" changes nothing
46
46
  And step "implement.closing" runs its script
47
+ And judge step "implement.coverage" answers:
48
+ """
49
+ [{"id":"AC-1","answer":"yes","p":0.9}]
50
+ """
47
51
 
48
52
  Scenario: a hinted lens runs on its hint, every other lens on its default
49
53
  When the process is driven
@@ -4,8 +4,8 @@ Feature: a naive pass the judge rules land goes straight to implement with no se
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, quality and
8
- review back to idle. The flag is pinned here so the per-flag test runs never
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
9
9
  move it off this path.
10
10
 
11
11
  Background:
@@ -59,36 +59,14 @@ Feature: a naive pass the judge rules land goes straight to implement with no se
59
59
  """
60
60
  And step "implement.health.check" changes nothing
61
61
  And step "implement.closing" runs its script
62
- And step "build.quality.correctness.reviewing" changes nothing
63
- And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
62
+ And judge step "implement.coverage" answers:
64
63
  """
65
- # Review: abc1234
66
-
67
- <!-- base: 0000000 -->
68
-
69
- ## greeting
70
-
71
- - [ ] ./src/greeting.ts#1-1
72
- typo fixed
64
+ [{"id":"AC-1","answer":"yes","p":0.9}]
73
65
  """
74
- And step "build.review.closing" runs its script
66
+ And step "build.quality.correctness.reviewing" changes nothing
75
67
 
76
68
  Scenario: a land judgment runs idle to idle with no seam gate or refactor
77
69
  When the process is driven
78
- Then the process rests at "build.review.await-review"
79
- Given a file ".gtd/REVIEW.md" with:
80
- """
81
- # Review: abc1234
82
-
83
- <!-- base: 0000000 -->
84
-
85
- ## greeting
86
-
87
- - [x] ./src/greeting.ts#1-1
88
- typo fixed
89
- """
90
- When gtd lands
91
- And the process is driven
92
70
  Then the process rests at "idle"
93
71
  And "src/greeting.ts" contains "Hello, world!"
94
72
  And the commit subjects end with:
@@ -102,12 +80,10 @@ Feature: a naive pass the judge rules land goes straight to implement with no se
102
80
  gtd(check): seam.check → seam.judge
103
81
  gtd(judge): seam.judge → implement.building
104
82
  gtd(agent): implement.building → implement.health.check
105
- gtd(check): implement.health.check → implement.closing
83
+ gtd(check): implement.health.check → implement.coverage
84
+ gtd(judge): implement.coverage → implement.closing
106
85
  gtd(check): implement.closing → build.quality.correctness.reviewing
107
- gtd(agent): build.quality.correctness.reviewing → build.review.reviewing
108
- gtd(agent): build.review.reviewing → build.review.await-review
109
- gtd(human): build.review.await-review → build.review.closing
110
- gtd(check): build.review.closing → idle
86
+ gtd(agent): build.quality.correctness.reviewing → idle
111
87
  """
112
88
  And the commit "gtd(human): idle → unwind" has a body containing "Gtd-Var: flag_risk_tier=0"
113
89
  And the commit "gtd(agent): seam.naive → seam.check" has a body containing "Gtd-Kept:"
@@ -0,0 +1,100 @@
1
+ @inmem
2
+ Feature: quality findings left unresolved hold a feature process short of idle
3
+
4
+ The quality lap is the last look `feature` takes. When the fix cap leaves
5
+ `.gtd/QUALITY.md` behind, the process rests for a person instead of ending
6
+ with findings nobody acted on.
7
+
8
+ Background:
9
+ Given the shipped workflows, as gtd install scaffolds them
10
+ And an environment variable "GTD_QUALITYREVIEWS" set to "correctness"
11
+ And an environment variable "GTD_FLAGMODE" set to "false"
12
+ And a file "src/calc.ts" with:
13
+ """
14
+ export const add = (a: number, b: number) => a + b
15
+ """
16
+ And the working tree is committed as "feat: add calculator"
17
+ And a file "TODO.md" with:
18
+ """
19
+ # Add subtract
20
+
21
+ subtract(a, b) returns the difference.
22
+ """
23
+ And step "unwind" changes nothing
24
+ And step "start-gate.check" changes nothing
25
+ And step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
26
+ """
27
+ # Requirements
28
+
29
+ ## Concerns
30
+
31
+ ### Subtract
32
+
33
+ - `subtract(a, b)` returns `a - b`.
34
+
35
+ ## Acceptance Criteria
36
+
37
+ - AC-1: subtract(a, b) returns a - b.
38
+ """
39
+ And step "design.gate.answer" changes nothing
40
+ And step "implement.building" writes "src/calc.ts" with:
41
+ """
42
+ export const add = (a: number, b: number) => a + b
43
+ export const subtract = (a: number, b: number) => a - b
44
+ """
45
+ And step "implement.health.check" changes nothing
46
+ And step "implement.closing" runs its script
47
+ And judge step "implement.coverage" answers:
48
+ """
49
+ [{"id":"AC-1","answer":"yes","p":0.9}]
50
+ """
51
+
52
+ Scenario: findings the fix cap left unresolved rest at build.quality-unresolved
53
+ Given step "build.quality.correctness.reviewing" writes ".gtd/QUALITY.md" with:
54
+ """
55
+ # Quality
56
+
57
+ - subtract has no test
58
+ """
59
+ And step "build.fix.quality.fixing" from now on changes nothing
60
+ And step "build.health.describe" writes ".gtd/ESCALATION.md" with:
61
+ """
62
+ # Escalation
63
+
64
+ The fix turn changed nothing.
65
+ """
66
+ When the process is driven
67
+ Then the process rests at "build.health.stop"
68
+ Given step "build.fix" writes "src/calc.ts" with:
69
+ """
70
+ export const add = (a: number, b: number) => a + b
71
+ export const subtract = (a: number, b: number) => a - b
72
+ // checked
73
+ """
74
+ And step "build.health.check" changes nothing
75
+ When gtd lands
76
+ And the process is driven
77
+ Then the process rests at "build.quality-unresolved"
78
+ And ".gtd/QUALITY.md" contains "subtract has no test"
79
+
80
+ When gtd lands
81
+ And the process is driven
82
+ Then the process rests at "build.quality-unresolved"
83
+
84
+ Given a file "src/calc.ts" with:
85
+ """
86
+ export const add = (a: number, b: number) => a + b
87
+ export const subtract = (a: number, b: number) => a - b
88
+ // checked by hand
89
+ """
90
+ And the file ".gtd/QUALITY.md" is deleted
91
+ And step "build.health.check" changes nothing
92
+ When gtd lands
93
+ And the process is driven
94
+ Then the process rests at "idle"
95
+ And the commit subjects end with:
96
+ """
97
+ gtd(check): build.health.check → build.quality-unresolved
98
+ gtd(human): build.quality-unresolved → build.health.check
99
+ gtd(check): build.health.check → idle
100
+ """
@@ -1,16 +1,16 @@
1
1
  # One SDLC path per file, so a changed path reds exactly one fixture — do not merge.
2
2
  @inmem
3
- Feature: a refactor that carried the whole feature still gets a feature review
3
+ Feature: a refactor that carried the whole feature still gets a quality lap over all of it
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
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 review covers
9
- the whole change, refactor included.
8
+ step, and with no code changed since the refactor the feature slice's
9
+ quality lap covers the whole change, refactor included.
10
10
 
11
11
  Background:
12
12
  Given the shipped workflows, as gtd install scaffolds them
13
- And an environment variable "GTD_QUALITYREVIEWS" set to ""
13
+ And an environment variable "GTD_QUALITYREVIEWS" set to "correctness"
14
14
  And an environment variable "GTD_REFACTORQUALITYREVIEWS" set to ""
15
15
  And an environment variable "GTD_FLAG_RISK_TIER" set to "0"
16
16
  And a file "src/calc.ts" with:
@@ -42,7 +42,7 @@ Feature: a refactor that carried the whole feature still gets a feature review
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 review and idle
45
+ Scenario: an empty 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
  """
@@ -94,25 +94,18 @@ Feature: a refactor that carried the whole feature still gets a feature review
94
94
  And step "implement.building" changes nothing
95
95
  And step "implement.health.check" changes nothing
96
96
  And step "implement.closing" runs its script
97
- And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
97
+ And judge step "implement.coverage" answers:
98
98
  """
99
- # Review: abc1234
100
-
101
- <!-- base: 0000000 -->
102
-
103
- ## calc
104
-
105
- - [ ] ./src/calc.ts#1-2
106
- subtract next to add
99
+ [{"id":"AC-1","answer":"yes","p":0.9}]
107
100
  """
108
101
  When gtd lands
109
102
  And the process is driven
110
- Then the process rests at "build.review.await-review"
103
+ Then the process rests at "build.quality.correctness.reviewing"
104
+ And the resting step's content contains the hash of the commit "feat: add calculator"
111
105
  And ".gtd/REQUIREMENTS.md" does not exist
112
106
 
113
- Given step "build.review.closing" runs its script
114
- When gtd lands
115
- And the process is driven
107
+ Given step "build.quality.correctness.reviewing" changes nothing
108
+ When the process is driven
116
109
  Then the process rests at "idle"
117
110
  And "src/calc.ts" contains "subtract"
118
111
  And the commit subjects end with:
@@ -120,11 +113,10 @@ Feature: a refactor that carried the whole feature still gets a feature review
120
113
  gtd(human): refactor.build.review.await-review → refactor.build.review.closing
121
114
  gtd(check): refactor.build.review.closing → implement.building
122
115
  gtd(agent): implement.building → implement.health.check
123
- gtd(check): implement.health.check → implement.closing
124
- gtd(check): implement.closing → build.review.reviewing
125
- gtd(agent): build.review.reviewing → build.review.await-review
126
- gtd(human): build.review.await-review → build.review.closing
127
- gtd(check): build.review.closing → idle
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 → idle
128
120
  """
129
121
  And the output lines of gtd "slices" start with:
130
122
  """
@@ -4,7 +4,7 @@ Feature: a refactor-first judgment walks one fixed path to idle
4
4
 
5
5
  With GTD_FLAG_RISK_TIER off, a naive pass touching
6
6
  an existing non-allowlisted file is ruled refactor-first, is refactored and
7
- that refactor reviewed, and only then implemented. The flags are
7
+ that refactor reviewed, and only then implemented, with no feature review. The flags are
8
8
  pinned so the per-flag test runs never move it off its path.
9
9
 
10
10
  Background:
@@ -95,18 +95,6 @@ Feature: a refactor-first judgment walks one fixed path to idle
95
95
  - [x] ./src/calc.ts#1-2
96
96
  add built on the shared op
97
97
  """
98
- And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
99
- """
100
- # Review: abc1234
101
-
102
- <!-- base: 0000000 -->
103
-
104
- ## calc
105
-
106
- - [ ] ./src/calc.ts#1-3
107
- subtract built on the shared op
108
- """
109
- And step "build.review.closing" runs its script
110
98
  When gtd lands
111
99
  And the process is driven
112
100
  Then the process rests at "implement.building"
@@ -120,22 +108,11 @@ Feature: a refactor-first judgment walks one fixed path to idle
120
108
  """
121
109
  And step "implement.health.check" changes nothing
122
110
  And step "implement.closing" runs its script
123
- When the process is driven
124
- Then the process rests at "build.review.await-review"
125
-
126
- Given a file ".gtd/REVIEW.md" with:
111
+ And judge step "implement.coverage" answers:
127
112
  """
128
- # Review: abc1234
129
-
130
- <!-- base: 0000000 -->
131
-
132
- ## calc
133
-
134
- - [x] ./src/calc.ts#1-3
135
- subtract built on the shared op
113
+ [{"id":"AC-1","answer":"yes","p":0.9}]
136
114
  """
137
- When gtd lands
138
- And the process is driven
115
+ When the process is driven
139
116
  Then the process rests at "idle"
140
117
  And "src/calc.ts" contains "op(a, b, 1)"
141
118
  And "src/calc.ts" contains "subtract"
@@ -157,11 +134,9 @@ Feature: a refactor-first judgment walks one fixed path to idle
157
134
  gtd(human): refactor.build.review.await-review → refactor.build.review.closing
158
135
  gtd(check): refactor.build.review.closing → implement.building
159
136
  gtd(agent): implement.building → implement.health.check
160
- gtd(check): implement.health.check → implement.closing
161
- gtd(check): implement.closing → build.review.reviewing
162
- gtd(agent): build.review.reviewing → build.review.await-review
163
- gtd(human): build.review.await-review → build.review.closing
164
- gtd(check): build.review.closing → idle
137
+ gtd(check): implement.health.check → implement.coverage
138
+ gtd(judge): implement.coverage → implement.closing
139
+ gtd(check): implement.closing → idle
165
140
  """
166
141
  And the commit "gtd(human): idle → unwind" has a body containing "Gtd-Var: flag_risk_tier=0"
167
142
  And the commit "gtd(agent): seam.naive → seam.check" has a body containing "Gtd-Kept:"