@pmelab/gtd 29.3.1 → 30.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 +55 -63
- package/dist/chunks/{reserved-Dmpg6dAp.mjs → reserved-iG86IR3I.mjs} +19 -233
- package/dist/chunks/{tail-CLyE5VFR.mjs → tail-DA2UuJNP.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 +14 -180
- 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 +29 -409
- 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 +74 -63
- package/src/workflows/feature/feature.ts +13 -35
- 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/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 +2 -3
- 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 +4 -104
- package/src/workflows/shared/text.ts +15 -223
- package/src/workflows/shared/vars.ts +1 -3
- package/src/workflows/feature/packages.test.ts +0 -368
- package/src/workflows/feature/packages.ts +0 -173
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
@inmem
|
|
2
2
|
Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_TIER
|
|
3
3
|
|
|
4
|
-
With
|
|
4
|
+
With GTD_FLAG_RISK_TIER on, design.triage asks
|
|
5
5
|
for a risk tier and production signals, and the design gate's exit lands them
|
|
6
6
|
as Gtd-Mark trailers: one `risk-tier=<low|high>`, one `production-signal`
|
|
7
7
|
per signal. A missing or unreadable tier is high. With the flag off, nothing
|
|
@@ -10,7 +10,6 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
|
|
|
10
10
|
Background:
|
|
11
11
|
Given the shipped workflows, as gtd install scaffolds them
|
|
12
12
|
And an environment variable "GTD_QUALITYREVIEWS" set to ""
|
|
13
|
-
And an environment variable "GTD_FLAG_NAIVE_PASS" set to "1"
|
|
14
13
|
|
|
15
14
|
Rule: with GTD_FLAG_RISK_TIER on
|
|
16
15
|
|
|
@@ -35,7 +34,7 @@ Feature: the risk tier is proposed at triage and recorded behind GTD_FLAG_RISK_T
|
|
|
35
34
|
And step "unwind" changes nothing
|
|
36
35
|
And step "start-gate.check" changes nothing
|
|
37
36
|
|
|
38
|
-
Scenario: with
|
|
37
|
+
Scenario: with the flag on the triage prompt asks for the tier and the signals
|
|
39
38
|
When the process is driven
|
|
40
39
|
Then the process rests at "design.triage"
|
|
41
40
|
And the resting step's content contains "## Risk Tier"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
@inmem
|
|
3
3
|
Feature: rejected naive passes walk one fixed path through the steer gate to idle
|
|
4
4
|
|
|
5
|
-
With
|
|
5
|
+
With GTD_FLAG_RISK_TIER off, three rejected naive
|
|
6
6
|
passes rest at the steer gate, where a human edit restarts the seam and the
|
|
7
7
|
steered rerun lands. The flags are pinned so the per-flag test runs never
|
|
8
8
|
move it off its path.
|
|
@@ -10,7 +10,6 @@ Feature: rejected naive passes walk one fixed path through the steer gate to idl
|
|
|
10
10
|
Background:
|
|
11
11
|
Given the shipped workflows, as gtd install scaffolds them
|
|
12
12
|
And an environment variable "GTD_QUALITYREVIEWS" set to ""
|
|
13
|
-
And an environment variable "GTD_FLAG_NAIVE_PASS" set to "1"
|
|
14
13
|
And an environment variable "GTD_FLAG_RISK_TIER" set to "0"
|
|
15
14
|
And a file "src/calc.ts" with:
|
|
16
15
|
"""
|
|
@@ -136,7 +135,6 @@ Feature: rejected naive passes walk one fixed path through the steer gate to idl
|
|
|
136
135
|
gtd(human): build.review.await-review → build.review.closing
|
|
137
136
|
gtd(check): build.review.closing → idle
|
|
138
137
|
"""
|
|
139
|
-
And the commit "gtd(human): idle → unwind" has a body containing "Gtd-Var: flag_naive_pass=1"
|
|
140
138
|
And the commit "gtd(human): idle → unwind" has a body containing "Gtd-Var: flag_risk_tier=0"
|
|
141
139
|
And the commit "gtd(agent): seam.naive → seam.judge" has a body containing "Gtd-Kept:"
|
|
142
140
|
And the commit "gtd(judge): seam.judge → seam.naive" has a body containing "\"answer\":\"reject\""
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
@inmem
|
|
2
|
-
Feature: the naive pass and seam judge
|
|
2
|
+
Feature: the naive pass and seam judge decide how a feature is built
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
off nothing changes: architecture and packages run as before.
|
|
4
|
+
After design a naive pass is set aside under a kept ref, the judge reads its
|
|
5
|
+
diff, and the process either implements straight away, refactors the seam
|
|
6
|
+
first behind a human approval, or retries the naive pass.
|
|
8
7
|
|
|
9
8
|
Background:
|
|
10
9
|
Given the shipped workflows, as gtd install scaffolds them
|
|
@@ -17,7 +16,6 @@ Feature: the naive pass and seam judge replace architecture behind GTD_FLAG_NAIV
|
|
|
17
16
|
}
|
|
18
17
|
"""
|
|
19
18
|
And an environment variable "GTD_QUALITYREVIEWS" set to ""
|
|
20
|
-
And an environment variable "GTD_FLAG_NAIVE_PASS" set to "1"
|
|
21
19
|
And a file "src/calc.ts" with:
|
|
22
20
|
"""
|
|
23
21
|
export const add = (a: number, b: number) => a + b
|
|
@@ -21,26 +21,6 @@ describe("the feature workflow's own steps declare skills — a step's rendered
|
|
|
21
21
|
expect(prompt).toContain("spec-driven-development, planning-and-task-breakdown")
|
|
22
22
|
})
|
|
23
23
|
|
|
24
|
-
it("author carries architecture.author's bundled skills", async () => {
|
|
25
|
-
const prompt = agentPrompt(await capture(() => steps.author(), "architecture"))
|
|
26
|
-
expect(prompt).toContain("api-and-interface-design, documentation-and-adrs, ponytail")
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it("decompose carries architecture.decompose's bundled skills", async () => {
|
|
30
|
-
const prompt = agentPrompt(await capture(() => steps.decompose(), "architecture"))
|
|
31
|
-
expect(prompt).toContain("incremental-implementation, planning-and-task-breakdown")
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it("build carries packages.item.building's bundled skills", async () => {
|
|
35
|
-
const prompt = agentPrompt(await capture(() => steps.build("pkg"), "packages.item"))
|
|
36
|
-
expect(prompt).toContain("test-driven-development, incremental-implementation")
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
it("fixSuite carries packages.item.fix.suite's bundled skills", async () => {
|
|
40
|
-
const prompt = agentPrompt(await capture(() => steps.fixSuite(), "packages.item"))
|
|
41
|
-
expect(prompt).toContain("debugging-and-error-recovery")
|
|
42
|
-
})
|
|
43
|
-
|
|
44
24
|
it("collecting carries build.review.collecting's bundled skills", async () => {
|
|
45
25
|
const prompt = agentPrompt(await capture(() => steps.collecting("capture"), "build"))
|
|
46
26
|
expect(prompt).toContain("code-review-and-quality")
|
|
@@ -2,15 +2,7 @@ import { human, run, scope, type ScopeAccess } from "../../flows/index.js"
|
|
|
2
2
|
import type { CollectRound } from "../../workflow-kit/index.js"
|
|
3
3
|
import * as t from "../shared/text.js"
|
|
4
4
|
import { riskTierOn } from "./tier.js"
|
|
5
|
-
import {
|
|
6
|
-
ARCHITECTURE,
|
|
7
|
-
coder,
|
|
8
|
-
FEEDBACK,
|
|
9
|
-
planner,
|
|
10
|
-
REFACTOR,
|
|
11
|
-
REQUIREMENTS,
|
|
12
|
-
REVIEW,
|
|
13
|
-
} from "../shared/steps.js"
|
|
5
|
+
import { planner, REFACTOR, REQUIREMENTS, REVIEW } from "../shared/steps.js"
|
|
14
6
|
|
|
15
7
|
/** Turn the sketch since `base` into `.gtd/REQUIREMENTS.md`. */
|
|
16
8
|
export const triage = (base: string): Promise<void> =>
|
|
@@ -22,22 +14,6 @@ export const triage = (base: string): Promise<void> =>
|
|
|
22
14
|
system: t.designSystem(),
|
|
23
15
|
})
|
|
24
16
|
|
|
25
|
-
export const author = (): Promise<void> =>
|
|
26
|
-
t.agentWithSkills("author", t.architectureAuthorPrompt(), {
|
|
27
|
-
label: "Refining the technical plan",
|
|
28
|
-
file: ARCHITECTURE,
|
|
29
|
-
mode: "qa",
|
|
30
|
-
model: planner(),
|
|
31
|
-
system: t.architectSystem(),
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
export const decompose = (): Promise<void> =>
|
|
35
|
-
t.agentWithSkills("decompose.decomposing", t.architectureDecomposePrompt(), {
|
|
36
|
-
label: "Decomposing into packages",
|
|
37
|
-
model: planner(),
|
|
38
|
-
system: t.architectSystem(),
|
|
39
|
-
})
|
|
40
|
-
|
|
41
17
|
export const answerProductQuestions = (): Promise<void> =>
|
|
42
18
|
human("gate.answer", {
|
|
43
19
|
message: t.designGateAnswerMessage(riskTierOn()),
|
|
@@ -47,30 +23,6 @@ export const answerProductQuestions = (): Promise<void> =>
|
|
|
47
23
|
acceptClean: true,
|
|
48
24
|
})
|
|
49
25
|
|
|
50
|
-
export const answerTechnicalQuestions = (): Promise<void> =>
|
|
51
|
-
human("gate.answer", {
|
|
52
|
-
message: t.architectureGateAnswerMessage(),
|
|
53
|
-
label: "Awaiting your technical answers",
|
|
54
|
-
file: ARCHITECTURE,
|
|
55
|
-
mode: "qa",
|
|
56
|
-
acceptClean: true,
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
export const build = (pkg: string): Promise<void> =>
|
|
60
|
-
t.agentWithSkills("building", t.packagesItemBuildingPrompt(pkg), {
|
|
61
|
-
label: "Building",
|
|
62
|
-
model: coder(),
|
|
63
|
-
system: t.builderSystem(),
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
export const fixSuite = (): Promise<void> =>
|
|
67
|
-
t.agentWithSkills("fix.suite.fixing", t.packagesItemFixSuitePrompt(), {
|
|
68
|
-
label: "Fixing the check",
|
|
69
|
-
file: FEEDBACK,
|
|
70
|
-
model: coder(),
|
|
71
|
-
system: t.builderSystem(),
|
|
72
|
-
})
|
|
73
|
-
|
|
74
26
|
/** Answer every `question` note inline in `.gtd/REVIEW.md`. */
|
|
75
27
|
export const answerReviewQuestions = (notes: readonly t.NoteInput[]): Promise<void> =>
|
|
76
28
|
t.agentWithSkills("review.answer-review-questions", t.buildReviewAnswerQuestionsPrompt(notes), {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
@inmem
|
|
2
|
-
Feature: a process keeps the
|
|
2
|
+
Feature: a process keeps the risk tier setting it started with when GTD_FLAG_RISK_TIER changes
|
|
3
3
|
|
|
4
4
|
The flag is recorded in the opening commit, so flipping it mid-process never
|
|
5
|
-
|
|
5
|
+
changes whether the process asks for and records a risk tier.
|
|
6
6
|
|
|
7
7
|
Background:
|
|
8
8
|
Given the shipped workflows, as gtd install scaffolds them
|
|
@@ -29,24 +29,30 @@ Feature: a process keeps the path it started on when GTD_FLAG_NAIVE_PASS changes
|
|
|
29
29
|
### Subtract
|
|
30
30
|
|
|
31
31
|
- `subtract(a, b)` returns `a - b`.
|
|
32
|
+
|
|
33
|
+
## Risk Tier
|
|
34
|
+
|
|
35
|
+
low — a new pure function
|
|
32
36
|
"""
|
|
33
37
|
|
|
34
|
-
Scenario: a process started with the flag off
|
|
35
|
-
Given the ambient environment variable "
|
|
38
|
+
Scenario: a process started with the flag off records no tier after the flag turns on
|
|
39
|
+
Given the ambient environment variable "GTD_FLAG_RISK_TIER" is "0"
|
|
36
40
|
When the process is driven
|
|
37
41
|
Then the process rests at "design.gate.answer"
|
|
38
|
-
Given the ambient environment variable "
|
|
42
|
+
Given the ambient environment variable "GTD_FLAG_RISK_TIER" is "1"
|
|
39
43
|
And step "design.gate.answer" changes nothing
|
|
40
44
|
When the process is driven
|
|
41
|
-
Then the process rests at "
|
|
42
|
-
And the commit "gtd(human): idle → unwind" has a body containing "Gtd-Var:
|
|
45
|
+
Then the process rests at "seam.naive"
|
|
46
|
+
And the commit "gtd(human): idle → unwind" has a body containing "Gtd-Var: flag_risk_tier=0"
|
|
47
|
+
And no commit body contains "Gtd-Mark:"
|
|
43
48
|
|
|
44
|
-
Scenario: a process started with the flag on
|
|
45
|
-
Given the ambient environment variable "
|
|
49
|
+
Scenario: a process started with the flag on records its tier after the flag turns off
|
|
50
|
+
Given the ambient environment variable "GTD_FLAG_RISK_TIER" is "1"
|
|
46
51
|
When the process is driven
|
|
47
52
|
Then the process rests at "design.gate.answer"
|
|
48
|
-
Given the ambient environment variable "
|
|
53
|
+
Given the ambient environment variable "GTD_FLAG_RISK_TIER" is "0"
|
|
49
54
|
And step "design.gate.answer" changes nothing
|
|
50
55
|
When the process is driven
|
|
51
56
|
Then the process rests at "seam.naive"
|
|
52
|
-
And the commit "gtd(human): idle → unwind" has a body containing "Gtd-Var:
|
|
57
|
+
And the commit "gtd(human): idle → unwind" has a body containing "Gtd-Var: flag_risk_tier=1"
|
|
58
|
+
And the last commit body contains "Gtd-Mark: risk-tier=low"
|
|
@@ -9,11 +9,9 @@ const withVars = (vars: Record<string, string>) => installContext(fixtureContext
|
|
|
9
9
|
|
|
10
10
|
describe("riskTierOn", () => {
|
|
11
11
|
it.each([
|
|
12
|
-
[{
|
|
13
|
-
[{
|
|
14
|
-
[{
|
|
15
|
-
[{ flag_naive_pass: "0", flag_risk_tier: "0" }, false],
|
|
16
|
-
[{ flag_naive_pass: "1" }, false],
|
|
12
|
+
[{ flag_risk_tier: "1" }, true],
|
|
13
|
+
[{ flag_risk_tier: "0" }, false],
|
|
14
|
+
[{}, false],
|
|
17
15
|
])("%j is %s", (vars, expected) => {
|
|
18
16
|
withVars(vars)
|
|
19
17
|
expect(riskTierOn()).toBe(expected)
|
|
@@ -4,8 +4,8 @@ import { REQUIREMENTS } from "../shared/steps.js"
|
|
|
4
4
|
|
|
5
5
|
export type RiskTier = "low" | "high"
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
export const riskTierOn = (): boolean => vars.
|
|
7
|
+
/** GTD_FLAG_RISK_TIER: pinned at process start, so a mid-process flip never moves a process. */
|
|
8
|
+
export const riskTierOn = (): boolean => vars.flag_risk_tier === "1"
|
|
9
9
|
|
|
10
10
|
const sectionLines = (
|
|
11
11
|
text: string | undefined,
|
package/src/workflows/index.ts
CHANGED
|
@@ -22,7 +22,6 @@ export {
|
|
|
22
22
|
export { lensesOf } from "../workflow-kit/index.js"
|
|
23
23
|
export * from "./feature/steps.js"
|
|
24
24
|
export * from "./feature/planning.js"
|
|
25
|
-
export * from "./feature/packages.js"
|
|
26
25
|
export * from "./feature/refactor.js"
|
|
27
26
|
export * from "./feature/seam.js"
|
|
28
27
|
export {
|
|
@@ -10,8 +10,6 @@ describe("the bundled workflow's access export", () => {
|
|
|
10
10
|
it("restricts writes on planning, review and lens scopes only", () => {
|
|
11
11
|
expect(bundled).toEqual({
|
|
12
12
|
design: { write: [] },
|
|
13
|
-
architecture: { write: [".gtd/REQUIREMENTS.md"] },
|
|
14
|
-
"architecture.decompose": { write: [".gtd/packages/**", ".gtd/ARCHITECTURE.md"] },
|
|
15
13
|
"build.review": { write: [".gtd/REVIEW.md"] },
|
|
16
14
|
"build.review.defer": { write: [".gtd/TODO.md"] },
|
|
17
15
|
"build.review.fix.nits": {},
|
|
@@ -12,8 +12,6 @@ export const access = (
|
|
|
12
12
|
vars: Readonly<Record<string, string>>,
|
|
13
13
|
): Readonly<Record<string, ScopeAccess>> => ({
|
|
14
14
|
design: { write: [] },
|
|
15
|
-
architecture: { write: [".gtd/REQUIREMENTS.md"] },
|
|
16
|
-
"architecture.decompose": { write: [".gtd/packages/**", ".gtd/ARCHITECTURE.md"] },
|
|
17
15
|
"build.review": { write: [".gtd/REVIEW.md"] },
|
|
18
16
|
"build.review.defer": { write: [".gtd/TODO.md"] },
|
|
19
17
|
// `{}` reopens what `build.review` restricted: the fixes edit code.
|
|
@@ -23,11 +23,10 @@ describe("the shipped workflow factories", () => {
|
|
|
23
23
|
expect(defaults).toHaveProperty(`flag_${name}`, defaultOn ? "1" : "0")
|
|
24
24
|
})
|
|
25
25
|
|
|
26
|
-
it("declare seamAllow
|
|
27
|
-
expect(Object.keys(ShippedOptions.fields)).not.toContain("flag_naive_pass")
|
|
26
|
+
it("declare a seamAllow default a factory option overrides", () => {
|
|
28
27
|
expect(configure(feature({ seamAllow: "docs/**" }))).toMatchObject({
|
|
29
28
|
ok: true,
|
|
30
|
-
build: { defaults: { seamAllow: "docs/**"
|
|
29
|
+
build: { defaults: { seamAllow: "docs/**" } },
|
|
31
30
|
})
|
|
32
31
|
})
|
|
33
32
|
|
|
@@ -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")
|
|
@@ -1306,45 +1248,3 @@ it("asks the refactor review, and only it, to flag a test-file hunk that is more
|
|
|
1306
1248
|
expect(refactor).toContain("give any test-file hunk its own chunk")
|
|
1307
1249
|
expect(first).not.toContain("give any test-file hunk its own chunk")
|
|
1308
1250
|
})
|
|
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
|
-
})
|