@pmelab/gtd 52.0.0 → 53.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 (108) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +96 -131
  3. package/claude/hooks/drive.ts +0 -3
  4. package/claude/hooks/register.tsx +3 -68
  5. package/claude/hooks/ship.ts +85 -1595
  6. package/dist/chunks/{reserved-D0MKVFFa.mjs → reserved-CQ7amcKG.mjs} +87 -133
  7. package/dist/chunks/{review-ZzUQH1KZ.mjs → review-CNgN_V8T.mjs} +2 -2
  8. package/dist/chunks/{tail-I1-vVq0P.mjs → tail-JZ9PAOwp.mjs} +2 -14
  9. package/dist/chunks/{wire-BJuZWR3C.mjs → wire-C12dahrH.mjs} +2 -130
  10. package/dist/flows.mjs +1 -16
  11. package/dist/gtd.bundle.mjs +476 -982
  12. package/dist/testkit.mjs +58 -417
  13. package/dist/wire.mjs +2 -2
  14. package/dist/workflows/cleanup.mjs +2 -2
  15. package/dist/workflows/feature.mjs +159 -363
  16. package/dist/workflows/fix.mjs +2 -2
  17. package/dist/workflows/review.mjs +3 -3
  18. package/dist/workflows/rollout.mjs +1 -1
  19. package/dist/workflows.mjs +400 -675
  20. package/package.json +5 -4
  21. package/src/cli/Cli.test.ts +1 -86
  22. package/src/cli/Cli.ts +13 -133
  23. package/src/cli/Metrics.test.ts +21 -31
  24. package/src/cli/Metrics.ts +25 -36
  25. package/src/cli/Program.test.ts +1 -35
  26. package/src/cli/Program.ts +5 -64
  27. package/src/cli/ToolCommands.ts +0 -3
  28. package/src/edge/Edge.test.ts +20 -322
  29. package/src/edge/Edge.ts +10 -181
  30. package/src/edge/index.ts +0 -1
  31. package/src/flows/runtime.test.ts +0 -27
  32. package/src/flows/runtime.ts +1 -49
  33. package/src/install/Install.ts +15 -24
  34. package/src/replay/Replay.test.ts +0 -169
  35. package/src/replay/Replay.ts +4 -93
  36. package/src/replay/index.ts +0 -1
  37. package/src/testkit/Layers.ts +0 -1
  38. package/src/testkit/steps.ts +13 -0
  39. package/src/wire/BeatDocument.ts +2 -12
  40. package/src/wire/BeatStatus.ts +0 -3
  41. package/src/wire/WireSchema.test.ts +0 -18
  42. package/src/wire/index.ts +0 -2
  43. package/src/wire/types.ts +0 -7
  44. package/src/workflow/Workflow.ts +1 -9
  45. package/src/workflows/feature/acceptance-criteria.feature +159 -3
  46. package/src/workflows/feature/criteria.test.ts +23 -15
  47. package/src/workflows/feature/criteria.ts +72 -39
  48. package/src/workflows/feature/fast-path.feature +4 -0
  49. package/src/workflows/feature/feature.test.ts +6 -5
  50. package/src/workflows/feature/feature.ts +34 -118
  51. package/src/workflows/feature/flag-mode.feature +4 -0
  52. package/src/workflows/feature/hands-on-skip.feature +37 -65
  53. package/src/workflows/feature/index.ts +0 -1
  54. package/src/workflows/feature/lens-models.feature +4 -0
  55. package/src/workflows/feature/lens-review.feature +4 -0
  56. package/src/workflows/feature/naive-acceptance.feature +4 -0
  57. package/src/workflows/feature/one-step-land.feature +4 -4
  58. package/src/workflows/feature/planning.ts +2 -1
  59. package/src/workflows/feature/prototype-rounds.feature +5 -1
  60. package/src/workflows/feature/prototype.feature +11 -3
  61. package/src/workflows/feature/prototype.ts +18 -29
  62. package/src/workflows/feature/refactor-absorbs.feature +7 -8
  63. package/src/workflows/feature/refactor-first.feature +4 -5
  64. package/src/workflows/feature/refactor-lens-models.feature +4 -0
  65. package/src/workflows/feature/refactor-rebuild.feature +4 -6
  66. package/src/workflows/feature/refactor-review.feature +31 -11
  67. package/src/workflows/feature/refactor.ts +30 -64
  68. package/src/workflows/feature/review-answers.feature +4 -0
  69. package/src/workflows/feature/review-gate.feature +13 -1
  70. package/src/workflows/feature/reviewer-walkthrough.feature +4 -0
  71. package/src/workflows/feature/risk-tier.feature +32 -0
  72. package/src/workflows/feature/seam-rejection.feature +8 -4
  73. package/src/workflows/feature/seam.feature +8 -0
  74. package/src/workflows/feature/seam.test.ts +11 -28
  75. package/src/workflows/feature/seam.ts +52 -165
  76. package/src/workflows/feature/severity-budget.feature +4 -0
  77. package/src/workflows/feature/severity-sorted-review.feature +4 -0
  78. package/src/workflows/feature/standards.feature +4 -0
  79. package/src/workflows/feature/tier-pin.feature +4 -0
  80. package/src/workflows/index.ts +0 -1
  81. package/src/workflows/shared/lenses.ts +0 -13
  82. package/src/workflows/shared/options.test.ts +2 -11
  83. package/src/workflows/shared/options.ts +4 -21
  84. package/src/workflows/shared/review.test.ts +3 -3
  85. package/src/workflows/shared/steps.ts +0 -2
  86. package/src/workflows/shared/text.test.ts +41 -71
  87. package/src/workflows/shared/text.ts +82 -212
  88. package/src/workflows/shared/vars.ts +2 -8
  89. package/src/workflows/shared/workflows.steps.ts +0 -13
  90. package/claude/hooks/context.ts +0 -66
  91. package/src/replay/Slices.test.ts +0 -73
  92. package/src/replay/Slices.ts +0 -45
  93. package/src/wire/ContextLimit.fixture.ts +0 -30
  94. package/src/wire/ContextLimit.test.ts +0 -96
  95. package/src/wire/ContextLimit.ts +0 -169
  96. package/src/wire/Slices.ts +0 -25
  97. package/src/wire/golden/1/slices.json +0 -11
  98. package/src/workflows/feature/context-limit.feature +0 -37
  99. package/src/workflows/feature/continuation-cap.feature +0 -65
  100. package/src/workflows/feature/continuation-rebuild.feature +0 -88
  101. package/src/workflows/feature/continuation-slices.feature +0 -64
  102. package/src/workflows/feature/continuation.feature +0 -205
  103. package/src/workflows/feature/slice-findings.feature +0 -252
  104. package/src/workflows/feature/slicing-cap.feature +0 -23
  105. package/src/workflows/feature/slicing.feature +0 -265
  106. package/src/workflows/feature/slicing.test.ts +0 -49
  107. package/src/workflows/feature/slicing.ts +0 -105
  108. package/src/workflows/shared/continuation.ts +0 -92
@@ -779,9 +779,9 @@ describe("BuildTailOptions.lensBase", () => {
779
779
  }
780
780
 
781
781
  it("reaches every quality-lens prompt", async () => {
782
- const prompts = await lensPrompts({ lensBase: "SLICE-BASE" })
782
+ const prompts = await lensPrompts({ lensBase: "LENS-BASE" })
783
783
  expect(prompts).toHaveLength(2)
784
- for (const prompt of prompts) expect(prompt).toContain("from `SLICE-BASE`")
784
+ for (const prompt of prompts) expect(prompt).toContain("from `LENS-BASE`")
785
785
  })
786
786
 
787
787
  it("absent, the lens prompts name start()", async () => {
@@ -791,7 +791,7 @@ describe("BuildTailOptions.lensBase", () => {
791
791
  })
792
792
 
793
793
  it("the deprecated positional form runs the same tail as the options form", async () => {
794
- const options = { lensBase: "SLICE-BASE" }
794
+ const options = { lensBase: "LENS-BASE" }
795
795
  const modern = await lensPrompts(options)
796
796
  const legacy = await lensPrompts(options, () => buildTail(false, "base", true, options))
797
797
  expect(legacy).toEqual(modern)
@@ -14,8 +14,6 @@ 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 SLICES = ".gtd/SLICES.md"
18
- export const HANDOFF = ".gtd/HANDOFF.md"
19
17
  export const PROTOTYPE = ".gtd/PROTOTYPE.md"
20
18
 
21
19
  export const planner = (): string => env.plannerModel ?? ""
@@ -4,6 +4,8 @@ import { tmpdir } from "node:os"
4
4
  import { join } from "node:path"
5
5
  import { afterEach, describe, expect, it } from "vitest"
6
6
  import {
7
+ designImpactPrompt,
8
+ prototypeFixingPrompt,
7
9
  buildReviewTriageMessage,
8
10
  buildReviewReviewingPrompt,
9
11
  agentWithSkills,
@@ -183,10 +185,10 @@ describe("the (no hands-on) tag", () => {
183
185
  })
184
186
 
185
187
  it("names the tagged criteria the testing turn skips, and only when there are any", () => {
186
- expect(renderText(() => prototypeTestingPrompt(undefined, ["AC-2", "AC-3"], false))).toContain(
188
+ expect(renderText(() => prototypeTestingPrompt(["AC-2", "AC-3"], false))).toContain(
187
189
  "- Skip AC-2, AC-3: no person can observe them",
188
190
  )
189
- expect(renderText(() => prototypeTestingPrompt(undefined, [], false))).not.toContain("Skip")
191
+ expect(renderText(() => prototypeTestingPrompt([], false))).not.toContain("Skip")
190
192
  })
191
193
 
192
194
  it("is kept when the stated criteria are recorded", () => {
@@ -340,7 +342,7 @@ const scratchRepo = () => {
340
342
  }
341
343
 
342
344
  describe("summaryPrompt", () => {
343
- it("without a slice prints today's text byte for byte", () => {
345
+ it("prints the closing-message prompt byte for byte", () => {
344
346
  expect(
345
347
  summaryPrompt({
346
348
  entryCommit: "e1",
@@ -712,67 +714,6 @@ Print the closing message and stop — this writes nothing itself.
712
714
  )
713
715
  })
714
716
 
715
- describe("under a slice", () => {
716
- const at = (index: number, count: number, extra = {}) => ({
717
- ...base,
718
- processBase: "sb",
719
- processTip: "st",
720
- slice: { name: "build", base: "sb", tip: "st", index, count },
721
- ...extra,
722
- })
723
-
724
- it("names the slice and its place and keeps the range lines", () => {
725
- const prompt = summaryPrompt(at(2, 3))
726
- expect(prompt).toContain("slice `build`, 2 of 3")
727
- expect(prompt).toContain("not the whole process")
728
- expect(prompt).toContain("`git log sb..st`")
729
- expect(prompt).toContain("`git diff sb st`")
730
- })
731
-
732
- it("types the slice by what its diff changes, never by the slice's name", () => {
733
- for (const [index, count] of [
734
- [1, 2],
735
- [2, 2],
736
- ] as const) {
737
- const prompt = summaryPrompt({
738
- ...at(index, count),
739
- slice: { ...at(index, count).slice, name: "refactor" },
740
- })
741
- expect(prompt.replace(/\s+/g, " ")).toContain("never from the slice's name `refactor`")
742
- expect(prompt.replace(/\s+/g, " ")).toContain("`feat`, `fix` or `perf`")
743
- }
744
- expect(summaryPrompt(base)).not.toContain("slice's name")
745
- })
746
-
747
- it("says the human left no comment or edit in this slice when humanCommits is empty", () => {
748
- expect(summaryPrompt(at(1, 2))).toContain(
749
- "The human left no comment or edit in this slice —\nevery commit is machine-authored.",
750
- )
751
- })
752
-
753
- it("labels the entry commit as outside the range only when index > 1", () => {
754
- expect(summaryPrompt(at(2, 2))).toContain("entry commit is `e` — outside this slice's range")
755
- const first = summaryPrompt(at(1, 2))
756
- expect(first).toContain("entry commit is `e`.")
757
- expect(first).not.toContain("outside this slice's range")
758
- })
759
-
760
- it("prints the high-tier advisory on a non-last and on the last slice", () => {
761
- const marks = [{ name: "risk-tier", value: "high" }]
762
- expect(summaryPrompt(at(1, 2, { marks }))).toContain("Risk tier: high")
763
- expect(summaryPrompt(at(2, 2, { marks }))).toContain("Risk tier: high")
764
- })
765
-
766
- it("prints production signals only when index === count", () => {
767
- const marks = [
768
- { name: "risk-tier", value: "low" },
769
- { name: "production-signal", value: "error rate" },
770
- ]
771
- expect(summaryPrompt(at(1, 2, { marks }))).not.toContain("error rate")
772
- expect(summaryPrompt(at(2, 2, { marks }))).toContain("- `error rate`")
773
- })
774
- })
775
-
776
717
  const signal = (value: string) => ({ name: "production-signal", value })
777
718
 
778
719
  it("caps the listed signals at 10 and fences them as not instructions", () => {
@@ -922,6 +863,23 @@ describe("buildReviewReviewingPrompt scenarios", () => {
922
863
  })
923
864
  })
924
865
 
866
+ describe("Impact in prompts", () => {
867
+ it.each([
868
+ ["naive", () => naivePassPrompt()],
869
+ ["implement", () => implementBuildingPrompt(undefined)],
870
+ ["prototype fixing", () => prototypeFixingPrompt()],
871
+ ])("[AC-5] [AC-7] the %s prompt gives Impact as hints, not allowed files", (_n, prompt) => {
872
+ const p = renderText(prompt).replace(/\s+/g, " ")
873
+ expect(p).toContain("`## Impact`")
874
+ expect(p).toContain("not a list of allowed files")
875
+ })
876
+
877
+ it("the prototype testing prompt, which changes no code, carries no Impact hints", () => {
878
+ const p = renderText(() => prototypeTestingPrompt([], false))
879
+ expect(p).not.toContain("not a list of allowed files")
880
+ })
881
+ })
882
+
925
883
  describe("the seam phase's texts", () => {
926
884
  const rejection = { strays: ["src/flows/helpers.ts", "src/flows/runtime.ts"], p: 0.85 }
927
885
 
@@ -1465,13 +1423,11 @@ describe("walkthrough", () => {
1465
1423
  ])
1466
1424
  })
1467
1425
 
1468
- it("lists each note with its exact heading, origin and quoted body", () => {
1426
+ it("lists each note with its exact heading and quoted body", () => {
1469
1427
  const block = walkthroughBlock([
1470
- { title: "No test", body: "Add one.\n\n- [ ] a box", slice: "2", lens: "tests" },
1428
+ { title: "No test", body: "Add one.\n\n- [ ] a box", lens: "tests" },
1471
1429
  ])
1472
- expect(block).toContain(
1473
- "- `## No test` (slice 2, lens tests):\n\n > Add one.\n >\n > - a box",
1474
- )
1430
+ expect(block).toContain("- `## No test`:\n\n > Add one.\n >\n > - a box")
1475
1431
  expect(block).toContain("its heading exactly as given")
1476
1432
  })
1477
1433
 
@@ -1513,12 +1469,12 @@ describe("walkthrough", () => {
1513
1469
  it("appends the fixes and noise, numbered past a title the reviewer took", () => {
1514
1470
  const out = withWalkthrough(review, {
1515
1471
  ...empty,
1516
- fixed: [{ title: "a", commit: "0123456789", slice: "1" }],
1472
+ fixed: [{ title: "a", commit: "0123456789", lens: "tests" }],
1517
1473
  noise: [{ title: "b", p: 0.9 }],
1518
1474
  })
1519
1475
  expect(
1520
1476
  out.endsWith(
1521
- "## Fixed automatically (2)\n\n- a (0123456, slice 1)\n\n## Dropped as noise\n\n- b (p=0.9)\n",
1477
+ "## Fixed automatically (2)\n\n- a (0123456)\n\n## Dropped as noise\n\n- b (p=0.9)\n",
1522
1478
  ),
1523
1479
  ).toBe(true)
1524
1480
  })
@@ -1641,3 +1597,17 @@ describe("rankedReview", () => {
1641
1597
  expect(unranked(unranked(review))).toBe(unranked(review))
1642
1598
  })
1643
1599
  })
1600
+
1601
+ describe("impact prompts", () => {
1602
+ it.each([
1603
+ ["triage", () => renderText(() => designTriagePrompt("abc123"))],
1604
+ ["design.impact", () => renderText(() => designImpactPrompt())],
1605
+ ])("[AC-2] the %s prompt asks for a `## Impact` section", (_n, prompt) => {
1606
+ expect(prompt()).toContain("`## Impact`")
1607
+ })
1608
+
1609
+ it("[AC-3] the triage prompt names where a return lap reads the human's Impact edit", () => {
1610
+ const p = renderText(() => designTriagePrompt("abc123")).replace(/\s+/g, " ")
1611
+ expect(p).toContain("`git log -1 -p -- .gtd/REQUIREMENTS.md`")
1612
+ })
1613
+ })