@pmelab/gtd 29.3.1 → 30.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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +64 -63
- package/claude/hooks/register.tsx +5 -0
- package/dist/chunks/{reserved-Dmpg6dAp.mjs → reserved-DDiiAZ8U.mjs} +35 -239
- package/dist/chunks/{tail-CLyE5VFR.mjs → tail-ILPlELuy.mjs} +1 -1
- package/dist/gtd.bundle.mjs +0 -5
- package/dist/testkit.mjs +0 -5
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +17 -182
- package/dist/workflows/fix.mjs +2 -2
- package/dist/workflows/review.mjs +2 -2
- package/dist/workflows/rollout.mjs +1 -1
- package/dist/workflows.mjs +47 -416
- package/package.json +1 -1
- package/skills/authoring/SKILL.md +15 -27
- package/src/cli/Cli.test.ts +6 -6
- package/src/edge/Edge.test.ts +7 -7
- package/src/flags/index.test.ts +0 -4
- package/src/flags/index.ts +0 -1
- package/src/workflow/load.test.ts +1 -1
- package/src/workflows/feature/fast-path.feature +1 -2
- package/src/workflows/feature/feature.test.ts +115 -63
- package/src/workflows/feature/feature.ts +21 -38
- package/src/workflows/feature/flag-mode.feature +157 -0
- package/src/workflows/feature/index.ts +0 -1
- package/src/workflows/feature/one-step-land.feature +4 -6
- package/src/workflows/feature/planning.ts +2 -27
- package/src/workflows/feature/refactor-absorbs.feature +1 -2
- package/src/workflows/feature/refactor-first.feature +1 -3
- package/src/workflows/feature/refactor-review.feature +0 -1
- package/src/workflows/feature/risk-tier.feature +2 -3
- package/src/workflows/feature/seam-rejection.feature +1 -3
- package/src/workflows/feature/seam.feature +4 -6
- package/src/workflows/feature/seam.ts +2 -0
- package/src/workflows/feature/steps.test.ts +0 -20
- package/src/workflows/feature/steps.ts +1 -49
- package/src/workflows/feature/{seam-pin.feature → tier-pin.feature} +17 -11
- package/src/workflows/feature/tier.test.ts +3 -5
- package/src/workflows/feature/tier.ts +2 -2
- package/src/workflows/index.ts +0 -1
- package/src/workflows/shared/access.test.ts +0 -2
- package/src/workflows/shared/access.ts +0 -2
- package/src/workflows/shared/options.test.ts +14 -3
- package/src/workflows/shared/options.ts +17 -2
- package/src/workflows/shared/prose.ts +4 -11
- package/src/workflows/shared/reserved.ts +2 -2
- package/src/workflows/shared/skills.test.ts +0 -11
- package/src/workflows/shared/skills.ts +2 -9
- package/src/workflows/shared/steps.ts +0 -1
- package/src/workflows/shared/text.test.ts +20 -104
- package/src/workflows/shared/text.ts +28 -229
- package/src/workflows/shared/vars.ts +2 -3
- package/src/workflows/feature/packages.test.ts +0 -368
- package/src/workflows/feature/packages.ts +0 -173
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
type WorkflowBase,
|
|
6
6
|
type WorkflowDoors,
|
|
7
7
|
type WorkflowFactory,
|
|
8
|
+
refuse,
|
|
9
|
+
vars,
|
|
8
10
|
} from "../../flows/index.js"
|
|
9
11
|
import { defaultFlagRegistry, defaults, envDefaults } from "./vars.js"
|
|
10
12
|
|
|
@@ -23,18 +25,31 @@ export const ShippedOptions = Schema.Struct({
|
|
|
23
25
|
testCommand: setting,
|
|
24
26
|
plannerModel: setting,
|
|
25
27
|
coderModel: setting,
|
|
28
|
+
flagMode: Schema.optionalWith(Schema.Boolean, { exact: true }),
|
|
26
29
|
})
|
|
27
30
|
|
|
28
31
|
export type ShippedOptions = Schema.Schema.Encoded<typeof ShippedOptions>
|
|
29
32
|
|
|
30
33
|
const over = (
|
|
31
34
|
base: Readonly<Record<string, string>>,
|
|
32
|
-
options: Readonly<Record<string, string>>,
|
|
35
|
+
options: Readonly<Record<string, string | boolean>>,
|
|
33
36
|
): Record<string, string> => ({
|
|
34
37
|
...base,
|
|
35
|
-
...Object.fromEntries(
|
|
38
|
+
...Object.fromEntries(
|
|
39
|
+
Object.entries(options)
|
|
40
|
+
.filter(([name]) => Object.hasOwn(base, name))
|
|
41
|
+
.map(([name, value]) => [name, String(value)]),
|
|
42
|
+
),
|
|
36
43
|
})
|
|
37
44
|
|
|
45
|
+
/** The `flagMode` setting, pinned at process start. Anything but true/false fails rather than guess a side. */
|
|
46
|
+
export const flagModeOn = (): boolean => {
|
|
47
|
+
const value = vars.flagMode ?? defaults.flagMode!
|
|
48
|
+
if (value !== "true" && value !== "false")
|
|
49
|
+
refuse(`gtd: flagMode must be "true" or "false", got ${JSON.stringify(value)}`)
|
|
50
|
+
return value === "true"
|
|
51
|
+
}
|
|
52
|
+
|
|
38
53
|
/** A shipped flow as a factory: its options override the settings it declares, each in its own kind. */
|
|
39
54
|
export const shipped = (
|
|
40
55
|
flow: Flow,
|
|
@@ -73,12 +73,6 @@ classified list of concerns, and hold the running product conversation
|
|
|
73
73
|
with the human it depends on. You are read by that human — write
|
|
74
74
|
plainly — not by a parser, except where a state says otherwise.`
|
|
75
75
|
|
|
76
|
-
export const architectPersona = `You are the technical planning voice in gtd's build pipeline: take over
|
|
77
|
-
once product concerns are settled, reading them cold, no carried
|
|
78
|
-
conversation. Work out the *how* per concern — structure, data models,
|
|
79
|
-
tech-stack choices, error handling — raise the technical open
|
|
80
|
-
questions, then pin interfaces and tests in the architecture document.`
|
|
81
|
-
|
|
82
76
|
export const reviewerPersona = `You are the independent reviewing mind in gtd's build pipeline —
|
|
83
77
|
deliberately separate from whoever wrote the code, with no attachment
|
|
84
78
|
to it. Write a structured review document grouping a diff into
|
|
@@ -88,11 +82,10 @@ and, when asked, fix the small nits the human flagged and the risks
|
|
|
88
82
|
you marked yourself, each in one batch.
|
|
89
83
|
Beyond that you never fix or build anything yourself.`
|
|
90
84
|
|
|
91
|
-
export const builderPersona = `You are the TDD implementer in gtd's build pipeline: build
|
|
92
|
-
|
|
85
|
+
export const builderPersona = `You are the TDD implementer in gtd's build pipeline: build the
|
|
86
|
+
task in front of you end to end, tests first, and come back to fix
|
|
93
87
|
things when redirected — a failing check, or reviewer feedback. Stay
|
|
94
|
-
strictly inside
|
|
95
|
-
package's files or code outside the task. Treat feedback as the
|
|
88
|
+
strictly inside that task; never touch code outside it. Treat feedback as the
|
|
96
89
|
diagnosis to act on; discard it, with reason, only when simply wrong.`
|
|
97
90
|
|
|
98
91
|
export const finisherPersona = `You are the closing identity in gtd's build pipeline — the last coder
|
|
@@ -136,7 +129,7 @@ export const questionBar = `- The goal is shared understanding, not a quota or a
|
|
|
136
129
|
settle it (treat an explicit \`.gtd/TODO.md\` directive, a committed
|
|
137
130
|
hand-edit, or — on a loop-back lap — the human's own review-round
|
|
138
131
|
edit/note as settled); or it would be expensive to undo once
|
|
139
|
-
|
|
132
|
+
code is written
|
|
140
133
|
- Where no gap in shared understanding exists, decide it yourself:
|
|
141
134
|
record it under \`## Answered Questions\` as
|
|
142
135
|
\`### <the point phrased as a question>\`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Summary } from "../../flows/index.js"
|
|
2
2
|
import * as t from "./text.js"
|
|
3
|
-
import {
|
|
3
|
+
import { REQUIREMENTS, REVIEW } from "./steps.js"
|
|
4
4
|
|
|
5
5
|
export { defaults, envDefaults } from "./vars.js"
|
|
6
6
|
export { skills } from "./skills.js"
|
|
@@ -8,4 +8,4 @@ export { access } from "./access.js"
|
|
|
8
8
|
|
|
9
9
|
export const summary: Summary = t.summaryPrompt
|
|
10
10
|
|
|
11
|
-
export const steering = { [REQUIREMENTS]: "qa", [
|
|
11
|
+
export const steering = { [REQUIREMENTS]: "qa", [REVIEW]: "review" }
|
|
@@ -14,11 +14,6 @@ describe("the bundled workflow's skills export", () => {
|
|
|
14
14
|
expect(Object.keys(bundled).sort()).toEqual(
|
|
15
15
|
[
|
|
16
16
|
"design",
|
|
17
|
-
"architecture",
|
|
18
|
-
"architecture.decompose",
|
|
19
|
-
"packages.item",
|
|
20
|
-
"packages.item.fix.suite",
|
|
21
|
-
"packages.item.health",
|
|
22
17
|
"build",
|
|
23
18
|
"build.health",
|
|
24
19
|
"build.fix.quality",
|
|
@@ -54,7 +49,6 @@ describe("the bundled workflow's skills export", () => {
|
|
|
54
49
|
|
|
55
50
|
it("keys no group scope or non-agent step", () => {
|
|
56
51
|
for (const group of [
|
|
57
|
-
"packages.item.fix",
|
|
58
52
|
"build.fix",
|
|
59
53
|
"build.review.fix",
|
|
60
54
|
"build.quality",
|
|
@@ -70,11 +64,6 @@ describe("the bundled workflow's skills export", () => {
|
|
|
70
64
|
it("pins each scope's list", () => {
|
|
71
65
|
expect(bundled).toMatchObject({
|
|
72
66
|
design: ["spec-driven-development", "planning-and-task-breakdown"],
|
|
73
|
-
architecture: ["api-and-interface-design", "documentation-and-adrs", "ponytail"],
|
|
74
|
-
"architecture.decompose": ["incremental-implementation", "planning-and-task-breakdown"],
|
|
75
|
-
"packages.item": ["test-driven-development", "incremental-implementation"],
|
|
76
|
-
"packages.item.fix.suite": ["debugging-and-error-recovery"],
|
|
77
|
-
"packages.item.health": ["debugging-and-error-recovery"],
|
|
78
67
|
build: ["debugging-and-error-recovery"],
|
|
79
68
|
"build.health": ["debugging-and-error-recovery"],
|
|
80
69
|
"build.fix.quality": ["incremental-implementation", "code-simplification"],
|
|
@@ -4,7 +4,7 @@ import { lensesOf, lensScopes } from "../../workflow-kit/index.js"
|
|
|
4
4
|
// The bundled workflow's default skills, one list per SCOPE, keyed by the
|
|
5
5
|
// scope's full name — the same name a `config.skills` entry addresses. A
|
|
6
6
|
// nested scope with no entry inherits its parent's; group scopes
|
|
7
|
-
// (`
|
|
7
|
+
// (`implement.fix`, `build.fix`, `build.review.fix`, `build.quality`, `refactor.fix`) run
|
|
8
8
|
// no turn and carry no key. `build.quality.<lens>` is one key per
|
|
9
9
|
// `qualityReviews` entry, so the export is a function of the vars.
|
|
10
10
|
export const skills = (
|
|
@@ -20,12 +20,6 @@ export const skills = (
|
|
|
20
20
|
}
|
|
21
21
|
return {
|
|
22
22
|
design: ["spec-driven-development", "planning-and-task-breakdown"],
|
|
23
|
-
architecture: ["api-and-interface-design", "documentation-and-adrs", "ponytail"],
|
|
24
|
-
"architecture.decompose": ["incremental-implementation", "planning-and-task-breakdown"],
|
|
25
|
-
"packages.item": ["test-driven-development", "incremental-implementation"],
|
|
26
|
-
"packages.item.fix.suite": ["debugging-and-error-recovery"],
|
|
27
|
-
// `health` is shared code reached from both `packages.item` and `build`.
|
|
28
|
-
"packages.item.health": ["debugging-and-error-recovery"],
|
|
29
23
|
// `fix`'s turn over the notes a review handed on.
|
|
30
24
|
notes: ["incremental-implementation", "code-simplification"],
|
|
31
25
|
// The refactor's own turns load `ponytail`; `refactor.build.*` carry the `build.*` lists.
|
|
@@ -34,11 +28,10 @@ export const skills = (
|
|
|
34
28
|
refactor: ["ponytail"],
|
|
35
29
|
"refactor.fix.suite": ["ponytail", "debugging-and-error-recovery"],
|
|
36
30
|
"refactor.health": ["ponytail", "debugging-and-error-recovery"],
|
|
37
|
-
// Declared whether or not `naive_pass` is on, so a config keyed on them keeps
|
|
38
|
-
// loading when the flag flips off.
|
|
39
31
|
seam: ["incremental-implementation"],
|
|
40
32
|
implement: ["test-driven-development", "incremental-implementation"],
|
|
41
33
|
"implement.fix.suite": ["debugging-and-error-recovery"],
|
|
34
|
+
// `health` is shared code reached from both `implement` and `build`.
|
|
42
35
|
"implement.health": ["debugging-and-error-recovery"],
|
|
43
36
|
...lensScopes(
|
|
44
37
|
"build.quality",
|
|
@@ -10,7 +10,6 @@ import * as t from "./text.js"
|
|
|
10
10
|
export const FEEDBACK = ".gtd/FEEDBACK.md"
|
|
11
11
|
export const ESCALATION = ".gtd/ESCALATION.md"
|
|
12
12
|
export const REQUIREMENTS = ".gtd/REQUIREMENTS.md"
|
|
13
|
-
export const ARCHITECTURE = ".gtd/ARCHITECTURE.md"
|
|
14
13
|
export const REVIEW = ".gtd/REVIEW.md"
|
|
15
14
|
export const REFACTOR = ".gtd/REFACTOR.md"
|
|
16
15
|
export const QUALITY = ".gtd/QUALITY.md"
|
|
@@ -8,10 +8,6 @@ import {
|
|
|
8
8
|
deferNotesScript,
|
|
9
9
|
buildReviewReviewingPrompt,
|
|
10
10
|
agentWithSkills,
|
|
11
|
-
architectureAuthorPrompt,
|
|
12
|
-
architectureDecomposePrompt,
|
|
13
|
-
packagesItemBuildingPrompt,
|
|
14
|
-
architectureGateAnswerMessage,
|
|
15
11
|
buildFixPrompt,
|
|
16
12
|
buildFixQualityPrompt,
|
|
17
13
|
buildReviewFixNitsPrompt,
|
|
@@ -167,23 +163,6 @@ describe("the shared open-question instruction", () => {
|
|
|
167
163
|
expect(prompt).toContain("one to four bullets, free-form")
|
|
168
164
|
expect(prompt).toContain("labels, no fixed bullet count")
|
|
169
165
|
})
|
|
170
|
-
|
|
171
|
-
it("is the identical block, full text, in both the design gate and the architecture gate", () => {
|
|
172
|
-
// Anchored at questionBar's own first and last lines (not a bullet
|
|
173
|
-
// partway in) so the whole ~3.4k-char block is compared — a divergence
|
|
174
|
-
// anywhere inside, including its opening bullets, fails this test the
|
|
175
|
-
// moment either gate's copy drifts from the shared source.
|
|
176
|
-
const start = "The goal is shared understanding, not a quota or an empty section"
|
|
177
|
-
const end = "Never tick a box yourself — the human ticks exactly one per question"
|
|
178
|
-
const sharedBlock = (prompt: string): string =>
|
|
179
|
-
prompt.slice(prompt.indexOf(start), prompt.indexOf(end) + end.length)
|
|
180
|
-
|
|
181
|
-
const designBlock = sharedBlock(renderText(() => designTriagePrompt("base")))
|
|
182
|
-
const architectureBlock = sharedBlock(renderText(() => architectureAuthorPrompt()))
|
|
183
|
-
|
|
184
|
-
expect(designBlock.length).toBeGreaterThan(3000)
|
|
185
|
-
expect(designBlock).toBe(architectureBlock)
|
|
186
|
-
})
|
|
187
166
|
})
|
|
188
167
|
|
|
189
168
|
describe("answered-entry footnote exception", () => {
|
|
@@ -196,32 +175,6 @@ describe("answered-entry footnote exception", () => {
|
|
|
196
175
|
expect(prompt).not.toContain("never re-open a settled")
|
|
197
176
|
expect(prompt).not.toContain("never re-open one")
|
|
198
177
|
})
|
|
199
|
-
|
|
200
|
-
it("is in the architecture return lap and keeps the first-lap settled line", () => {
|
|
201
|
-
const prompt = renderText(() => architectureAuthorPrompt())
|
|
202
|
-
expect(prompt).toContain(`entry stays settled, except one ${exception}`)
|
|
203
|
-
expect(prompt).not.toContain("never re-open a settled")
|
|
204
|
-
expect(prompt).toMatch(/treat every decision[\s\S]*as settled — never re-open it/)
|
|
205
|
-
})
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
describe("architectureAuthorPrompt", () => {
|
|
209
|
-
it("qualifies the PERMISSIVE default with a settled-requirements exception", () => {
|
|
210
|
-
const prompt = renderText(() => architectureAuthorPrompt())
|
|
211
|
-
expect(prompt).toContain("is an open question, not a")
|
|
212
|
-
})
|
|
213
|
-
it("names the four content sections in order and the entry forms", () => {
|
|
214
|
-
const prompt = renderText(() => architectureAuthorPrompt())
|
|
215
|
-
const at = ["## Interfaces", "## Call Stacks", "## E2E Scenarios", "## Unit Tests"].map((h) =>
|
|
216
|
-
prompt.indexOf(h),
|
|
217
|
-
)
|
|
218
|
-
expect(at.every((i) => i >= 0)).toBe(true)
|
|
219
|
-
expect([...at].sort((a, b) => a - b)).toEqual(at)
|
|
220
|
-
expect(prompt).toContain("- unit: <path>")
|
|
221
|
-
expect(prompt).toContain("- e2e: <path>")
|
|
222
|
-
expect(prompt).toContain("- chore: <path>")
|
|
223
|
-
expect(prompt).toContain("No e2e change.")
|
|
224
|
-
})
|
|
225
178
|
})
|
|
226
179
|
|
|
227
180
|
describe("buildFixPrompt", () => {
|
|
@@ -688,16 +641,11 @@ Print the closing message and stop — this writes nothing itself.
|
|
|
688
641
|
it("treats a flag-on process with no tier mark as high", () => {
|
|
689
642
|
const prompt = summaryPrompt({
|
|
690
643
|
...base,
|
|
691
|
-
vars: { flag_risk_tier: "1"
|
|
644
|
+
vars: { flag_risk_tier: "1" },
|
|
692
645
|
})
|
|
693
646
|
expect(prompt).toContain("Risk tier: high")
|
|
694
647
|
})
|
|
695
648
|
|
|
696
|
-
it("marks no tier when the risk-tier flag is on without naive-pass", () => {
|
|
697
|
-
const prompt = summaryPrompt({ ...base, vars: { flag_risk_tier: "1", flag_naive_pass: "0" } })
|
|
698
|
-
expect(prompt).not.toContain("Risk tier")
|
|
699
|
-
})
|
|
700
|
-
|
|
701
649
|
it("says the human left no comment or edit this process when humanCommits is empty", () => {
|
|
702
650
|
expect(summaryPrompt(base)).toContain(
|
|
703
651
|
"The human left no comment or edit this process —\nevery commit is machine-authored.",
|
|
@@ -812,10 +760,9 @@ describe("buildReviewCollectingPrompt without a refactor", () => {
|
|
|
812
760
|
})
|
|
813
761
|
|
|
814
762
|
describe("the thread rules", () => {
|
|
815
|
-
it("are in
|
|
763
|
+
it("are in both answering prompts and no longer say a footnote is human input only", () => {
|
|
816
764
|
const prompts = renderText(() => [
|
|
817
765
|
designTriagePrompt("base"),
|
|
818
|
-
architectureAuthorPrompt(),
|
|
819
766
|
buildReviewCollectingPrompt("capture"),
|
|
820
767
|
])
|
|
821
768
|
for (const prompt of prompts) {
|
|
@@ -837,13 +784,9 @@ describe("code threads in prompts", () => {
|
|
|
837
784
|
{ path: "src/b.ts", line: 9, waitingOn: "human", first: "done", faults: [] },
|
|
838
785
|
] as const
|
|
839
786
|
|
|
840
|
-
it("lists the code threads waiting on the agent in
|
|
787
|
+
it("lists the code threads waiting on the agent in both gates", () => {
|
|
841
788
|
const prompts = renderText(
|
|
842
|
-
() => [
|
|
843
|
-
designTriagePrompt("base"),
|
|
844
|
-
architectureAuthorPrompt(),
|
|
845
|
-
buildReviewCollectingPrompt("capture"),
|
|
846
|
-
],
|
|
789
|
+
() => [designTriagePrompt("base"), buildReviewCollectingPrompt("capture")],
|
|
847
790
|
{ codeThreads: waiting },
|
|
848
791
|
)
|
|
849
792
|
for (const prompt of prompts) {
|
|
@@ -866,7 +809,6 @@ describe("code threads in prompts", () => {
|
|
|
866
809
|
it("teaches the syntax once in the footnote rules", () => {
|
|
867
810
|
for (const message of renderText(() => [
|
|
868
811
|
designGateAnswerMessage(),
|
|
869
|
-
architectureGateAnswerMessage(),
|
|
870
812
|
buildReviewAwaitReviewMessage("base"),
|
|
871
813
|
])) {
|
|
872
814
|
expect(message).toContain("gtd check --open-threads")
|
|
@@ -1048,6 +990,22 @@ describe("the seam phase's texts", () => {
|
|
|
1048
990
|
expect(prompt).toContain("Implement them properly")
|
|
1049
991
|
})
|
|
1050
992
|
|
|
993
|
+
it("flag mode off asks for the naive version and no release flag, whatever the tier or lap", () => {
|
|
994
|
+
for (const [tier, amend] of [
|
|
995
|
+
["high", false],
|
|
996
|
+
["low", false],
|
|
997
|
+
["high", true],
|
|
998
|
+
] as const) {
|
|
999
|
+
const prompt = renderText(() => implementBuildingPrompt(undefined, tier, amend, false, false))
|
|
1000
|
+
expect(prompt).toContain("Add no release flag — this project ships without release flags")
|
|
1001
|
+
expect(prompt).not.toContain("new release flag")
|
|
1002
|
+
expect(prompt).not.toContain("low risk tier")
|
|
1003
|
+
}
|
|
1004
|
+
expect(
|
|
1005
|
+
renderText(() => implementBuildingPrompt(undefined, "high", false, false, false)),
|
|
1006
|
+
).toContain("the quickest way that works")
|
|
1007
|
+
})
|
|
1008
|
+
|
|
1051
1009
|
it("keeps a fix turn to the feedback file's rules", () => {
|
|
1052
1010
|
expect(renderText(() => implementFixPrompt())).toContain(".gtd/FEEDBACK.md")
|
|
1053
1011
|
})
|
|
@@ -1306,45 +1264,3 @@ it("asks the refactor review, and only it, to flag a test-file hunk that is more
|
|
|
1306
1264
|
expect(refactor).toContain("give any test-file hunk its own chunk")
|
|
1307
1265
|
expect(first).not.toContain("give any test-file hunk its own chunk")
|
|
1308
1266
|
})
|
|
1309
|
-
|
|
1310
|
-
describe("architectureDecomposePrompt tests rule", () => {
|
|
1311
|
-
it("states one rule: write-or-change, relied-on marked (unchanged), no other untouched", () => {
|
|
1312
|
-
const prompt = renderText(() => architectureDecomposePrompt()).replace(/\s+/g, " ")
|
|
1313
|
-
expect(prompt).toContain("List every test the package writes or changes")
|
|
1314
|
-
expect(prompt).toContain("also list an existing test a criterion relies on")
|
|
1315
|
-
expect(prompt).toContain("list no other untouched test")
|
|
1316
|
-
expect(prompt).toContain("- unit: \\`<path>\\` (unchanged)")
|
|
1317
|
-
expect(prompt).toContain("- e2e: \\`<path>\\` (unchanged)")
|
|
1318
|
-
expect(prompt).toContain("only decompose places the marker")
|
|
1319
|
-
})
|
|
1320
|
-
})
|
|
1321
|
-
|
|
1322
|
-
describe("packagesItemBuildingPrompt", () => {
|
|
1323
|
-
const prompt = renderText(() => packagesItemBuildingPrompt(".gtd/packages/01-a.md")).replace(
|
|
1324
|
-
/\s+/g,
|
|
1325
|
-
" ",
|
|
1326
|
-
)
|
|
1327
|
-
it("carries the evidence kinds and the already-done check", () => {
|
|
1328
|
-
for (const w of [
|
|
1329
|
-
"evidence per criterion",
|
|
1330
|
-
"commit, file, symbol, issue URL, or command output — and change nothing else",
|
|
1331
|
-
"it was not already done",
|
|
1332
|
-
"via `gh`",
|
|
1333
|
-
])
|
|
1334
|
-
expect(prompt).toContain(w)
|
|
1335
|
-
})
|
|
1336
|
-
it("states the already-met, no-tests out-of-tree and implement-normally rules flat", () => {
|
|
1337
|
-
expect(prompt).toContain("Every criterion already met in the tree")
|
|
1338
|
-
expect(prompt).toContain("No `## Tests` and work outside the tree")
|
|
1339
|
-
expect(prompt).toContain(
|
|
1340
|
-
"Any other package**: implement normally, write no `.gtd/SATISFIED.md`",
|
|
1341
|
-
)
|
|
1342
|
-
})
|
|
1343
|
-
it("says an (unchanged) line must exist and stay untouched", () => {
|
|
1344
|
-
expect(prompt).toContain("(unchanged)")
|
|
1345
|
-
expect(prompt).toContain("stay untouched")
|
|
1346
|
-
expect(prompt).toContain("creating, editing or deleting it refuses the turn")
|
|
1347
|
-
expect(prompt).toContain("must exist before the turn")
|
|
1348
|
-
expect(prompt).toContain("stop without creating it")
|
|
1349
|
-
})
|
|
1350
|
-
})
|