@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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +96 -131
- package/claude/hooks/drive.ts +0 -3
- package/claude/hooks/register.tsx +3 -68
- package/claude/hooks/ship.ts +85 -1595
- package/dist/chunks/{reserved-D0MKVFFa.mjs → reserved-CQ7amcKG.mjs} +87 -133
- package/dist/chunks/{review-ZzUQH1KZ.mjs → review-CNgN_V8T.mjs} +2 -2
- package/dist/chunks/{tail-I1-vVq0P.mjs → tail-JZ9PAOwp.mjs} +2 -14
- package/dist/chunks/{wire-BJuZWR3C.mjs → wire-C12dahrH.mjs} +2 -130
- package/dist/flows.mjs +1 -16
- package/dist/gtd.bundle.mjs +476 -982
- package/dist/testkit.mjs +58 -417
- package/dist/wire.mjs +2 -2
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +159 -363
- package/dist/workflows/fix.mjs +2 -2
- package/dist/workflows/review.mjs +3 -3
- package/dist/workflows/rollout.mjs +1 -1
- package/dist/workflows.mjs +400 -675
- package/package.json +5 -4
- package/src/cli/Cli.test.ts +1 -86
- package/src/cli/Cli.ts +13 -133
- package/src/cli/Metrics.test.ts +21 -31
- package/src/cli/Metrics.ts +25 -36
- package/src/cli/Program.test.ts +1 -35
- package/src/cli/Program.ts +5 -64
- package/src/cli/ToolCommands.ts +0 -3
- package/src/edge/Edge.test.ts +20 -322
- package/src/edge/Edge.ts +10 -181
- package/src/edge/index.ts +0 -1
- package/src/flows/runtime.test.ts +0 -27
- package/src/flows/runtime.ts +1 -49
- package/src/install/Install.ts +15 -24
- package/src/replay/Replay.test.ts +0 -169
- package/src/replay/Replay.ts +4 -93
- package/src/replay/index.ts +0 -1
- package/src/testkit/Layers.ts +0 -1
- package/src/testkit/steps.ts +13 -0
- package/src/wire/BeatDocument.ts +2 -12
- package/src/wire/BeatStatus.ts +0 -3
- package/src/wire/WireSchema.test.ts +0 -18
- package/src/wire/index.ts +0 -2
- package/src/wire/types.ts +0 -7
- package/src/workflow/Workflow.ts +1 -9
- package/src/workflows/feature/acceptance-criteria.feature +159 -3
- package/src/workflows/feature/criteria.test.ts +23 -15
- package/src/workflows/feature/criteria.ts +72 -39
- package/src/workflows/feature/fast-path.feature +4 -0
- package/src/workflows/feature/feature.test.ts +6 -5
- package/src/workflows/feature/feature.ts +34 -118
- package/src/workflows/feature/flag-mode.feature +4 -0
- package/src/workflows/feature/hands-on-skip.feature +37 -65
- package/src/workflows/feature/index.ts +0 -1
- package/src/workflows/feature/lens-models.feature +4 -0
- package/src/workflows/feature/lens-review.feature +4 -0
- package/src/workflows/feature/naive-acceptance.feature +4 -0
- package/src/workflows/feature/one-step-land.feature +4 -4
- package/src/workflows/feature/planning.ts +2 -1
- package/src/workflows/feature/prototype-rounds.feature +5 -1
- package/src/workflows/feature/prototype.feature +11 -3
- package/src/workflows/feature/prototype.ts +18 -29
- package/src/workflows/feature/refactor-absorbs.feature +7 -8
- package/src/workflows/feature/refactor-first.feature +4 -5
- package/src/workflows/feature/refactor-lens-models.feature +4 -0
- package/src/workflows/feature/refactor-rebuild.feature +4 -6
- package/src/workflows/feature/refactor-review.feature +31 -11
- package/src/workflows/feature/refactor.ts +30 -64
- package/src/workflows/feature/review-answers.feature +4 -0
- package/src/workflows/feature/review-gate.feature +13 -1
- package/src/workflows/feature/reviewer-walkthrough.feature +4 -0
- package/src/workflows/feature/risk-tier.feature +32 -0
- package/src/workflows/feature/seam-rejection.feature +8 -4
- package/src/workflows/feature/seam.feature +8 -0
- package/src/workflows/feature/seam.test.ts +11 -28
- package/src/workflows/feature/seam.ts +52 -165
- package/src/workflows/feature/severity-budget.feature +4 -0
- package/src/workflows/feature/severity-sorted-review.feature +4 -0
- package/src/workflows/feature/standards.feature +4 -0
- package/src/workflows/feature/tier-pin.feature +4 -0
- package/src/workflows/index.ts +0 -1
- package/src/workflows/shared/lenses.ts +0 -13
- package/src/workflows/shared/options.test.ts +2 -11
- package/src/workflows/shared/options.ts +4 -21
- package/src/workflows/shared/review.test.ts +3 -3
- package/src/workflows/shared/steps.ts +0 -2
- package/src/workflows/shared/text.test.ts +41 -71
- package/src/workflows/shared/text.ts +82 -212
- package/src/workflows/shared/vars.ts +2 -8
- package/src/workflows/shared/workflows.steps.ts +0 -13
- package/claude/hooks/context.ts +0 -66
- package/src/replay/Slices.test.ts +0 -73
- package/src/replay/Slices.ts +0 -45
- package/src/wire/ContextLimit.fixture.ts +0 -30
- package/src/wire/ContextLimit.test.ts +0 -96
- package/src/wire/ContextLimit.ts +0 -169
- package/src/wire/Slices.ts +0 -25
- package/src/wire/golden/1/slices.json +0 -11
- package/src/workflows/feature/context-limit.feature +0 -37
- package/src/workflows/feature/continuation-cap.feature +0 -65
- package/src/workflows/feature/continuation-rebuild.feature +0 -88
- package/src/workflows/feature/continuation-slices.feature +0 -64
- package/src/workflows/feature/continuation.feature +0 -205
- package/src/workflows/feature/slice-findings.feature +0 -252
- package/src/workflows/feature/slicing-cap.feature +0 -23
- package/src/workflows/feature/slicing.feature +0 -265
- package/src/workflows/feature/slicing.test.ts +0 -49
- package/src/workflows/feature/slicing.ts +0 -105
- 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: "
|
|
782
|
+
const prompts = await lensPrompts({ lensBase: "LENS-BASE" })
|
|
783
783
|
expect(prompts).toHaveLength(2)
|
|
784
|
-
for (const prompt of prompts) expect(prompt).toContain("from `
|
|
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: "
|
|
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(
|
|
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(
|
|
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("
|
|
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
|
|
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",
|
|
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",
|
|
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
|
|
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
|
+
})
|