@pmelab/gtd 58.2.0 → 59.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 (133) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +115 -390
  3. package/dist/chunks/{Schema-B3o6eRNU.mjs → testkit-CMBAAyRK.mjs} +13409 -3351
  4. package/dist/gtd.bundle.mjs +76 -59
  5. package/dist/testkit/gherkin.mjs +1 -1
  6. package/dist/testkit.mjs +1 -1
  7. package/package.json +3 -42
  8. package/skills/authoring/SKILL.md +31 -25
  9. package/src/cli/Cli.test.ts +15 -19
  10. package/src/cli/Cli.ts +2 -4
  11. package/src/cli/Program.test.ts +60 -29
  12. package/src/cli/Program.ts +5 -7
  13. package/src/edge/Edge.ts +3 -3
  14. package/src/flags/index.test.ts +0 -13
  15. package/src/flags/index.ts +1 -3
  16. package/src/install/Install.test.ts +14 -11
  17. package/src/install/Install.ts +38 -15
  18. package/src/install/index.ts +1 -2
  19. package/src/replay/Replay.test.ts +1 -1
  20. package/src/replay/index.ts +1 -1
  21. package/src/step/planStart.ts +6 -1
  22. package/src/twins/FlowScripts.ts +1 -1
  23. package/src/workflow/index.ts +1 -0
  24. package/src/workflow/load.test.ts +138 -113
  25. package/src/workflow/load.ts +1 -1
  26. package/src/workflow/vars.test.ts +30 -3
  27. package/src/workflow/vars.ts +34 -2
  28. package/src/workflow-kit/index.ts +1 -1
  29. package/dist/chunks/reserved-lMYCaapY.mjs +0 -1990
  30. package/dist/chunks/review-Bfaror38.mjs +0 -427
  31. package/dist/chunks/tail-Bd7L940p.mjs +0 -355
  32. package/dist/chunks/testkit-JDtpNKyi.mjs +0 -10410
  33. package/dist/workflows/cleanup.mjs +0 -36
  34. package/dist/workflows/feature.mjs +0 -1018
  35. package/dist/workflows/fix.mjs +0 -41
  36. package/dist/workflows/review.mjs +0 -45
  37. package/dist/workflows/rollout.mjs +0 -48
  38. package/dist/workflows.mjs +0 -40651
  39. package/src/install/ConfigScaffold.ts +0 -22
  40. package/src/workflows/cleanup/cleanup-path.feature +0 -94
  41. package/src/workflows/cleanup/cleanup.feature +0 -315
  42. package/src/workflows/cleanup/cleanup.test.ts +0 -276
  43. package/src/workflows/cleanup/cleanup.ts +0 -52
  44. package/src/workflows/cleanup/index.ts +0 -6
  45. package/src/workflows/feature/acceptance-criteria-tier.feature +0 -25
  46. package/src/workflows/feature/acceptance-criteria.feature +0 -389
  47. package/src/workflows/feature/criteria-coverage.feature +0 -102
  48. package/src/workflows/feature/criteria.test.ts +0 -147
  49. package/src/workflows/feature/criteria.ts +0 -267
  50. package/src/workflows/feature/fast-path.feature +0 -93
  51. package/src/workflows/feature/feature.test.ts +0 -338
  52. package/src/workflows/feature/feature.ts +0 -156
  53. package/src/workflows/feature/green.ts +0 -19
  54. package/src/workflows/feature/index.ts +0 -18
  55. package/src/workflows/feature/lens-models.feature +0 -69
  56. package/src/workflows/feature/lens-review.feature +0 -566
  57. package/src/workflows/feature/naive-acceptance.feature +0 -147
  58. package/src/workflows/feature/naive-test.feature +0 -263
  59. package/src/workflows/feature/one-step-land.feature +0 -93
  60. package/src/workflows/feature/planning.ts +0 -41
  61. package/src/workflows/feature/refactor-absorbs.feature +0 -131
  62. package/src/workflows/feature/refactor-first.feature +0 -158
  63. package/src/workflows/feature/refactor-lens-models.feature +0 -79
  64. package/src/workflows/feature/refactor-rebuild.feature +0 -273
  65. package/src/workflows/feature/refactor-review.feature +0 -1528
  66. package/src/workflows/feature/refactor.test.ts +0 -1220
  67. package/src/workflows/feature/refactor.ts +0 -368
  68. package/src/workflows/feature/release-flag.feature +0 -167
  69. package/src/workflows/feature/release-flag.test.ts +0 -49
  70. package/src/workflows/feature/release-flag.ts +0 -46
  71. package/src/workflows/feature/review-gate.feature +0 -241
  72. package/src/workflows/feature/reviewer-walkthrough.feature +0 -142
  73. package/src/workflows/feature/risk-tier.feature +0 -351
  74. package/src/workflows/feature/seam-rejection.feature +0 -146
  75. package/src/workflows/feature/seam.feature +0 -297
  76. package/src/workflows/feature/seam.test.ts +0 -527
  77. package/src/workflows/feature/seam.ts +0 -426
  78. package/src/workflows/feature/standards.feature +0 -354
  79. package/src/workflows/feature/steps.test.ts +0 -65
  80. package/src/workflows/feature/steps.ts +0 -45
  81. package/src/workflows/feature/tier-pin.feature +0 -66
  82. package/src/workflows/feature/tier.test.ts +0 -166
  83. package/src/workflows/feature/tier.ts +0 -56
  84. package/src/workflows/fix/fix-lens-origin.feature +0 -114
  85. package/src/workflows/fix/fix-quality.feature +0 -212
  86. package/src/workflows/fix/fix.feature +0 -79
  87. package/src/workflows/fix/fix.test.ts +0 -74
  88. package/src/workflows/fix/fix.ts +0 -52
  89. package/src/workflows/fix/index.ts +0 -6
  90. package/src/workflows/index.ts +0 -47
  91. package/src/workflows/review/index.ts +0 -6
  92. package/src/workflows/review/review-door.feature +0 -199
  93. package/src/workflows/review/review.test.ts +0 -27
  94. package/src/workflows/review/review.ts +0 -60
  95. package/src/workflows/rollout/index.ts +0 -6
  96. package/src/workflows/rollout/rollout.feature +0 -510
  97. package/src/workflows/rollout/rollout.test.ts +0 -263
  98. package/src/workflows/rollout/rollout.ts +0 -82
  99. package/src/workflows/shared/__fixtures__/feature-collecting-prompt.txt +0 -98
  100. package/src/workflows/shared/access.test.ts +0 -62
  101. package/src/workflows/shared/access.ts +0 -24
  102. package/src/workflows/shared/config.fixture.ts +0 -12
  103. package/src/workflows/shared/fences.ts +0 -24
  104. package/src/workflows/shared/health.test.ts +0 -148
  105. package/src/workflows/shared/health.ts +0 -191
  106. package/src/workflows/shared/learn.test.ts +0 -140
  107. package/src/workflows/shared/learn.ts +0 -201
  108. package/src/workflows/shared/lenses.test.ts +0 -39
  109. package/src/workflows/shared/lenses.ts +0 -294
  110. package/src/workflows/shared/options.test.ts +0 -157
  111. package/src/workflows/shared/options.ts +0 -92
  112. package/src/workflows/shared/progress.feature +0 -124
  113. package/src/workflows/shared/prose.ts +0 -263
  114. package/src/workflows/shared/recheck.feature +0 -60
  115. package/src/workflows/shared/registry.test.ts +0 -345
  116. package/src/workflows/shared/registry.ts +0 -183
  117. package/src/workflows/shared/reserved.ts +0 -11
  118. package/src/workflows/shared/review.test.ts +0 -1062
  119. package/src/workflows/shared/review.ts +0 -446
  120. package/src/workflows/shared/skills.test.ts +0 -121
  121. package/src/workflows/shared/skills.ts +0 -49
  122. package/src/workflows/shared/standards.test.ts +0 -90
  123. package/src/workflows/shared/standards.ts +0 -156
  124. package/src/workflows/shared/steps.test.ts +0 -219
  125. package/src/workflows/shared/steps.ts +0 -201
  126. package/src/workflows/shared/tail.test.ts +0 -454
  127. package/src/workflows/shared/tail.ts +0 -264
  128. package/src/workflows/shared/text.fixture.ts +0 -128
  129. package/src/workflows/shared/text.test.ts +0 -1430
  130. package/src/workflows/shared/text.ts +0 -1700
  131. package/src/workflows/shared/threads.test.ts +0 -129
  132. package/src/workflows/shared/vars.ts +0 -47
  133. package/src/workflows/shared/workflows.steps.ts +0 -54
@@ -27,8 +27,8 @@ import {
27
27
  restoredOutcome,
28
28
  } from "../emit/index.js"
29
29
  import { HISTORY_REF } from "../platform/index.js"
30
- import { CONFIG_SCAFFOLD } from "../install/index.js"
31
30
  import { contentHash } from "../workflow/index.js"
31
+ import { WORKFLOWS_REPO } from "../install/index.js"
32
32
 
33
33
  const run = async (
34
34
  repo: InMemRepo,
@@ -64,15 +64,6 @@ const seed = (source: string, extra: Record<string, string> = {}): InMemRepo =>
64
64
  return repo
65
65
  }
66
66
 
67
- /** A repo `gtd install` set up: its config runs the shipped workflows. */
68
- const seedBundled = (): InMemRepo => {
69
- const repo = new InMemRepo()
70
- repo.writeFile("README.md", "# test project\n")
71
- repo.writeFile("gtd.config.ts", CONFIG_SCAFFOLD)
72
- repo.seedCommit("chore: init")
73
- return repo
74
- }
75
-
76
67
  const IDLE_THEN_WORKING = `import { agent, human } from "@pmelab/gtd/flows"
77
68
 
78
69
  export default async () => {
@@ -81,6 +72,15 @@ export default async () => {
81
72
  }
82
73
  `
83
74
 
75
+ /** A repo at rest: its config runs {@link IDLE_THEN_WORKING}. */
76
+ const seedIdle = (): InMemRepo => {
77
+ const repo = new InMemRepo()
78
+ repo.writeFile("README.md", "# test project\n")
79
+ repo.writeFile("gtd.config.ts", IDLE_THEN_WORKING)
80
+ repo.seedCommit("chore: init")
81
+ return repo
82
+ }
83
+
84
84
  describe("gtd --workflow <name> — a custom workflow's named workflow", () => {
85
85
  const WORKFLOW = `import { agent, human } from "@pmelab/gtd/flows"
86
86
 
@@ -194,9 +194,50 @@ export const defaults = { greeting: "hello" }
194
194
  })
195
195
  })
196
196
 
197
- describe("gtd --workflow <name> — the shipped workflows", () => {
197
+ describe("gtd install", () => {
198
+ it("where no gtd.config.ts governs, prints the briefing and warns where to get a workflow — writing nothing", async () => {
199
+ const repo = new InMemRepo()
200
+ repo.writeFile("README.md", "# test project\n")
201
+ repo.seedCommit("chore: init")
202
+ const before = repo.snapshot()
203
+ const { stdout, stderr, exitCode } = await run(repo, "install")
204
+ expect(exitCode).toBe(0)
205
+ expect(stdout).toMatch(/Make sure a .gtd\.config\.ts. governs the repository/)
206
+ expect(stderr).toContain(
207
+ "gtd install: no gtd.config.ts governs here, and gtd ships no workflow",
208
+ )
209
+ expect(stderr).toContain(WORKFLOWS_REPO)
210
+ expect(repo.hasPath("gtd.config.ts")).toBe(false)
211
+ expect(repo.snapshot()).toBe(before)
212
+ })
213
+
214
+ it("where a gtd.config.ts governs, prints the briefing alone", async () => {
215
+ const repo = seedIdle()
216
+ const { stdout, stderr, exitCode } = await run(repo, "install")
217
+ expect(exitCode).toBe(0)
218
+ expect(stdout).toMatch(/Make sure a .gtd\.config\.ts. governs the repository/)
219
+ expect(stderr).not.toContain(WORKFLOWS_REPO)
220
+ })
221
+ })
222
+
223
+ describe("gtd --workflow <name> — a workflow that fixes its diff base", () => {
224
+ const WORKFLOW = `import { human } from "@pmelab/gtd/flows"
225
+
226
+ export default async () => {
227
+ await human("idle", { message: "hi" })
228
+ }
229
+
230
+ export const review = async () => {
231
+ await human("checking", { message: "check" })
232
+ }
233
+
234
+ export const defaults = { reviewBase: "" }
235
+
236
+ export const base = (workflow, vars) => (workflow === "review" ? vars.reviewBase : undefined)
237
+ `
238
+
198
239
  it("--workflow review --var reviewBase=<base> emits a script anchoring the process to that base", async () => {
199
- const repo = seedBundled()
240
+ const repo = seed(WORKFLOW)
200
241
  const base = repo.commitHistory().at(-1)!.hash
201
242
  repo.writeFile("scratch.txt", "more work\n")
202
243
  repo.seedCommit("chore: more work")
@@ -209,26 +250,16 @@ describe("gtd --workflow <name> — the shipped workflows", () => {
209
250
  `reviewBase=${base}`,
210
251
  )
211
252
  expect(exitCode).toBe(0)
212
- expect(stdout).toContain("gtd(human): review-gate.check")
253
+ expect(stdout).toContain("gtd(human): checking")
213
254
  expect(stdout).toContain("Gtd-Workflow: review")
214
255
  expect(stdout).toContain(`Gtd-Review-Base: ${base}`)
215
256
  expect(repo.commitHistory()).toHaveLength(before)
216
257
  })
217
-
218
- it("--workflow fix emits a script that would start a fix process", async () => {
219
- const repo = seedBundled()
220
- const before = repo.commitHistory().length
221
- const { stdout, exitCode } = await run(repo, "--workflow", "fix")
222
- expect(exitCode).toBe(0)
223
- expect(stdout).toContain("gtd(human): fix-precheck")
224
- expect(stdout).toContain("Gtd-Workflow: fix")
225
- expect(repo.commitHistory()).toHaveLength(before)
226
- })
227
258
  })
228
259
 
229
260
  describe("config loading", () => {
230
261
  it('gtd next --verbose narrates "config: module" exactly once — the extra load that surfaces warnings is silent', async () => {
231
- const repo = seedBundled()
262
+ const repo = seedIdle()
232
263
  const { stderr, exitCode } = await run(repo, "next", "--verbose")
233
264
  expect(exitCode).toBe(0)
234
265
  expect(stderr.match(/config: module /g)).toHaveLength(1)
@@ -287,7 +318,7 @@ describe("gtd next --json — log path emission", () => {
287
318
  const seededRepo = (): InMemRepo => {
288
319
  const repo = new InMemRepo()
289
320
  repo.writeFile("NOTE.md", "a note\n")
290
- repo.writeFile("gtd.config.ts", CONFIG_SCAFFOLD)
321
+ repo.writeFile("gtd.config.ts", IDLE_THEN_WORKING)
291
322
  repo.seedCommit("gtd: init")
292
323
  return repo
293
324
  }
@@ -1752,7 +1783,7 @@ describe("runCommand — refuses in a repository with no commits", () => {
1752
1783
  )
1753
1784
 
1754
1785
  it("a repository with a commit passes the guard and dispatches normally", async () => {
1755
- const repo = seedBundled()
1786
+ const repo = seedIdle()
1756
1787
  const written: string[] = []
1757
1788
  const out = { write: (chunk: string) => written.push(chunk), flush: () => {} }
1758
1789
 
@@ -1765,7 +1796,7 @@ describe("runCommand — refuses in a repository with no commits", () => {
1765
1796
  })
1766
1797
 
1767
1798
  it("gtd ui in a repository WITH commits reaches its own dispatch, past the guard", async () => {
1768
- const repo = seedBundled()
1799
+ const repo = seedIdle()
1769
1800
  const out = { write: () => {}, flush: () => {} }
1770
1801
  const requests: unknown[] = []
1771
1802
  const ui = (request: unknown) => Effect.sync(() => void requests.push(request))
@@ -1950,7 +1981,7 @@ describe("gtd next/land --json=<path> — the select branch", () => {
1950
1981
  const seededRepo = (): InMemRepo => {
1951
1982
  const repo = new InMemRepo()
1952
1983
  repo.writeFile("NOTE.md", "a note\n")
1953
- repo.writeFile("gtd.config.ts", CONFIG_SCAFFOLD)
1984
+ repo.writeFile("gtd.config.ts", IDLE_THEN_WORKING)
1954
1985
  repo.seedCommit("gtd: init")
1955
1986
  return repo
1956
1987
  }
@@ -1987,7 +2018,7 @@ describe("gtd next/land --json=<path> — the select branch", () => {
1987
2018
 
1988
2019
  it("an absent selector writes zero bytes to stdout and exits EXIT_OK", async () => {
1989
2020
  const repo = seededRepo()
1990
- // The built-in workflow's initial `idle` state declares no `mode:` —
2021
+ // The workflow's initial `idle` step declares no `mode:` —
1991
2022
  // `mode` is a real, present-but-`undefined` field on the beat document
1992
2023
  // there (see `src/wire/BeatStatus.ts`'s `statusOf`), not merely an
1993
2024
  // unknown path.
@@ -87,7 +87,7 @@ import {
87
87
  abandonNoopOutcome,
88
88
  restoredOutcome,
89
89
  } from "../emit/index.js"
90
- import { CONFIG_SCAFFOLD, renderBriefing } from "../install/index.js"
90
+ import { renderBriefing, WORKFLOWS_REPO } from "../install/index.js"
91
91
  import { ToolCommands, type SelfGtd } from "./ToolCommands.js"
92
92
  import {
93
93
  SelectorUsageError,
@@ -207,13 +207,12 @@ const runLspCommand = (): Effect.Effect<void, Error, ToolCommands> =>
207
207
 
208
208
  /**
209
209
  * Prints the briefing, emitted by the binary itself so it's always current.
210
- * At a repository root no `gtd.config.ts` governs yet, it also writes one
211
- * importing gtd's shipped workflows, left uncommitted.
212
- * Anywhere else it writes nothing.
210
+ * gtd ships no workflow, so where no `gtd.config.ts` governs it says where to
211
+ * get one; it writes nothing.
213
212
  */
214
213
  const runInstallCommand = (
215
214
  out: ArtifactOut,
216
- ): Effect.Effect<void, Error, GitService | Workspace | Host | ConfigDiscovery | Narrator> =>
215
+ ): Effect.Effect<void, Error, GitService | Host | ConfigDiscovery | Narrator> =>
217
216
  Effect.gen(function* () {
218
217
  const briefing = renderBriefing()
219
218
  out.write(briefing.endsWith("\n") ? briefing : briefing + "\n")
@@ -222,9 +221,8 @@ const runInstallCommand = (
222
221
  if (topLevel._tag === "Left") return
223
222
  if ((yield* host.realPath(topLevel.right)) !== (yield* host.realPath(host.root))) return
224
223
  if ((yield* (yield* ConfigDiscovery).workflowModule(host.root, host.home)) !== undefined) return
225
- yield* (yield* Workspace).write(WORKFLOW_MODULE, CONFIG_SCAFFOLD)
226
224
  yield* (yield* Narrator).warn(
227
- `gtd install: wrote ${WORKFLOW_MODULE}, importing gtd's shipped workflows — commit it before your first \`gtd land\``,
225
+ `gtd install: no ${WORKFLOW_MODULE} governs here, and gtd ships no workflow — get one from ${WORKFLOWS_REPO} or write your own (step 2 of the briefing above)`,
228
226
  )
229
227
  })
230
228
 
package/src/edge/Edge.ts CHANGED
@@ -1476,12 +1476,12 @@ const historySteps = (run: ProcessRun): readonly HistoryStep[] => {
1476
1476
  const versionWarningFor = (recorded: string | undefined): string | undefined =>
1477
1477
  recorded === GTD_VERSION
1478
1478
  ? undefined
1479
- : `gtd: warning: this history was recorded by ${recorded === undefined ? "an unknown gtd version" : `gtd ${recorded}`} and is replayed by gtd ${GTD_VERSION} — a workflow imported from @pmelab/gtd/workflows may have changed since`
1479
+ : `gtd: warning: this history was recorded by ${recorded === undefined ? "an unknown gtd version" : `gtd ${recorded}`} and is replayed by gtd ${GTD_VERSION} — the engine the workflow runs on may have changed since`
1480
1480
 
1481
1481
  /**
1482
1482
  * Replays the closed run at `tip` under today's config. The workflow is not
1483
- * fingerprinted for a version change: one imported from `@pmelab/gtd/workflows`
1484
- * moves with every upgrade, and the version warning is the only signal.
1483
+ * fingerprinted for a version change: the engine under it moves with every
1484
+ * upgrade, and the version warning is the only signal.
1485
1485
  */
1486
1486
  export const historyTrail = (tip: string): Effect.Effect<HistoryTrail, Error, RestRequirements> =>
1487
1487
  Effect.gen(function* () {
@@ -102,16 +102,3 @@ describe("flagSettings", () => {
102
102
  })
103
103
  })
104
104
  })
105
-
106
- describe("risk_tier", () => {
107
- it("is a release flag, off by default, resolved from GTD_FLAG_RISK_TIER", () => {
108
- expect(FLAGS.find((f) => f.name === "risk_tier")).toMatchObject({ kind: "release" })
109
- expect(resolveFlags({}).risk_tier).toBe(false)
110
- expect(resolveFlags({ GTD_FLAG_RISK_TIER: "1" }).risk_tier).toBe(true)
111
- })
112
-
113
- it("is pinned as flag_risk_tier", () => {
114
- expect(flagSettings(resolveFlags({ GTD_FLAG_RISK_TIER: "1" })).flag_risk_tier).toBe("1")
115
- expect(flagSettings(resolveFlags({})).flag_risk_tier).toBe("0")
116
- })
117
- })
@@ -13,9 +13,7 @@ export interface FlagDeclaration {
13
13
  readonly defaultOn?: boolean
14
14
  }
15
15
 
16
- export const FLAGS = [
17
- { name: "risk_tier", owner: "pmelab", expiry: "2027-01-15", kind: "release" },
18
- ] as const satisfies readonly FlagDeclaration[]
16
+ export const FLAGS = [] as const satisfies readonly FlagDeclaration[]
19
17
 
20
18
  /** A misspelt name is a compile error. */
21
19
  export type Flags<R extends readonly FlagDeclaration[] = typeof FLAGS> = {
@@ -9,8 +9,13 @@ import { readFileSync } from "node:fs"
9
9
  import { resolve } from "node:path"
10
10
  import { createRequire } from "node:module"
11
11
  import { describe, expect, it } from "vitest"
12
- import { EDIT_COMMAND, DOOR_COMMAND, MINIMAL_DRIVER, renderBriefing } from "./Install.js"
13
- import { CONFIG_SCAFFOLD } from "./index.js"
12
+ import {
13
+ EDIT_COMMAND,
14
+ DOOR_COMMAND,
15
+ MINIMAL_DRIVER,
16
+ renderBriefing,
17
+ WORKFLOWS_REPO,
18
+ } from "./Install.js"
14
19
 
15
20
  const SUITE_PATHS = ["gtd-build", "gtd-edit", "gtd-door"]
16
21
  const SUPERSEDED_PATHS = ["gtd-fix", "gtd-review"]
@@ -110,6 +115,13 @@ describe("renderBriefing", () => {
110
115
  expect(briefing).not.toContain(".gtdrc")
111
116
  })
112
117
 
118
+ it("says gtd ships no workflow, offering gtd-workflows or the user's own", () => {
119
+ const briefing = renderBriefing()
120
+ expect(briefing).toContain("gtd ships no")
121
+ expect(briefing).toContain(`git clone ${WORKFLOWS_REPO}`)
122
+ expect(briefing).toMatch(/\*\*Their own workflow\.\*\*/)
123
+ })
124
+
113
125
  it("instructs committing the config before the first drive", () => {
114
126
  expect(renderBriefing()).toMatch(/commit (it|the config).{0,40}before.{0,20}drive/is)
115
127
  })
@@ -425,12 +437,3 @@ describe("DOOR_COMMAND", () => {
425
437
  expect(DOOR_COMMAND).toMatch(/usage: gtd-door <name> \[args\.\.\.\]" >&2\n\s*exit 2/)
426
438
  })
427
439
  })
428
-
429
- describe("bundled door lists", () => {
430
- it("the scaffold's exports stay feature, review, fix, never rollout or cleanup", () => {
431
- const code = CONFIG_SCAFFOLD.split("\n")
432
- .filter((l) => !l.startsWith("//"))
433
- .join("\n")
434
- expect(code).not.toMatch(/rollout|cleanup/)
435
- })
436
- })
@@ -1,6 +1,8 @@
1
1
  // `MINIMAL_DRIVER` is pinned equal to https://github.com/pmelab/gtd/blob/main/docs/driver.md's "A complete minimal driver" fenced bash block by `Install.test.ts`, so the two can never drift.
2
2
  import { GTD_VERSION } from "./Version.js"
3
3
 
4
+ export const WORKFLOWS_REPO = "https://github.com/pmelab/gtd-workflows"
5
+
4
6
  export const MINIMAL_DRIVER = `#!/usr/bin/env sh
5
7
  set -eu
6
8
 
@@ -82,7 +84,7 @@ while :; do
82
84
  system="$(gtd next --json=system)"
83
85
  validate="$(gtd next --json=validate)"
84
86
  log="$(gtd next --json=log)"
85
- # \`model\` is a hint, not a model name — the bundled workflow ships
87
+ # \`model\` is a hint, not a model name — gtd-workflows' default ships
86
88
  # \`smart\` and \`base\`, which \`claude --model\` rejects. Resolve them
87
89
  # from your environment; unset, the hint is dropped and the agent runs
88
90
  # its own default. Any other value is a real model name.
@@ -420,14 +422,37 @@ follow in the same numbered list.
420
422
  existing config, the project's own README/docs) and ask the user what
421
423
  they're trying to accomplish with gtd here — don't assume a fresh
422
424
  install or silently start a process on their behalf.
423
- 2. **Make sure a \`gtd.config.ts\` governs the repository.** Run at the
424
- repository root, \`gtd install\` writes one importing gtd's shipped
425
- workflows when none exists yet (its own message says so). Repository
426
- settings — steering-file modes, \`gtd ui\`, per-scope skills and access —
427
- go in its \`config\` export; workflow settings are options of the
428
- workflow factory it exports.
429
- 3. **Commit the config before the first drive.** \`gtd install\` leaves the
430
- file it writes uncommitted on purpose — an uncommitted config is a
425
+ 2. **Make sure a \`gtd.config.ts\` governs the repository.** gtd ships no
426
+ workflow: the config imports the workflows it runs. Ask the user which
427
+ way they want:
428
+ - **A workflow from ${WORKFLOWS_REPO}.** Its \`default\` is a feature →
429
+ review → fix workflow. It is used from a checkout pinned on this
430
+ machine:
431
+
432
+ \`\`\`sh
433
+ git clone ${WORKFLOWS_REPO} ~/.local/share/gtd/workflows
434
+ ~/.local/share/gtd/workflows/bin/gtd-wf pin origin/main # or a tag
435
+ ~/.local/share/gtd/workflows/bin/gtd-wf config default > gtd.config.ts
436
+ \`\`\`
437
+
438
+ \`pin\` checks the ref out and bundles each workflow; \`config\` prints a
439
+ config importing the bundle by its ABSOLUTE path. Tell the user what
440
+ that means: the committed config works only where the checkout sits at
441
+ that same path, so a teammate or CI needs their own checkout there (or
442
+ their own config). If a checkout exists already, \`gtd-wf status\`
443
+ shows its pin — reuse it rather than cloning again. Read the
444
+ repository's README for the workflows it offers and their options.
445
+ - **Their own workflow.** A workflow is TypeScript the config exports,
446
+ written against \`@pmelab/gtd/flows\` — see
447
+ https://github.com/pmelab/gtd/blob/main/docs/configuration.md, and
448
+ https://github.com/pmelab/gtd/blob/main/docs/testing.md to test it. A
449
+ copy of a gtd-workflows workflow (\`gtd-wf new <name> --from default\`
450
+ in a clone of it) is a good start.
451
+
452
+ Repository settings — steering-file modes, \`gtd ui\`, per-scope skills
453
+ and access — go in the config's \`config\` export; workflow settings are
454
+ options of the workflow factory it exports.
455
+ 3. **Commit the config before the first drive.** An uncommitted config is a
431
456
  pending change, so starting a process without committing it first turns
432
457
  the config into the very first thing the process plans against. Commit
433
458
  it, THEN start driving.
@@ -453,7 +478,8 @@ follow in the same numbered list.
453
478
  from every prompt line and writing no model exports, so every turn runs
454
479
  on the chosen CLI's own default tier.
455
480
  6. **If model hints are honored, resolve them to real model identifiers.**
456
- The bundled workflow ships \`plannerModel: smart\` and \`coderModel: base\`.
481
+ A workflow may declare model hints — gtd-workflows' \`default\` ships
482
+ \`plannerModel: smart\` and \`coderModel: base\`.
457
483
  **Those are opaque hints, not model names** — gtd never interprets them,
458
484
  and passing \`--model smart\` to a real CLI fails on the first prompt
459
485
  beat. Derive the chosen CLI's own available models at install time — its
@@ -550,11 +576,8 @@ hands off to \`gtd-build\`, which halts at the blocked gate and prints it,
550
576
  rather than starting the review; \`gtd-door fix\` on a GREEN suite is a no-op
551
577
  straight back to \`idle\` — the exec'd \`gtd-build\` exits immediately on it.
552
578
  \`gtd doors\` lists the doors a repository offers (those of the workflows its
553
- config exports — the bundled \`fix\` and \`review [base]\`, plus the opt-in
554
- \`rollout <flag>\` (turn a release flag default-on) and \`cleanup <flag>\`
555
- (remove it), which a config adds by exporting \`rollout\` from
556
- \`@pmelab/gtd/workflows/rollout\` and \`cleanup\` from
557
- \`@pmelab/gtd/workflows/cleanup\` — plus any it declares). Refuses with
579
+ config exports — gtd-workflows' \`default\` brings \`fix\` and
580
+ \`review [base]\` — plus any the config declares). Refuses with
558
581
  \`usage: gtd-door <name> [args...]\` on stderr and exit \`2\` when the name is
559
582
  missing — nothing was even attempted. Default path: \`~/.local/bin/gtd-door\`.
560
583
 
@@ -1,3 +1,2 @@
1
- export { renderBriefing } from "./Install.js"
2
- export { CONFIG_SCAFFOLD } from "./ConfigScaffold.js"
1
+ export { renderBriefing, WORKFLOWS_REPO } from "./Install.js"
3
2
  export { GTD_VERSION } from "./Version.js"
@@ -175,7 +175,7 @@ const restName = (outcome: ReplayOutcome): string => {
175
175
  }
176
176
 
177
177
  // Plain loops, a counter cap, and helper-driven branches: the shape the
178
- // bundled workflow has.
178
+ // workflow might have.
179
179
  const fixLoop = async () => {
180
180
  await human("idle", { file: "TODO.md" })
181
181
  for (let attempt = 1; ; attempt++) {
@@ -15,6 +15,6 @@ export {
15
15
  type CommitSpec,
16
16
  type JudgeVerdict,
17
17
  } from "./Trailers.js"
18
- export { accessShapeFault, foldAccess } from "./Access.js"
18
+ export { accessShapeFault } from "./Access.js"
19
19
  export { formatFault } from "./Format.js"
20
20
  export { refNameFault } from "./RefName.js"
@@ -4,6 +4,7 @@ import {
4
4
  ConfigDiscovery,
5
5
  ConfigService,
6
6
  multilineSetting,
7
+ ownFlagRefusal,
7
8
  resolveStartVars,
8
9
  StartFlags,
9
10
  type ConfigFingerprint,
@@ -110,7 +111,11 @@ export const planStart = (
110
111
  if (overrideRefusal !== undefined) return overrideRefusal
111
112
 
112
113
  const baseOf = workflow.def.base
113
- const vars = resolveStartVars(workflow.vars, yield* StartFlags, varOverrides, (yield* Host).env)
114
+ const flags = yield* StartFlags
115
+ const hostEnv = (yield* Host).env
116
+ const badFlag = ownFlagRefusal(workflow.vars, flags, hostEnv)
117
+ if (badFlag !== undefined) return refusal(`${commandLabel}: ${badFlag}`)
118
+ const vars = resolveStartVars(workflow.vars, flags, varOverrides, hostEnv)
114
119
  const multiline = multilineSetting(vars)
115
120
  if (multiline !== undefined) {
116
121
  return refusal(
@@ -224,7 +224,7 @@ const recognizePatch = (repo: InMemRepo, script: string): FlowScriptResult | und
224
224
 
225
225
  type PreambleOutcome = { readonly exit: boolean; readonly output: string }
226
226
 
227
- /** One preamble line — the closed vocabulary the bundled workflows write there. */
227
+ /** One preamble line — the closed vocabulary workflows write there. */
228
228
  const runPreambleLine = (repo: InMemRepo, line: string): PreambleOutcome => {
229
229
  const quoted = words(line)
230
230
  if (line === "exit 0") return { exit: true, output: "" }
@@ -15,6 +15,7 @@ export {
15
15
  export { formatDiagnostic } from "./Diagnostic.js"
16
16
  export {
17
17
  resolveVars,
18
+ ownFlagRefusal,
18
19
  resolveStartVars,
19
20
  StartFlags,
20
21
  multilineSetting,