@pmelab/gtd 57.0.0 → 58.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 (46) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +9 -8
  3. package/dist/chunks/{reserved-X2RkNHin.mjs → reserved-BzAFXuz_.mjs} +26 -19
  4. package/dist/chunks/{review-v7sh5GF4.mjs → review-CVqRS0mc.mjs} +2 -2
  5. package/dist/chunks/{tail-S04KEewi.mjs → tail-JQzB21Q3.mjs} +1 -1
  6. package/dist/workflows/cleanup.mjs +2 -2
  7. package/dist/workflows/feature.mjs +52 -13
  8. package/dist/workflows/fix.mjs +2 -2
  9. package/dist/workflows/review.mjs +3 -3
  10. package/dist/workflows/rollout.mjs +1 -1
  11. package/dist/workflows.mjs +74 -28
  12. package/package.json +1 -1
  13. package/src/workflows/feature/acceptance-criteria.feature +32 -0
  14. package/src/workflows/feature/criteria-coverage.feature +0 -1
  15. package/src/workflows/feature/criteria.ts +10 -4
  16. package/src/workflows/feature/fast-path.feature +4 -0
  17. package/src/workflows/feature/feature.test.ts +3 -39
  18. package/src/workflows/feature/feature.ts +3 -4
  19. package/src/workflows/feature/lens-models.feature +0 -1
  20. package/src/workflows/feature/lens-review.feature +0 -1
  21. package/src/workflows/feature/naive-acceptance.feature +4 -0
  22. package/src/workflows/feature/naive-test.feature +11 -6
  23. package/src/workflows/feature/one-step-land.feature +4 -0
  24. package/src/workflows/feature/planning.ts +2 -0
  25. package/src/workflows/feature/refactor-absorbs.feature +4 -0
  26. package/src/workflows/feature/refactor-first.feature +4 -0
  27. package/src/workflows/feature/refactor-lens-models.feature +4 -0
  28. package/src/workflows/feature/refactor-rebuild.feature +4 -0
  29. package/src/workflows/feature/refactor-review.feature +20 -0
  30. package/src/workflows/feature/release-flag.feature +167 -0
  31. package/src/workflows/feature/release-flag.test.ts +49 -0
  32. package/src/workflows/feature/release-flag.ts +46 -0
  33. package/src/workflows/feature/review-gate.feature +0 -1
  34. package/src/workflows/feature/reviewer-walkthrough.feature +0 -1
  35. package/src/workflows/feature/risk-tier.feature +32 -0
  36. package/src/workflows/feature/seam-rejection.feature +8 -0
  37. package/src/workflows/feature/seam.feature +8 -0
  38. package/src/workflows/feature/seam.test.ts +20 -7
  39. package/src/workflows/feature/seam.ts +7 -7
  40. package/src/workflows/feature/standards.feature +0 -1
  41. package/src/workflows/shared/options.test.ts +3 -10
  42. package/src/workflows/shared/options.ts +0 -9
  43. package/src/workflows/shared/text.test.ts +36 -28
  44. package/src/workflows/shared/text.ts +37 -20
  45. package/src/workflows/shared/vars.ts +0 -1
  46. package/src/workflows/feature/flag-mode.feature +0 -88
@@ -33,6 +33,10 @@ Feature: the refactor review's lenses take their model hints from refactorQualit
33
33
 
34
34
  - the greeting module
35
35
 
36
+ ## Release Flag
37
+
38
+ new
39
+
36
40
  ## Acceptance Criteria
37
41
 
38
42
  - AC-1: the change behaves as described.
@@ -45,6 +45,10 @@ Feature: refactor-first writes a refactor note, refactors on its own, then rebui
45
45
 
46
46
  - the greeting module
47
47
 
48
+ ## Release Flag
49
+
50
+ new
51
+
48
52
  ## Acceptance Criteria
49
53
 
50
54
  - AC-1: subtract returns the difference.
@@ -51,6 +51,10 @@ Feature: the refactor is reviewed on its own before the feature is implemented
51
51
 
52
52
  - the greeting module
53
53
 
54
+ ## Release Flag
55
+
56
+ new
57
+
54
58
  ## Acceptance Criteria
55
59
 
56
60
  - AC-1: the change behaves as described.
@@ -149,6 +153,10 @@ Feature: the refactor is reviewed on its own before the feature is implemented
149
153
 
150
154
  - the greeting module
151
155
 
156
+ ## Release Flag
157
+
158
+ new
159
+
152
160
  ## Acceptance Criteria
153
161
 
154
162
  - AC-1: the change behaves as described.
@@ -246,6 +254,10 @@ Feature: the refactor is reviewed on its own before the feature is implemented
246
254
 
247
255
  - the greeting module
248
256
 
257
+ ## Release Flag
258
+
259
+ new
260
+
249
261
  ## Acceptance Criteria
250
262
 
251
263
  - AC-1: the change behaves as described.
@@ -1193,6 +1205,10 @@ Feature: the refactor is reviewed on its own before the feature is implemented
1193
1205
 
1194
1206
  - the greeting module
1195
1207
 
1208
+ ## Release Flag
1209
+
1210
+ new
1211
+
1196
1212
  ## Acceptance Criteria
1197
1213
 
1198
1214
  - AC-1: the change behaves as described.
@@ -1318,6 +1334,10 @@ Feature: the refactor is reviewed on its own before the feature is implemented
1318
1334
 
1319
1335
  - the greeting module
1320
1336
 
1337
+ ## Release Flag
1338
+
1339
+ new
1340
+
1321
1341
  ## Acceptance Criteria
1322
1342
 
1323
1343
  - AC-1: the change behaves as described.
@@ -0,0 +1,167 @@
1
+ @inmem
2
+ Feature: a release flag only when the requirements ask for one
3
+
4
+ A change ships unflagged unless `.gtd/REQUIREMENTS.md` carries a
5
+ `## Release Flag` section — written by triage, or by the skip path's impact
6
+ turn, only when the sketch asks for a flag, and editable at the design gate.
7
+ Its first line is `new` (a new release flag) or `existing: <NAME>` (behind a
8
+ flag that already exists). Without it no seam judge or refactor runs and the
9
+ naive pass lands as the implementation; with it the seam judge rules on the
10
+ pass.
11
+
12
+ Background:
13
+ Given the shipped workflows, as gtd install scaffolds them
14
+ And an environment variable "GTD_QUALITYREVIEWS" set to ""
15
+ And an environment variable "GTD_FLAG_RISK_TIER" set to "0"
16
+ And a file "src/calc.ts" with:
17
+ """
18
+ export const add = (a: number, b: number) => a + b
19
+ """
20
+ And the working tree is committed as "feat: add calculator"
21
+ And step "unwind" changes nothing
22
+ And step "start-gate.check" changes nothing
23
+ And step "seam.naive" writes "src/calc.test.ts" with:
24
+ """
25
+ it("[AC-1] subtracts", () => {})
26
+ """
27
+ And step "seam.naive" writes "src/calc.ts" with:
28
+ """
29
+ export const add = (a: number, b: number) => a + b
30
+ export const subtract = (a: number, b: number) => a - b
31
+ """
32
+ And step "seam.check" changes nothing
33
+
34
+ Rule: the design path
35
+
36
+ Background:
37
+ Given a file "TODO.md" with:
38
+ """
39
+ # Add subtract
40
+
41
+ subtract(a, b) returns the difference.
42
+ """
43
+ And step "design.gate.answer" changes nothing
44
+
45
+ Scenario: with no Release Flag section the naive pass lands unjudged, and no flag is asked for
46
+ Given step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
47
+ """
48
+ # Requirements
49
+
50
+ ## Impact
51
+
52
+ - the calculator module
53
+
54
+ ## Acceptance Criteria
55
+
56
+ - AC-1: subtract(a, b) returns a - b.
57
+ """
58
+ And step "implement.applying" runs its script
59
+ And step "implement.health.check" changes nothing
60
+ And step "implement.closing" runs its script
61
+ When the process is driven
62
+ Then the process rests at "build.review.reviewing"
63
+ And "src/calc.ts" contains "subtract"
64
+ And no commit body contains "seam.judge"
65
+ And the commit subjects end with:
66
+ """
67
+ gtd(human): design.gate.answer → seam.naive
68
+ gtd(agent): seam.naive → seam.check
69
+ gtd(check): seam.check → implement.applying
70
+ gtd(check): implement.applying → implement.health.check
71
+ gtd(check): implement.health.check → implement.closing
72
+ gtd(check): implement.closing → build.review.reviewing
73
+ """
74
+
75
+ Scenario: a new release flag asked for sends the naive pass to the seam judge
76
+ Given step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
77
+ """
78
+ # Requirements
79
+
80
+ ## Impact
81
+
82
+ - the calculator module
83
+
84
+ ## Release Flag
85
+
86
+ new
87
+
88
+ ## Acceptance Criteria
89
+
90
+ - AC-1: subtract(a, b) returns a - b.
91
+ """
92
+ When the process is driven
93
+ Then the process rests at "seam.judge"
94
+
95
+ Scenario: an existing flag asked for sends the naive pass to the seam judge too
96
+ Given step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
97
+ """
98
+ # Requirements
99
+
100
+ ## Impact
101
+
102
+ - the calculator module
103
+
104
+ ## Release Flag
105
+
106
+ existing: GTD_FLAG_BETA
107
+
108
+ ## Acceptance Criteria
109
+
110
+ - AC-1: subtract(a, b) returns a - b.
111
+ """
112
+ When the process is driven
113
+ Then the process rests at "seam.judge"
114
+
115
+ Scenario: a Release Flag section that asks for neither is refused
116
+ Given step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
117
+ """
118
+ # Requirements
119
+
120
+ ## Impact
121
+
122
+ - the calculator module
123
+
124
+ ## Release Flag
125
+
126
+ maybe later
127
+
128
+ ## Acceptance Criteria
129
+
130
+ - AC-1: subtract(a, b) returns a - b.
131
+ """
132
+ Then driving the process is refused with "release-flag"
133
+
134
+ Rule: the skip path
135
+
136
+ Background:
137
+ Given a file "TODO.md" with:
138
+ """
139
+ # Add subtract
140
+
141
+ Ship it behind the existing GTD_FLAG_BETA flag.
142
+
143
+ ## Acceptance criteria
144
+
145
+ - subtract(a, b) returns a - b.
146
+ """
147
+ And judge step "design.clear" answers:
148
+ """
149
+ [{"id":"clear","answer":"yes","p":0.9}]
150
+ """
151
+ And step "design.normalise" runs its script
152
+
153
+ Scenario: the impact turn records the flag the sketch asks for, and the seam judge rules
154
+ Given step "design.impact" appends to ".gtd/REQUIREMENTS.md":
155
+ """
156
+
157
+ ## Impact
158
+
159
+ - the calculator module
160
+
161
+ ## Release Flag
162
+
163
+ existing: GTD_FLAG_BETA
164
+ """
165
+ When the process is driven
166
+ Then the process rests at "seam.judge"
167
+ And ".gtd/REQUIREMENTS.md" contains "existing: GTD_FLAG_BETA"
@@ -0,0 +1,49 @@
1
+ import { describe, expect, it } from "vitest"
2
+ import { releaseFlagOf } from "./release-flag.js"
3
+
4
+ const requirements = (section: string): string =>
5
+ `# Requirements\n\n## Impact\n\n- a module\n\n${section}## Acceptance Criteria\n\n- AC-1: a\n`
6
+
7
+ describe("releaseFlagOf", () => {
8
+ it("asks for no flag without the section, or with none", () => {
9
+ expect(releaseFlagOf(requirements(""))).toEqual({ kind: "none" })
10
+ expect(releaseFlagOf(requirements("## Release Flag\n\nnone\n\n"))).toEqual({ kind: "none" })
11
+ })
12
+
13
+ it("reads new and an existing flag's name, backticked or not", () => {
14
+ expect(releaseFlagOf(requirements("## Release Flag\n\nnew\n\n"))).toEqual({ kind: "new" })
15
+ for (const line of [
16
+ "existing: GTD_FLAG_BETA",
17
+ "existing: `GTD_FLAG_BETA`",
18
+ "Existing:GTD_FLAG_BETA",
19
+ ])
20
+ expect(releaseFlagOf(requirements(`## Release Flag\n\n${line}\n\n`))).toEqual({
21
+ kind: "existing",
22
+ name: "GTD_FLAG_BETA",
23
+ })
24
+ })
25
+
26
+ it("reads only the first line under the heading, and only a level-2 heading", () => {
27
+ expect(
28
+ releaseFlagOf(requirements("## Release Flag\n\nnew\n\nbecause the sketch says so\n\n")),
29
+ ).toEqual({
30
+ kind: "new",
31
+ })
32
+ expect(releaseFlagOf(requirements("### Release Flag\n\nnew\n\n"))).toEqual({ kind: "none" })
33
+ })
34
+
35
+ it("ignores a fenced heading", () => {
36
+ expect(releaseFlagOf(requirements("```\n## Release Flag\n\nnew\n```\n\n"))).toEqual({
37
+ kind: "none",
38
+ })
39
+ })
40
+
41
+ it("names the line it cannot read", () => {
42
+ expect(releaseFlagOf(requirements("## Release Flag\n\nmaybe later\n\n"))).toEqual({
43
+ unreadable: "maybe later",
44
+ })
45
+ expect(releaseFlagOf(requirements("## Release Flag\n\nexisting:\n\n"))).toEqual({
46
+ unreadable: "existing:",
47
+ })
48
+ })
49
+ })
@@ -0,0 +1,46 @@
1
+ import { read, refuse } from "../../flows/index.js"
2
+ import { HEADING, markFenced } from "../shared/fences.js"
3
+ import { REQUIREMENTS } from "../shared/steps.js"
4
+ import type { ReleaseFlag } from "../shared/text.js"
5
+
6
+ const NAME = /^existing:\s*`?([A-Za-z_][\w.-]*)`?$/i
7
+
8
+ const isFlagHeading = (h: RegExpExecArray | null): boolean =>
9
+ h !== null && h[1]!.length === 2 && /^release flag$/i.test(h[2]!.trim())
10
+
11
+ /** The first line under `## Release Flag`, `undefined` without the section. */
12
+ const requestLine = (text: string): string | undefined => {
13
+ const lines = markFenced(text)
14
+ const at = lines.findIndex((l) => !l.fenced && isFlagHeading(HEADING.exec(l.line)))
15
+ if (at === -1) return undefined
16
+ const body = lines.slice(at + 1)
17
+ const end = body.findIndex((l) => !l.fenced && HEADING.test(l.line))
18
+ return (end === -1 ? body : body.slice(0, end)).find((l) => l.line.trim() !== "")?.line.trim()
19
+ }
20
+
21
+ /** What `text` asks for, or why it cannot be read. No section asks for no flag. */
22
+ export const releaseFlagOf = (text: string): ReleaseFlag | { readonly unreadable: string } => {
23
+ const line = requestLine(text)
24
+ if (line === undefined || /^none$/i.test(line)) return { kind: "none" }
25
+ if (/^new$/i.test(line)) return { kind: "new" }
26
+ const name = NAME.exec(line)?.[1]
27
+ return name === undefined ? { unreadable: line } : { kind: "existing", name }
28
+ }
29
+
30
+ /** Refuse `file` when its `## Release Flag` section is neither `none`, `new` nor `existing: <NAME>`. */
31
+ export const requireReleaseFlag = (file: string): void => {
32
+ const request = releaseFlagOf(read(file) ?? "")
33
+ if (!("unreadable" in request)) return
34
+ refuse(
35
+ `gtd land: release-flag: ${file}'s "## Release Flag" reads ${JSON.stringify(request.unreadable)} — its first line must be \`new\`, \`existing: <NAME>\` or \`none\`, or leave the section out for no flag`,
36
+ )
37
+ }
38
+
39
+ /** What `.gtd/REQUIREMENTS.md` asks for now; a person's unreadable edit is refused. */
40
+ export const requestedFlag = (): ReleaseFlag => {
41
+ requireReleaseFlag(REQUIREMENTS)
42
+ return releaseFlagOf(read(REQUIREMENTS) ?? "") as ReleaseFlag
43
+ }
44
+
45
+ /** Whether the change goes behind a release flag: only then does the seam judge rule. */
46
+ export const flagged = (): boolean => requestedFlag().kind !== "none"
@@ -11,7 +11,6 @@ Feature: a feature process ends at the human review
11
11
  Background:
12
12
  Given the shipped workflows, as gtd install scaffolds them
13
13
  And an environment variable "GTD_QUALITYREVIEWS" set to ""
14
- And an environment variable "GTD_FLAGMODE" set to "false"
15
14
  And a file "src/calc.ts" with:
16
15
  """
17
16
  export const add = (a: number, b: number) => a + b
@@ -11,7 +11,6 @@ Feature: a reviewer walks the human through the feature's code
11
11
  Background:
12
12
  Given the shipped workflows, as gtd install scaffolds them
13
13
  And an environment variable "GTD_QUALITYREVIEWS" set to ""
14
- And an environment variable "GTD_FLAGMODE" set to "false"
15
14
  And a file "src/calc.ts" with:
16
15
  """
17
16
  export const add = (a: number, b: number) => a + b
@@ -54,6 +54,10 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
54
54
 
55
55
  - the greeting module
56
56
 
57
+ ## Release Flag
58
+
59
+ new
60
+
57
61
  ## Acceptance Criteria
58
62
 
59
63
  - AC-1: the change behaves as described.
@@ -77,6 +81,10 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
77
81
 
78
82
  - the greeting module
79
83
 
84
+ ## Release Flag
85
+
86
+ new
87
+
80
88
  ## Acceptance Criteria
81
89
 
82
90
  - AC-1: the change behaves as described.
@@ -101,6 +109,10 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
101
109
 
102
110
  - the greeting module
103
111
 
112
+ ## Release Flag
113
+
114
+ new
115
+
104
116
  ## Acceptance Criteria
105
117
 
106
118
  - AC-1: the change behaves as described.
@@ -134,6 +146,10 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
134
146
 
135
147
  - the greeting module
136
148
 
149
+ ## Release Flag
150
+
151
+ new
152
+
137
153
  ## Acceptance Criteria
138
154
 
139
155
  - AC-1: the change behaves as described.
@@ -162,6 +178,10 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
162
178
 
163
179
  - the greeting module
164
180
 
181
+ ## Release Flag
182
+
183
+ new
184
+
165
185
  ## Acceptance Criteria
166
186
 
167
187
  - AC-1: the change behaves as described.
@@ -211,6 +231,10 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
211
231
 
212
232
  - the greeting module
213
233
 
234
+ ## Release Flag
235
+
236
+ new
237
+
214
238
  ## Acceptance Criteria
215
239
 
216
240
  - AC-1: the change behaves as described.
@@ -261,6 +285,10 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
261
285
 
262
286
  - the greeting module
263
287
 
288
+ ## Release Flag
289
+
290
+ new
291
+
264
292
  ## Acceptance Criteria
265
293
 
266
294
  - AC-1: the change behaves as described.
@@ -290,6 +318,10 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
290
318
 
291
319
  - the greeting module
292
320
 
321
+ ## Release Flag
322
+
323
+ new
324
+
293
325
  ## Acceptance Criteria
294
326
 
295
327
  - AC-1: the change behaves as described.
@@ -38,6 +38,10 @@ Feature: rejected naive passes walk one fixed path through the steer gate to the
38
38
 
39
39
  - the greeting module
40
40
 
41
+ ## Release Flag
42
+
43
+ new
44
+
41
45
  ## Acceptance Criteria
42
46
 
43
47
  - AC-1: the change behaves as described.
@@ -83,6 +87,10 @@ Feature: rejected naive passes walk one fixed path through the steer gate to the
83
87
 
84
88
  - the greeting module
85
89
 
90
+ ## Release Flag
91
+
92
+ new
93
+
86
94
  ## Acceptance Criteria
87
95
 
88
96
  - AC-1: the change behaves as described.
@@ -48,6 +48,10 @@ Feature: the naive pass and seam judge decide how a feature is built
48
48
 
49
49
  - the greeting module
50
50
 
51
+ ## Release Flag
52
+
53
+ new
54
+
51
55
  ## Acceptance Criteria
52
56
 
53
57
  - AC-1: the change behaves as described.
@@ -241,6 +245,10 @@ Feature: the naive pass and seam judge decide how a feature is built
241
245
 
242
246
  - the greeting module
243
247
 
248
+ ## Release Flag
249
+
250
+ new
251
+
244
252
  ## Acceptance Criteria
245
253
 
246
254
  - AC-1: the change behaves as described.
@@ -234,18 +234,24 @@ interface Seamed {
234
234
  readonly outcome: SeamOutcome | Error
235
235
  }
236
236
 
237
+ const FLAGGED = "# Requirements\n\n## Release Flag\n\nnew\n"
238
+
237
239
  /** Runs `seamBuild` against a scripted context: the judge answers `answer`, every review signs off. */
238
240
  const driveSeam = async (
239
241
  answer: string,
240
242
  stray: Change = change("src/a.ts", "modified"),
241
243
  vars: Record<string, string> = {},
242
244
  reds = 0,
245
+ requirements = FLAGGED,
243
246
  ): Promise<Seamed> => {
244
247
  let red = false
245
248
  let redsLeft = reds
246
249
  const names: string[] = []
247
250
  const stack: string[] = []
248
- const files = new Map<string, string>([["src/a.ts", "a"]])
251
+ const files = new Map<string, string>([
252
+ ["src/a.ts", "a"],
253
+ [".gtd/REQUIREMENTS.md", requirements],
254
+ ])
249
255
  let current = "c0"
250
256
  const effects: Record<string, () => void> = {
251
257
  note: () => void files.set(NOTE, "Seam: a shared op"),
@@ -302,8 +308,8 @@ describe("seamBuild", () => {
302
308
  expect(h.names).toEqual(["seam.naive", "seam.check", "seam.judge"])
303
309
  })
304
310
 
305
- it("with flagMode off resolves a vetted pass to land with no judge", async () => {
306
- const h = await driveSeam("reject", undefined, { flagMode: "false" })
311
+ it("with no release flag asked for resolves a vetted pass to land with no judge", async () => {
312
+ const h = await driveSeam("reject", undefined, {}, 0, "# Requirements\n")
307
313
  expect(h.outcome).toEqual({ reference: "abc", base: "c-base", land: true })
308
314
  expect(h.names).toEqual(["seam.naive", "seam.check"])
309
315
  })
@@ -393,6 +399,7 @@ describe("seamBuild", () => {
393
399
 
394
400
  describe("implement", () => {
395
401
  let lastOptions: Extract<StepRequest, { kind: "agent" }>["options"] | undefined
402
+ let requirements = FLAGGED
396
403
  const building = async (
397
404
  reference: string | undefined,
398
405
  tier?: "low" | "high",
@@ -414,6 +421,7 @@ describe("implement", () => {
414
421
  pushScope: () => undefined,
415
422
  popScope: () => undefined,
416
423
  changesSince: () => [],
424
+ read: (path: string) => (path === ".gtd/REQUIREMENTS.md" ? requirements : undefined),
417
425
  },
418
426
  ),
419
427
  )
@@ -506,9 +514,14 @@ describe("implement", () => {
506
514
  expect(prompt).toContain("otherwise put")
507
515
  })
508
516
 
509
- it("amend on tier low says to add no release flag", async () => {
510
- const prompt = await building(undefined, "low", true)
511
- expect(prompt).toContain("Add no release flag")
512
- expect(prompt).toContain("amend")
517
+ it("asks for no release flag when the requirements ask for none, whatever the tier", async () => {
518
+ requirements = "# Requirements\n"
519
+ try {
520
+ const prompt = await building(undefined, "high", true)
521
+ expect(prompt).toContain("Add no release flag")
522
+ expect(prompt).toContain("amend")
523
+ } finally {
524
+ requirements = FLAGGED
525
+ }
513
526
  })
514
527
  })
@@ -37,7 +37,8 @@ import {
37
37
  reviewedRefactor,
38
38
  } from "./refactor.js"
39
39
  import { isCode } from "../shared/review.js"
40
- import { flagModeOn, naiveTestOn } from "../shared/options.js"
40
+ import { naiveTestOn } from "../shared/options.js"
41
+ import { flagged, requestedFlag } from "./release-flag.js"
41
42
  import type { RiskTier } from "./tier.js"
42
43
 
43
44
  /** Naive-pass retries after a `reject` or a red suite; the next one rests at `seam.steer`. */
@@ -187,10 +188,9 @@ const build = async (options: ImplementOptions): Promise<void> => {
187
188
  "building",
188
189
  t.implementBuildingPrompt(
189
190
  options.reference,
190
- options.tier,
191
191
  options.amend,
192
192
  options.afterRefactor,
193
- flagModeOn(),
193
+ requestedFlag(),
194
194
  options.rebuild,
195
195
  ),
196
196
  {
@@ -237,14 +237,14 @@ export const implementing = (options: ImplementOptions): Promise<readonly string
237
237
  interface Decision {
238
238
  readonly reference: string
239
239
  readonly tip: KeptTip
240
- /** Absent on `land` (or with `flagMode` off, unjudged): the pass itself is the implementation. */
240
+ /** Absent on `land` (or unjudged, with no release flag asked for): the pass itself is the implementation. */
241
241
  readonly shape?: DiffShapeResult
242
242
  }
243
243
 
244
244
  /** Resolves `true` when the human steered (edited), `false` on a clean overrule. */
245
245
  const steer = async (retry: t.NaiveRetry): Promise<boolean> => {
246
246
  await human("steer", {
247
- message: t.seamSteerMessage(retry, flagModeOn()),
247
+ message: t.seamSteerMessage(retry, flagged()),
248
248
  label: "Steer or overrule the naive pass",
249
249
  file: REQUIREMENTS,
250
250
  acceptClean: true,
@@ -328,7 +328,7 @@ const tried = async (tip: KeptTip): Promise<t.NaiveTested | undefined> => {
328
328
  label: "Applying the naive pass to try it",
329
329
  })
330
330
  await human("try", {
331
- message: t.naiveTryMessage(flagModeOn()),
331
+ message: t.naiveTryMessage(flagged()),
332
332
  label: "Try the naive pass",
333
333
  file: NAIVE_TEST,
334
334
  acceptClean: true,
@@ -359,7 +359,7 @@ const decide = (): Promise<Decision> =>
359
359
  }
360
360
  }
361
361
  const outcome =
362
- verdict ?? (flagModeOn() ? await ruled(tip, vetting.again) : { reference: tip.hash, tip })
362
+ verdict ?? (flagged() ? await ruled(tip, vetting.again) : { reference: tip.hash, tip })
363
363
  if (!("kind" in outcome)) return outcome
364
364
  retry = outcome.kind === "retry" ? outcome.retry : undefined
365
365
  }
@@ -12,7 +12,6 @@ Feature: the review learns standards from the human's answers
12
12
  Background:
13
13
  Given the shipped workflows, as gtd install scaffolds them
14
14
  And an environment variable "GTD_QUALITYREVIEWS" set to ""
15
- And an environment variable "GTD_FLAGMODE" set to "false"
16
15
  And a file "src/calc.ts" with:
17
16
  """
18
17
  export const add = (a: number, b: number) => a + b
@@ -30,16 +30,9 @@ 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
- })
33
+ it("refuse the removed flagMode option", () => {
34
+ expect(defaults.flagMode).toBeUndefined()
35
+ expect(configure(feature({ flagMode: false } as never))).toMatchObject({ ok: false })
43
36
  })
44
37
 
45
38
  it("declare flag_risk_tier off by default; it is no factory option", () => {
@@ -27,7 +27,6 @@ export const ShippedOptions = Schema.Struct({
27
27
  testCommand: setting,
28
28
  plannerModel: setting,
29
29
  coderModel: setting,
30
- flagMode: Schema.optionalWith(Schema.Boolean, { exact: true }),
31
30
  naiveTest: Schema.optionalWith(Schema.Boolean, { exact: true }),
32
31
  })
33
32
 
@@ -45,14 +44,6 @@ const over = (
45
44
  ),
46
45
  })
47
46
 
48
- /** The `flagMode` setting, pinned at process start. Anything but true/false fails rather than guess a side. */
49
- export const flagModeOn = (): boolean => {
50
- const value = vars.flagMode ?? defaults.flagMode!
51
- if (value !== "true" && value !== "false")
52
- refuse(`gtd: flagMode must be "true" or "false", got ${JSON.stringify(value)}`)
53
- return value === "true"
54
- }
55
-
56
47
  /** The `naiveTest` setting, pinned at process start. Anything but true/false fails rather than guess. */
57
48
  export const naiveTestOn = (): boolean => {
58
49
  const value = vars.naiveTest ?? defaults.naiveTest!