@pmelab/gtd 58.1.0 → 59.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 (142) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +115 -390
  3. package/dist/chunks/{Schema-B3o6eRNU.mjs → testkit-CMBAAyRK.mjs} +13409 -3351
  4. package/dist/gtd.bundle.mjs +76 -59
  5. package/dist/testkit/gherkin.mjs +30 -2
  6. package/dist/testkit.mjs +1 -1
  7. package/package.json +3 -42
  8. package/skills/authoring/SKILL.md +31 -25
  9. package/src/cli/Cli.test.ts +15 -19
  10. package/src/cli/Cli.ts +2 -4
  11. package/src/cli/Program.test.ts +60 -29
  12. package/src/cli/Program.ts +5 -7
  13. package/src/edge/Edge.ts +3 -3
  14. package/src/flags/index.test.ts +0 -13
  15. package/src/flags/index.ts +1 -3
  16. package/src/gherkin/git-control.feature +78 -0
  17. package/src/gherkin/index.ts +35 -1
  18. package/src/install/Install.test.ts +14 -11
  19. package/src/install/Install.ts +38 -15
  20. package/src/install/index.ts +1 -2
  21. package/src/replay/Replay.test.ts +1 -1
  22. package/src/replay/index.ts +1 -1
  23. package/src/step/planStart.ts +6 -1
  24. package/src/testkit/TestRepo.test.ts +118 -2
  25. package/src/testkit/TestRepo.ts +57 -6
  26. package/src/testkit/index.ts +1 -0
  27. package/src/twins/EmittedScriptRecognizer.ts +12 -14
  28. package/src/twins/FlowScripts.ts +10 -9
  29. package/src/twins/InMemRepo.ts +26 -1
  30. package/src/twins/Layers.ts +12 -1
  31. package/src/twins/index.ts +6 -1
  32. package/src/workflow/index.ts +1 -0
  33. package/src/workflow/load.test.ts +138 -113
  34. package/src/workflow/load.ts +1 -1
  35. package/src/workflow/vars.test.ts +30 -3
  36. package/src/workflow/vars.ts +34 -2
  37. package/src/workflow-kit/index.ts +1 -1
  38. package/dist/chunks/reserved-lMYCaapY.mjs +0 -1990
  39. package/dist/chunks/review-Bfaror38.mjs +0 -427
  40. package/dist/chunks/tail-Bd7L940p.mjs +0 -355
  41. package/dist/chunks/testkit-D1-auYau.mjs +0 -10362
  42. package/dist/workflows/cleanup.mjs +0 -36
  43. package/dist/workflows/feature.mjs +0 -1018
  44. package/dist/workflows/fix.mjs +0 -41
  45. package/dist/workflows/review.mjs +0 -45
  46. package/dist/workflows/rollout.mjs +0 -48
  47. package/dist/workflows.mjs +0 -40651
  48. package/src/install/ConfigScaffold.ts +0 -22
  49. package/src/workflows/cleanup/cleanup-path.feature +0 -94
  50. package/src/workflows/cleanup/cleanup.feature +0 -315
  51. package/src/workflows/cleanup/cleanup.test.ts +0 -276
  52. package/src/workflows/cleanup/cleanup.ts +0 -52
  53. package/src/workflows/cleanup/index.ts +0 -6
  54. package/src/workflows/feature/acceptance-criteria-tier.feature +0 -25
  55. package/src/workflows/feature/acceptance-criteria.feature +0 -389
  56. package/src/workflows/feature/criteria-coverage.feature +0 -102
  57. package/src/workflows/feature/criteria.test.ts +0 -147
  58. package/src/workflows/feature/criteria.ts +0 -267
  59. package/src/workflows/feature/fast-path.feature +0 -93
  60. package/src/workflows/feature/feature.test.ts +0 -338
  61. package/src/workflows/feature/feature.ts +0 -156
  62. package/src/workflows/feature/green.ts +0 -19
  63. package/src/workflows/feature/index.ts +0 -18
  64. package/src/workflows/feature/lens-models.feature +0 -69
  65. package/src/workflows/feature/lens-review.feature +0 -566
  66. package/src/workflows/feature/naive-acceptance.feature +0 -147
  67. package/src/workflows/feature/naive-test.feature +0 -263
  68. package/src/workflows/feature/one-step-land.feature +0 -93
  69. package/src/workflows/feature/planning.ts +0 -41
  70. package/src/workflows/feature/refactor-absorbs.feature +0 -131
  71. package/src/workflows/feature/refactor-first.feature +0 -158
  72. package/src/workflows/feature/refactor-lens-models.feature +0 -79
  73. package/src/workflows/feature/refactor-rebuild.feature +0 -273
  74. package/src/workflows/feature/refactor-review.feature +0 -1528
  75. package/src/workflows/feature/refactor.test.ts +0 -1220
  76. package/src/workflows/feature/refactor.ts +0 -368
  77. package/src/workflows/feature/release-flag.feature +0 -167
  78. package/src/workflows/feature/release-flag.test.ts +0 -49
  79. package/src/workflows/feature/release-flag.ts +0 -46
  80. package/src/workflows/feature/review-gate.feature +0 -241
  81. package/src/workflows/feature/reviewer-walkthrough.feature +0 -142
  82. package/src/workflows/feature/risk-tier.feature +0 -351
  83. package/src/workflows/feature/seam-rejection.feature +0 -146
  84. package/src/workflows/feature/seam.feature +0 -297
  85. package/src/workflows/feature/seam.test.ts +0 -527
  86. package/src/workflows/feature/seam.ts +0 -426
  87. package/src/workflows/feature/standards.feature +0 -354
  88. package/src/workflows/feature/steps.test.ts +0 -65
  89. package/src/workflows/feature/steps.ts +0 -45
  90. package/src/workflows/feature/tier-pin.feature +0 -66
  91. package/src/workflows/feature/tier.test.ts +0 -166
  92. package/src/workflows/feature/tier.ts +0 -56
  93. package/src/workflows/fix/fix-lens-origin.feature +0 -114
  94. package/src/workflows/fix/fix-quality.feature +0 -212
  95. package/src/workflows/fix/fix.feature +0 -79
  96. package/src/workflows/fix/fix.test.ts +0 -74
  97. package/src/workflows/fix/fix.ts +0 -52
  98. package/src/workflows/fix/index.ts +0 -6
  99. package/src/workflows/index.ts +0 -47
  100. package/src/workflows/review/index.ts +0 -6
  101. package/src/workflows/review/review-door.feature +0 -199
  102. package/src/workflows/review/review.test.ts +0 -27
  103. package/src/workflows/review/review.ts +0 -60
  104. package/src/workflows/rollout/index.ts +0 -6
  105. package/src/workflows/rollout/rollout.feature +0 -510
  106. package/src/workflows/rollout/rollout.test.ts +0 -263
  107. package/src/workflows/rollout/rollout.ts +0 -82
  108. package/src/workflows/shared/__fixtures__/feature-collecting-prompt.txt +0 -98
  109. package/src/workflows/shared/access.test.ts +0 -62
  110. package/src/workflows/shared/access.ts +0 -24
  111. package/src/workflows/shared/config.fixture.ts +0 -12
  112. package/src/workflows/shared/fences.ts +0 -24
  113. package/src/workflows/shared/health.test.ts +0 -148
  114. package/src/workflows/shared/health.ts +0 -191
  115. package/src/workflows/shared/learn.test.ts +0 -140
  116. package/src/workflows/shared/learn.ts +0 -201
  117. package/src/workflows/shared/lenses.test.ts +0 -39
  118. package/src/workflows/shared/lenses.ts +0 -294
  119. package/src/workflows/shared/options.test.ts +0 -157
  120. package/src/workflows/shared/options.ts +0 -92
  121. package/src/workflows/shared/progress.feature +0 -124
  122. package/src/workflows/shared/prose.ts +0 -263
  123. package/src/workflows/shared/recheck.feature +0 -60
  124. package/src/workflows/shared/registry.test.ts +0 -345
  125. package/src/workflows/shared/registry.ts +0 -183
  126. package/src/workflows/shared/reserved.ts +0 -11
  127. package/src/workflows/shared/review.test.ts +0 -1062
  128. package/src/workflows/shared/review.ts +0 -446
  129. package/src/workflows/shared/skills.test.ts +0 -121
  130. package/src/workflows/shared/skills.ts +0 -49
  131. package/src/workflows/shared/standards.test.ts +0 -90
  132. package/src/workflows/shared/standards.ts +0 -156
  133. package/src/workflows/shared/steps.test.ts +0 -219
  134. package/src/workflows/shared/steps.ts +0 -201
  135. package/src/workflows/shared/tail.test.ts +0 -454
  136. package/src/workflows/shared/tail.ts +0 -264
  137. package/src/workflows/shared/text.fixture.ts +0 -128
  138. package/src/workflows/shared/text.test.ts +0 -1430
  139. package/src/workflows/shared/text.ts +0 -1700
  140. package/src/workflows/shared/threads.test.ts +0 -129
  141. package/src/workflows/shared/vars.ts +0 -47
  142. package/src/workflows/shared/workflows.steps.ts +0 -54
@@ -1,166 +0,0 @@
1
- import { afterEach, describe, expect, it } from "vitest"
2
- import { installContext, type FlowContext } from "../../flows/index.js"
3
- import { fixtureContext } from "../shared/text.fixture.js"
4
- import { productionSignals, readRiskTier, recordRisk, riskTierOn } from "./tier.js"
5
-
6
- afterEach(() => installContext(undefined))
7
-
8
- const withVars = (vars: Record<string, string>) => installContext(fixtureContext({ vars }))
9
-
10
- describe("riskTierOn", () => {
11
- it.each([
12
- [{ flag_risk_tier: "1" }, true],
13
- [{ flag_risk_tier: "0" }, false],
14
- [{}, false],
15
- ])("%j is %s", (vars, expected) => {
16
- withVars(vars)
17
- expect(riskTierOn()).toBe(expected)
18
- })
19
- })
20
-
21
- describe("readRiskTier", () => {
22
- const doc = (body: string) =>
23
- `# Requirements\n\n## Risk Tier\n\n${body}\n\n## Answered Questions\n`
24
-
25
- it.each(["low", "`low`", "**Low** — trivial copy", "LOW - copy", "_low_ — x"])(
26
- "reads %j as low",
27
- (body) => expect(readRiskTier(doc(body))).toBe("low"),
28
- )
29
-
30
- it.each(["high", "medium", "lower", "", " \n\n"])("reads %j as high", (body) =>
31
- expect(readRiskTier(doc(body))).toBe("high"),
32
- )
33
-
34
- it("reads a missing section and undefined as high", () => {
35
- expect(readRiskTier("# Requirements\n\n## Other\n\nlow\n")).toBe("high")
36
- expect(readRiskTier(undefined)).toBe("high")
37
- })
38
-
39
- it("takes the first non-blank line only and stops at the next section", () => {
40
- expect(readRiskTier("## Risk Tier\n\nhigh — why\nlow\n")).toBe("high")
41
- expect(readRiskTier("## Risk Tier\n\n## Other\n\nlow\n")).toBe("high")
42
- })
43
- })
44
-
45
- describe("readRiskTier fences", () => {
46
- it("ignores a backtick-fenced example above the real section", () => {
47
- const text = "```\n## Risk Tier\n\nlow\n```\n\n## Risk Tier\n\nhigh\n"
48
- expect(readRiskTier(text)).toBe("high")
49
- })
50
-
51
- it("reads a fenced example with no real section as high", () => {
52
- expect(readRiskTier("```md\n## Risk Tier\n\nlow\n```\n")).toBe("high")
53
- })
54
-
55
- it("keeps the signals section open across a fence and skips fenced items", () => {
56
- const signals = "## Production Signals\n- before\n```\n# x\n- fake\n```\n- after\n"
57
- expect(productionSignals(signals)).toEqual(["before", "after"])
58
- })
59
-
60
- it("ignores a tilde-fenced example above the real section", () => {
61
- const text = "~~~\n## Risk Tier\n\nlow\n~~~\n\n## Risk Tier\n\nhigh\n"
62
- expect(readRiskTier(text)).toBe("high")
63
- })
64
-
65
- it("does not read a fence opener's info string as the tier", () => {
66
- expect(readRiskTier("## Risk Tier\n\n```low\nhigh\n```\n")).toBe("high")
67
- expect(readRiskTier("## Risk Tier\n\n~~~low\nlow\n~~~\n")).toBe("high")
68
- })
69
-
70
- it("fails closed on an unclosed fence before the real section", () => {
71
- expect(readRiskTier("```\nstray\n\n## Risk Tier\n\nlow\n")).toBe("high")
72
- })
73
-
74
- it("does not close a 4-backtick fence with 3 backticks", () => {
75
- const text = "````\n```\n## Risk Tier\n\nlow\n````\n"
76
- expect(readRiskTier(text)).toBe("high")
77
- })
78
-
79
- it("does not open a fence on a backtick line indented 4 spaces", () => {
80
- expect(readRiskTier(" ```\n## Risk Tier\n\nlow\n")).toBe("low")
81
- })
82
- })
83
-
84
- describe("readRiskTier fence closers", () => {
85
- it.each([
86
- ["a tilde line does not close a backtick fence", "```\n~~~\n## Risk Tier\n\nlow\n```\n"],
87
- ["a closer with an info string does not close", "```\n```js\n## Risk Tier\n\nlow\n```\n"],
88
- ])("%s", (_, text) => {
89
- expect(readRiskTier(text)).toBe("high")
90
- })
91
- })
92
-
93
- describe("productionSignals", () => {
94
- it("lists top-level items only, skipping blanks and indented lines", () => {
95
- const text = `## Production Signals
96
-
97
- - error rate on /pay
98
- not a signal
99
- - nested detail
100
- -
101
- - spaced out\t signal
102
- * star item
103
-
104
- ## Answered Questions
105
-
106
- - not a signal
107
- `
108
- expect(productionSignals(text)).toEqual([
109
- "error rate on /pay",
110
- "spaced out signal",
111
- "star item",
112
- ])
113
- })
114
-
115
- it("skips a fenced Production Signals example", () => {
116
- const text = "```\n## Production Signals\n- fake\n```\n\n## Production Signals\n- real\n"
117
- expect(productionSignals(text)).toEqual(["real"])
118
- })
119
-
120
- it("drops control characters", () => {
121
- expect(productionSignals("## Production Signals\n- a\u001bb\u0085c\n")).toEqual(["abc"])
122
- })
123
-
124
- it("is empty for a missing section or undefined", () => {
125
- expect(productionSignals("## Risk Tier\nlow\n")).toEqual([])
126
- expect(productionSignals(undefined)).toEqual([])
127
- })
128
- })
129
-
130
- describe("recordRisk", () => {
131
- const record = (requirements: string | undefined) => {
132
- const marks: [string, string][] = []
133
- installContext({
134
- ...fixtureContext({ read: () => requirements }),
135
- mark: (name: string, value: string) => marks.push([name, value]),
136
- } as unknown as FlowContext)
137
- return { tier: recordRisk(), marks }
138
- }
139
-
140
- it("marks the tier first, then one production-signal each", () => {
141
- const { tier, marks } = record(
142
- "## Risk Tier\nhigh — payments\n\n## Production Signals\n- a\n- b\n",
143
- )
144
- expect(tier).toBe("high")
145
- expect(marks).toEqual([
146
- ["risk-tier", "high"],
147
- ["production-signal", "a"],
148
- ["production-signal", "b"],
149
- ])
150
- })
151
-
152
- it("marks risk-tier=low too", () => {
153
- const { tier, marks } = record("## Risk Tier\nlow — copy\n\n## Production Signals\n- a\n")
154
- expect(tier).toBe("low")
155
- expect(marks).toEqual([
156
- ["risk-tier", "low"],
157
- ["production-signal", "a"],
158
- ])
159
- })
160
-
161
- it("treats a missing file as high", () => {
162
- const { tier, marks } = record(undefined)
163
- expect(tier).toBe("high")
164
- expect(marks).toEqual([["risk-tier", "high"]])
165
- })
166
- })
@@ -1,56 +0,0 @@
1
- import { mark, read, vars } from "../../flows/index.js"
2
- import { markFenced } from "../shared/fences.js"
3
- import { REQUIREMENTS } from "../shared/steps.js"
4
-
5
- export type RiskTier = "low" | "high"
6
-
7
- /** GTD_FLAG_RISK_TIER: pinned at process start, so a mid-process flip never moves a process. */
8
- export const riskTierOn = (): boolean => vars.flag_risk_tier === "1"
9
-
10
- const sectionLines = (
11
- text: string | undefined,
12
- title: string,
13
- ): readonly { line: string; fenced: boolean }[] => {
14
- if (text === undefined) return []
15
- const heading = new RegExp(`^##[ \\t]+${title}[ \\t]*$`, "i")
16
- // An unclosed fence hides the rest, so the tier reads `high` (fail-closed).
17
- const lines = markFenced(text)
18
- const start = lines.findIndex((l) => !l.fenced && heading.test(l.line))
19
- if (start < 0) return []
20
- const rest = lines.slice(start + 1)
21
- const end = rest.findIndex((l) => !l.fenced && /^#{1,2}[ \t]/.test(l.line))
22
- return end < 0 ? rest : rest.slice(0, end)
23
- }
24
-
25
- /** Anything but a legible `low` is `high`: under-tiering is the risky direction. */
26
- export const readRiskTier = (requirements: string | undefined): RiskTier => {
27
- const first = sectionLines(requirements, "Risk Tier").find(
28
- (l) => !l.fenced && l.line.trim() !== "",
29
- )?.line
30
- const word = first?.trim().split(/\s+/)[0]?.replace(/[`*_]/g, "").toLowerCase()
31
- return word === "low" ? "low" : "high"
32
- }
33
-
34
- // eslint-disable-next-line no-control-regex -- the control range IS the point
35
- const CONTROL = /[\x00-\x1f\x7f-\x9f\u2028\u2029]/g
36
-
37
- const tidy = (text: string): string => text.replace(/\s+/g, " ").replace(CONTROL, "").trim()
38
-
39
- export const productionSignals = (requirements: string | undefined): readonly string[] => {
40
- const items: string[] = []
41
- for (const { line, fenced } of sectionLines(requirements, "Production Signals")) {
42
- if (fenced) continue
43
- const top = /^[-*+][ \t]+(.*)$/.exec(line)
44
- if (top !== null) items.push(top[1]!)
45
- }
46
- return items.map(tidy).filter((item) => item !== "")
47
- }
48
-
49
- /** The tier is always marked, first, so the last `risk-tier` mark opens the current lap's marks. */
50
- export const recordRisk = (): RiskTier => {
51
- const requirements = read(REQUIREMENTS)
52
- const tier = readRiskTier(requirements)
53
- mark("risk-tier", tier)
54
- for (const signal of productionSignals(requirements)) mark("production-signal", signal)
55
- return tier
56
- }
@@ -1,114 +0,0 @@
1
- @inmem
2
- Feature: each finding at the finding gate names the lens that wrote it
3
-
4
- Lenses append to `.gtd/QUALITY.md` in turn, so a finding belongs to the lens
5
- whose turn added it — even when two lenses give findings the same title.
6
-
7
- Background:
8
- Given the shipped workflows, as gtd install scaffolds them
9
- And an environment variable "GTD_QUALITYREVIEWS" set to "correctness,tests"
10
- And a file "src/calc.ts" with:
11
- """
12
- export const add = (a: number, b: number) => a + b
13
- """
14
- And the working tree is committed as "feat: add calculator"
15
- And a file ".gtd/REVIEW.orig.md" with:
16
- """
17
- # Review: abc1234
18
-
19
- <!-- base: 0000000 -->
20
-
21
- ## calc
22
-
23
- - [ ] ./src/calc.ts#1-1
24
- new add function
25
- """
26
- And a file ".gtd/REVIEW.md" with:
27
- """
28
- # Review: abc1234
29
-
30
- <!-- base: 0000000 -->
31
-
32
- ## calc
33
-
34
- - [ ] ./src/calc.ts#1-1
35
- new add function — rename it to sum
36
- """
37
- And the working tree is committed as "chore: hand off a review"
38
- And step "notes.fixing" writes "src/calc.ts" with:
39
- """
40
- export const sum = (a: number, b: number) => a + b
41
- """
42
- And step "notes.closing" runs its script
43
- And step "build.health.check" changes nothing
44
- And step "build.quality.correctness.reviewing" writes ".gtd/QUALITY.md" with:
45
- """
46
- # Quality
47
-
48
- ## sum is untested
49
-
50
- From correctness.
51
- """
52
- And step "build.quality.tests.reviewing" writes ".gtd/QUALITY.md" with:
53
- """
54
- # Quality
55
-
56
- ## sum is untested
57
-
58
- From correctness.
59
-
60
- ## sum is untested
61
-
62
- From tests.
63
- """
64
- And step "build.quality.gating" runs its script
65
- And step "build.quality.closing" runs its script
66
- And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
67
- """
68
- # Review: abc1234
69
-
70
- <!-- base: 0000000 -->
71
-
72
- ## calc
73
-
74
- - [ ] ./src/calc.ts#1-1
75
- renamed to sum
76
- """
77
- And step "build.review.carrying" runs its script
78
-
79
- Scenario: two lenses' findings with one title each name their own lens
80
- Given judge step "build.quality.triage" answers:
81
- """
82
- [{"id":"Q1","answer":"design","p":0.9},{"id":"Q2","answer":"design","p":0.9}]
83
- """
84
- When gtd opens door "fix"
85
- And the process is driven
86
- Then the process rests at "build.quality.gate"
87
- And ".gtd/QUALITY.md" contains:
88
- """
89
- ## sum is untested
90
-
91
- _From lens correctness._
92
-
93
- From correctness.
94
-
95
- - [ ] fix
96
-
97
- ## sum is untested
98
-
99
- _From lens tests._
100
-
101
- From tests.
102
-
103
- - [ ] fix
104
- """
105
- When gtd lands
106
- And the process is driven
107
- Then the process rests at "build.review.await-review"
108
- And ".gtd/REVIEW.md" contains:
109
- """
110
- Dropped at the finding gate:
111
-
112
- - sum is untested (lens correctness)
113
- - sum is untested (lens tests)
114
- """
@@ -1,212 +0,0 @@
1
- @inmem
2
- Feature: fix's quality lap is triaged, and its leftovers are decided at the finding gate
3
-
4
- A judge sorts the lap's findings: mechanical ones are fixed for up to three
5
- turns, and the design ones — with whatever the fix turns left, and the
6
- noise — wait at the finding gate, never at an escalation. A ticked one is
7
- fixed on the spot; the hand-off review lists what became of each in its
8
- summary and carries no finding on to the next `fix`. With no requirements
9
- to check against, the `criteria-coverage` lens runs no turn.
10
-
11
- Background:
12
- Given the shipped workflows, as gtd install scaffolds them
13
- And an environment variable "GTD_QUALITYREVIEWS" set to "criteria-coverage, correctness"
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 ".gtd/REVIEW.orig.md" with:
20
- """
21
- # Review: abc1234
22
-
23
- <!-- base: 0000000 -->
24
-
25
- ## calc
26
-
27
- - [ ] ./src/calc.ts#1-1
28
- new add function
29
- """
30
- And a file ".gtd/REVIEW.md" with:
31
- """
32
- # Review: abc1234
33
-
34
- <!-- base: 0000000 -->
35
-
36
- ## calc
37
-
38
- - [ ] ./src/calc.ts#1-1
39
- new add function — rename it to sum
40
- """
41
- And the working tree is committed as "chore: hand off a review"
42
- And step "notes.fixing" writes "src/calc.ts" with:
43
- """
44
- export const sum = (a: number, b: number) => a + b
45
- """
46
- And step "notes.closing" runs its script
47
- And step "build.health.check" changes nothing
48
- And step "build.quality.correctness.reviewing" writes ".gtd/QUALITY.md" with:
49
- """
50
- # Quality
51
-
52
- ## sum has no negative test
53
-
54
- Add a test summing negative numbers.
55
-
56
- ## sum should be named plus
57
-
58
- The glossary says plus.
59
- """
60
- And step "build.quality.sorting" runs its script
61
- And step "build.quality.gating" runs its script
62
- And step "build.quality.picking" runs its script
63
- And step "build.quality.closing" runs its script
64
- And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
65
- """
66
- # Review: abc1234
67
-
68
- <!-- base: 0000000 -->
69
-
70
- ## calc
71
-
72
- - [ ] ./src/calc.ts#1-1
73
- renamed to sum
74
- """
75
- And step "build.review.carrying" runs its script
76
-
77
- Scenario: a design finding and one the fix cap left reach the finding gate, and a ticked one is fixed there
78
- Given judge step "build.quality.triage" answers:
79
- """
80
- [{"id":"Q1","answer":"mechanical","p":0.9},{"id":"Q2","answer":"design","p":0.9}]
81
- """
82
- And step "build.fix.quality.fixing" changes nothing
83
- When gtd opens door "fix"
84
- And the process is driven
85
- Then the process rests at "build.quality.gate"
86
- And no commit body contains "build.health.describe"
87
- And no commit body contains "build.quality.criteria-coverage"
88
- And ".gtd/QUALITY.md" contains:
89
- """
90
- ## sum should be named plus
91
-
92
- _From lens correctness._
93
-
94
- The glossary says plus.
95
-
96
- - [ ] fix
97
-
98
- ## sum has no negative test
99
-
100
- _From lens correctness. The fixer left it unresolved._
101
-
102
- Add a test summing negative numbers.
103
-
104
- - [ ] fix
105
- """
106
- Given a file ".gtd/QUALITY.md" with:
107
- """
108
- # Quality
109
-
110
- ## sum should be named plus
111
-
112
- The glossary says plus.
113
-
114
- - [x] fix
115
-
116
- ## sum has no negative test
117
-
118
- Add a test summing negative numbers.
119
-
120
- - [ ] fix
121
- """
122
- And step "build.fix.quality.fixing" is no longer scripted
123
- And step "build.fix.quality.fixing" writes "src/calc.ts" with:
124
- """
125
- export const plus = (a: number, b: number) => a + b
126
- """
127
- And step "build.fix.quality.fixing" deletes ".gtd/QUALITY.md"
128
- When gtd lands
129
- And the process is driven
130
- Then the process rests at "build.review.await-review"
131
- And ".gtd/QUALITY.md" does not exist
132
- And ".gtd/REVIEW.md" contains:
133
- """
134
- Fixed at the finding gate:
135
-
136
- - sum should be named plus (
137
- """
138
- And ".gtd/REVIEW.md" contains:
139
- """
140
- Dropped at the finding gate:
141
-
142
- - sum has no negative test (lens correctness)
143
- """
144
- And ".gtd/REVIEW.md" does not contain "Tick exactly one:"
145
- And the commit subjects end with:
146
- """
147
- gtd(human): build.quality.gate → build.quality.picking
148
- gtd(check): build.quality.picking → build.fix.quality.fixing
149
- gtd(agent): build.fix.quality.fixing → build.health.check
150
- gtd(check): build.health.check → build.review.reviewing
151
- gtd(agent): build.review.reviewing → build.review.carrying
152
- gtd(check): build.review.carrying → build.review.await-review
153
- """
154
-
155
- Scenario: a fixed finding and a noise one are listed in the review
156
- Given judge step "build.quality.triage" answers:
157
- """
158
- [{"id":"Q1","answer":"mechanical","p":0.9},{"id":"Q2","answer":"noise","p":0.9}]
159
- """
160
- And step "build.fix.quality.fixing" writes "src/calc.test.ts" with:
161
- """
162
- it("sums negative numbers", () => {})
163
- """
164
- And step "build.fix.quality.fixing" deletes ".gtd/QUALITY.md"
165
- And step "build.quality.gate" changes nothing
166
- When gtd opens door "fix"
167
- And the process is driven
168
- Then the process rests at "build.review.await-review"
169
- And ".gtd/REVIEW.md" contains "Fixed automatically:"
170
- And ".gtd/REVIEW.md" contains "- sum has no negative test ("
171
- And ".gtd/REVIEW.md" contains the short hash of the commit "gtd(agent): build.fix.quality.fixing → build.health.check"
172
- And ".gtd/REVIEW.md" contains "- sum should be named plus (p=0.9, lens correctness)"
173
-
174
- Scenario: a risk the hand-off reviewer marks is fixed and re-reviewed in its own draft, the summary added once
175
- Given judge step "build.quality.triage" answers:
176
- """
177
- [{"id":"Q1","answer":"mechanical","p":0.9},{"id":"Q2","answer":"noise","p":0.9}]
178
- """
179
- And step "build.fix.quality.fixing" deletes ".gtd/QUALITY.md"
180
- And step "build.quality.gate" changes nothing
181
- And step "build.review.reviewing" is no longer scripted
182
- And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
183
- """
184
- # Review: abc1234
185
-
186
- <!-- base: 0000000 -->
187
-
188
- ## calc
189
-
190
- - [ ] ./src/calc.ts#1-1 — Risk: overflows on large numbers
191
- """
192
- When gtd opens door "fix"
193
- And the process is driven
194
- Then the process rests at "build.review.fix.risks.fixing"
195
- And ".gtd/REVIEW.md" does not contain "## Quality lap"
196
- Given step "build.review.reviewing" is no longer scripted
197
- And step "build.review.reviewing" replaces the text "Risk: overflows on large numbers" in ".gtd/REVIEW.md" with "sum, now guarded"
198
- And step "build.review.fix.risks.fixing" changes nothing
199
- When the process is driven
200
- Then the process rests at "build.review.await-review"
201
- And ".gtd/REVIEW.md" contains "sum, now guarded"
202
- And ".gtd/REVIEW.md" contains "## Quality lap" exactly once
203
- And ".gtd/REVIEW.md" contains "## calc" exactly once
204
- And the commit subjects end with:
205
- """
206
- gtd(check): build.quality.closing → build.review.reviewing
207
- gtd(agent): build.review.reviewing → build.review.fix.risks.fixing
208
- gtd(agent): build.review.fix.risks.fixing → build.health.check
209
- gtd(check): build.health.check → build.review.reviewing
210
- gtd(agent): build.review.reviewing → build.review.carrying
211
- gtd(check): build.review.carrying → build.review.await-review
212
- """
@@ -1,79 +0,0 @@
1
- Feature: fix fixes the notes a hand-off leaves it
2
-
3
- A hand-off is `.gtd/REVIEW.md` with the human's notes beside the reviewer's
4
- own `.gtd/REVIEW.orig.md`; `gtd door fix` picks both up.
5
-
6
- Background:
7
- Given the shipped workflows, as gtd install scaffolds them
8
- And an environment variable "GTD_QUALITYREVIEWS" set to ""
9
- And a file "src/calc.ts" with:
10
- """
11
- export const add = (a: number, b: number) => a + b
12
- """
13
- And the working tree is committed as "feat: add calculator"
14
-
15
- Scenario: a handed-off note is fixed, and both review files are removed
16
- Given a file ".gtd/REVIEW.orig.md" with:
17
- """
18
- # Review: abc1234
19
-
20
- <!-- base: 0000000 -->
21
-
22
- ## calc
23
-
24
- - [ ] ./src/calc.ts#1-1
25
- new add function
26
- """
27
- And a file ".gtd/REVIEW.md" with:
28
- """
29
- # Review: abc1234
30
-
31
- <!-- base: 0000000 -->
32
-
33
- ## calc
34
-
35
- - [ ] ./src/calc.ts#1-1
36
- new add function — rename it to sum
37
- """
38
- And the working tree is committed as "chore: hand off a review"
39
- And step "build.health.check" changes nothing
40
- And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
41
- """
42
- # Review: abc1234
43
-
44
- <!-- base: 0000000 -->
45
-
46
- ## calc
47
-
48
- - [ ] ./src/calc.ts#1-1
49
- new add function
50
- """
51
- And step "notes.fixing" writes "src/calc.ts" with:
52
- """
53
- export const sum = (a: number, b: number) => a + b
54
- """
55
- And step "notes.closing" runs its script
56
- When gtd opens door "fix"
57
- Then the resting step's content contains "rename it to sum"
58
- # The fix goes to its own review, which the scripted reviewer writes afresh.
59
- When the process is driven
60
- Then the process rests at "build.review.await-review"
61
- And the commit subjects end with:
62
- """
63
- gtd(human): notes.fixing
64
- gtd(agent): notes.fixing → notes.closing
65
- gtd(check): notes.closing → build.health.check
66
- gtd(check): build.health.check → build.review.reviewing
67
- gtd(agent): build.review.reviewing → build.review.await-review
68
- """
69
- And ".gtd/REVIEW.orig.md" does not exist
70
- And ".gtd/REVIEW.md" contains "new add function"
71
- And "src/calc.ts" contains "sum"
72
-
73
- Scenario: a reviewer's copy with no review is refused
74
- Given a file ".gtd/REVIEW.orig.md" with:
75
- """
76
- # Review: abc1234
77
- """
78
- And the working tree is committed as "chore: stray copy"
79
- Then opening door "fix" is refused with "has no .gtd/REVIEW.md"