@pmelab/gtd 23.2.0 → 24.1.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 (236) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +44 -11
  3. package/claude/hooks/access.ts +1 -1
  4. package/dist/chunks/Schema-Dh-mkwkq.mjs +25353 -0
  5. package/dist/chunks/reserved-ejn-LVYO.mjs +1388 -0
  6. package/dist/chunks/rolldown-runtime-8H4AJuhK.mjs +14 -0
  7. package/dist/chunks/steering-D-PvRC5W.mjs +16296 -0
  8. package/dist/chunks/wire-DA7q2ikx.mjs +473 -0
  9. package/dist/flows.mjs +525 -0
  10. package/dist/gtd.bundle.mjs +43830 -56044
  11. package/dist/steering.mjs +2 -0
  12. package/dist/testkit.mjs +13006 -0
  13. package/dist/wire.mjs +2 -0
  14. package/dist/workflow-kit.mjs +352 -0
  15. package/dist/workflows/feature.mjs +315 -0
  16. package/dist/workflows/fix.mjs +40 -0
  17. package/dist/workflows/review.mjs +25 -0
  18. package/dist/workflows.mjs +1953 -1724
  19. package/package.json +70 -8
  20. package/skills/authoring/SKILL.md +5 -4
  21. package/src/cli/Cli.test.ts +1329 -0
  22. package/src/cli/Cli.ts +1348 -0
  23. package/src/cli/Program.test.ts +2262 -0
  24. package/src/cli/Program.ts +1331 -0
  25. package/src/cli/ToolCommands.ts +43 -0
  26. package/src/cli/index.ts +14 -0
  27. package/src/edge/Edge.test.ts +1016 -0
  28. package/src/edge/Edge.ts +1225 -0
  29. package/src/edge/index.ts +16 -0
  30. package/src/emit/Emit.test.ts +425 -0
  31. package/src/emit/Emit.ts +139 -0
  32. package/src/emit/GitScript.test.ts +260 -0
  33. package/src/emit/GitScript.ts +131 -0
  34. package/src/emit/ModeContradiction.test.ts +120 -0
  35. package/src/emit/ModeContradiction.ts +66 -0
  36. package/src/emit/OutcomeScript.test.ts +238 -0
  37. package/src/emit/OutcomeScript.ts +73 -0
  38. package/src/emit/SteeringMode.test.ts +274 -0
  39. package/src/emit/SteeringMode.ts +256 -0
  40. package/src/emit/index.ts +40 -0
  41. package/src/flows/factory.ts +5 -1
  42. package/src/flows/runtime.ts +3 -0
  43. package/src/install/ConfigScaffold.ts +19 -0
  44. package/src/install/Install.test.ts +426 -0
  45. package/src/install/Install.ts +634 -0
  46. package/src/install/Version.ts +27 -0
  47. package/src/install/index.ts +3 -0
  48. package/src/platform/CommandRunner.ts +74 -0
  49. package/src/platform/Commentary.test.ts +126 -0
  50. package/src/platform/Commentary.ts +92 -0
  51. package/src/platform/Git.test.ts +50 -0
  52. package/src/platform/Git.ts +552 -0
  53. package/src/platform/Host.ts +42 -0
  54. package/src/platform/RetainedHistory.test.ts +65 -0
  55. package/src/platform/RetainedHistory.ts +34 -0
  56. package/src/platform/Workspace.test.ts +669 -0
  57. package/src/platform/Workspace.ts +631 -0
  58. package/src/platform/WorktreeState.test.ts +162 -0
  59. package/src/platform/WorktreeState.ts +50 -0
  60. package/src/platform/index.ts +22 -0
  61. package/src/replay/Access.test.ts +53 -0
  62. package/src/replay/Access.ts +46 -0
  63. package/src/replay/Format.test.ts +20 -0
  64. package/src/replay/Format.ts +14 -0
  65. package/src/replay/Replay.test.ts +887 -0
  66. package/src/replay/Replay.ts +669 -0
  67. package/src/replay/Trailers.test.ts +117 -0
  68. package/src/replay/Trailers.ts +213 -0
  69. package/src/replay/Tree.test.ts +25 -0
  70. package/src/replay/Tree.ts +45 -0
  71. package/src/replay/index.ts +11 -0
  72. package/src/steering/Blocks.fixture.ts +6 -0
  73. package/src/steering/Blocks.test.ts +167 -0
  74. package/src/steering/Blocks.ts +321 -0
  75. package/src/steering/CodeThreads.test.ts +127 -0
  76. package/src/steering/CodeThreads.ts +197 -0
  77. package/src/steering/Edits.test.ts +19 -0
  78. package/src/steering/Edits.ts +20 -0
  79. package/src/steering/Eol.test.ts +16 -0
  80. package/src/steering/Eol.ts +2 -0
  81. package/src/steering/FORMATS.md +58 -0
  82. package/src/steering/Footnotes.test.ts +977 -0
  83. package/src/steering/Footnotes.ts +661 -0
  84. package/src/steering/Inline.fixture.ts +7 -0
  85. package/src/steering/Inline.test.ts +227 -0
  86. package/src/steering/Inline.ts +195 -0
  87. package/src/steering/MarkdownTree.test.ts +247 -0
  88. package/src/steering/MarkdownTree.ts +260 -0
  89. package/src/steering/Outline.ts +19 -0
  90. package/src/steering/SteeringFormat.test.ts +91 -0
  91. package/src/steering/SteeringFormat.ts +267 -0
  92. package/src/steering/SteeringFormats.test.ts +163 -0
  93. package/src/steering/Threads.ts +163 -0
  94. package/src/steering/contract.test.ts +264 -0
  95. package/src/steering/fixtures/code-threads/cache.stripped.ts.txt +6 -0
  96. package/src/steering/fixtures/code-threads/cache.ts.md +15 -0
  97. package/src/steering/fixtures/free-form/notes.md +12 -0
  98. package/src/steering/fixtures/free-form/notes.read.json +42 -0
  99. package/src/steering/fixtures/qa/answered.md +23 -0
  100. package/src/steering/fixtures/qa/answered.read.json +119 -0
  101. package/src/steering/fixtures/qa/requirements.md +160 -0
  102. package/src/steering/fixtures/qa/requirements.read.json +475 -0
  103. package/src/steering/fixtures/review/review.md +76 -0
  104. package/src/steering/fixtures/review/review.read.json +237 -0
  105. package/src/steering/fixtures/review/ticked.md +23 -0
  106. package/src/steering/fixtures/review/ticked.read.json +99 -0
  107. package/src/steering/fixtures/review-notes/baseline.md +14 -0
  108. package/src/steering/fixtures/review-notes/noted.md +19 -0
  109. package/src/steering/freeform.test.ts +311 -0
  110. package/src/steering/freeform.ts +148 -0
  111. package/src/steering/index.test.ts +62 -0
  112. package/src/steering/index.ts +140 -0
  113. package/src/steering/qa.test.ts +2160 -0
  114. package/src/steering/qa.ts +1104 -0
  115. package/src/steering/review.test.ts +1829 -0
  116. package/src/steering/review.ts +1180 -0
  117. package/src/step/LandStep.ts +31 -0
  118. package/src/step/RepoSnapshot.ts +29 -0
  119. package/src/step/accessRefusal.test.ts +58 -0
  120. package/src/step/accessRefusal.ts +26 -0
  121. package/src/step/index.ts +5 -0
  122. package/src/step/planStart.test.ts +202 -0
  123. package/src/step/planStart.ts +167 -0
  124. package/src/step/planStep.test.ts +104 -0
  125. package/src/step/planStep.ts +89 -0
  126. package/src/step/snapshot.fixture.ts +28 -0
  127. package/src/testkit/EmittedScriptRecognizer.test.ts +639 -0
  128. package/src/testkit/EmittedScriptRecognizer.ts +642 -0
  129. package/src/testkit/FakeGitOperations.ts +95 -0
  130. package/src/testkit/GitTiers.test.ts +11 -0
  131. package/src/testkit/GitTiers.ts +1052 -0
  132. package/src/testkit/InMemRepo.ts +467 -0
  133. package/src/testkit/Layers.test.ts +58 -0
  134. package/src/testkit/Layers.ts +241 -0
  135. package/src/testkit/StrictGitOperations.ts +31 -0
  136. package/src/testkit/TestRepo.test.ts +119 -0
  137. package/src/testkit/TestRepo.ts +274 -0
  138. package/src/testkit/cliIo.ts +36 -0
  139. package/src/testkit/index.ts +9 -0
  140. package/src/testkit/steps.ts +177 -0
  141. package/src/testkit/testkit.feature +65 -0
  142. package/src/wire/BeatDocument.test.ts +419 -0
  143. package/src/wire/BeatDocument.ts +114 -0
  144. package/src/wire/BeatPlain.test.ts +113 -0
  145. package/src/wire/BeatPlain.ts +127 -0
  146. package/src/wire/BeatStatus.test.ts +59 -0
  147. package/src/wire/BeatStatus.ts +81 -0
  148. package/src/wire/Demand.test.ts +142 -0
  149. package/src/wire/Demand.ts +108 -0
  150. package/src/wire/ExitCodes.test.ts +21 -0
  151. package/src/wire/ExitCodes.ts +16 -0
  152. package/src/wire/Glob.ts +29 -0
  153. package/src/wire/Judge.ts +22 -0
  154. package/src/wire/Land.test.ts +120 -0
  155. package/src/wire/Land.ts +75 -0
  156. package/src/wire/Select.test.ts +148 -0
  157. package/src/wire/Select.ts +78 -0
  158. package/src/wire/Sessions.test.ts +59 -0
  159. package/src/wire/Sessions.ts +39 -0
  160. package/src/wire/WireSchema.test.ts +162 -0
  161. package/src/wire/constants.ts +22 -0
  162. package/src/wire/golden/1/beat-capture.json +34 -0
  163. package/src/wire/golden/1/beat-message.json +34 -0
  164. package/src/wire/golden/1/beat-prompt.json +44 -0
  165. package/src/wire/golden/1/beat-script.json +34 -0
  166. package/src/wire/golden/1/beat-stalled.json +34 -0
  167. package/src/wire/golden/1/judge.json +14 -0
  168. package/src/wire/golden/1/land-noop.json +10 -0
  169. package/src/wire/golden/1/land.json +10 -0
  170. package/src/wire/index.ts +12 -0
  171. package/src/wire/types.ts +46 -0
  172. package/src/workflow/Diagnostic.test.ts +93 -0
  173. package/src/workflow/Diagnostic.ts +54 -0
  174. package/src/workflow/SteeringFormats.test.ts +27 -0
  175. package/src/workflow/SteeringFormats.ts +6 -0
  176. package/src/workflow/Workflow.ts +77 -0
  177. package/src/workflow/compile.test.ts +53 -0
  178. package/src/workflow/compile.ts +56 -0
  179. package/src/workflow/discovery.ts +122 -0
  180. package/src/workflow/fingerprint.test.ts +52 -0
  181. package/src/workflow/fingerprint.ts +53 -0
  182. package/src/workflow/index.ts +33 -0
  183. package/src/workflow/load.test.ts +1017 -0
  184. package/src/workflow/load.ts +836 -0
  185. package/src/workflow/skills.test.ts +109 -0
  186. package/src/workflow/skills.ts +90 -0
  187. package/src/workflow/vars.ts +33 -0
  188. package/src/workflow-kit/health.test.ts +30 -0
  189. package/src/workflow-kit/health.ts +86 -0
  190. package/src/workflow-kit/index.ts +7 -0
  191. package/src/workflow-kit/quality.test.ts +17 -0
  192. package/src/workflow-kit/quality.ts +45 -0
  193. package/src/workflow-kit/review.ts +324 -0
  194. package/src/{workflows → workflow-kit}/scenarios.ts +21 -28
  195. package/src/workflows/feature/feature.test.ts +32 -0
  196. package/src/workflows/{bundled.ts → feature/feature.ts} +11 -63
  197. package/src/workflows/feature/index.ts +10 -0
  198. package/src/workflows/{packages.test.ts → feature/packages.test.ts} +2 -2
  199. package/src/workflows/{packages.ts → feature/packages.ts} +3 -3
  200. package/src/workflows/{planning.ts → feature/planning.ts} +2 -3
  201. package/src/workflows/{review.test.ts → feature/review.test.ts} +9 -44
  202. package/src/workflows/feature/review.ts +106 -0
  203. package/src/workflows/feature/steps.test.ts +82 -0
  204. package/src/workflows/feature/steps.ts +92 -0
  205. package/src/workflows/fix/fix.feature +80 -0
  206. package/src/workflows/fix/fix.test.ts +74 -0
  207. package/src/workflows/fix/fix.ts +52 -0
  208. package/src/workflows/fix/index.ts +6 -0
  209. package/src/workflows/index.ts +36 -2
  210. package/src/workflows/review/index.ts +6 -0
  211. package/src/workflows/review/review.test.ts +99 -0
  212. package/src/workflows/review/review.ts +33 -0
  213. package/src/workflows/{access.test.ts → shared/access.test.ts} +1 -1
  214. package/src/workflows/{access.ts → shared/access.ts} +3 -5
  215. package/src/workflows/shared/config.fixture.ts +12 -0
  216. package/src/workflows/{health.test.ts → shared/health.test.ts} +1 -1
  217. package/src/workflows/{health.ts → shared/health.ts} +38 -55
  218. package/src/workflows/{options.test.ts → shared/options.test.ts} +2 -2
  219. package/src/workflows/{options.ts → shared/options.ts} +14 -2
  220. package/src/workflows/shared/reserved.ts +11 -0
  221. package/src/workflows/{scenarios.test.ts → shared/scenarios.test.ts} +1 -1
  222. package/src/workflows/shared/scenarios.ts +35 -0
  223. package/src/workflows/{skills.test.ts → shared/skills.test.ts} +3 -1
  224. package/src/workflows/{skills.ts → shared/skills.ts} +7 -6
  225. package/src/workflows/{steps.test.ts → shared/steps.test.ts} +1 -66
  226. package/src/workflows/{steps.ts → shared/steps.ts} +8 -98
  227. package/src/workflows/shared/tail.test.ts +205 -0
  228. package/src/workflows/shared/tail.ts +135 -0
  229. package/src/workflows/{text.fixture.ts → shared/text.fixture.ts} +2 -2
  230. package/src/workflows/{text.ts → shared/text.ts} +48 -7
  231. package/src/workflows/{threads.test.ts → shared/threads.test.ts} +1 -1
  232. package/src/workflows/shared/workflows.steps.ts +7 -0
  233. package/src/workflows/review.ts +0 -362
  234. /package/src/workflows/{prose.ts → shared/prose.ts} +0 -0
  235. /package/src/workflows/{text.test.ts → shared/text.test.ts} +0 -0
  236. /package/src/workflows/{vars.ts → shared/vars.ts} +0 -0
@@ -0,0 +1,36 @@
1
+ import type { CliIo, ToolCommands } from "../cli/index.js"
2
+ import type { InMemRepo } from "./InMemRepo.js"
3
+ import { testLayers } from "./Layers.js"
4
+
5
+ export interface CapturedCliResult {
6
+ readonly stdout: string
7
+ readonly stderr: string
8
+ readonly exitCode: number
9
+ }
10
+
11
+ export const makeCapturingCliIo = (
12
+ repo: InMemRepo,
13
+ env: Readonly<Record<string, string | undefined>> = {},
14
+ tools: Partial<ToolCommands["Type"]> = {},
15
+ ): { readonly io: CliIo; readonly result: () => CapturedCliResult } => {
16
+ let stdout = ""
17
+ let stderr = ""
18
+ let exitCode = 0
19
+ const io: CliIo = {
20
+ stdout: (chunk) => {
21
+ stdout += chunk
22
+ },
23
+ stderr: (chunk) => {
24
+ stderr += chunk
25
+ },
26
+ exit: (code) => {
27
+ exitCode = code
28
+ },
29
+ // `narrate: io.stderr` is the SAME sink `stderr` above already captures
30
+ // errors into — narration lands in the exact same buffer, exactly like a
31
+ // real invocation's narration and remediation share one fd.
32
+ layers: (verbose) =>
33
+ testLayers(repo, { env, narrate: (line) => (stderr += line), verbose, tools }),
34
+ }
35
+ return { io, result: () => ({ stdout, stderr, exitCode }) }
36
+ }
@@ -0,0 +1,9 @@
1
+ export { runsItsScript, testRepo, type TestRepo } from "./TestRepo.js"
2
+ export { InMemRepo } from "./InMemRepo.js"
3
+ export { makeInMemoryWorkspaceOps, testLayers, type ScriptedCommand } from "./Layers.js"
4
+ export { makeCapturingCliIo } from "./cliIo.js"
5
+ export { applyEmittedScript } from "./EmittedScriptRecognizer.js"
6
+ export { gitTiers, runGitServiceContract, runGitScriptContract } from "./GitTiers.js"
7
+ export { strictGitOperations } from "./StrictGitOperations.js"
8
+ export { fakeGitOperations } from "./FakeGitOperations.js"
9
+ export type { TestkitWorld } from "./steps.js"
@@ -0,0 +1,177 @@
1
+ // Generic quickpickle steps over `testRepo`: they name no workflow, so any
2
+ // workflow's `.feature` can use them. A workflow's own steps live beside it.
3
+ import {
4
+ Given,
5
+ QuickPickleWorld,
6
+ Then,
7
+ When,
8
+ setWorldConstructor,
9
+ type InfoConstructor,
10
+ type QuickPickleWorldInterface,
11
+ } from "quickpickle"
12
+ import assert from "node:assert"
13
+ import type { TestContext } from "vitest"
14
+ import { runsItsScript, testRepo, type Actor, type TestRepo } from "./TestRepo.js"
15
+
16
+ export class TestkitWorld extends QuickPickleWorld {
17
+ // Re-declared: under `nodenext` a subclass loses the base's members from TS's view.
18
+ declare info: QuickPickleWorldInterface["info"]
19
+
20
+ constructor(context: TestContext, info: InfoConstructor) {
21
+ super(context, info)
22
+ }
23
+
24
+ private gtdConfig: string | undefined = undefined
25
+ private env: Record<string, string> = {}
26
+ /** Per step, what its scripted actor does, in the order the scenario declared it. */
27
+ actions = new Map<string, Actor[]>()
28
+ verdicts = new Map<string, string>()
29
+ private created: TestRepo | undefined = undefined
30
+
31
+ // The repository is created on first use and keeps the config and
32
+ // environment it was created with, so a later change to either is refused
33
+ // rather than silently ignored.
34
+ configure(config: string): void {
35
+ this.assertNotCreated("a gtd config")
36
+ this.gtdConfig = config
37
+ }
38
+
39
+ setEnv(name: string, value: string): void {
40
+ this.assertNotCreated(`the environment variable ${name}`)
41
+ this.env[name] = value
42
+ }
43
+
44
+ private assertNotCreated(what: string): void {
45
+ if (this.created !== undefined) {
46
+ throw new Error(`${what} must be set before any step that touches the repository`)
47
+ }
48
+ }
49
+
50
+ repo(): TestRepo {
51
+ if (this.gtdConfig === undefined)
52
+ throw new Error("no gtd config: add a `gtd config` step first")
53
+ return (this.created ??= testRepo({ workflow: this.gtdConfig, env: this.env }))
54
+ }
55
+
56
+ act(step: string, action: Actor): void {
57
+ this.actions.set(step, [...(this.actions.get(step) ?? []), action])
58
+ }
59
+
60
+ async drive(): Promise<void> {
61
+ const actors: Record<string, Actor> = {}
62
+ for (const [step, actions] of this.actions) {
63
+ actors[step] = async (beat, repo) => {
64
+ for (const action of actions) await action(beat, repo)
65
+ }
66
+ }
67
+ for (const [step, verdict] of this.verdicts) actors[step] = () => verdict
68
+ await this.repo().drive(actors)
69
+ }
70
+ }
71
+
72
+ setWorldConstructor(TestkitWorld)
73
+
74
+ const withNewline = (content: string): string => (content.endsWith("\n") ? content : `${content}\n`)
75
+
76
+ const args = (list: string): string[] => list.split(" ").filter((arg) => arg !== "")
77
+
78
+ Given("a gtd config with:", (world: TestkitWorld, source: string) => {
79
+ world.configure(withNewline(source))
80
+ })
81
+
82
+ Given(
83
+ "an environment variable {string} set to {string}",
84
+ (world: TestkitWorld, name: string, value: string) => {
85
+ world.setEnv(name, value)
86
+ },
87
+ )
88
+
89
+ Given("a file {string} with:", (world: TestkitWorld, path: string, content: string) => {
90
+ world.repo().write(path, withNewline(content))
91
+ })
92
+
93
+ Given("the working tree is committed as {string}", (world: TestkitWorld, message: string) => {
94
+ world.repo().commit(message)
95
+ })
96
+
97
+ Given("step {string} changes nothing", (world: TestkitWorld, step: string) => {
98
+ world.act(step, () => undefined)
99
+ })
100
+
101
+ Given(
102
+ "step {string} writes {string} with:",
103
+ (world: TestkitWorld, step: string, path: string, content: string) => {
104
+ world.act(step, (_, repo) => repo.write(path, withNewline(content)))
105
+ },
106
+ )
107
+
108
+ Given("step {string} runs its script", (world: TestkitWorld, step: string) => {
109
+ world.act(step, runsItsScript)
110
+ })
111
+
112
+ Given("judge step {string} answers:", (world: TestkitWorld, step: string, verdict: string) => {
113
+ world.verdicts.set(step, String(verdict))
114
+ })
115
+
116
+ When("gtd starts workflow {string}", async (world: TestkitWorld, workflow: string) => {
117
+ await world.repo().start(workflow)
118
+ })
119
+
120
+ When(
121
+ "gtd starts workflow {string} with {string}",
122
+ async (world: TestkitWorld, workflow: string, list: string) => {
123
+ await world.repo().start(workflow, ...args(list))
124
+ },
125
+ )
126
+
127
+ When("gtd opens door {string}", async (world: TestkitWorld, door: string) => {
128
+ await world.repo().door(door)
129
+ })
130
+
131
+ When(
132
+ "gtd opens door {string} with {string}",
133
+ async (world: TestkitWorld, door: string, list: string) => {
134
+ await world.repo().door(door, ...args(list))
135
+ },
136
+ )
137
+
138
+ When("gtd lands", async (world: TestkitWorld) => {
139
+ await world.repo().land()
140
+ })
141
+
142
+ When("the process is driven", async (world: TestkitWorld) => {
143
+ await world.drive()
144
+ })
145
+
146
+ Then(
147
+ "opening door {string} is refused with {string}",
148
+ async (world: TestkitWorld, door: string, message: string) => {
149
+ await assert.rejects(world.repo().door(door), (error: Error) => error.message.includes(message))
150
+ },
151
+ )
152
+
153
+ Then("the process rests at {string}", async (world: TestkitWorld, step: string) => {
154
+ assert.strictEqual((await world.repo().next()).state, step)
155
+ })
156
+
157
+ Then("the resting step's content contains {string}", async (world: TestkitWorld, text: string) => {
158
+ const { content } = await world.repo().next()
159
+ assert.ok(content.includes(text), content)
160
+ })
161
+
162
+ Then("the last commit subject is {string}", (world: TestkitWorld, subject: string) => {
163
+ assert.strictEqual(world.repo().subjects().at(-1), subject)
164
+ })
165
+
166
+ Then("the commit subjects end with:", (world: TestkitWorld, list: string) => {
167
+ const expected = list.split("\n").filter((line) => line.trim() !== "")
168
+ assert.deepStrictEqual(world.repo().subjects().slice(-expected.length), expected)
169
+ })
170
+
171
+ Then("{string} contains {string}", (world: TestkitWorld, path: string, text: string) => {
172
+ assert.ok(world.repo().read(path)?.includes(text), `${path}: ${world.repo().read(path)}`)
173
+ })
174
+
175
+ Then("{string} does not exist", (world: TestkitWorld, path: string) => {
176
+ assert.strictEqual(world.repo().read(path), undefined)
177
+ })
@@ -0,0 +1,65 @@
1
+ Feature: testkit drives any workflow against the real engine
2
+
3
+ A workflow the scenario writes itself, never a shipped one: scripted actors
4
+ stand in for the agent, the check and the judge.
5
+
6
+ Background:
7
+ Given a gtd config with:
8
+ """
9
+ import { agent, human, judge, read, run } from "@pmelab/gtd/flows"
10
+
11
+ export const tiny = async () => {
12
+ await agent("plan", "Write PLAN.md.")
13
+ await run("check", "npm test > .gtd/FEEDBACK.md 2>&1 && rm .gtd/FEEDBACK.md")
14
+ if (read(".gtd/FEEDBACK.md") !== undefined) await agent("repair", "Make the suite pass.")
15
+ const { answers } = await judge("ready", {
16
+ questions: [{ id: "ready", primitive: "noul", instructions: "Ready?", criteria: "" }],
17
+ evidence: { plan: read("PLAN.md") ?? "" },
18
+ })
19
+ await human(answers.ready?.answer === "yes" ? "ship" : "rework")
20
+ }
21
+
22
+ export default tiny
23
+ """
24
+
25
+ Scenario: a green check goes straight to the judge, whose verdict picks the gate
26
+ Given step "plan" writes "PLAN.md" with:
27
+ """
28
+ add two numbers
29
+ """
30
+ And step "check" changes nothing
31
+ And judge step "ready" answers:
32
+ """
33
+ [{ "id": "ready", "answer": true, "p": 0.9 }]
34
+ """
35
+ When gtd starts workflow "tiny"
36
+ And the process is driven
37
+ Then the process rests at "ship"
38
+ And the commit subjects end with:
39
+ """
40
+ gtd(human): plan
41
+ gtd(agent): plan → check
42
+ gtd(check): check → ready
43
+ gtd(judge): ready → ship
44
+ """
45
+ And "PLAN.md" contains "add two numbers"
46
+
47
+ Scenario: a red check sends the process to repair, and an unscripted step is where it rests
48
+ Given step "plan" writes "PLAN.md" with:
49
+ """
50
+ add two numbers
51
+ """
52
+ And step "check" writes ".gtd/FEEDBACK.md" with:
53
+ """
54
+ 1 test failing
55
+ """
56
+ When gtd starts workflow "tiny"
57
+ And the process is driven
58
+ Then the process rests at "repair"
59
+ And the resting step's content contains "Make the suite pass."
60
+ And the last commit subject is "gtd(check): check → repair"
61
+
62
+ # Fails if run: proves the `workflows` project honours @skip.
63
+ @skip
64
+ Scenario: a scenario tagged @skip never runs
65
+ Then the process rests at "a step this workflow does not have"