@pmelab/gtd 51.0.0 → 52.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 (66) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +41 -38
  3. package/dist/chunks/{reserved-CUkk9XgL.mjs → reserved-D0MKVFFa.mjs} +28 -34
  4. package/dist/chunks/{review-BaCXlIT2.mjs → review-ZzUQH1KZ.mjs} +11 -9
  5. package/dist/chunks/{steering-DtUVUz9l.mjs → steering-CLhu8C7q.mjs} +262 -52
  6. package/dist/chunks/{tail-CX6Pdsql.mjs → tail-I1-vVq0P.mjs} +1 -1
  7. package/dist/gtd.bundle.mjs +224 -68
  8. package/dist/steering.mjs +2 -2
  9. package/dist/testkit.mjs +27 -26
  10. package/dist/web/main.css +1 -1
  11. package/dist/web/main.js +510 -147
  12. package/dist/workflow-kit.mjs +1 -1
  13. package/dist/workflows/cleanup.mjs +2 -2
  14. package/dist/workflows/feature.mjs +181 -222
  15. package/dist/workflows/fix.mjs +3 -3
  16. package/dist/workflows/review.mjs +3 -3
  17. package/dist/workflows/rollout.mjs +1 -1
  18. package/dist/workflows.mjs +478 -313
  19. package/package.json +1 -1
  20. package/src/edge/Edge.ts +1 -30
  21. package/src/replay/Replay.test.ts +4 -8
  22. package/src/replay/Replay.ts +35 -3
  23. package/src/replay/index.ts +7 -1
  24. package/src/steering/FORMATS.md +4 -1
  25. package/src/steering/SteeringFormat.ts +7 -1
  26. package/src/steering/contract.test.ts +2 -1
  27. package/src/steering/fixtures/review/sectioned.md +31 -0
  28. package/src/steering/fixtures/review/sectioned.read.json +119 -0
  29. package/src/steering/index.ts +4 -0
  30. package/src/steering/qa.ts +1 -1
  31. package/src/steering/review.test.ts +270 -4
  32. package/src/steering/review.ts +328 -69
  33. package/src/workflows/feature/criteria.test.ts +39 -1
  34. package/src/workflows/feature/criteria.ts +21 -7
  35. package/src/workflows/feature/fast-path.feature +1 -6
  36. package/src/workflows/feature/feature.ts +1 -2
  37. package/src/workflows/feature/flag-mode.feature +1 -6
  38. package/src/workflows/feature/green.ts +22 -0
  39. package/src/workflows/feature/hands-on-skip.feature +344 -0
  40. package/src/workflows/feature/lens-models.feature +0 -4
  41. package/src/workflows/feature/lens-review.feature +6 -8
  42. package/src/workflows/feature/one-step-land.feature +1 -6
  43. package/src/workflows/feature/prototype-rounds.feature +1 -1
  44. package/src/workflows/feature/prototype.feature +10 -9
  45. package/src/workflows/feature/prototype.ts +48 -17
  46. package/src/workflows/feature/refactor-absorbs.feature +3 -8
  47. package/src/workflows/feature/refactor-first.feature +3 -8
  48. package/src/workflows/feature/refactor-rebuild.feature +7 -12
  49. package/src/workflows/feature/refactor-review.feature +52 -72
  50. package/src/workflows/feature/review-answers.feature +90 -5
  51. package/src/workflows/feature/review-gate.feature +11 -53
  52. package/src/workflows/feature/reviewer-walkthrough.feature +29 -12
  53. package/src/workflows/feature/seam-rejection.feature +1 -6
  54. package/src/workflows/feature/seam.feature +1 -6
  55. package/src/workflows/feature/seam.test.ts +1 -1
  56. package/src/workflows/feature/seam.ts +2 -3
  57. package/src/workflows/feature/severity-budget.feature +7 -7
  58. package/src/workflows/feature/severity-sorted-review.feature +50 -54
  59. package/src/workflows/feature/slice-findings.feature +5 -15
  60. package/src/workflows/feature/slicing.feature +4 -24
  61. package/src/workflows/feature/standards.feature +0 -4
  62. package/src/workflows/shared/review.ts +26 -6
  63. package/src/workflows/shared/text.test.ts +45 -11
  64. package/src/workflows/shared/text.ts +45 -38
  65. package/src/workflows/feature/coverage.feature +0 -165
  66. package/src/workflows/feature/coverage.ts +0 -120
@@ -1,120 +0,0 @@
1
- import {
2
- answered,
3
- changes,
4
- changesSince,
5
- human,
6
- judge,
7
- mark,
8
- read,
9
- start,
10
- } from "../../flows/index.js"
11
- import { healthy } from "../shared/health.js"
12
- import { coder, FEEDBACK, REQUIREMENTS } from "../shared/steps.js"
13
- import * as t from "../shared/text.js"
14
- import {
15
- criteriaOf,
16
- criterionIds,
17
- requireCriteriaFrozen,
18
- sliceIds,
19
- type CriteriaSlice,
20
- } from "./criteria.js"
21
- import { isTestFile } from "./refactor.js"
22
- import { isCode } from "../shared/review.js"
23
-
24
- /** Below this the judge's `yes` is not trusted: a wrongly covered ID ends the feature without its test. */
25
- const COVERED_MIN_P = 0.7
26
-
27
- /** Covering turns before the process rests at `uncovered`; a human edit there refills them. */
28
- const COVERING_RETRIES = 2
29
-
30
- const frozen = (): void => requireCriteriaFrozen(REQUIREMENTS)
31
-
32
- export const green = (guard?: () => void): Promise<void> =>
33
- healthy(
34
- () =>
35
- t.agentWithSkills("fix.suite.fixing", t.implementFixPrompt(), {
36
- label: "Fixing the check",
37
- file: FEEDBACK,
38
- model: coder(),
39
- system: t.builderSystem(),
40
- }),
41
- {
42
- guard: () => {
43
- frozen()
44
- guard?.()
45
- },
46
- guardRests: false,
47
- },
48
- )
49
-
50
- // A budget cut keeps the end, so the files naming a criterion ID go last.
51
- const testsSince = (base: string): string =>
52
- changesSince(base)
53
- .filter((c) => c.after !== undefined && isCode(c.path) && isTestFile(c.path))
54
- .sort((a, b) => Number(/\bAC-\d/.test(a.after!)) - Number(/\bAC-\d/.test(b.after!)))
55
- .map((c) => `=== ${c.path} (${c.status})\n${c.after}`)
56
- .join("\n")
57
-
58
- /**
59
- * The criterion IDs of `.gtd/REQUIREMENTS.md` — read afresh, never carried
60
- * over, only `slice`'s when given — no passing test since `base` covers, by the judge.
61
- */
62
- const uncoveredSince = async (
63
- base: string,
64
- slice: CriteriaSlice | undefined,
65
- ): Promise<{ readonly covered: string[]; readonly uncovered: string[] }> => {
66
- const requirements = read(REQUIREMENTS) ?? ""
67
- const own = criterionIds(requirements, slice)
68
- const criteria = (criteriaOf(requirements) ?? []).filter((c) => own.includes(c.id))
69
- if (criteria.length === 0) return { covered: [], uncovered: [] }
70
- const { answers } = await judge("coverage", {
71
- questions: criteria.map(t.coverageQuestion),
72
- evidence: { tests: testsSince(base) },
73
- message: t.coverageMessage(),
74
- label: "Judging acceptance-criteria coverage",
75
- })
76
- // A budget cut keeps the evidence's end: a `no` may miss tests the judge never saw, a `yes` names one it did.
77
- const covers = (id: string): boolean => answered(answers[id], "yes", COVERED_MIN_P)
78
- const ids = criteria.map((c) => c.id)
79
- return { covered: ids.filter(covers), uncovered: ids.filter((id) => !covers(id)) }
80
- }
81
-
82
- const record = (covered: readonly string[], uncovered: readonly string[] = []): void => {
83
- for (const id of covered) mark("covers", id)
84
- for (const id of uncovered) mark("uncovered", id)
85
- }
86
-
87
- /**
88
- * Hold a green change until every acceptance criterion has a passing test,
89
- * sending uncovered IDs back to a covering turn; marks the IDs covered on the
90
- * landing that ends it. A human may overrule once the retries are spent.
91
- */
92
- export const requireCoverage = async (base = start(), slice?: CriteriaSlice): Promise<void> => {
93
- let retries = 0
94
- for (;;) {
95
- const { covered, uncovered } = await uncoveredSince(base, slice)
96
- if (uncovered.length === 0) return record(covered)
97
- if (retries < COVERING_RETRIES) {
98
- retries++
99
- await t.agentWithSkills("covering", t.coveringPrompt(uncovered, sliceIds(slice)), {
100
- label: "Covering the acceptance criteria",
101
- model: coder(),
102
- system: t.builderSystem(),
103
- // The judge may have missed a test that exists: an empty turn asks it again.
104
- allowEmpty: true,
105
- })
106
- frozen()
107
- } else {
108
- await human("uncovered", {
109
- message: t.uncoveredMessage(uncovered),
110
- label: "Acceptance criteria without a test",
111
- file: REQUIREMENTS,
112
- acceptClean: true,
113
- })
114
- if (!changes().some((c) => isCode(c.path))) return record(covered, uncovered)
115
- frozen()
116
- retries = 0
117
- }
118
- await green()
119
- }
120
- }