@pmelab/gtd 50.0.0 → 51.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 (49) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +32 -24
  3. package/dist/chunks/{reserved-CdOzKFVX.mjs → reserved-CUkk9XgL.mjs} +93 -4
  4. package/dist/chunks/{review-BChXEl2Y.mjs → review-BaCXlIT2.mjs} +2 -2
  5. package/dist/chunks/{tail-DRM2eC1z.mjs → tail-CX6Pdsql.mjs} +1 -1
  6. package/dist/workflows/cleanup.mjs +2 -2
  7. package/dist/workflows/feature.mjs +144 -11
  8. package/dist/workflows/fix.mjs +2 -2
  9. package/dist/workflows/review.mjs +3 -3
  10. package/dist/workflows/rollout.mjs +1 -1
  11. package/dist/workflows.mjs +234 -12
  12. package/package.json +1 -1
  13. package/src/workflows/feature/coverage.feature +1 -0
  14. package/src/workflows/feature/fast-path.feature +1 -0
  15. package/src/workflows/feature/feature.ts +4 -1
  16. package/src/workflows/feature/flag-mode.feature +1 -1
  17. package/src/workflows/feature/lens-models.feature +1 -0
  18. package/src/workflows/feature/lens-review.feature +1 -0
  19. package/src/workflows/feature/one-step-land.feature +1 -0
  20. package/src/workflows/feature/prototype-rounds.feature +152 -0
  21. package/src/workflows/feature/prototype.feature +419 -0
  22. package/src/workflows/feature/prototype.test.ts +43 -0
  23. package/src/workflows/feature/prototype.ts +181 -0
  24. package/src/workflows/feature/refactor-absorbs.feature +9 -1
  25. package/src/workflows/feature/refactor-first.feature +13 -3
  26. package/src/workflows/feature/refactor-rebuild.feature +29 -5
  27. package/src/workflows/feature/refactor-review.feature +111 -13
  28. package/src/workflows/feature/refactor.test.ts +14 -4
  29. package/src/workflows/feature/refactor.ts +2 -0
  30. package/src/workflows/feature/review-answers.feature +1 -0
  31. package/src/workflows/feature/review-gate.feature +1 -0
  32. package/src/workflows/feature/reviewer-walkthrough.feature +1 -0
  33. package/src/workflows/feature/seam-rejection.feature +1 -0
  34. package/src/workflows/feature/seam.feature +1 -0
  35. package/src/workflows/feature/seam.test.ts +3 -2
  36. package/src/workflows/feature/seam.ts +19 -5
  37. package/src/workflows/feature/severity-budget.feature +1 -0
  38. package/src/workflows/feature/severity-sorted-review.feature +1 -0
  39. package/src/workflows/feature/slice-findings.feature +1 -0
  40. package/src/workflows/feature/slicing-cap.feature +9 -0
  41. package/src/workflows/feature/slicing.feature +1 -0
  42. package/src/workflows/feature/standards.feature +1 -0
  43. package/src/workflows/shared/options.ts +1 -0
  44. package/src/workflows/shared/prose.ts +6 -0
  45. package/src/workflows/shared/skills.test.ts +4 -0
  46. package/src/workflows/shared/skills.ts +5 -0
  47. package/src/workflows/shared/steps.ts +1 -0
  48. package/src/workflows/shared/text.ts +104 -3
  49. package/src/workflows/shared/vars.ts +3 -0
@@ -9,6 +9,7 @@ Feature: every slice's quality lap is triaged, and its leftovers wait for the re
9
9
 
10
10
  Background:
11
11
  Given the shipped workflows, as gtd install scaffolds them
12
+ And an environment variable "GTD_PROTOTYPEROUNDS" set to "0"
12
13
  And an environment variable "GTD_QUALITYREVIEWS" set to "correctness"
13
14
  And an environment variable "GTD_FLAGMODE" set to "false"
14
15
  And an environment variable "GTD_SLICEMAXCRITERIA" set to "1"
@@ -12,3 +12,12 @@ Feature: a bad sliceMaxCriteria refuses the landing that would pin it
12
12
  # Something
13
13
  """
14
14
  Then driving the process is refused with "sliceMaxCriteria must be a positive integer, got \"0\""
15
+
16
+ Scenario: a prototypeRounds that is not a whole number refuses the idle landing
17
+ Given the shipped workflows, as gtd install scaffolds them
18
+ And an environment variable "GTD_PROTOTYPEROUNDS" set to "-1"
19
+ And a file "TODO.md" with:
20
+ """
21
+ # Something
22
+ """
23
+ Then driving the process is refused with "prototypeRounds must be a non-negative integer, got \"-1\""
@@ -8,6 +8,7 @@ Feature: a change with more criteria than the cap is built in coarse slices
8
8
 
9
9
  Background:
10
10
  Given the shipped workflows, as gtd install scaffolds them
11
+ And an environment variable "GTD_PROTOTYPEROUNDS" set to "0"
11
12
  And an environment variable "GTD_QUALITYREVIEWS" set to ""
12
13
  And an environment variable "GTD_FLAGMODE" set to "false"
13
14
  And an environment variable "GTD_SLICEMAXCRITERIA" set to "2"
@@ -11,6 +11,7 @@ Feature: the review learns standards from the human's answers
11
11
 
12
12
  Background:
13
13
  Given the shipped workflows, as gtd install scaffolds them
14
+ And an environment variable "GTD_PROTOTYPEROUNDS" set to "0"
14
15
  And an environment variable "GTD_QUALITYREVIEWS" set to "correctness"
15
16
  And an environment variable "GTD_FLAGMODE" set to "false"
16
17
  And a file "src/calc.ts" with:
@@ -27,6 +27,7 @@ export const ShippedOptions = Schema.Struct({
27
27
  qualityReviews: setting,
28
28
  refactorQualityReviews: setting,
29
29
  continuations: setting,
30
+ prototypeRounds: setting,
30
31
  continuationStall: setting,
31
32
  testCommand: setting,
32
33
  plannerModel: setting,
@@ -88,6 +88,12 @@ things when redirected — a failing check, or reviewer feedback. Stay
88
88
  strictly inside that task; never touch code outside it. Treat feedback as the
89
89
  diagnosis to act on; discard it, with reason, only when simply wrong.`
90
90
 
91
+ export const testerPersona = `You are the hands-on tester in gtd's build pipeline: use what was just
92
+ built the way a person would — run the real program, open the real
93
+ page, call the real endpoint — and report exactly what you did and
94
+ what you saw. Never fix anything yourself, and never pass a criterion
95
+ you did not watch hold.`
96
+
91
97
  export const finisherPersona = `You are the closing identity in gtd's build pipeline — the last coder
92
98
  before it closes. Fix a late-breaking failing check after sign-off,
93
99
  focused and minimal. Every turn lands its own commit — nothing here
@@ -29,6 +29,10 @@ describe("the bundled workflow's skills export", () => {
29
29
  "implement",
30
30
  "implement.fix.suite",
31
31
  "implement.health",
32
+ "implement.prototype",
33
+ "implement.prototype.fix",
34
+ "implement.prototype.fix.suite",
35
+ "implement.prototype.health",
32
36
  "build.quality.correctness",
33
37
  "build.quality.owasp-security",
34
38
  "build.quality.ponytail-review",
@@ -33,6 +33,11 @@ export const skills = (
33
33
  seam: ["incremental-implementation"],
34
34
  implement: ["test-driven-development", "incremental-implementation"],
35
35
  "implement.fix.suite": ["debugging-and-error-recovery"],
36
+ // The hands-on tester runs the program, never the suite: no TDD.
37
+ "implement.prototype": [],
38
+ "implement.prototype.fix": ["test-driven-development", "incremental-implementation"],
39
+ "implement.prototype.fix.suite": ["debugging-and-error-recovery"],
40
+ "implement.prototype.health": ["debugging-and-error-recovery"],
36
41
  // `health` is shared code reached from both `implement` and `build`.
37
42
  "implement.health": ["debugging-and-error-recovery"],
38
43
  ...lensScopes(
@@ -16,6 +16,7 @@ export const REFACTOR_NOTE = ".gtd/REFACTOR-NOTE.md"
16
16
  export const QUALITY = ".gtd/QUALITY.md"
17
17
  export const SLICES = ".gtd/SLICES.md"
18
18
  export const HANDOFF = ".gtd/HANDOFF.md"
19
+ export const PROTOTYPE = ".gtd/PROTOTYPE.md"
19
20
 
20
21
  export const planner = (): string => env.plannerModel ?? ""
21
22
  export const coder = (): string => env.coderModel ?? ""
@@ -41,6 +41,7 @@ import {
41
41
  reviewerPersona,
42
42
  builderPersona,
43
43
  finisherPersona,
44
+ testerPersona,
44
45
  escalationPersona,
45
46
  stateFileRules,
46
47
  questionBar,
@@ -485,8 +486,11 @@ const acceptanceCriteriaBlock = `
485
486
  Whichever lap this is, \`.gtd/REQUIREMENTS.md\` carries a
486
487
  \`## Acceptance Criteria\` section: one \`- AC-<n>: <criterion>\` line
487
488
  per criterion, numbered from \`AC-1\`. They bind every later step —
488
- the build is done when each one holds — so each must be checkable: a
489
- test or a command shows it holds or not. A later lap keeps every ID:
489
+ the build is done when each one holds — so each must be behaviour a
490
+ test can pin: one that fails when it does not hold. A green suite, a
491
+ passing typecheck or lint, a generator or build that runs — the health
492
+ check enforces those on every lap, so none is a criterion; fold what it
493
+ must produce into a behaviour a test can observe. A later lap keeps every ID:
490
494
  reword a criterion in place, append a new one with the next unused
491
495
  number, delete a dropped one — never renumber, never reuse an ID. When
492
496
  the file holds no list (a review loop-back rewrites it), recover the
@@ -600,7 +604,7 @@ export const clearCriteriaQuestion: JudgeQuestion = {
600
604
  instructions:
601
605
  "state.criteria lists the acceptance criteria the human's sketch (state.sketch) stated, as gtd numbered them. Decide whether they can stand as the binding definition of done, with no design conversation.",
602
606
  criteria:
603
- "yes: every criterion is clear and checkable — a test or a command can show it holds or not — and together they say what to build. no: any one criterion is vague, subjective or untestable, or the sketch leaves an open product question. Unsure means no.",
607
+ "yes: every criterion is clear behaviour a test can pin — a test would fail if it did not hold — and together they say what to build. no: any one criterion is vague, subjective or untestable, only a command can check it (a suite, typecheck, lint, build or generator run that passes), or the sketch leaves an open product question. Unsure means no.",
604
608
  }
605
609
 
606
610
  export const clearCriteriaMessage = (): string =>
@@ -1980,3 +1984,100 @@ Two ways forward (then run \`gtd land\`):
1980
1984
  - **Overrule** — land a clean tree to finish without them. The slice records
1981
1985
  the IDs it covers and, as \`uncovered\`, the ones it does not
1982
1986
  `
1987
+
1988
+ export const testerSystem = (): string =>
1989
+ `${testerPersona}
1990
+
1991
+ ${agentConduct}`
1992
+
1993
+ const PROTOTYPE_FORMAT = `- Write \`.gtd/PROTOTYPE.md\`, replacing any earlier report: one \`## \` chunk
1994
+ per criterion, headed \`## AC-<n>: pass\`, \`## AC-<n>: fail\` or
1995
+ \`## AC-<n>: blocked\`, then one chunk per problem you found outside any
1996
+ criterion, headed \`## Problem <short title>: fail\` (\`: pass\` once a
1997
+ re-test shows it fixed). Under each: the exact steps you ran, what you
1998
+ observed (output, a screenshot path, the error), and for a failure what
1999
+ you expected instead
2000
+ - \`blocked\` is for a criterion you could not exercise, or one whose
2001
+ expected behaviour itself looks wrong — say which and why: a person
2002
+ decides it, never a fixer`
2003
+
2004
+ export const prototypeTestingPrompt = (
2005
+ slice: readonly string[] | undefined,
2006
+ retest: boolean,
2007
+ ): string =>
2008
+ `${stateFileRules}
2009
+ - Read \`.gtd/REQUIREMENTS.md\`: the settled requirements for this change
2010
+ ${sliceLine(slice)}- The change is built and its suite is green. Now test it by hand: use
2011
+ every acceptance criterion the way a person would and watch what
2012
+ happens. Running the test suite is not this — it already ran
2013
+ - Find how this project is exercised for real, in this order: a project
2014
+ skill that launches or drives it (e.g. \`.claude/skills/*\`); a manual
2015
+ testing or running section in \`AGENTS.md\`, \`CLAUDE.md\` or \`README.md\`;
2016
+ failing those, the project's shape — a browser or Playwright for a web
2017
+ project, the built CLI in a scratch directory for a CLI, a request client
2018
+ for an API
2019
+ - Found none and recognised no shape: write only a chunk headed
2020
+ \`## No testing instructions\` saying what you looked for, and finish —
2021
+ a person is asked; never guess
2022
+ - Test in a disposable place only — a scratch directory outside this
2023
+ repository, a throwaway repository, a dev server on a free port — never
2024
+ against the user's real data or this working tree. Point every tool's
2025
+ output (screenshots, traces, test results) outside the repository too
2026
+ ${
2027
+ retest
2028
+ ? `- This is a re-test: \`.gtd/PROTOTYPE.md\` holds the last round's report and
2029
+ a fix turn or a person has changed the code since. Re-test every failed or blocked criterion and
2030
+ every problem it lists in full, then re-run the passed ones quickly
2031
+ `
2032
+ : ""
2033
+ }${PROTOTYPE_FORMAT}
2034
+ - Change nothing but \`.gtd/PROTOTYPE.md\` — a turn that does is refused
2035
+ - Leave everything uncommitted and finish your turn — do not commit
2036
+ `
2037
+
2038
+ export const prototypeFixingPrompt = (slice: readonly string[] | undefined): string =>
2039
+ `${stateFileRules}
2040
+ - Read \`.gtd/REQUIREMENTS.md\`: the settled requirements for this change
2041
+ ${sliceLine(slice)}${criteriaBinding(true)}
2042
+ - Read \`.gtd/PROTOTYPE.md\`: a hands-on test of the change. Fix every
2043
+ \`fail\` chunk of this change's criteria${slice === undefined ? "" : " (this slice's only)"} or a problem found
2044
+ beside them, at its cause, with a test that shows it; leave \`blocked\`
2045
+ ones to the person
2046
+ - A \`fail\` the code does not explain (the tester misread what it saw) has
2047
+ nothing to fix: leave it and change nothing for it — the next round re-tests
2048
+ - Leave \`.gtd/PROTOTYPE.md\` as it is: the next round re-tests from it
2049
+ - Run \`${env.testCommand}\` and finish only once it is green
2050
+ - Leave everything uncommitted and finish your turn — do not commit
2051
+ `
2052
+
2053
+ const openItems = (items: readonly { subject: string; verdict: string }[]): string =>
2054
+ items.map((item) => `- ${item.subject}: ${item.verdict}`).join("\n")
2055
+
2056
+ export const prototypeOpenMessage = (
2057
+ items: readonly { subject: string; verdict: string }[],
2058
+ rounds: number,
2059
+ ): string =>
2060
+ `The hands-on test of this slice still has open items${rounds > 0 ? ` after ${rounds} fix round${rounds === 1 ? "" : "s"}` : ", and none a fixer can take"}:
2061
+ ${openItems(items)}
2062
+
2063
+ \`.gtd/PROTOTYPE.md\` holds the last report: the steps run and what was seen.
2064
+
2065
+ Two ways forward (then run \`gtd land\`):
2066
+ - **Fix** — change the code, or the testing instructions, yourself (the
2067
+ criteria are settled: an edit to them is refused); the suite runs and the
2068
+ slice is tested again, with a fresh round budget
2069
+ - **Overrule** — land a clean tree to go on to the quality lap with these
2070
+ items open
2071
+ `
2072
+
2073
+ export const prototypeUnguidedMessage = (): string =>
2074
+ `The hands-on test found no way to exercise this project for real — no
2075
+ project skill, no manual testing section in \`AGENTS.md\`, \`CLAUDE.md\` or
2076
+ \`README.md\`, and no project shape it recognised. \`.gtd/PROTOTYPE.md\` says
2077
+ what it looked for.
2078
+
2079
+ Two ways forward (then run \`gtd land\`):
2080
+ - **Instruct** — add testing instructions (a project skill, or a manual
2081
+ testing section); the slice is tested again
2082
+ - **Skip** — land a clean tree to go on to the quality lap untested
2083
+ `
@@ -41,6 +41,9 @@ export const defaults: Readonly<Record<string, string>> = {
41
41
  // stopped at its context limit before a person is asked, and how many in a
42
42
  // row may close nothing still open.
43
43
  continuations: "3",
44
+ // Fix-and-retest rounds of a slice's prototype test before a person is
45
+ // asked; "0" skips the test.
46
+ prototypeRounds: "3",
44
47
  continuationStall: "2",
45
48
  }
46
49