@pmelab/gtd 58.2.0 → 59.0.1

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 (150) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +120 -395
  3. package/dist/chunks/{Schema-B3o6eRNU.mjs → testkit-07Z_ENch.mjs} +13503 -3460
  4. package/dist/chunks/{wire-C12dahrH.mjs → wire-BTUhRGXj.mjs} +1 -1
  5. package/dist/flows.mjs +1 -1
  6. package/dist/gtd.bundle.mjs +484 -497
  7. package/dist/testkit/gherkin.mjs +145 -65
  8. package/dist/testkit.mjs +1 -1
  9. package/dist/wire.mjs +1 -1
  10. package/package.json +3 -42
  11. package/skills/authoring/SKILL.md +34 -27
  12. package/src/cli/Cli.test.ts +148 -71
  13. package/src/cli/Cli.ts +384 -463
  14. package/src/cli/Help.test.ts +47 -0
  15. package/src/cli/Help.ts +70 -0
  16. package/src/cli/Program.test.ts +127 -33
  17. package/src/cli/Program.ts +70 -25
  18. package/src/edge/Edge.ts +4 -4
  19. package/src/emit/Emit.test.ts +1 -1
  20. package/src/emit/Emit.ts +1 -1
  21. package/src/flags/index.test.ts +0 -13
  22. package/src/flags/index.ts +1 -3
  23. package/src/flows/factory.ts +3 -1
  24. package/src/gherkin/index.ts +135 -71
  25. package/src/gherkin/messages.test.ts +90 -0
  26. package/src/gherkin/messages.ts +84 -0
  27. package/src/install/Install.test.ts +14 -11
  28. package/src/install/Install.ts +38 -15
  29. package/src/install/index.ts +1 -2
  30. package/src/replay/Replay.test.ts +1 -1
  31. package/src/replay/index.ts +1 -1
  32. package/src/step/planStart.ts +6 -1
  33. package/src/testkit/TestRepo.test.ts +47 -2
  34. package/src/testkit/TestRepo.ts +51 -6
  35. package/src/twins/EmittedScriptRecognizer.ts +5 -8
  36. package/src/twins/FlowScripts.ts +1 -1
  37. package/src/wire/Land.ts +1 -1
  38. package/src/workflow/index.ts +1 -0
  39. package/src/workflow/load.test.ts +138 -113
  40. package/src/workflow/load.ts +7 -7
  41. package/src/workflow/skills.test.ts +5 -5
  42. package/src/workflow/skills.ts +1 -1
  43. package/src/workflow/vars.test.ts +30 -3
  44. package/src/workflow/vars.ts +34 -2
  45. package/src/workflow-kit/index.ts +1 -1
  46. package/dist/chunks/reserved-lMYCaapY.mjs +0 -1990
  47. package/dist/chunks/review-Bfaror38.mjs +0 -427
  48. package/dist/chunks/tail-Bd7L940p.mjs +0 -355
  49. package/dist/chunks/testkit-JDtpNKyi.mjs +0 -10410
  50. package/dist/workflows/cleanup.mjs +0 -36
  51. package/dist/workflows/feature.mjs +0 -1018
  52. package/dist/workflows/fix.mjs +0 -41
  53. package/dist/workflows/review.mjs +0 -45
  54. package/dist/workflows/rollout.mjs +0 -48
  55. package/dist/workflows.mjs +0 -40651
  56. package/src/install/ConfigScaffold.ts +0 -22
  57. package/src/workflows/cleanup/cleanup-path.feature +0 -94
  58. package/src/workflows/cleanup/cleanup.feature +0 -315
  59. package/src/workflows/cleanup/cleanup.test.ts +0 -276
  60. package/src/workflows/cleanup/cleanup.ts +0 -52
  61. package/src/workflows/cleanup/index.ts +0 -6
  62. package/src/workflows/feature/acceptance-criteria-tier.feature +0 -25
  63. package/src/workflows/feature/acceptance-criteria.feature +0 -389
  64. package/src/workflows/feature/criteria-coverage.feature +0 -102
  65. package/src/workflows/feature/criteria.test.ts +0 -147
  66. package/src/workflows/feature/criteria.ts +0 -267
  67. package/src/workflows/feature/fast-path.feature +0 -93
  68. package/src/workflows/feature/feature.test.ts +0 -338
  69. package/src/workflows/feature/feature.ts +0 -156
  70. package/src/workflows/feature/green.ts +0 -19
  71. package/src/workflows/feature/index.ts +0 -18
  72. package/src/workflows/feature/lens-models.feature +0 -69
  73. package/src/workflows/feature/lens-review.feature +0 -566
  74. package/src/workflows/feature/naive-acceptance.feature +0 -147
  75. package/src/workflows/feature/naive-test.feature +0 -263
  76. package/src/workflows/feature/one-step-land.feature +0 -93
  77. package/src/workflows/feature/planning.ts +0 -41
  78. package/src/workflows/feature/refactor-absorbs.feature +0 -131
  79. package/src/workflows/feature/refactor-first.feature +0 -158
  80. package/src/workflows/feature/refactor-lens-models.feature +0 -79
  81. package/src/workflows/feature/refactor-rebuild.feature +0 -273
  82. package/src/workflows/feature/refactor-review.feature +0 -1528
  83. package/src/workflows/feature/refactor.test.ts +0 -1220
  84. package/src/workflows/feature/refactor.ts +0 -368
  85. package/src/workflows/feature/release-flag.feature +0 -167
  86. package/src/workflows/feature/release-flag.test.ts +0 -49
  87. package/src/workflows/feature/release-flag.ts +0 -46
  88. package/src/workflows/feature/review-gate.feature +0 -241
  89. package/src/workflows/feature/reviewer-walkthrough.feature +0 -142
  90. package/src/workflows/feature/risk-tier.feature +0 -351
  91. package/src/workflows/feature/seam-rejection.feature +0 -146
  92. package/src/workflows/feature/seam.feature +0 -297
  93. package/src/workflows/feature/seam.test.ts +0 -527
  94. package/src/workflows/feature/seam.ts +0 -426
  95. package/src/workflows/feature/standards.feature +0 -354
  96. package/src/workflows/feature/steps.test.ts +0 -65
  97. package/src/workflows/feature/steps.ts +0 -45
  98. package/src/workflows/feature/tier-pin.feature +0 -66
  99. package/src/workflows/feature/tier.test.ts +0 -166
  100. package/src/workflows/feature/tier.ts +0 -56
  101. package/src/workflows/fix/fix-lens-origin.feature +0 -114
  102. package/src/workflows/fix/fix-quality.feature +0 -212
  103. package/src/workflows/fix/fix.feature +0 -79
  104. package/src/workflows/fix/fix.test.ts +0 -74
  105. package/src/workflows/fix/fix.ts +0 -52
  106. package/src/workflows/fix/index.ts +0 -6
  107. package/src/workflows/index.ts +0 -47
  108. package/src/workflows/review/index.ts +0 -6
  109. package/src/workflows/review/review-door.feature +0 -199
  110. package/src/workflows/review/review.test.ts +0 -27
  111. package/src/workflows/review/review.ts +0 -60
  112. package/src/workflows/rollout/index.ts +0 -6
  113. package/src/workflows/rollout/rollout.feature +0 -510
  114. package/src/workflows/rollout/rollout.test.ts +0 -263
  115. package/src/workflows/rollout/rollout.ts +0 -82
  116. package/src/workflows/shared/__fixtures__/feature-collecting-prompt.txt +0 -98
  117. package/src/workflows/shared/access.test.ts +0 -62
  118. package/src/workflows/shared/access.ts +0 -24
  119. package/src/workflows/shared/config.fixture.ts +0 -12
  120. package/src/workflows/shared/fences.ts +0 -24
  121. package/src/workflows/shared/health.test.ts +0 -148
  122. package/src/workflows/shared/health.ts +0 -191
  123. package/src/workflows/shared/learn.test.ts +0 -140
  124. package/src/workflows/shared/learn.ts +0 -201
  125. package/src/workflows/shared/lenses.test.ts +0 -39
  126. package/src/workflows/shared/lenses.ts +0 -294
  127. package/src/workflows/shared/options.test.ts +0 -157
  128. package/src/workflows/shared/options.ts +0 -92
  129. package/src/workflows/shared/progress.feature +0 -124
  130. package/src/workflows/shared/prose.ts +0 -263
  131. package/src/workflows/shared/recheck.feature +0 -60
  132. package/src/workflows/shared/registry.test.ts +0 -345
  133. package/src/workflows/shared/registry.ts +0 -183
  134. package/src/workflows/shared/reserved.ts +0 -11
  135. package/src/workflows/shared/review.test.ts +0 -1062
  136. package/src/workflows/shared/review.ts +0 -446
  137. package/src/workflows/shared/skills.test.ts +0 -121
  138. package/src/workflows/shared/skills.ts +0 -49
  139. package/src/workflows/shared/standards.test.ts +0 -90
  140. package/src/workflows/shared/standards.ts +0 -156
  141. package/src/workflows/shared/steps.test.ts +0 -219
  142. package/src/workflows/shared/steps.ts +0 -201
  143. package/src/workflows/shared/tail.test.ts +0 -454
  144. package/src/workflows/shared/tail.ts +0 -264
  145. package/src/workflows/shared/text.fixture.ts +0 -128
  146. package/src/workflows/shared/text.test.ts +0 -1430
  147. package/src/workflows/shared/text.ts +0 -1700
  148. package/src/workflows/shared/threads.test.ts +0 -129
  149. package/src/workflows/shared/vars.ts +0 -47
  150. package/src/workflows/shared/workflows.steps.ts +0 -54
@@ -1,74 +0,0 @@
1
- import { describe, expect, it } from "vitest"
2
- import { runsItsScript, testRepo } from "../../testkit/index.js"
3
- import { CONFIG_SCAFFOLD } from "../../install/index.js"
4
- import { reviewDoc } from "../shared/config.fixture.js"
5
-
6
- const REVIEW = ".gtd/REVIEW.md"
7
- const ORIG = ".gtd/REVIEW.orig.md"
8
- const CALC = "export const add = (a: number, b: number) => a + b\n"
9
-
10
- describe("the fix workflow", () => {
11
- it("with no handed-off review, repairs a red baseline and hands it to review", async () => {
12
- const repo = testRepo({ workflow: CONFIG_SCAFFOLD, env: { GTD_QUALITYREVIEWS: "" } })
13
- await repo.start("fix")
14
- const rest = await repo.drive({
15
- "fix-precheck": (_, r) => r.write(".gtd/FEEDBACK.md", "1 test failing\n"),
16
- "build.fix": (beat, r) => {
17
- expect(beat.content).toContain(".gtd/FEEDBACK.md")
18
- r.remove(".gtd/FEEDBACK.md")
19
- r.write("src/repair.ts", "export const repaired = true\n")
20
- },
21
- "build.health.check": () => undefined,
22
- })
23
- expect(rest).toMatchObject({ state: "build.review.reviewing", actor: "agent" })
24
- expect(repo.subjects().slice(1)).toEqual([
25
- "gtd(human): fix-precheck",
26
- "gtd(check): fix-precheck → build.fix",
27
- "gtd(agent): build.fix → build.health.check",
28
- "gtd(check): build.health.check → build.review.reviewing",
29
- ])
30
- })
31
-
32
- it("with a green baseline, has nothing to fix", async () => {
33
- const repo = testRepo({ workflow: CONFIG_SCAFFOLD, env: { GTD_QUALITYREVIEWS: "" } })
34
- await repo.start("fix")
35
- const rest = await repo.drive({ "fix-precheck": () => undefined })
36
- expect(rest.state).toBe("idle")
37
- expect(repo.subjects().at(-1)).toBe("gtd(check): fix-precheck → idle")
38
- })
39
-
40
- it("fixes a handed-off review's notes and risks in one turn, removes both review files, then checks health", async () => {
41
- const repo = testRepo({
42
- workflow: CONFIG_SCAFFOLD,
43
- env: { GTD_QUALITYREVIEWS: "" },
44
- files: {
45
- "src/calc.ts": CALC,
46
- [REVIEW]: reviewDoc("add — rename it to sum", "Risk: drops the carry"),
47
- [ORIG]: reviewDoc("add", "Risk: drops the carry"),
48
- },
49
- })
50
- await repo.door("fix")
51
- const rest = await repo.drive({
52
- "notes.fixing": (beat, r) => {
53
- expect(beat.content).toContain("rename it to sum")
54
- expect(beat.content).toContain("Risk: drops the carry")
55
- r.write("src/calc.ts", CALC.replace("add", "sum"))
56
- },
57
- "notes.closing": runsItsScript,
58
- })
59
- expect(rest.state).toBe("build.health.check")
60
- expect(repo.subjects().slice(1)).toEqual([
61
- "gtd(human): notes.fixing",
62
- "gtd(agent): notes.fixing → notes.closing",
63
- "gtd(check): notes.closing → build.health.check",
64
- ])
65
- expect(repo.read("src/calc.ts")).toContain("sum")
66
- expect(repo.read(REVIEW)).toBeUndefined()
67
- expect(repo.read(ORIG)).toBeUndefined()
68
- })
69
-
70
- it("refuses a review with no reviewer's copy, so no note is deleted unread", async () => {
71
- const repo = testRepo({ workflow: CONFIG_SCAFFOLD, files: { [REVIEW]: reviewDoc("add") } })
72
- await expect(repo.door("fix")).rejects.toThrow(/not handed off/)
73
- })
74
- })
@@ -1,52 +0,0 @@
1
- import { read, refuse, removeScript, run, scope, start } from "../../flows/index.js"
2
- import { reviewNotes, reviewRisks } from "../../steering/index.js"
3
- import { baseline } from "../shared/health.js"
4
- import { shipped } from "../shared/options.js"
5
- import { coder, REVIEW } from "../shared/steps.js"
6
- import { handOffTail, REVIEW_ORIG } from "../shared/tail.js"
7
- import * as t from "../shared/text.js"
8
-
9
- /**
10
- * Fix every note a human left in a handed-off `.gtd/REVIEW.md` and every
11
- * risk its reviewer marked, in one turn; the review files go once it is done.
12
- */
13
- export const fixReviewNotes = (): Promise<void> =>
14
- scope("notes", async () => {
15
- const record = read(REVIEW) ?? ""
16
- const orig = read(REVIEW_ORIG)
17
- // Without the reviewer's copy no note can be told from the review's own text,
18
- // and closing would delete notes never read — also keeps a stray review out.
19
- if (orig === undefined) {
20
- return refuse(
21
- `gtd: ${REVIEW} has no ${REVIEW_ORIG}, so it was not handed off by a review — remove it, or restore ${REVIEW_ORIG}.`,
22
- )
23
- }
24
- const notes = reviewNotes(orig, record)
25
- await t.agentWithSkills("fixing", t.fixReviewNotesPrompt(notes, reviewRisks(record)), {
26
- label: "Fixing your review notes",
27
- file: REVIEW,
28
- model: coder(),
29
- system: t.finisherSystem(),
30
- // Praise and questions are answered without an edit; the hand-off review that follows catches an unaddressed note.
31
- allowEmpty: true,
32
- })
33
- await run("closing", removeScript([REVIEW, REVIEW_ORIG]), { label: "Closing the review" })
34
- })
35
-
36
- /**
37
- * Fix what a review handed on, else repair a red baseline — then the
38
- * hand-off review of the result. A note there is handed to the next `fix`.
39
- */
40
- export const fixFlow = async (): Promise<void> => {
41
- if (read(REVIEW) !== undefined) {
42
- await fixReviewNotes()
43
- return handOffTail(false, start())
44
- }
45
- if (read(REVIEW_ORIG) !== undefined) {
46
- return refuse(`gtd: ${REVIEW_ORIG} has no ${REVIEW} — remove it, or restore ${REVIEW}.`)
47
- }
48
- if (await baseline("fix-precheck")) return
49
- await handOffTail(true, start())
50
- }
51
-
52
- export const fix = shipped(fixFlow, { fix: {} })
@@ -1,6 +0,0 @@
1
- import { fix } from "./fix.js"
2
-
3
- export { fix, fixFlow, fixReviewNotes } from "./fix.js"
4
- export * from "../shared/reserved.js"
5
-
6
- export default fix
@@ -1,47 +0,0 @@
1
- // The `@pmelab/gtd/workflows` entry, built on its own: core never imports it,
2
- // a repository's `gtd.config.ts` does.
3
- import type { Doors } from "../flows/index.js"
4
- import { feature } from "./feature/feature.js"
5
-
6
- export { threads, type ThreadInfo } from "../flows/index.js"
7
- export * from "./shared/reserved.js"
8
- export { FlagOptions, ShippedOptions } from "./shared/options.js"
9
- export { agentWithSkills, type ReviewRange } from "./shared/text.js"
10
- export * from "./shared/steps.js"
11
- export * from "./shared/health.js"
12
- export {
13
- closeReview,
14
- handOffReview,
15
- handOffTail,
16
- qualityLap,
17
- REVIEW_ORIG,
18
- tail,
19
- type TailOptions,
20
- } from "./shared/tail.js"
21
- export { type Finding, type LensReport } from "./shared/lenses.js"
22
- export { lensEntriesOf, lensesOf, lensOf } from "../workflow-kit/index.js"
23
- export * from "./feature/steps.js"
24
- export * from "./feature/criteria.js"
25
- export * from "./feature/planning.js"
26
- export * from "./feature/refactor.js"
27
- export * from "./feature/seam.js"
28
- export {
29
- buildTail,
30
- type BuildTailOptions,
31
- type FeedbackTarget,
32
- type ReviewOutcome,
33
- } from "./shared/review.js"
34
- export * from "./feature/feature.js"
35
- export { base, review, reviewFlow } from "./review/review.js"
36
- export { fix, fixFlow, fixReviewNotes } from "./fix/fix.js"
37
- export { rollout, rolloutFlow } from "./rollout/rollout.js"
38
- export { cleanup, cleanupFlow } from "./cleanup/cleanup.js"
39
-
40
- export default feature
41
-
42
- /**
43
- * @deprecated Empty: each shipped workflow builds its own doors, so a config
44
- * gets `fix` and `review [base]` by exporting `fix` and `review`, and
45
- * `rollout <flag>` and `cleanup <flag>` by exporting `rollout` and `cleanup`.
46
- */
47
- export const doors: Doors = {}
@@ -1,6 +0,0 @@
1
- import { review } from "./review.js"
2
-
3
- export { review, reviewFlow } from "./review.js"
4
- export * from "../shared/reserved.js"
5
-
6
- export default review
@@ -1,199 +0,0 @@
1
- @inmem
2
- Feature: the review door fixes its notes in the same process
3
-
4
- `gtd door review` runs the feature's review over everything since its base:
5
- the triaged quality lap and its finding gate, then a reviewer's walkthrough
6
- of the change with the lap's summary, where the human's notes are fixed in
7
- place under the health loop before the review comes back. A sign-off ends
8
- the process; nothing is left for `fix`.
9
-
10
- Background:
11
- Given the shipped workflows, as gtd install scaffolds them
12
- And an environment variable "GTD_QUALITYREVIEWS" set to "correctness"
13
- And a file "src/calc.ts" with:
14
- """
15
- export const add = (a: number, b: number) => a + b
16
- """
17
- And the working tree is committed as "feat: add calculator"
18
- And step "review-gate.check" changes nothing
19
- And step "build.health.check" changes nothing
20
- And step "build.quality.correctness.reviewing" writes ".gtd/QUALITY.md" with:
21
- """
22
- # Quality
23
-
24
- ## add has no test
25
-
26
- Add a test for add.
27
- """
28
- And judge step "build.quality.triage" answers:
29
- """
30
- [{"id":"Q1","answer":"design","p":0.9}]
31
- """
32
- And step "build.quality.gating" runs its script
33
- And step "build.quality.gate" changes nothing
34
- And step "build.quality.closing" runs its script
35
- And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
36
- """
37
- # Review: abc1234
38
-
39
- <!-- base: 0000000 -->
40
-
41
- ## Calculator
42
-
43
- - [ ] ./src/calc.ts#1-1 — add(a, b)
44
- """
45
- And step "build.review.carrying" runs its script
46
- And step "build.review.closing" runs its script
47
-
48
- Scenario: the review is a walkthrough, its summary listing what the finding gate dropped
49
- When gtd opens door "review" with "HEAD~1"
50
- And the process is driven
51
- Then the process rests at "build.review.await-review"
52
- And the commit subjects end with:
53
- """
54
- gtd(human): review-gate.check
55
- gtd(check): review-gate.check → build.quality.correctness.reviewing
56
- gtd(agent): build.quality.correctness.reviewing → build.quality.triage
57
- gtd(judge): build.quality.triage → build.quality.gating
58
- gtd(check): build.quality.gating → build.quality.gate
59
- gtd(human): build.quality.gate → build.quality.closing
60
- gtd(check): build.quality.closing → build.review.reviewing
61
- gtd(agent): build.review.reviewing → build.review.carrying
62
- gtd(check): build.review.carrying → build.review.await-review
63
- """
64
- And ".gtd/REVIEW.md" contains:
65
- """
66
- ## Calculator
67
-
68
- - [ ] ./src/calc.ts#1-1 — add(a, b)
69
-
70
- ## Quality lap
71
- """
72
- And ".gtd/REVIEW.md" contains:
73
- """
74
- Dropped at the finding gate:
75
-
76
- - add has no test (lens correctness)
77
- """
78
- And ".gtd/REVIEW.md" does not contain "Tick exactly one:"
79
- When gtd lands
80
- And the process is driven
81
- Then the process rests at "idle"
82
- And ".gtd/REVIEW.md" does not exist
83
- And ".gtd/REVIEW.orig.md" does not exist
84
-
85
- Scenario: a nit note is fixed in place, then reviewed again
86
- Given judge step "build.review.triage" answers:
87
- """
88
- [{"id":"note-1","answer":"nit","p":0.9}]
89
- """
90
- And step "build.review.fix.nits.fixing" writes "src/calc.test.ts" with:
91
- """
92
- it("adds", () => {})
93
- """
94
- When gtd opens door "review" with "HEAD~1"
95
- And the process is driven
96
- And the text "— add(a, b)" in ".gtd/REVIEW.md" is replaced with "— add(a, b) — add a test for it"
97
- And gtd lands
98
- And the process is driven
99
- Then the process rests at "build.review.await-review"
100
- And the commit subjects end with:
101
- """
102
- gtd(human): build.review.await-review → build.review.triage
103
- gtd(judge): build.review.triage → build.review.fix.nits.fixing
104
- gtd(agent): build.review.fix.nits.fixing → build.health.check
105
- gtd(check): build.health.check → build.review.closing
106
- gtd(check): build.review.closing → build.review.reviewing
107
- gtd(agent): build.review.reviewing → build.review.carrying
108
- gtd(check): build.review.carrying → build.review.await-review
109
- """
110
- And "src/calc.test.ts" contains "adds"
111
-
112
- Scenario: a note asking for a change is fixed in place, then reviewed again
113
- Given judge step "build.review.triage" answers:
114
- """
115
- [{"id":"note-1","answer":"edit","p":0.9}]
116
- """
117
- And step "build.review.collecting" writes ".gtd/REQUIREMENTS.md" with:
118
- """
119
- # Requirements
120
-
121
- ## Test add with negatives
122
-
123
- add(-1, -2) returns -3.
124
- """
125
- And step "feedback.fixing" is prompted with ".gtd/REQUIREMENTS.md"
126
- And step "feedback.fixing" writes "src/calc.test.ts" with:
127
- """
128
- it("adds negatives", () => {})
129
- """
130
- And step "re-unwind" runs its script
131
- And step "feedback.closing" runs its script
132
- When gtd opens door "review" with "HEAD~1"
133
- And the process is driven
134
- And the text "— add(a, b)" in ".gtd/REVIEW.md" is replaced with "— add(a, b) — test negatives too"
135
- And gtd lands
136
- And the process is driven
137
- Then the process rests at "build.review.await-review"
138
- And the commit subjects end with:
139
- """
140
- gtd(human): build.review.await-review → build.review.triage
141
- gtd(judge): build.review.triage → build.review.closing
142
- gtd(check): build.review.closing → build.review.collecting
143
- gtd(agent): build.review.collecting → re-unwind
144
- gtd(check): re-unwind → feedback.fixing
145
- gtd(agent): feedback.fixing → feedback.closing
146
- gtd(check): feedback.closing → build.health.check
147
- gtd(check): build.health.check → build.quality.correctness.reviewing
148
- gtd(agent): build.quality.correctness.reviewing → build.quality.triage
149
- gtd(judge): build.quality.triage → build.quality.gating
150
- gtd(check): build.quality.gating → build.quality.gate
151
- gtd(human): build.quality.gate → build.quality.closing
152
- gtd(check): build.quality.closing → build.review.reviewing
153
- gtd(agent): build.review.reviewing → build.review.carrying
154
- gtd(check): build.review.carrying → build.review.await-review
155
- """
156
- And ".gtd/REQUIREMENTS.md" does not exist
157
- And "src/calc.test.ts" contains "adds negatives"
158
- And ".gtd/REVIEW.md" does not contain "test negatives too"
159
-
160
- Scenario: a leftover requirements file is refused before any step
161
- Given a file ".gtd/REQUIREMENTS.md" with:
162
- """
163
- # Requirements
164
- """
165
- And the working tree is committed as "chore: leftover requirements"
166
- Then opening door "review" is refused with ".gtd/REQUIREMENTS.md is left over"
167
-
168
- Scenario: a note added with its own boxes is fixed as ticked, never judged
169
- Given step "build.review.fix.nits.fixing" writes "src/calc.ts" with:
170
- """
171
- export const add = (a: number, b: number): number => a + b
172
- """
173
- When gtd opens door "review" with "HEAD~1"
174
- And the process is driven
175
- And ".gtd/REVIEW.md" gets appended:
176
- """
177
- ## src/calc.ts:1 (thread PRRT_1)
178
-
179
- > Annotate the return type.
180
-
181
- Tick exactly one:
182
-
183
- - [x] fix as suggested
184
- - [ ] won't fix
185
- - [ ] _your answer_
186
- """
187
- And gtd lands
188
- And the process is driven
189
- Then the process rests at "build.review.await-review"
190
- And the commit subjects end with:
191
- """
192
- gtd(human): build.review.await-review → build.review.fix.nits.fixing
193
- gtd(agent): build.review.fix.nits.fixing → build.health.check
194
- gtd(check): build.health.check → build.review.closing
195
- gtd(check): build.review.closing → build.review.reviewing
196
- gtd(agent): build.review.reviewing → build.review.carrying
197
- gtd(check): build.review.carrying → build.review.await-review
198
- """
199
- And "src/calc.ts" contains "): number =>"
@@ -1,27 +0,0 @@
1
- import { describe, expect, it } from "vitest"
2
- import { testRepo } from "../../testkit/index.js"
3
- import { CONFIG_SCAFFOLD } from "../../install/index.js"
4
- import { reviewDoc } from "../shared/config.fixture.js"
5
- import { base } from "./review.js"
6
-
7
- const REVIEW = ".gtd/REVIEW.md"
8
- const ORIG = ".gtd/REVIEW.orig.md"
9
-
10
- describe("the review workflow", () => {
11
- it("refuses a pending hand-off before requesting any step, the gate's check included", async () => {
12
- const repo = testRepo({
13
- workflow: CONFIG_SCAFFOLD,
14
- files: { [REVIEW]: reviewDoc("note"), [ORIG]: reviewDoc("orig") },
15
- })
16
- await expect(repo.door("review")).rejects.toThrow(".gtd/REVIEW.orig.md is a pending hand-off")
17
- expect(repo.subjects()).toEqual(["chore: initial commit"])
18
- })
19
- })
20
-
21
- describe("the deprecated shipped base", () => {
22
- it("answers for a workflow named review only, mapping reviewBase", () => {
23
- expect(base("review", {})).toBe("")
24
- expect(base("review", { reviewBase: "v1" })).toBe("v1")
25
- expect(base("audit", { reviewBase: "v1" })).toBeUndefined()
26
- })
27
- })
@@ -1,60 +0,0 @@
1
- import { read, refuse, removeScript, run, start, type WorkflowBase } from "../../flows/index.js"
2
- import { buildTail, reUnwind } from "../shared/review.js"
3
- import { gate } from "../shared/health.js"
4
- import { shipped } from "../shared/options.js"
5
- import { coder, REQUIREMENTS } from "../shared/steps.js"
6
- import { refusePendingHandOff } from "../shared/tail.js"
7
- import * as t from "../shared/text.js"
8
-
9
- const fixFeedback = async (): Promise<void> => {
10
- await t.agentWithSkills("feedback.fixing", t.reviewFeedbackFixPrompt(), {
11
- label: "Fixing your review feedback",
12
- file: REQUIREMENTS,
13
- model: coder(),
14
- system: t.finisherSystem(),
15
- // An earlier nit fix may already have built the concern.
16
- allowEmpty: true,
17
- })
18
- await run("feedback.closing", removeScript([REQUIREMENTS]), { label: "Closing the feedback" })
19
- }
20
-
21
- /**
22
- * The feature's review of everything since `reviewBase` — a green gate, the
23
- * triaged quality lap, then the human review — with no design phase: a
24
- * review that asks for changes is fixed in place and reviewed again, until
25
- * sign-off.
26
- */
27
- export const reviewFlow = async (): Promise<void> => {
28
- refusePendingHandOff()
29
- // A review's feedback is whatever sits in this file, so a leftover one would be built, then deleted.
30
- if (read(REQUIREMENTS) !== undefined) {
31
- refuse(`gtd: ${REQUIREMENTS} is left over — finish or remove it before a review.`)
32
- }
33
- await gate("review-gate", t.reviewGateBlockedMessage())
34
- const base = start()
35
- // The gate just proved the tree green.
36
- let outcome = await buildTail(base, { green: true })
37
- while (outcome.verdict === "feedback") {
38
- await reUnwind(outcome)
39
- await fixFeedback()
40
- // The human reviews the whole change again; the lenses only what the fix changed.
41
- outcome = await buildTail(base, { lensBase: outcome.base })
42
- }
43
- }
44
-
45
- export const review = shipped(
46
- reviewFlow,
47
- {
48
- review: {
49
- args: [{ name: "base", optional: true }],
50
- // Annotated: inferred, the literal union only fits under exactOptionalPropertyTypes.
51
- vars: ({ base }): Readonly<Record<string, string>> =>
52
- base === undefined ? {} : { reviewBase: base },
53
- },
54
- },
55
- (_workflow, vars) => vars.reviewBase || "",
56
- )
57
-
58
- /** @deprecated The `review` factory carries its own base; kept so a hand-written `review` re-exporting it still maps `reviewBase`. */
59
- export const base: WorkflowBase = (workflow, vars) =>
60
- workflow === "review" ? (vars.reviewBase ?? "") : undefined
@@ -1,6 +0,0 @@
1
- import { rollout } from "./rollout.js"
2
-
3
- export { rollout, rolloutFlow } from "./rollout.js"
4
- export * from "../shared/reserved.js"
5
-
6
- export default rollout