@pmelab/gtd 17.0.0 → 17.3.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 +9 -0
- package/README.md +59 -23
- package/bin/gtd +3 -0
- package/claude/hooks/drive.ts +169 -0
- package/claude/hooks/entry.ts +34 -0
- package/claude/hooks/handoff.ts +174 -0
- package/claude/hooks/judge.ts +99 -0
- package/claude/hooks/models.ts +19 -0
- package/claude/hooks/register.tsx +828 -0
- package/claude/hooks/ship.ts +450 -0
- package/claude/hooks/wording.ts +85 -0
- package/claude/types/index.d.ts +33 -0
- package/dist/gtd.bundle.mjs +130 -25
- package/hooks/hooks.json +1 -0
- package/package.json +9 -2
- package/skills/authoring/SKILL.md +248 -0
- package/src/workflows/prose.ts +12 -4
- package/src/workflows/review.test.ts +87 -1
- package/src/workflows/review.ts +19 -4
- package/src/workflows/skills.test.ts +2 -1
- package/src/workflows/skills.ts +1 -0
- package/src/workflows/steps.test.ts +53 -0
- package/src/workflows/steps.ts +34 -8
- package/src/workflows/text.test.ts +62 -0
- package/src/workflows/text.ts +75 -12
- package/src/workflows/vars.ts +2 -1
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest"
|
|
2
2
|
import {
|
|
3
|
+
buildReviewReviewingPrompt,
|
|
3
4
|
agentWithSkills,
|
|
4
5
|
architectureAuthorPrompt,
|
|
5
6
|
architectureGateAnswerMessage,
|
|
6
7
|
buildFixQualityPrompt,
|
|
8
|
+
buildQualityReviewingPrompt,
|
|
7
9
|
buildReviewAwaitReviewMessage,
|
|
8
10
|
buildReviewCollectingPrompt,
|
|
9
11
|
designGateAnswerMessage,
|
|
@@ -101,6 +103,25 @@ describe("the shared open-question instruction", () => {
|
|
|
101
103
|
})
|
|
102
104
|
})
|
|
103
105
|
|
|
106
|
+
describe("answered-entry footnote exception", () => {
|
|
107
|
+
const exception = "carrying a human footnote on its"
|
|
108
|
+
it("is in the design return lap and loop-back, keeping the deleted-question rule", () => {
|
|
109
|
+
const prompt = renderText(() => designTriagePrompt("base"))
|
|
110
|
+
expect(prompt).toContain("Never re-raise a deleted question")
|
|
111
|
+
expect(prompt).toContain(`entry stays settled, except one ${exception}`)
|
|
112
|
+
expect(prompt).toContain(`stays settled, except\n one ${exception}`)
|
|
113
|
+
expect(prompt).not.toContain("never re-open a settled")
|
|
114
|
+
expect(prompt).not.toContain("never re-open one")
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it("is in the architecture return lap and keeps the first-lap settled line", () => {
|
|
118
|
+
const prompt = renderText(() => architectureAuthorPrompt())
|
|
119
|
+
expect(prompt).toContain(`entry stays settled, except one ${exception}`)
|
|
120
|
+
expect(prompt).not.toContain("never re-open a settled")
|
|
121
|
+
expect(prompt).toMatch(/treat every decision[\s\S]*as settled — never re-open it/)
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
|
|
104
125
|
describe("architectureAuthorPrompt", () => {
|
|
105
126
|
it("qualifies the PERMISSIVE default with a settled-requirements exception", () => {
|
|
106
127
|
const prompt = renderText(() => architectureAuthorPrompt())
|
|
@@ -116,6 +137,37 @@ describe("buildFixQualityPrompt", () => {
|
|
|
116
137
|
})
|
|
117
138
|
})
|
|
118
139
|
|
|
140
|
+
describe("buildFixQualityPrompt scope", () => {
|
|
141
|
+
it("fixes every finding, blocking or not", () => {
|
|
142
|
+
const prompt = renderText(() => buildFixQualityPrompt())
|
|
143
|
+
expect(prompt).toContain("fix every finding in every chunk, blocking or not")
|
|
144
|
+
expect(prompt).not.toContain("found something blocking")
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
describe("buildQualityReviewingPrompt", () => {
|
|
149
|
+
const prompt = renderText(() => buildQualityReviewingPrompt("x"))
|
|
150
|
+
it("traces, reads touched paths on a large change, and judges tests by reasoning", () => {
|
|
151
|
+
expect(prompt).toContain("Trace, do not skim")
|
|
152
|
+
expect(prompt).toContain("order of external calls against")
|
|
153
|
+
expect(prompt).toContain("read the touched code")
|
|
154
|
+
expect(prompt).toContain("would fail with the guarded")
|
|
155
|
+
expect(prompt).toContain("never by running a")
|
|
156
|
+
expect(prompt).toContain("pins a bug is a finding")
|
|
157
|
+
})
|
|
158
|
+
it("writes every finding and approves only when nothing was found", () => {
|
|
159
|
+
expect(prompt).toContain("APPEND every finding you have, blocking or not")
|
|
160
|
+
expect(prompt).toContain("only when this lens found nothing at all")
|
|
161
|
+
expect(prompt).not.toContain("Write nothing when nothing is blocking")
|
|
162
|
+
})
|
|
163
|
+
it("adds a brief block only when given one", () => {
|
|
164
|
+
expect(prompt).not.toContain("This lens's brief")
|
|
165
|
+
expect(renderText(() => buildQualityReviewingPrompt("x", "BRIEF-TEXT"))).toContain(
|
|
166
|
+
"This lens's brief:\nBRIEF-TEXT",
|
|
167
|
+
)
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
|
|
119
171
|
describe("summaryPrompt", () => {
|
|
120
172
|
it("puts the total and every model's cost on a line of its own", () => {
|
|
121
173
|
const prompt = summaryPrompt({
|
|
@@ -198,3 +250,13 @@ describe("code threads in prompts", () => {
|
|
|
198
250
|
}
|
|
199
251
|
})
|
|
200
252
|
})
|
|
253
|
+
|
|
254
|
+
describe("buildReviewReviewingPrompt risk marking", () => {
|
|
255
|
+
it("states the Risk: marker rule and the re-review rule", () => {
|
|
256
|
+
const prompt = renderText(() => buildReviewReviewingPrompt("base"))
|
|
257
|
+
expect(prompt).toContain("Open a hunk's note with `Risk:` only for a concrete defect")
|
|
258
|
+
expect(prompt).toContain("never a style remark")
|
|
259
|
+
expect(prompt).toContain("describe what the fix changed under the")
|
|
260
|
+
expect(prompt).toContain("re-mark only a risk the fix did not resolve")
|
|
261
|
+
})
|
|
262
|
+
})
|
package/src/workflows/text.ts
CHANGED
|
@@ -191,8 +191,10 @@ ${questionBarReturn}
|
|
|
191
191
|
\`git show ${base}\`. (On the first lap that hash
|
|
192
192
|
is the process's own diff base, \`${start()}\` — this
|
|
193
193
|
branch doesn't apply)
|
|
194
|
-
- Every decision under \`## Answered Questions\` stays settled
|
|
195
|
-
|
|
194
|
+
- Every decision under \`## Answered Questions\` stays settled, except
|
|
195
|
+
one carrying a human footnote on its \`### \` heading — the human's
|
|
196
|
+
review-round edit is where such notes arrive; fold the note in and
|
|
197
|
+
rewrite the answer or move it back to \`## Open Questions\`. A genuinely open PRODUCT point may still
|
|
196
198
|
raise a fresh \`## Open Questions\` entry: the product gate sits
|
|
197
199
|
on this path exactly as on the first lap
|
|
198
200
|
- Before grouping anything, run \`${env.testCommand}\`
|
|
@@ -505,28 +507,70 @@ ${agentConduct}`
|
|
|
505
507
|
|
|
506
508
|
export const buildFixQualityPrompt = (): string =>
|
|
507
509
|
`${stateFileRules}
|
|
508
|
-
- Read \`.gtd/QUALITY.md\` — one \`## \` chunk per quality
|
|
509
|
-
|
|
510
|
-
|
|
510
|
+
- Read \`.gtd/QUALITY.md\` — one \`## \` chunk per finding a quality
|
|
511
|
+
dimension wrote. Merge duplicate findings across dimensions
|
|
512
|
+
FIRST, then fix every finding in every chunk, blocking or not
|
|
511
513
|
- When findings conflict, missing test signal beats line count —
|
|
512
514
|
a test is never deleted to satisfy a simplification finding
|
|
513
515
|
- Delete \`.gtd/QUALITY.md\` once every finding is resolved
|
|
514
516
|
- Leave everything else uncommitted and finish your turn
|
|
515
517
|
`
|
|
516
518
|
|
|
517
|
-
|
|
519
|
+
/** Brief for a lens the workflow defines itself; a lens with none is just a skill of that name. */
|
|
520
|
+
export const correctnessBrief = `- Trace partial-failure and retry paths: what a step that fails
|
|
521
|
+
after saving an id leaves behind, and whether the retry resumes
|
|
522
|
+
it or starts over
|
|
523
|
+
- Check validation done before an external side effect — its
|
|
524
|
+
format, not just its presence
|
|
525
|
+
- Check invariants that parallel write paths share: every path
|
|
526
|
+
that writes the same data must enforce what the main path
|
|
527
|
+
enforces
|
|
528
|
+
- New mock behaviour without a contract test against the live
|
|
529
|
+
behaviour (mock/live drift) is a finding`
|
|
530
|
+
|
|
531
|
+
export const conventionsBrief = `- Read every \`AGENTS.md\` and \`CLAUDE.md\` in the repository root
|
|
532
|
+
and in each touched file's directory ancestry, end to end, plus
|
|
533
|
+
every file they pull in by \`@path\`
|
|
534
|
+
- Every violation of them in the change is a finding — quote the
|
|
535
|
+
rule it breaks`
|
|
536
|
+
|
|
537
|
+
export const specChallengeBrief = `- Find this process's planning documents in history:
|
|
538
|
+
\`git log <start>..HEAD\` (\`<start>\` is the commit above) over the steering directory
|
|
539
|
+
(\`.gtd/\`), then \`git show\` the last version of the
|
|
540
|
+
requirements, architecture and package files
|
|
541
|
+
- Flag a spec decision that conflicts with a system invariant — an
|
|
542
|
+
existing test, a documented constraint, or a data invariant
|
|
543
|
+
other code relies on — naming the decision and the invariant
|
|
544
|
+
- With no planning documents in history, write nothing`
|
|
545
|
+
|
|
546
|
+
export const buildQualityReviewingPrompt = (lens: string, brief?: string): string =>
|
|
518
547
|
`${stateFileRules}
|
|
519
548
|
- The only state file this turn writes is \`.gtd/QUALITY.md\` — no
|
|
520
549
|
other files for notes or output
|
|
521
550
|
- Review the whole assembled change, from \`${start()}\`
|
|
522
551
|
to the working tree, through this ONE quality lens only —
|
|
523
|
-
\`${lens}
|
|
524
|
-
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
-
|
|
552
|
+
\`${lens}\`
|
|
553
|
+
- Trace, do not skim: follow the order of external calls against
|
|
554
|
+
the resume/retry logic. On a large change, read the touched code
|
|
555
|
+
paths, not just the diff hunks
|
|
556
|
+
- A test counts as coverage only if it would fail with the guarded
|
|
557
|
+
behaviour removed — decide that by reasoning, never by running a
|
|
558
|
+
mutation-testing tool. A test that pins a bug is a finding, not
|
|
559
|
+
praise
|
|
560
|
+
- APPEND every finding you have, blocking or not, as a \`## \`
|
|
561
|
+
chunk to \`.gtd/QUALITY.md\` — never overwrite what an earlier
|
|
562
|
+
dimension already wrote there
|
|
563
|
+
- Write nothing only when this lens found nothing at all — then a
|
|
528
564
|
clean turn IS this dimension's approval
|
|
529
|
-
|
|
565
|
+
${
|
|
566
|
+
brief
|
|
567
|
+
? `
|
|
568
|
+
This lens's brief:
|
|
569
|
+
${brief}
|
|
570
|
+
|
|
571
|
+
`
|
|
572
|
+
: ""
|
|
573
|
+
}- Touch no other state file, and leave everything uncommitted
|
|
530
574
|
`
|
|
531
575
|
|
|
532
576
|
export const reviewerSystem = (): string =>
|
|
@@ -642,6 +686,17 @@ The nit notes are:
|
|
|
642
686
|
|
|
643
687
|
${notesCapture(notes)}`
|
|
644
688
|
|
|
689
|
+
export const buildReviewFixRisksPrompt = (notes: readonly NoteInput[]): string =>
|
|
690
|
+
`${stateFileRules}
|
|
691
|
+
- Fix every risk below in this one turn, all together
|
|
692
|
+
- Where a risk is behavioural, add a test that fails without the fix
|
|
693
|
+
- Leave \`.gtd/REVIEW.md\` untouched — the re-review rewrites it
|
|
694
|
+
- Leave everything uncommitted and finish your turn
|
|
695
|
+
|
|
696
|
+
The risk notes are:
|
|
697
|
+
|
|
698
|
+
${notesCapture(notes)}`
|
|
699
|
+
|
|
645
700
|
export const reviewEditNotesCapture = (
|
|
646
701
|
commit: string,
|
|
647
702
|
edits: readonly NoteInput[],
|
|
@@ -767,6 +822,14 @@ review the changes:
|
|
|
767
822
|
- [ ] ./path/to/file.ts#42-70 — what this hunk does
|
|
768
823
|
and here is more detail, continued below it
|
|
769
824
|
|
|
825
|
+
Open a hunk's note with \`Risk:\` only for a concrete defect
|
|
826
|
+
the change introduces, never a style remark — each such note is
|
|
827
|
+
fixed automatically before the human sees the review. On the
|
|
828
|
+
re-review after a fix, describe what the fix changed under the
|
|
829
|
+
hunk it touched, and re-mark only a risk the fix did not resolve:
|
|
830
|
+
|
|
831
|
+
- [ ] ./path/to/file.ts#42-70 — Risk: what is wrong
|
|
832
|
+
|
|
770
833
|
A note sitting entirely on the line(s) beneath the pointer is
|
|
771
834
|
also valid. Either way, the note must never start with a bare \`./path\` token
|
|
772
835
|
— that parses as a second pointer, not a note
|
package/src/workflows/vars.ts
CHANGED
|
@@ -16,7 +16,8 @@ export const defaults: Readonly<Record<string, string>> = {
|
|
|
16
16
|
// `./review.ts`) rather than naming one step's skill list, so it is a
|
|
17
17
|
// setting — pairing with `build.quality.reviewing`'s entry in
|
|
18
18
|
// `./skills.ts`, which replaces the lens on every one of those turns.
|
|
19
|
-
qualityReviews:
|
|
19
|
+
qualityReviews:
|
|
20
|
+
"correctness, owasp-security, ponytail-review, test-audit, conventions, spec-challenge",
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
/** Environment settings. */
|