@pmelab/gtd 30.0.0 → 31.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 (37) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +23 -9
  3. package/claude/hooks/register.tsx +5 -0
  4. package/dist/chunks/{reserved-iG86IR3I.mjs → reserved-CHTJTzpA.mjs} +68 -9
  5. package/dist/chunks/{tail-DA2UuJNP.mjs → tail-CCsDBbZe.mjs} +1 -1
  6. package/dist/workflows/cleanup.mjs +2 -2
  7. package/dist/workflows/feature.mjs +141 -11
  8. package/dist/workflows/fix.mjs +2 -2
  9. package/dist/workflows/review.mjs +2 -2
  10. package/dist/workflows/rollout.mjs +1 -1
  11. package/dist/workflows.mjs +207 -18
  12. package/package.json +1 -1
  13. package/src/workflows/feature/acceptance-criteria-tier.feature +25 -0
  14. package/src/workflows/feature/acceptance-criteria.feature +189 -0
  15. package/src/workflows/feature/criteria.test.ts +106 -0
  16. package/src/workflows/feature/criteria.ts +168 -0
  17. package/src/workflows/feature/fast-path.feature +4 -0
  18. package/src/workflows/feature/feature.test.ts +81 -10
  19. package/src/workflows/feature/feature.ts +18 -10
  20. package/src/workflows/feature/flag-mode.feature +165 -0
  21. package/src/workflows/feature/index.ts +1 -0
  22. package/src/workflows/feature/one-step-land.feature +4 -0
  23. package/src/workflows/feature/planning.ts +2 -0
  24. package/src/workflows/feature/refactor-absorbs.feature +4 -0
  25. package/src/workflows/feature/refactor-first.feature +4 -0
  26. package/src/workflows/feature/refactor-review.feature +24 -0
  27. package/src/workflows/feature/risk-tier.feature +32 -0
  28. package/src/workflows/feature/seam-rejection.feature +8 -0
  29. package/src/workflows/feature/seam.feature +8 -0
  30. package/src/workflows/feature/seam.ts +14 -7
  31. package/src/workflows/feature/tier-pin.feature +4 -0
  32. package/src/workflows/index.ts +1 -0
  33. package/src/workflows/shared/options.test.ts +12 -0
  34. package/src/workflows/shared/options.ts +17 -2
  35. package/src/workflows/shared/text.test.ts +16 -0
  36. package/src/workflows/shared/text.ts +90 -8
  37. package/src/workflows/shared/vars.ts +1 -0
@@ -0,0 +1,165 @@
1
+ @inmem
2
+ Feature: flag mode off builds a feature with no release flag, seam judge or refactor
3
+
4
+ `flagMode` is a factory option of the shipped workflows, on by default. Off,
5
+ the project ships without feature flags: no seam judge and no refactor run,
6
+ and the naive implementation goes straight through the health loop and the
7
+ quality lap.
8
+
9
+ Background:
10
+ Given a gtd config with:
11
+ """
12
+ import { feature } from "@pmelab/gtd/workflows/feature"
13
+ export { summary, steering, skills, access } from "@pmelab/gtd/workflows/feature"
14
+ export default feature({ flagMode: false })
15
+ """
16
+ And an environment variable "GTD_QUALITYREVIEWS" set to "correctness"
17
+ And a file "src/calc.ts" with:
18
+ """
19
+ export const add = (a: number, b: number) => a + b
20
+ """
21
+ And the working tree is committed as "feat: add calculator"
22
+ And a file "TODO.md" with:
23
+ """
24
+ # Add subtract
25
+
26
+ subtract(a, b) returns the difference.
27
+ """
28
+ And step "unwind" changes nothing
29
+ And step "start-gate.check" changes nothing
30
+ And step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
31
+ """
32
+ # Requirements
33
+
34
+ ## Concerns
35
+
36
+ ### Subtract
37
+
38
+ - `subtract(a, b)` returns `a - b`.
39
+
40
+ ## Acceptance Criteria
41
+
42
+ - AC-1: the change behaves as described.
43
+ """
44
+ And step "design.gate.answer" changes nothing
45
+
46
+ Scenario: flag mode off skips the seam judge and the refactor, and asks for no flag
47
+ When the process is driven
48
+ Then the process rests at "implement.building"
49
+ And the resting step's content contains "the quickest way that works"
50
+ And the resting step's content contains "Add no release flag — this project ships without release flags"
51
+ And the resting step's content does not contain "new release flag"
52
+ Given step "implement.building" writes "src/calc.ts" with:
53
+ """
54
+ export const add = (a: number, b: number) => a + b
55
+ export const subtract = (a: number, b: number) => a - b
56
+ """
57
+ And step "implement.health.check" changes nothing
58
+ And step "implement.closing" runs its script
59
+ And step "build.quality.correctness.reviewing" changes nothing
60
+ When the process is driven
61
+ Then the process rests at "build.review.reviewing"
62
+ And "src/calc.ts" contains "subtract"
63
+ And the commit subjects end with:
64
+ """
65
+ chore: initial commit
66
+ feat: add calculator
67
+ gtd(human): idle → unwind
68
+ gtd(check): unwind → start-gate.check
69
+ gtd(check): start-gate.check → design.triage
70
+ gtd(agent): design.triage → design.gate.answer
71
+ gtd(human): design.gate.answer → implement.building
72
+ gtd(agent): implement.building → implement.health.check
73
+ gtd(check): implement.health.check → implement.closing
74
+ 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:
115
+ """
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:"
@@ -1,6 +1,7 @@
1
1
  import { feature } from "./feature.js"
2
2
 
3
3
  export * from "./steps.js"
4
+ export * from "./criteria.js"
4
5
  export * from "./planning.js"
5
6
  export * from "./refactor.js"
6
7
  export * from "./seam.js"
@@ -34,6 +34,10 @@ Feature: a naive pass the judge rules land goes straight to implement with no se
34
34
  ### Typo
35
35
 
36
36
  - greeting reads "Hello, world".
37
+
38
+ ## Acceptance Criteria
39
+
40
+ - AC-1: the change behaves as described.
37
41
  """
38
42
  And step "design.gate.answer" changes nothing
39
43
  And step "seam.naive" writes "src/greeting.ts" with:
@@ -8,6 +8,7 @@ import {
8
8
  scope,
9
9
  } from "../../flows/index.js"
10
10
  import { REQUIREMENTS } from "../shared/steps.js"
11
+ import { requireCriteria } from "./criteria.js"
11
12
  import { answerProductQuestions, triage } from "./steps.js"
12
13
 
13
14
  /**
@@ -31,5 +32,6 @@ export const design = (base: string): Promise<void> =>
31
32
  await triage(base)
32
33
  requireProgress(REQUIREMENTS)
33
34
  requireReplies(REQUIREMENTS)
35
+ requireCriteria(REQUIREMENTS)
34
36
  } while (await noteGate(REQUIREMENTS, answerProductQuestions))
35
37
  })
@@ -35,6 +35,10 @@ Feature: a refactor that carried the whole feature still gets a feature review
35
35
  ### Subtract
36
36
 
37
37
  - `subtract(a, b)` returns `a - b`.
38
+
39
+ ## Acceptance Criteria
40
+
41
+ - AC-1: the change behaves as described.
38
42
  """
39
43
  And step "design.gate.answer" changes nothing
40
44
 
@@ -34,6 +34,10 @@ Feature: a refactor-first judgment walks one fixed path to idle
34
34
  ### Subtract
35
35
 
36
36
  - `subtract(a, b)` returns `a - b`.
37
+
38
+ ## Acceptance Criteria
39
+
40
+ - AC-1: the change behaves as described.
37
41
  """
38
42
  And step "design.gate.answer" changes nothing
39
43
 
@@ -46,6 +46,10 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
46
46
  ### Subtract
47
47
 
48
48
  - `subtract(a, b)` returns `a - b`.
49
+
50
+ ## Acceptance Criteria
51
+
52
+ - AC-1: the change behaves as described.
49
53
  """
50
54
  And step "design.gate.answer" changes nothing
51
55
  And step "seam.naive" writes "src/calc.ts" with:
@@ -126,6 +130,10 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
126
130
  ### Subtract
127
131
 
128
132
  - `subtract(a, b)` returns `a - b`.
133
+
134
+ ## Acceptance Criteria
135
+
136
+ - AC-1: the change behaves as described.
129
137
  """
130
138
  And step "design.gate.answer" changes nothing
131
139
  And step "seam.naive" writes "src/calc.ts" with:
@@ -205,6 +213,10 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
205
213
  ### Subtract
206
214
 
207
215
  - `subtract(a, b)` returns `a - b`.
216
+
217
+ ## Acceptance Criteria
218
+
219
+ - AC-1: the change behaves as described.
208
220
  """
209
221
  And step "design.gate.answer" changes nothing
210
222
  And step "seam.naive" writes "src/calc.ts" with:
@@ -449,6 +461,10 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
449
461
  # Requirements
450
462
 
451
463
  - multiply, designed
464
+
465
+ ## Acceptance Criteria
466
+
467
+ - AC-1: the change behaves as described.
452
468
  """
453
469
  And step "implement.building" writes "src/calc.ts" with:
454
470
  """
@@ -1404,6 +1420,10 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
1404
1420
  ### Subtract
1405
1421
 
1406
1422
  - `subtract(a, b)` returns `a - b`.
1423
+
1424
+ ## Acceptance Criteria
1425
+
1426
+ - AC-1: the change behaves as described.
1407
1427
  """
1408
1428
  And step "design.gate.answer" changes nothing
1409
1429
  And step "seam.naive" writes "src/calc.ts" with:
@@ -1582,6 +1602,10 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
1582
1602
  ### Subtract
1583
1603
 
1584
1604
  - `subtract(a, b)` returns `a - b`.
1605
+
1606
+ ## Acceptance Criteria
1607
+
1608
+ - AC-1: the change behaves as described.
1585
1609
  """
1586
1610
  And step "design.gate.answer" changes nothing
1587
1611
  And judge step "seam.judge" answers:
@@ -49,6 +49,10 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
49
49
  ## Risk Tier
50
50
 
51
51
  low — trivial copy change
52
+
53
+ ## Acceptance Criteria
54
+
55
+ - AC-1: the change behaves as described.
52
56
  """
53
57
  And step "design.gate.answer" changes nothing
54
58
  When the process is driven
@@ -64,6 +68,10 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
64
68
  ## Risk Tier
65
69
 
66
70
  high — touches payments
71
+
72
+ ## Acceptance Criteria
73
+
74
+ - AC-1: the change behaves as described.
67
75
  """
68
76
  And step "design.gate.answer" changes nothing
69
77
  When the process is driven
@@ -80,6 +88,10 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
80
88
  ## Concerns
81
89
 
82
90
  ### Subtract
91
+
92
+ ## Acceptance Criteria
93
+
94
+ - AC-1: the change behaves as described.
83
95
  """
84
96
  And step "design.gate.answer" changes nothing
85
97
  When the process is driven
@@ -105,6 +117,10 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
105
117
 
106
118
  - error rate on /subtract stays flat
107
119
  - p95 latency
120
+
121
+ ## Acceptance Criteria
122
+
123
+ - AC-1: the change behaves as described.
108
124
  """
109
125
  And step "design.gate.answer" changes nothing
110
126
  When the process is driven
@@ -125,6 +141,10 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
125
141
  ## Risk Tier
126
142
 
127
143
  low — trivial copy change
144
+
145
+ ## Acceptance Criteria
146
+
147
+ - AC-1: the change behaves as described.
128
148
  """
129
149
  And step "seam.naive" writes "src/calc.ts" with:
130
150
  """
@@ -157,6 +177,10 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
157
177
  ## Concerns
158
178
 
159
179
  ### Subtract
180
+
181
+ ## Acceptance Criteria
182
+
183
+ - AC-1: the change behaves as described.
160
184
  """
161
185
  And step "design.gate.answer" changes nothing
162
186
  When the process is driven
@@ -199,6 +223,10 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
199
223
  ## Production Signals
200
224
 
201
225
  - error rate stays flat
226
+
227
+ ## Acceptance Criteria
228
+
229
+ - AC-1: the change behaves as described.
202
230
  """
203
231
  And step "design.gate.answer" changes nothing
204
232
  When the process is driven
@@ -220,6 +248,10 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
220
248
  ## Risk Tier
221
249
 
222
250
  low — trivial
251
+
252
+ ## Acceptance Criteria
253
+
254
+ - AC-1: the change behaves as described.
223
255
  """
224
256
  And step "design.gate.answer" changes nothing
225
257
  And step "seam.naive" writes "src/calc.ts" with:
@@ -33,6 +33,10 @@ Feature: rejected naive passes walk one fixed path through the steer gate to idl
33
33
  ### Subtract
34
34
 
35
35
  - `subtract(a, b)` returns `a - b`.
36
+
37
+ ## Acceptance Criteria
38
+
39
+ - AC-1: the change behaves as described.
36
40
  """
37
41
  And step "design.gate.answer" changes nothing
38
42
 
@@ -77,6 +81,10 @@ Feature: rejected naive passes walk one fixed path through the steer gate to idl
77
81
  ### Subtract
78
82
 
79
83
  - `subtract(a, b)` returns `a - b`, in the calculator module.
84
+
85
+ ## Acceptance Criteria
86
+
87
+ - AC-1: the change behaves as described.
80
88
  """
81
89
  And step "seam.naive" from now on changes nothing
82
90
  And step "seam.naive" writes "src/calc.ts" with:
@@ -43,6 +43,10 @@ Feature: the naive pass and seam judge decide how a feature is built
43
43
  ### Subtract
44
44
 
45
45
  - `subtract(a, b)` returns `a - b`.
46
+
47
+ ## Acceptance Criteria
48
+
49
+ - AC-1: the change behaves as described.
46
50
  """
47
51
  And step "design.gate.answer" changes nothing
48
52
 
@@ -184,6 +188,10 @@ Feature: the naive pass and seam judge decide how a feature is built
184
188
  ### Subtract
185
189
 
186
190
  - `subtract(a, b)` returns `a - b`, in a new module.
191
+
192
+ ## Acceptance Criteria
193
+
194
+ - AC-1: the change behaves as described.
187
195
  """
188
196
  When gtd lands
189
197
  Then the process rests at "seam.naive"
@@ -16,7 +16,9 @@ import { healthy } from "../shared/health.js"
16
16
  import { coder, FEEDBACK, REQUIREMENTS } from "../shared/steps.js"
17
17
  import * as t from "../shared/text.js"
18
18
  import { defaults } from "../shared/vars.js"
19
+ import { requireCriteria, requireCriteriaFrozen } from "./criteria.js"
19
20
  import { refactorStrays, reviewedRefactor } from "./refactor.js"
21
+ import { flagModeOn } from "../shared/options.js"
20
22
  import type { RiskTier } from "./tier.js"
21
23
 
22
24
  /** Naive-pass retries after a `reject`; the next `reject` rests at `seam.steer`. */
@@ -71,6 +73,7 @@ export function implement(
71
73
  options.tier,
72
74
  options.amend,
73
75
  options.afterRefactor,
76
+ flagModeOn(),
74
77
  ),
75
78
  {
76
79
  label: "Implementing",
@@ -79,13 +82,16 @@ export function implement(
79
82
  ...(options.afterRefactor === true ? { allowEmpty: true } : {}),
80
83
  },
81
84
  )
82
- await healthy(() =>
83
- t.agentWithSkills("fix.suite.fixing", t.implementFixPrompt(), {
84
- label: "Fixing the check",
85
- file: FEEDBACK,
86
- model: coder(),
87
- system: t.builderSystem(),
88
- }),
85
+ requireCriteriaFrozen(REQUIREMENTS)
86
+ await healthy(
87
+ () =>
88
+ t.agentWithSkills("fix.suite.fixing", t.implementFixPrompt(), {
89
+ label: "Fixing the check",
90
+ file: FEEDBACK,
91
+ model: coder(),
92
+ system: t.builderSystem(),
93
+ }),
94
+ { guard: () => requireCriteriaFrozen(REQUIREMENTS), guardRests: false },
89
95
  )
90
96
  await run("closing", removeScript([REQUIREMENTS]), { label: "Closing out the implementation" })
91
97
  })
@@ -122,6 +128,7 @@ const decide = (): Promise<Decision> =>
122
128
  })
123
129
  // Any edit steers, with a fresh budget and no stale rejection; clean overrules.
124
130
  if (changes().length === 0) return { reference: tip.hash }
131
+ requireCriteria(REQUIREMENTS)
125
132
  rejection = undefined
126
133
  retries = 0
127
134
  }
@@ -33,6 +33,10 @@ Feature: a process keeps the risk tier setting it started with when GTD_FLAG_RIS
33
33
  ## Risk Tier
34
34
 
35
35
  low — a new pure function
36
+
37
+ ## Acceptance Criteria
38
+
39
+ - AC-1: the change behaves as described.
36
40
  """
37
41
 
38
42
  Scenario: a process started with the flag off records no tier after the flag turns on
@@ -21,6 +21,7 @@ export {
21
21
  } from "./shared/tail.js"
22
22
  export { lensesOf } from "../workflow-kit/index.js"
23
23
  export * from "./feature/steps.js"
24
+ export * from "./feature/criteria.js"
24
25
  export * from "./feature/planning.js"
25
26
  export * from "./feature/refactor.js"
26
27
  export * from "./feature/seam.js"
@@ -30,6 +30,18 @@ describe("the shipped workflow factories", () => {
30
30
  })
31
31
  })
32
32
 
33
+ it("default flagMode on and lay a boolean factory option over it as a string", () => {
34
+ expect(defaults.flagMode).toBe("true")
35
+ expect(configure(feature({ flagMode: false }))).toMatchObject({
36
+ ok: true,
37
+ build: { defaults: { flagMode: "false" } },
38
+ })
39
+ expect(configure(review({ flagMode: true }))).toMatchObject({
40
+ ok: true,
41
+ build: { defaults: { flagMode: "true" } },
42
+ })
43
+ })
44
+
33
45
  it("declare flag_risk_tier off by default; it is no factory option", () => {
34
46
  expect(Object.keys(ShippedOptions.fields)).not.toContain("flag_risk_tier")
35
47
  })
@@ -5,6 +5,8 @@ import {
5
5
  type WorkflowBase,
6
6
  type WorkflowDoors,
7
7
  type WorkflowFactory,
8
+ refuse,
9
+ vars,
8
10
  } from "../../flows/index.js"
9
11
  import { defaultFlagRegistry, defaults, envDefaults } from "./vars.js"
10
12
 
@@ -23,18 +25,31 @@ export const ShippedOptions = Schema.Struct({
23
25
  testCommand: setting,
24
26
  plannerModel: setting,
25
27
  coderModel: setting,
28
+ flagMode: Schema.optionalWith(Schema.Boolean, { exact: true }),
26
29
  })
27
30
 
28
31
  export type ShippedOptions = Schema.Schema.Encoded<typeof ShippedOptions>
29
32
 
30
33
  const over = (
31
34
  base: Readonly<Record<string, string>>,
32
- options: Readonly<Record<string, string>>,
35
+ options: Readonly<Record<string, string | boolean>>,
33
36
  ): Record<string, string> => ({
34
37
  ...base,
35
- ...Object.fromEntries(Object.entries(options).filter(([name]) => Object.hasOwn(base, name))),
38
+ ...Object.fromEntries(
39
+ Object.entries(options)
40
+ .filter(([name]) => Object.hasOwn(base, name))
41
+ .map(([name, value]) => [name, String(value)]),
42
+ ),
36
43
  })
37
44
 
45
+ /** The `flagMode` setting, pinned at process start. Anything but true/false fails rather than guess a side. */
46
+ export const flagModeOn = (): boolean => {
47
+ const value = vars.flagMode ?? defaults.flagMode!
48
+ if (value !== "true" && value !== "false")
49
+ refuse(`gtd: flagMode must be "true" or "false", got ${JSON.stringify(value)}`)
50
+ return value === "true"
51
+ }
52
+
38
53
  /** A shipped flow as a factory: its options override the settings it declares, each in its own kind. */
39
54
  export const shipped = (
40
55
  flow: Flow,
@@ -990,6 +990,22 @@ describe("the seam phase's texts", () => {
990
990
  expect(prompt).toContain("Implement them properly")
991
991
  })
992
992
 
993
+ it("flag mode off asks for the naive version and no release flag, whatever the tier or lap", () => {
994
+ for (const [tier, amend] of [
995
+ ["high", false],
996
+ ["low", false],
997
+ ["high", true],
998
+ ] as const) {
999
+ const prompt = renderText(() => implementBuildingPrompt(undefined, tier, amend, false, false))
1000
+ expect(prompt).toContain("Add no release flag — this project ships without release flags")
1001
+ expect(prompt).not.toContain("new release flag")
1002
+ expect(prompt).not.toContain("low risk tier")
1003
+ }
1004
+ expect(
1005
+ renderText(() => implementBuildingPrompt(undefined, "high", false, false, false)),
1006
+ ).toContain("the quickest way that works")
1007
+ })
1008
+
993
1009
  it("keeps a fix turn to the feedback file's rules", () => {
994
1010
  expect(renderText(() => implementFixPrompt())).toContain(".gtd/FEEDBACK.md")
995
1011
  })