@pmelab/gtd 56.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 (56) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +29 -35
  3. package/dist/chunks/{reserved-B8EC-WE8.mjs → reserved-BzAFXuz_.mjs} +56 -131
  4. package/dist/chunks/{review-HdrX0FJ7.mjs → review-CVqRS0mc.mjs} +2 -2
  5. package/dist/chunks/{tail-BSLah2mA.mjs → tail-JQzB21Q3.mjs} +1 -1
  6. package/dist/workflows/cleanup.mjs +2 -2
  7. package/dist/workflows/feature.mjs +99 -170
  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 +150 -297
  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 -2
  15. package/src/workflows/feature/criteria.test.ts +1 -41
  16. package/src/workflows/feature/criteria.ts +12 -18
  17. package/src/workflows/feature/fast-path.feature +4 -1
  18. package/src/workflows/feature/feature.test.ts +3 -39
  19. package/src/workflows/feature/feature.ts +6 -8
  20. package/src/workflows/feature/lens-models.feature +0 -2
  21. package/src/workflows/feature/lens-review.feature +0 -2
  22. package/src/workflows/feature/naive-acceptance.feature +4 -0
  23. package/src/workflows/feature/naive-test.feature +263 -0
  24. package/src/workflows/feature/one-step-land.feature +4 -1
  25. package/src/workflows/feature/planning.ts +2 -0
  26. package/src/workflows/feature/refactor-absorbs.feature +4 -1
  27. package/src/workflows/feature/refactor-first.feature +4 -1
  28. package/src/workflows/feature/refactor-lens-models.feature +4 -0
  29. package/src/workflows/feature/refactor-rebuild.feature +4 -1
  30. package/src/workflows/feature/refactor-review.feature +20 -1
  31. package/src/workflows/feature/release-flag.feature +167 -0
  32. package/src/workflows/feature/release-flag.test.ts +49 -0
  33. package/src/workflows/feature/release-flag.ts +46 -0
  34. package/src/workflows/feature/review-gate.feature +1 -3
  35. package/src/workflows/feature/reviewer-walkthrough.feature +0 -2
  36. package/src/workflows/feature/risk-tier.feature +32 -0
  37. package/src/workflows/feature/seam-rejection.feature +8 -1
  38. package/src/workflows/feature/seam.feature +8 -1
  39. package/src/workflows/feature/seam.test.ts +21 -9
  40. package/src/workflows/feature/seam.ts +60 -14
  41. package/src/workflows/feature/standards.feature +0 -2
  42. package/src/workflows/shared/options.test.ts +3 -10
  43. package/src/workflows/shared/options.ts +5 -6
  44. package/src/workflows/shared/prose.ts +0 -6
  45. package/src/workflows/shared/skills.test.ts +0 -4
  46. package/src/workflows/shared/skills.ts +0 -5
  47. package/src/workflows/shared/steps.ts +1 -1
  48. package/src/workflows/shared/text.test.ts +58 -69
  49. package/src/workflows/shared/text.ts +79 -162
  50. package/src/workflows/shared/vars.ts +2 -4
  51. package/src/workflows/feature/flag-mode.feature +0 -88
  52. package/src/workflows/feature/hands-on-skip.feature +0 -316
  53. package/src/workflows/feature/prototype-rounds.feature +0 -156
  54. package/src/workflows/feature/prototype.feature +0 -428
  55. package/src/workflows/feature/prototype.test.ts +0 -43
  56. package/src/workflows/feature/prototype.ts +0 -198
@@ -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"
@@ -4,15 +4,13 @@ Feature: a feature process ends at the human review
4
4
  After the quality lap the process rests at the review gate,
5
5
  `.gtd/REVIEW.md`. A sign-off ends the process. A note asking for a change in
6
6
  behaviour starts a feedback lap — design, then implement with no new naive
7
- pass or seam judge, the hands-on test, the quality lap — and the review
7
+ pass or seam judge, the quality lap — and the review
8
8
  again. Its design keeps a built criterion's ID and numbers
9
9
  new behaviour above every ID handed out.
10
10
 
11
11
  Background:
12
12
  Given the shipped workflows, as gtd install scaffolds them
13
- And an environment variable "GTD_PROTOTYPEROUNDS" set to "0"
14
13
  And an environment variable "GTD_QUALITYREVIEWS" set to ""
15
- And an environment variable "GTD_FLAGMODE" set to "false"
16
14
  And a file "src/calc.ts" with:
17
15
  """
18
16
  export const add = (a: number, b: number) => a + b
@@ -10,9 +10,7 @@ Feature: a reviewer walks the human through the feature's code
10
10
 
11
11
  Background:
12
12
  Given the shipped workflows, as gtd install scaffolds them
13
- And an environment variable "GTD_PROTOTYPEROUNDS" set to "0"
14
13
  And an environment variable "GTD_QUALITYREVIEWS" set to ""
15
- And an environment variable "GTD_FLAGMODE" set to "false"
16
14
  And a file "src/calc.ts" with:
17
15
  """
18
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.
@@ -9,7 +9,6 @@ Feature: rejected naive passes walk one fixed path through the steer gate to the
9
9
 
10
10
  Background:
11
11
  Given the shipped workflows, as gtd install scaffolds them
12
- And an environment variable "GTD_PROTOTYPEROUNDS" set to "0"
13
12
  And an environment variable "GTD_QUALITYREVIEWS" set to ""
14
13
  And an environment variable "GTD_FLAG_RISK_TIER" set to "0"
15
14
  And a file "src/calc.ts" with:
@@ -39,6 +38,10 @@ Feature: rejected naive passes walk one fixed path through the steer gate to the
39
38
 
40
39
  - the greeting module
41
40
 
41
+ ## Release Flag
42
+
43
+ new
44
+
42
45
  ## Acceptance Criteria
43
46
 
44
47
  - AC-1: the change behaves as described.
@@ -84,6 +87,10 @@ Feature: rejected naive passes walk one fixed path through the steer gate to the
84
87
 
85
88
  - the greeting module
86
89
 
90
+ ## Release Flag
91
+
92
+ new
93
+
87
94
  ## Acceptance Criteria
88
95
 
89
96
  - AC-1: the change behaves as described.
@@ -7,7 +7,6 @@ Feature: the naive pass and seam judge decide how a feature is built
7
7
 
8
8
  Background:
9
9
  Given the shipped workflows, as gtd install scaffolds them
10
- And an environment variable "GTD_PROTOTYPEROUNDS" set to "0"
11
10
  And the gtd config ends with:
12
11
  """
13
12
  export const config = {
@@ -49,6 +48,10 @@ Feature: the naive pass and seam judge decide how a feature is built
49
48
 
50
49
  - the greeting module
51
50
 
51
+ ## Release Flag
52
+
53
+ new
54
+
52
55
  ## Acceptance Criteria
53
56
 
54
57
  - AC-1: the change behaves as described.
@@ -242,6 +245,10 @@ Feature: the naive pass and seam judge decide how a feature is built
242
245
 
243
246
  - the greeting module
244
247
 
248
+ ## Release Flag
249
+
250
+ new
251
+
245
252
  ## Acceptance Criteria
246
253
 
247
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",
@@ -403,8 +410,7 @@ describe("implement", () => {
403
410
  lastOptions = undefined
404
411
  installContext(
405
412
  fixtureContext(
406
- // The hands-on test has its own scenarios.
407
- { vars: { prototypeRounds: "0" } },
413
+ {},
408
414
  {
409
415
  step: (request: StepRequest) => {
410
416
  requests.push(request)
@@ -415,6 +421,7 @@ describe("implement", () => {
415
421
  pushScope: () => undefined,
416
422
  popScope: () => undefined,
417
423
  changesSince: () => [],
424
+ read: (path: string) => (path === ".gtd/REQUIREMENTS.md" ? requirements : undefined),
418
425
  },
419
426
  ),
420
427
  )
@@ -507,9 +514,14 @@ describe("implement", () => {
507
514
  expect(prompt).toContain("otherwise put")
508
515
  })
509
516
 
510
- it("amend on tier low says to add no release flag", async () => {
511
- const prompt = await building(undefined, "low", true)
512
- expect(prompt).toContain("Add no release flag")
513
- 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
+ }
514
526
  })
515
527
  })
@@ -3,6 +3,7 @@ import {
3
3
  agent,
4
4
  applyScript,
5
5
  changes,
6
+ changesSince,
6
7
  check,
7
8
  codeChanges,
8
9
  env,
@@ -14,6 +15,7 @@ import {
14
15
  read,
15
16
  refuse,
16
17
  removeScript,
18
+ restoreScript,
17
19
  run,
18
20
  scope,
19
21
  vars,
@@ -22,11 +24,10 @@ import {
22
24
  type DiffShapeResult,
23
25
  type KeptTip,
24
26
  } from "../../flows/index.js"
25
- import { coder, FEEDBACK, PROTOTYPE, REFACTOR_NOTE, REQUIREMENTS } from "../shared/steps.js"
27
+ import { coder, FEEDBACK, NAIVE_TEST, REFACTOR_NOTE, REQUIREMENTS } from "../shared/steps.js"
26
28
  import * as t from "../shared/text.js"
27
29
  import { defaults } from "../shared/vars.js"
28
30
  import { green } from "./green.js"
29
- import { prototypeTest } from "./prototype.js"
30
31
  import { criterionIds, requireCriteria, requireCriteriaFrozen } from "./criteria.js"
31
32
  import {
32
33
  COMMENT,
@@ -36,7 +37,8 @@ import {
36
37
  reviewedRefactor,
37
38
  } from "./refactor.js"
38
39
  import { isCode } from "../shared/review.js"
39
- import { flagModeOn } from "../shared/options.js"
40
+ import { naiveTestOn } from "../shared/options.js"
41
+ import { flagged, requestedFlag } from "./release-flag.js"
40
42
  import type { RiskTier } from "./tier.js"
41
43
 
42
44
  /** Naive-pass retries after a `reject` or a red suite; the next one rests at `seam.steer`. */
@@ -53,7 +55,7 @@ export const judgeSeam = (tip: KeptTip): Promise<DiffShapeResult> =>
53
55
  exempt: tip.changes.filter((c) => c.status === "added").map((c) => c.path),
54
56
  })
55
57
 
56
- export const naivePass = (retry?: t.NaiveRetry): Promise<void> =>
58
+ export const naivePass = (retry?: t.NaiveRetry | t.NaiveTested): Promise<void> =>
57
59
  t.agentWithSkills("naive", t.naivePassPrompt(retry), {
58
60
  label: retry === undefined ? "Writing the naive pass" : "Rewriting the naive pass",
59
61
  keep: true,
@@ -186,10 +188,9 @@ const build = async (options: ImplementOptions): Promise<void> => {
186
188
  "building",
187
189
  t.implementBuildingPrompt(
188
190
  options.reference,
189
- options.tier,
190
191
  options.amend,
191
192
  options.afterRefactor,
192
- flagModeOn(),
193
+ requestedFlag(),
193
194
  options.rebuild,
194
195
  ),
195
196
  {
@@ -226,9 +227,8 @@ export const implementing = (options: ImplementOptions): Promise<readonly string
226
227
  if (built) await build(options)
227
228
  else requireCriteriaFrozen(REQUIREMENTS)
228
229
  await green()
229
- await prototypeTest()
230
230
  const ids = criterionIds(read(REQUIREMENTS) ?? "")
231
- await run("closing", removeScript([REQUIREMENTS, REFACTOR_NOTE, PROTOTYPE]), {
231
+ await run("closing", removeScript([REQUIREMENTS, REFACTOR_NOTE]), {
232
232
  label: "Closing out the implementation",
233
233
  })
234
234
  return ids
@@ -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,
@@ -277,7 +277,7 @@ const budget = (): ((next: t.NaiveRetry) => Promise<Verdict>) => {
277
277
  }
278
278
 
279
279
  interface Vetting {
280
- readonly again: ReturnType<typeof budget>
280
+ again: ReturnType<typeof budget>
281
281
  /** Set once a human overrules a test fault: the rule does not fit this change, so later passes skip it. */
282
282
  waived: boolean
283
283
  }
@@ -304,16 +304,62 @@ const ruled = async (tip: KeptTip, again: Vetting["again"]): Promise<Decision |
304
304
  return next.kind === "overruled" ? { reference: tip.hash, tip } : next
305
305
  }
306
306
 
307
+ /** Back to `before`: every code path, and the notes file. `.gtd/` edits (the requirements) stay. */
308
+ const untryScript = (before: string): string => {
309
+ const code = changesSince(before).filter((c) => isCode(c.path))
310
+ return restoreScript(
311
+ head(),
312
+ {
313
+ restore: code.filter((c) => c.status !== "added").map((c) => c.path),
314
+ remove: [...code.filter((c) => c.status === "added").map((c) => c.path), NAIVE_TEST],
315
+ },
316
+ before,
317
+ )
318
+ }
319
+
320
+ /**
321
+ * A person tries the green pass `tip` in the working tree. Resolves
322
+ * `undefined` on a clean landing (signed off), else what they sent back;
323
+ * either way the pass, their code edits and the notes file leave the tree.
324
+ */
325
+ const tried = async (tip: KeptTip): Promise<t.NaiveTested | undefined> => {
326
+ const before = head()
327
+ await run("applying", applyScript(tip.hash, FEEDBACK, t.naiveApplyFailure(tip.hash), [".gtd"]), {
328
+ label: "Applying the naive pass to try it",
329
+ })
330
+ await human("try", {
331
+ message: t.naiveTryMessage(flagged()),
332
+ label: "Try the naive pass",
333
+ file: NAIVE_TEST,
334
+ acceptClean: true,
335
+ })
336
+ const landed = changes()
337
+ const edits = landed.some((c) => isCode(c.path)) ? head() : undefined
338
+ const notes = read(NAIVE_TEST)?.trim() || undefined
339
+ if (landed.some((c) => c.path === REQUIREMENTS)) requireCriteria(REQUIREMENTS)
340
+ await run("restoring", untryScript(before), { label: "Taking the naive pass out again" })
341
+ return landed.length === 0 ? undefined : { tested: tip.hash, notes, edits }
342
+ }
343
+
307
344
  const decide = (): Promise<Decision> =>
308
345
  scope("seam", async () => {
309
346
  const vetting: Vetting = { again: budget(), waived: false }
310
- let retry: t.NaiveRetry | undefined
347
+ let retry: t.NaiveRetry | t.NaiveTested | undefined
311
348
  for (;;) {
312
349
  await naivePass(retry)
313
350
  const tip = kept("naive")!
351
+ const verdict = await vetted(tip, vetting)
352
+ if (verdict === undefined && naiveTestOn()) {
353
+ const sentBack = await tried(tip)
354
+ if (sentBack !== undefined) {
355
+ // A person's feedback is no failed attempt: the next pass gets a full retry budget.
356
+ vetting.again = budget()
357
+ retry = sentBack
358
+ continue
359
+ }
360
+ }
314
361
  const outcome =
315
- (await vetted(tip, vetting)) ??
316
- (flagModeOn() ? await ruled(tip, vetting.again) : { reference: tip.hash, tip })
362
+ verdict ?? (flagged() ? await ruled(tip, vetting.again) : { reference: tip.hash, tip })
317
363
  if (!("kind" in outcome)) return outcome
318
364
  retry = outcome.kind === "retry" ? outcome.retry : undefined
319
365
  }
@@ -11,9 +11,7 @@ Feature: the review learns standards from the human's answers
11
11
 
12
12
  Background:
13
13
  Given the shipped workflows, as gtd install scaffolds them
14
- And an environment variable "GTD_PROTOTYPEROUNDS" set to "0"
15
14
  And an environment variable "GTD_QUALITYREVIEWS" set to ""
16
- And an environment variable "GTD_FLAGMODE" set to "false"
17
15
  And a file "src/calc.ts" with:
18
16
  """
19
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", () => {
@@ -24,11 +24,10 @@ export const ShippedOptions = Schema.Struct({
24
24
  seamAllow: setting,
25
25
  qualityReviews: setting,
26
26
  refactorQualityReviews: setting,
27
- prototypeRounds: setting,
28
27
  testCommand: setting,
29
28
  plannerModel: setting,
30
29
  coderModel: setting,
31
- flagMode: Schema.optionalWith(Schema.Boolean, { exact: true }),
30
+ naiveTest: Schema.optionalWith(Schema.Boolean, { exact: true }),
32
31
  })
33
32
 
34
33
  export type ShippedOptions = Schema.Schema.Encoded<typeof ShippedOptions>
@@ -45,11 +44,11 @@ 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!
47
+ /** The `naiveTest` setting, pinned at process start. Anything but true/false fails rather than guess. */
48
+ export const naiveTestOn = (): boolean => {
49
+ const value = vars.naiveTest ?? defaults.naiveTest!
51
50
  if (value !== "true" && value !== "false")
52
- refuse(`gtd: flagMode must be "true" or "false", got ${JSON.stringify(value)}`)
51
+ refuse(`gtd: naiveTest must be "true" or "false", got ${JSON.stringify(value)}`)
53
52
  return value === "true"
54
53
  }
55
54
 
@@ -88,12 +88,6 @@ things when redirected — a failing check, or reviewer feedback. Stay
88
88
  strictly inside that task; never touch code outside it. Treat feedback as the
89
89
  diagnosis to act on; discard it, with reason, only when simply wrong.`
90
90
 
91
- export const testerPersona = `You are the hands-on tester in gtd's build pipeline: use what was just
92
- built the way a person would — run the real program, open the real
93
- page, call the real endpoint — and report exactly what you did and
94
- what you saw. Never fix anything yourself, and never pass a criterion
95
- you did not watch hold.`
96
-
97
91
  export const finisherPersona = `You are the closing identity in gtd's build pipeline — the last coder
98
92
  before it closes. Fix a late-breaking failing check after sign-off,
99
93
  focused and minimal. Every turn lands its own commit — nothing here
@@ -29,10 +29,6 @@ describe("the bundled workflow's skills export", () => {
29
29
  "implement",
30
30
  "implement.fix.suite",
31
31
  "implement.health",
32
- "implement.prototype",
33
- "implement.prototype.fix",
34
- "implement.prototype.fix.suite",
35
- "implement.prototype.health",
36
32
  "build.quality.correctness",
37
33
  "build.quality.owasp-security",
38
34
  "build.quality.ponytail-review",
@@ -33,11 +33,6 @@ export const skills = (
33
33
  seam: ["incremental-implementation"],
34
34
  implement: ["test-driven-development", "incremental-implementation"],
35
35
  "implement.fix.suite": ["debugging-and-error-recovery"],
36
- // The hands-on tester runs the program, never the suite: no TDD.
37
- "implement.prototype": [],
38
- "implement.prototype.fix": ["test-driven-development", "incremental-implementation"],
39
- "implement.prototype.fix.suite": ["debugging-and-error-recovery"],
40
- "implement.prototype.health": ["debugging-and-error-recovery"],
41
36
  // `health` is shared code reached from both `implement` and `build`.
42
37
  "implement.health": ["debugging-and-error-recovery"],
43
38
  ...lensScopes(
@@ -14,7 +14,7 @@ export const REVIEW = ".gtd/REVIEW.md"
14
14
  export const REFACTOR = ".gtd/REFACTOR.md"
15
15
  export const REFACTOR_NOTE = ".gtd/REFACTOR-NOTE.md"
16
16
  export const QUALITY = ".gtd/QUALITY.md"
17
- export const PROTOTYPE = ".gtd/PROTOTYPE.md"
17
+ export const NAIVE_TEST = ".gtd/NAIVE-TEST.md"
18
18
 
19
19
  export const planner = (): string => env.plannerModel ?? ""
20
20
  export const coder = (): string => env.coderModel ?? ""