@pmelab/gtd 45.2.0 → 47.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 +15 -30
- package/dist/chunks/{reserved-FWrjy7zo.mjs → reserved-DF1HD6jL.mjs} +42 -48
- package/dist/chunks/{review-Dhl4vvDe.mjs → review-Bv40kZu9.mjs} +9 -23
- package/dist/chunks/{tail-B-Es6Hwj.mjs → tail-D89fU_wf.mjs} +68 -39
- package/dist/gtd.bundle.mjs +5 -7
- package/dist/testkit.mjs +5 -7
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +26 -22
- package/dist/workflows/fix.mjs +2 -2
- package/dist/workflows/review.mjs +14 -4
- package/dist/workflows/rollout.mjs +1 -1
- package/dist/workflows.mjs +270 -429
- package/package.json +1 -6
- package/src/install/ConfigScaffold.ts +1 -2
- package/src/install/Install.ts +4 -5
- package/src/workflows/cleanup/cleanup.test.ts +11 -9
- package/src/workflows/feature/feature.ts +38 -28
- package/src/workflows/feature/lens-review.feature +3 -1
- package/src/workflows/feature/slice-findings.feature +198 -0
- package/src/workflows/fix/fix-lens-origin.feature +101 -0
- package/src/workflows/fix/fix-quality.feature +131 -0
- package/src/workflows/index.ts +1 -3
- package/src/workflows/review/review.ts +15 -3
- package/src/workflows/shared/lenses.ts +30 -9
- package/src/workflows/shared/options.ts +1 -21
- package/src/workflows/shared/review.ts +5 -27
- package/src/workflows/shared/skills.test.ts +0 -2
- package/src/workflows/shared/skills.ts +0 -3
- package/src/workflows/shared/tail.ts +74 -57
- package/src/workflows/shared/text.ts +86 -33
- package/src/workflows/shared/vars.ts +0 -8
- package/dist/workflows/tests.mjs +0 -193
- package/src/workflows/feature/quality-unresolved.feature +0 -121
- package/src/workflows/tests/index.ts +0 -6
- package/src/workflows/tests/tests-options.feature +0 -52
- package/src/workflows/tests/tests.feature +0 -266
- package/src/workflows/tests/tests.test.ts +0 -51
- package/src/workflows/tests/tests.ts +0 -270
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pmelab/gtd",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "47.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Git-aware CLI that emits the next prompt for an autonomous coding agent based on the current repository state",
|
|
6
6
|
"bin": {
|
|
@@ -62,11 +62,6 @@
|
|
|
62
62
|
"source": "./src/workflows/cleanup/index.ts",
|
|
63
63
|
"default": "./dist/workflows/cleanup.mjs"
|
|
64
64
|
},
|
|
65
|
-
"./workflows/tests": {
|
|
66
|
-
"types": "./src/workflows/tests/index.ts",
|
|
67
|
-
"source": "./src/workflows/tests/index.ts",
|
|
68
|
-
"default": "./dist/workflows/tests.mjs"
|
|
69
|
-
},
|
|
70
65
|
"./testkit": {
|
|
71
66
|
"types": "./src/testkit/index.ts",
|
|
72
67
|
"source": "./src/testkit/index.ts",
|
|
@@ -6,8 +6,7 @@ export const CONFIG_SCAFFOLD = `// The workflows gtd runs here: gtd's shipped fe
|
|
|
6
6
|
// its own doors: \`gtd door review [base]\` and \`gtd door fix\`. Export \`rollout\`
|
|
7
7
|
// from \`@pmelab/gtd/workflows/rollout\` and \`cleanup\` from
|
|
8
8
|
// \`@pmelab/gtd/workflows/cleanup\` to add \`gtd door rollout <flag>\` and
|
|
9
|
-
// \`gtd door cleanup <flag
|
|
10
|
-
// to add \`gtd door tests\`.
|
|
9
|
+
// \`gtd door cleanup <flag>\`.
|
|
11
10
|
export {
|
|
12
11
|
feature as default,
|
|
13
12
|
feature,
|
package/src/install/Install.ts
CHANGED
|
@@ -516,11 +516,10 @@ rather than starting the review; \`gtd-door fix\` on a GREEN suite is a no-op
|
|
|
516
516
|
straight back to \`idle\` — the exec'd \`gtd-build\` exits immediately on it.
|
|
517
517
|
\`gtd doors\` lists the doors a repository offers (those of the workflows its
|
|
518
518
|
config exports — the bundled \`fix\` and \`review [base]\`, plus the opt-in
|
|
519
|
-
\`rollout <flag>\` (turn a release flag default-on)
|
|
520
|
-
(remove it)
|
|
521
|
-
|
|
522
|
-
\`@pmelab/gtd/workflows/cleanup\`
|
|
523
|
-
\`@pmelab/gtd/workflows/tests\` — plus any it declares). Refuses with
|
|
519
|
+
\`rollout <flag>\` (turn a release flag default-on) and \`cleanup <flag>\`
|
|
520
|
+
(remove it), which a config adds by exporting \`rollout\` from
|
|
521
|
+
\`@pmelab/gtd/workflows/rollout\` and \`cleanup\` from
|
|
522
|
+
\`@pmelab/gtd/workflows/cleanup\` — plus any it declares). Refuses with
|
|
524
523
|
\`usage: gtd-door <name> [args...]\` on stderr and exit \`2\` when the name is
|
|
525
524
|
missing — nothing was even attempted. Default path: \`~/.local/bin/gtd-door\`.
|
|
526
525
|
|
|
@@ -90,6 +90,13 @@ describe("the cleanup guard over every later agent turn", () => {
|
|
|
90
90
|
"build.health.check": () => undefined,
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
// One lens finding, judged mechanical, so it reaches the fix turn.
|
|
94
|
+
const MECHANICAL: Actors = {
|
|
95
|
+
"build.quality.correctness.reviewing": (_, r) =>
|
|
96
|
+
r.write(".gtd/QUALITY.md", "# Quality\n\n## a finding\n\nFix it.\n"),
|
|
97
|
+
"build.quality.triage": () => '[{"id":"Q1","answer":"mechanical","p":0.9}]',
|
|
98
|
+
}
|
|
99
|
+
|
|
93
100
|
// Without the shipped access rules, which would refuse a source write from a read-only turn first: the guard is the second line.
|
|
94
101
|
const NO_ACCESS = CONFIG.replace(" access,\n", "")
|
|
95
102
|
|
|
@@ -185,15 +192,11 @@ describe("the cleanup guard over every later agent turn", () => {
|
|
|
185
192
|
).rejects.toThrow("src/b.ts:1 still names GTD_FLAG_LIVE")
|
|
186
193
|
})
|
|
187
194
|
|
|
188
|
-
it("refuses
|
|
195
|
+
it("refuses a quality fix turn that re-adds the env var", async () => {
|
|
189
196
|
await expect(
|
|
190
197
|
driven(guarded("correctness", NO_ACCESS), {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
"build.fix.quality.fixing": () => undefined,
|
|
194
|
-
"build.health.describe": () => undefined,
|
|
195
|
-
"build.health.stop": () => undefined,
|
|
196
|
-
"build.fix": (_, r) => r.write("src/b.ts", NAMED),
|
|
198
|
+
...MECHANICAL,
|
|
199
|
+
"build.fix.quality.fixing": (_, r) => r.write("src/b.ts", NAMED),
|
|
197
200
|
}),
|
|
198
201
|
).rejects.toThrow("src/b.ts:1 still names GTD_FLAG_LIVE")
|
|
199
202
|
})
|
|
@@ -209,8 +212,7 @@ describe("the cleanup guard over every later agent turn", () => {
|
|
|
209
212
|
it("refuses a quality fix turn that restores the registry entry", async () => {
|
|
210
213
|
await expect(
|
|
211
214
|
driven(guarded("correctness"), {
|
|
212
|
-
|
|
213
|
-
r.write(".gtd/QUALITY.md", "- a finding\n"),
|
|
215
|
+
...MECHANICAL,
|
|
214
216
|
"build.fix.quality.fixing": (_, r) => r.write("src/flags/index.ts", REGISTRY),
|
|
215
217
|
}),
|
|
216
218
|
).rejects.toThrow('still declares flag "live"')
|
|
@@ -9,9 +9,10 @@ import {
|
|
|
9
9
|
slice,
|
|
10
10
|
start,
|
|
11
11
|
} from "../../flows/index.js"
|
|
12
|
-
import { gate
|
|
12
|
+
import { gate } from "../shared/health.js"
|
|
13
|
+
import { joinReports, type LensReport } from "../shared/lenses.js"
|
|
13
14
|
import { flagModeOn, shipped } from "../shared/options.js"
|
|
14
|
-
import { FEEDBACK,
|
|
15
|
+
import { FEEDBACK, REQUIREMENTS } from "../shared/steps.js"
|
|
15
16
|
import { refusePendingHandOff, tail } from "../shared/tail.js"
|
|
16
17
|
import * as t from "../shared/text.js"
|
|
17
18
|
import {
|
|
@@ -36,7 +37,8 @@ import { recordRisk, riskTierOn, type RiskTier } from "./tier.js"
|
|
|
36
37
|
// green-baseline gate → design → per slice of the criteria, a naive pass,
|
|
37
38
|
// judged with `flagMode` on: it lands as the implementation, or (refactoring
|
|
38
39
|
// first when the judge asks) is built fresh from → the health loop → the
|
|
39
|
-
// quality lap
|
|
40
|
+
// triaged quality lap, whose leftovers are carried on → after the last slice,
|
|
41
|
+
// human review of the whole change and every slice's findings, which
|
|
40
42
|
// signs off (the episode ends back at `idle`) or sends a design → implement
|
|
41
43
|
// lap.
|
|
42
44
|
|
|
@@ -63,10 +65,12 @@ export const buildFeature = async (firstBase: string): Promise<void> => {
|
|
|
63
65
|
const one = slices.length === 1
|
|
64
66
|
// A steer may delete every criterion of a later slice: there is nothing to build there.
|
|
65
67
|
const built = (i: number): boolean => sliceIds(sliceCriteria(slices, i))?.length !== 0
|
|
68
|
+
const reports: { slice: string; report: LensReport }[] = []
|
|
66
69
|
for (const i of slices.keys()) {
|
|
67
70
|
if (!built(i)) continue
|
|
68
71
|
const n = one ? "" : `-${i + 1}`
|
|
69
72
|
await buildSlice(i === 0 ? firstBase : head(), {
|
|
73
|
+
number: `${i + 1}`,
|
|
70
74
|
tier,
|
|
71
75
|
criteria: sliceCriteria(slices, i),
|
|
72
76
|
refactor: `refactor${n}`,
|
|
@@ -76,6 +80,7 @@ export const buildFeature = async (firstBase: string): Promise<void> => {
|
|
|
76
80
|
// Every slice but the one is built unreviewed, so the review covers them all.
|
|
77
81
|
reviewBase: one ? undefined : firstBase,
|
|
78
82
|
handedOut,
|
|
83
|
+
laps: reports,
|
|
79
84
|
})
|
|
80
85
|
}
|
|
81
86
|
}
|
|
@@ -88,6 +93,8 @@ const gather = (handedOut: Set<string>): readonly string[] => {
|
|
|
88
93
|
}
|
|
89
94
|
|
|
90
95
|
interface SliceBuild {
|
|
96
|
+
/** The slice's number, from 1. */
|
|
97
|
+
readonly number: string
|
|
91
98
|
readonly tier: RiskTier | undefined
|
|
92
99
|
readonly criteria: CriteriaSlice | undefined
|
|
93
100
|
readonly refactor: string
|
|
@@ -97,6 +104,8 @@ interface SliceBuild {
|
|
|
97
104
|
readonly reviewBase?: string | undefined
|
|
98
105
|
/** Every criterion ID handed out so far; the slice adds those a steer adds. */
|
|
99
106
|
readonly handedOut: Set<string>
|
|
107
|
+
/** Each earlier slice's lap report, by slice number, for the review after the last one. */
|
|
108
|
+
readonly laps: { slice: string; report: LensReport }[]
|
|
100
109
|
}
|
|
101
110
|
|
|
102
111
|
/** A naive pass (judged only with `flagMode` on), then implement and the quality lap; after the last slice, the review. */
|
|
@@ -127,37 +136,38 @@ const buildSlice = async (sliceBase: string, build: SliceBuild): Promise<void> =
|
|
|
127
136
|
if (more) {
|
|
128
137
|
// The criteria stay in the tree for the next slice; no quality fix may move them.
|
|
129
138
|
const guard = () => requireCriteriaFrozen(REQUIREMENTS)
|
|
130
|
-
|
|
139
|
+
// No gate between slices: what the lap left waits for the review after the last one.
|
|
140
|
+
const report = await tail(async (_, report) => report, {
|
|
141
|
+
green: true,
|
|
142
|
+
lensBase: base,
|
|
143
|
+
guard,
|
|
144
|
+
guardRests: false,
|
|
145
|
+
})
|
|
146
|
+
build.laps.push({ slice: build.number, report })
|
|
131
147
|
return
|
|
132
148
|
}
|
|
133
149
|
const reviewBase = build.reviewBase ?? base
|
|
134
|
-
|
|
150
|
+
const outcome = await reviewedTail(reviewBase, base, { slice: build.number, laps: build.laps })
|
|
151
|
+
await featureFeedback(outcome, base, { closed, used })
|
|
135
152
|
})
|
|
136
153
|
}
|
|
137
154
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/** The triaged quality lap, then the human review since `base`. */
|
|
155
|
-
const reviewedTail = (base: string, lensBase: string): Promise<ReviewOutcome> =>
|
|
156
|
-
tail((escalations, report) => humanReview(base, escalations, { report }), {
|
|
157
|
-
green: true,
|
|
158
|
-
lensBase,
|
|
159
|
-
triage: true,
|
|
160
|
-
})
|
|
155
|
+
/** The quality lap of slice `last.slice`, then the human review since `base`, listing what earlier slices' `laps` left too. */
|
|
156
|
+
const reviewedTail = (
|
|
157
|
+
base: string,
|
|
158
|
+
lensBase: string,
|
|
159
|
+
last: { readonly slice: string; readonly laps: SliceBuild["laps"] } = { slice: "", laps: [] },
|
|
160
|
+
): Promise<ReviewOutcome> =>
|
|
161
|
+
tail(
|
|
162
|
+
(escalations, report) =>
|
|
163
|
+
humanReview(base, escalations, {
|
|
164
|
+
report:
|
|
165
|
+
last.laps.length === 0
|
|
166
|
+
? report
|
|
167
|
+
: joinReports([...last.laps, { slice: last.slice, report }]),
|
|
168
|
+
}),
|
|
169
|
+
{ green: true, lensBase },
|
|
170
|
+
)
|
|
161
171
|
|
|
162
172
|
// seam.naive / seam.judge never run on a feedback lap: its design → implement is built unjudged
|
|
163
173
|
// and unrefactored, held to every criterion's coverage since the process start.
|
|
@@ -74,6 +74,7 @@ Feature: the quality lenses are the only agentic review
|
|
|
74
74
|
Should subtract round? The criterion does not say.
|
|
75
75
|
"""
|
|
76
76
|
And step "build.quality.sorting" runs its script
|
|
77
|
+
And step "build.quality.closing" runs its script
|
|
77
78
|
And step "build.review.composing" runs its script
|
|
78
79
|
|
|
79
80
|
Scenario: a mechanical finding is fixed and listed, noise dropped and listed, a design one noted
|
|
@@ -184,7 +185,8 @@ Feature: the quality lenses are the only agentic review
|
|
|
184
185
|
gtd(agent): build.fix.quality.fixing
|
|
185
186
|
gtd(agent): build.fix.quality.fixing
|
|
186
187
|
gtd(agent): build.fix.quality.fixing → build.health.check
|
|
187
|
-
gtd(check): build.health.check → build.
|
|
188
|
+
gtd(check): build.health.check → build.quality.closing
|
|
189
|
+
gtd(check): build.quality.closing → build.review.composing
|
|
188
190
|
gtd(check): build.review.composing → build.review.await-review
|
|
189
191
|
"""
|
|
190
192
|
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
@inmem
|
|
2
|
+
Feature: every slice's quality lap is triaged, and its leftovers wait for the review
|
|
3
|
+
|
|
4
|
+
A judge sorts each slice's findings like the last slice's (see
|
|
5
|
+
`lens-review.feature`): mechanical ones are fixed, noise is dropped, design
|
|
6
|
+
ones are never sent to the fixer. Nothing between slices asks a person
|
|
7
|
+
anything — what a lap leaves is carried to the review after the last slice,
|
|
8
|
+
each finding naming its slice and lens.
|
|
9
|
+
|
|
10
|
+
Background:
|
|
11
|
+
Given the shipped workflows, as gtd install scaffolds them
|
|
12
|
+
And an environment variable "GTD_QUALITYREVIEWS" set to "correctness"
|
|
13
|
+
And an environment variable "GTD_FLAGMODE" set to "false"
|
|
14
|
+
And an environment variable "GTD_SLICEMAXCRITERIA" set to "1"
|
|
15
|
+
And a file "src/calc.ts" with:
|
|
16
|
+
"""
|
|
17
|
+
export const add = (a: number, b: number) => a + b
|
|
18
|
+
"""
|
|
19
|
+
And the working tree is committed as "feat: add calculator"
|
|
20
|
+
And a file "TODO.md" with:
|
|
21
|
+
"""
|
|
22
|
+
# More arithmetic
|
|
23
|
+
|
|
24
|
+
subtract and multiply.
|
|
25
|
+
"""
|
|
26
|
+
And step "unwind" changes nothing
|
|
27
|
+
And step "start-gate.check" changes nothing
|
|
28
|
+
And step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
|
|
29
|
+
"""
|
|
30
|
+
# Requirements
|
|
31
|
+
|
|
32
|
+
## Acceptance Criteria
|
|
33
|
+
|
|
34
|
+
- AC-1: subtract(a, b) returns a - b.
|
|
35
|
+
- AC-2: multiply(a, b) returns a * b.
|
|
36
|
+
"""
|
|
37
|
+
And step "design.gate.answer" changes nothing
|
|
38
|
+
And step "design.slicing" writes ".gtd/SLICES.md" with:
|
|
39
|
+
"""
|
|
40
|
+
# Slices
|
|
41
|
+
|
|
42
|
+
## Subtract
|
|
43
|
+
|
|
44
|
+
- AC-1
|
|
45
|
+
|
|
46
|
+
## Multiply
|
|
47
|
+
|
|
48
|
+
- AC-2
|
|
49
|
+
"""
|
|
50
|
+
And step "seam.naive", when prompted with "This slice builds AC-1 only", writes "src/sub.test.ts" with:
|
|
51
|
+
"""
|
|
52
|
+
it("[AC-1] subtracts", () => {})
|
|
53
|
+
"""
|
|
54
|
+
And step "seam.naive", when prompted with "This slice builds AC-2 only", writes "src/mul.test.ts" with:
|
|
55
|
+
"""
|
|
56
|
+
it("[AC-2] multiplies", () => {})
|
|
57
|
+
"""
|
|
58
|
+
And step "seam.check" changes nothing
|
|
59
|
+
And step "implement.applying" runs its script
|
|
60
|
+
And step "implement.health.check" changes nothing
|
|
61
|
+
And step "implement.closing" runs its script
|
|
62
|
+
And judge step "implement.coverage", when its questions contain "AC-1", answers:
|
|
63
|
+
"""
|
|
64
|
+
[{"id":"AC-1","answer":"yes","p":0.9}]
|
|
65
|
+
"""
|
|
66
|
+
And judge step "implement.coverage", when its questions contain "AC-2", answers:
|
|
67
|
+
"""
|
|
68
|
+
[{"id":"AC-2","answer":"yes","p":0.9}]
|
|
69
|
+
"""
|
|
70
|
+
And step "build.quality.sorting" runs its script
|
|
71
|
+
And step "build.quality.closing" runs its script
|
|
72
|
+
And step "build.health.check" changes nothing
|
|
73
|
+
And step "build.review.composing" runs its script
|
|
74
|
+
|
|
75
|
+
Scenario: a finding questioning a criterion reaches the final review, never the fixer
|
|
76
|
+
Given step "build.quality.correctness.reviewing" writes ".gtd/QUALITY.md" with:
|
|
77
|
+
"""
|
|
78
|
+
# Quality
|
|
79
|
+
|
|
80
|
+
## AC-1 ignores floating point
|
|
81
|
+
|
|
82
|
+
Should subtract round? The criterion does not say.
|
|
83
|
+
"""
|
|
84
|
+
And judge step "build.quality.triage" answers:
|
|
85
|
+
"""
|
|
86
|
+
[{"id":"Q1","answer":"design","p":0.9}]
|
|
87
|
+
"""
|
|
88
|
+
When the process is driven
|
|
89
|
+
Then the process rests at "build.review.await-review"
|
|
90
|
+
And no commit body contains "build.fix.quality.fixing"
|
|
91
|
+
And ".gtd/QUALITY.md" does not exist
|
|
92
|
+
And ".gtd/REVIEW.md" contains:
|
|
93
|
+
"""
|
|
94
|
+
## AC-1 ignores floating point
|
|
95
|
+
|
|
96
|
+
_From slice 1, lens correctness._
|
|
97
|
+
|
|
98
|
+
Should subtract round? The criterion does not say.
|
|
99
|
+
"""
|
|
100
|
+
And ".gtd/REVIEW.md" contains:
|
|
101
|
+
"""
|
|
102
|
+
## AC-1 ignores floating point (2)
|
|
103
|
+
|
|
104
|
+
_From slice 2, lens correctness._
|
|
105
|
+
"""
|
|
106
|
+
And the commit subjects end with:
|
|
107
|
+
"""
|
|
108
|
+
gtd(check): implement.closing → build.quality.correctness.reviewing
|
|
109
|
+
gtd(agent): build.quality.correctness.reviewing → build.quality.triage
|
|
110
|
+
gtd(judge): build.quality.triage → build.quality.sorting
|
|
111
|
+
gtd(check): build.quality.sorting → seam.naive
|
|
112
|
+
gtd(agent): seam.naive → seam.check
|
|
113
|
+
gtd(check): seam.check → implement.applying
|
|
114
|
+
gtd(check): implement.applying → implement.health.check
|
|
115
|
+
gtd(check): implement.health.check → implement.coverage
|
|
116
|
+
gtd(judge): implement.coverage → implement.closing
|
|
117
|
+
gtd(check): implement.closing → build.quality.correctness.reviewing
|
|
118
|
+
gtd(agent): build.quality.correctness.reviewing → build.quality.triage
|
|
119
|
+
gtd(judge): build.quality.triage → build.quality.sorting
|
|
120
|
+
gtd(check): build.quality.sorting → build.review.composing
|
|
121
|
+
gtd(check): build.review.composing → build.review.await-review
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
Scenario: a mechanical finding the fix cap leaves in an early slice is carried, never escalated
|
|
125
|
+
Given step "build.quality.correctness.reviewing" writes ".gtd/QUALITY.md" with:
|
|
126
|
+
"""
|
|
127
|
+
# Quality
|
|
128
|
+
|
|
129
|
+
## subtract has no negative test
|
|
130
|
+
|
|
131
|
+
Add a test subtracting a larger number.
|
|
132
|
+
"""
|
|
133
|
+
And judge step "build.quality.triage" answers:
|
|
134
|
+
"""
|
|
135
|
+
[{"id":"Q1","answer":"mechanical","p":0.9}]
|
|
136
|
+
"""
|
|
137
|
+
And step "build.fix.quality.fixing" changes nothing
|
|
138
|
+
When the process is driven
|
|
139
|
+
Then the process rests at "build.review.await-review"
|
|
140
|
+
And no commit body contains "build.health.describe"
|
|
141
|
+
And ".gtd/REVIEW.md" contains:
|
|
142
|
+
"""
|
|
143
|
+
## subtract has no negative test
|
|
144
|
+
|
|
145
|
+
_From slice 1, lens correctness._
|
|
146
|
+
|
|
147
|
+
_The fixer left this unresolved._
|
|
148
|
+
"""
|
|
149
|
+
And the commit subjects end with:
|
|
150
|
+
"""
|
|
151
|
+
gtd(check): implement.closing → build.quality.correctness.reviewing
|
|
152
|
+
gtd(agent): build.quality.correctness.reviewing → build.quality.triage
|
|
153
|
+
gtd(judge): build.quality.triage → build.fix.quality.fixing
|
|
154
|
+
gtd(agent): build.fix.quality.fixing
|
|
155
|
+
gtd(agent): build.fix.quality.fixing
|
|
156
|
+
gtd(agent): build.fix.quality.fixing → build.health.check
|
|
157
|
+
gtd(check): build.health.check → build.quality.closing
|
|
158
|
+
gtd(check): build.quality.closing → seam.naive
|
|
159
|
+
gtd(agent): seam.naive → seam.check
|
|
160
|
+
gtd(check): seam.check → implement.applying
|
|
161
|
+
gtd(check): implement.applying → implement.health.check
|
|
162
|
+
gtd(check): implement.health.check → implement.coverage
|
|
163
|
+
gtd(judge): implement.coverage → implement.closing
|
|
164
|
+
gtd(check): implement.closing → build.quality.correctness.reviewing
|
|
165
|
+
gtd(agent): build.quality.correctness.reviewing → build.quality.triage
|
|
166
|
+
gtd(judge): build.quality.triage → build.fix.quality.fixing
|
|
167
|
+
gtd(agent): build.fix.quality.fixing
|
|
168
|
+
gtd(agent): build.fix.quality.fixing
|
|
169
|
+
gtd(agent): build.fix.quality.fixing → build.health.check
|
|
170
|
+
gtd(check): build.health.check → build.quality.closing
|
|
171
|
+
gtd(check): build.quality.closing → build.review.composing
|
|
172
|
+
gtd(check): build.review.composing → build.review.await-review
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
Scenario: the final review lists every slice's fixes with their commits
|
|
176
|
+
Given step "build.quality.correctness.reviewing" writes ".gtd/QUALITY.md" with:
|
|
177
|
+
"""
|
|
178
|
+
# Quality
|
|
179
|
+
|
|
180
|
+
## subtract has no negative test
|
|
181
|
+
|
|
182
|
+
Add a test subtracting a larger number.
|
|
183
|
+
"""
|
|
184
|
+
And judge step "build.quality.triage" answers:
|
|
185
|
+
"""
|
|
186
|
+
[{"id":"Q1","answer":"mechanical","p":0.9}]
|
|
187
|
+
"""
|
|
188
|
+
And step "build.fix.quality.fixing" writes "src/edge.test.ts" with:
|
|
189
|
+
"""
|
|
190
|
+
it("subtracts a larger number", () => {})
|
|
191
|
+
"""
|
|
192
|
+
And step "build.fix.quality.fixing" deletes ".gtd/QUALITY.md"
|
|
193
|
+
When the process is driven
|
|
194
|
+
Then the process rests at "build.review.await-review"
|
|
195
|
+
And ".gtd/REVIEW.md" contains ", slice 1, lens correctness)"
|
|
196
|
+
And ".gtd/REVIEW.md" contains ", slice 2, lens correctness)"
|
|
197
|
+
And ".gtd/REVIEW.md" contains the short hash of the commit "gtd(agent): build.fix.quality.fixing → build.health.check"
|
|
198
|
+
And no commit body contains "build.quality.closing"
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
@inmem
|
|
2
|
+
Feature: each finding the reviewer is handed names the lens that wrote it
|
|
3
|
+
|
|
4
|
+
Lenses append to `.gtd/QUALITY.md` in turn, so a finding belongs to the lens
|
|
5
|
+
whose turn added it — even when two lenses give findings the same title,
|
|
6
|
+
which the review then tells apart by a numbered suffix.
|
|
7
|
+
|
|
8
|
+
Background:
|
|
9
|
+
Given the shipped workflows, as gtd install scaffolds them
|
|
10
|
+
And an environment variable "GTD_QUALITYREVIEWS" set to "correctness,tests"
|
|
11
|
+
And a file "src/calc.ts" with:
|
|
12
|
+
"""
|
|
13
|
+
export const add = (a: number, b: number) => a + b
|
|
14
|
+
"""
|
|
15
|
+
And the working tree is committed as "feat: add calculator"
|
|
16
|
+
And a file ".gtd/REVIEW.orig.md" with:
|
|
17
|
+
"""
|
|
18
|
+
# Review: abc1234
|
|
19
|
+
|
|
20
|
+
<!-- base: 0000000 -->
|
|
21
|
+
|
|
22
|
+
## calc
|
|
23
|
+
|
|
24
|
+
- [ ] ./src/calc.ts#1-1
|
|
25
|
+
new add function
|
|
26
|
+
"""
|
|
27
|
+
And a file ".gtd/REVIEW.md" with:
|
|
28
|
+
"""
|
|
29
|
+
# Review: abc1234
|
|
30
|
+
|
|
31
|
+
<!-- base: 0000000 -->
|
|
32
|
+
|
|
33
|
+
## calc
|
|
34
|
+
|
|
35
|
+
- [ ] ./src/calc.ts#1-1
|
|
36
|
+
new add function — rename it to sum
|
|
37
|
+
"""
|
|
38
|
+
And the working tree is committed as "chore: hand off a review"
|
|
39
|
+
And step "notes.fixing" writes "src/calc.ts" with:
|
|
40
|
+
"""
|
|
41
|
+
export const sum = (a: number, b: number) => a + b
|
|
42
|
+
"""
|
|
43
|
+
And step "notes.closing" runs its script
|
|
44
|
+
And step "build.health.check" changes nothing
|
|
45
|
+
And step "build.quality.correctness.reviewing" writes ".gtd/QUALITY.md" with:
|
|
46
|
+
"""
|
|
47
|
+
# Quality
|
|
48
|
+
|
|
49
|
+
## sum is untested
|
|
50
|
+
|
|
51
|
+
From correctness.
|
|
52
|
+
"""
|
|
53
|
+
And step "build.quality.tests.reviewing" writes ".gtd/QUALITY.md" with:
|
|
54
|
+
"""
|
|
55
|
+
# Quality
|
|
56
|
+
|
|
57
|
+
## sum is untested
|
|
58
|
+
|
|
59
|
+
From correctness.
|
|
60
|
+
|
|
61
|
+
## sum is untested
|
|
62
|
+
|
|
63
|
+
From tests.
|
|
64
|
+
"""
|
|
65
|
+
And step "build.quality.sorting" runs its script
|
|
66
|
+
And step "build.quality.closing" runs its script
|
|
67
|
+
And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
|
|
68
|
+
"""
|
|
69
|
+
# Review: abc1234
|
|
70
|
+
|
|
71
|
+
<!-- base: 0000000 -->
|
|
72
|
+
|
|
73
|
+
## calc
|
|
74
|
+
|
|
75
|
+
- [ ] ./src/calc.ts#1-1
|
|
76
|
+
renamed to sum
|
|
77
|
+
"""
|
|
78
|
+
And step "build.review.carrying" runs its script
|
|
79
|
+
|
|
80
|
+
Scenario: two lenses' findings with one title each name their own lens
|
|
81
|
+
Given judge step "build.quality.triage" answers:
|
|
82
|
+
"""
|
|
83
|
+
[{"id":"Q1","answer":"design","p":0.9},{"id":"Q2","answer":"design","p":0.9}]
|
|
84
|
+
"""
|
|
85
|
+
When gtd opens door "fix"
|
|
86
|
+
And the process is driven
|
|
87
|
+
Then the process rests at "build.review.await-review"
|
|
88
|
+
And ".gtd/REVIEW.md" contains:
|
|
89
|
+
"""
|
|
90
|
+
## sum is untested
|
|
91
|
+
|
|
92
|
+
_From lens correctness._
|
|
93
|
+
|
|
94
|
+
From correctness.
|
|
95
|
+
|
|
96
|
+
## sum is untested (2)
|
|
97
|
+
|
|
98
|
+
_From lens tests._
|
|
99
|
+
|
|
100
|
+
From tests.
|
|
101
|
+
"""
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
@inmem
|
|
2
|
+
Feature: fix's quality lap is triaged, and its leftovers go to the hand-off review
|
|
3
|
+
|
|
4
|
+
A judge sorts the lap's findings: mechanical ones are fixed for up to three
|
|
5
|
+
turns, noise is dropped, and design ones — with whatever the fix turns left —
|
|
6
|
+
are added to the reviewer's `.gtd/REVIEW.md` by gtd, never to a person's
|
|
7
|
+
gate before the review.
|
|
8
|
+
|
|
9
|
+
Background:
|
|
10
|
+
Given the shipped workflows, as gtd install scaffolds them
|
|
11
|
+
And an environment variable "GTD_QUALITYREVIEWS" set to "correctness"
|
|
12
|
+
And a file "src/calc.ts" with:
|
|
13
|
+
"""
|
|
14
|
+
export const add = (a: number, b: number) => a + b
|
|
15
|
+
"""
|
|
16
|
+
And the working tree is committed as "feat: add calculator"
|
|
17
|
+
And a file ".gtd/REVIEW.orig.md" with:
|
|
18
|
+
"""
|
|
19
|
+
# Review: abc1234
|
|
20
|
+
|
|
21
|
+
<!-- base: 0000000 -->
|
|
22
|
+
|
|
23
|
+
## calc
|
|
24
|
+
|
|
25
|
+
- [ ] ./src/calc.ts#1-1
|
|
26
|
+
new add function
|
|
27
|
+
"""
|
|
28
|
+
And a file ".gtd/REVIEW.md" with:
|
|
29
|
+
"""
|
|
30
|
+
# Review: abc1234
|
|
31
|
+
|
|
32
|
+
<!-- base: 0000000 -->
|
|
33
|
+
|
|
34
|
+
## calc
|
|
35
|
+
|
|
36
|
+
- [ ] ./src/calc.ts#1-1
|
|
37
|
+
new add function — rename it to sum
|
|
38
|
+
"""
|
|
39
|
+
And the working tree is committed as "chore: hand off a review"
|
|
40
|
+
And step "notes.fixing" writes "src/calc.ts" with:
|
|
41
|
+
"""
|
|
42
|
+
export const sum = (a: number, b: number) => a + b
|
|
43
|
+
"""
|
|
44
|
+
And step "notes.closing" runs its script
|
|
45
|
+
And step "build.health.check" changes nothing
|
|
46
|
+
And step "build.quality.correctness.reviewing" writes ".gtd/QUALITY.md" with:
|
|
47
|
+
"""
|
|
48
|
+
# Quality
|
|
49
|
+
|
|
50
|
+
## sum has no negative test
|
|
51
|
+
|
|
52
|
+
Add a test summing negative numbers.
|
|
53
|
+
|
|
54
|
+
## sum should be named plus
|
|
55
|
+
|
|
56
|
+
The glossary says plus.
|
|
57
|
+
"""
|
|
58
|
+
And step "build.quality.sorting" runs its script
|
|
59
|
+
And step "build.quality.closing" runs its script
|
|
60
|
+
And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
|
|
61
|
+
"""
|
|
62
|
+
# Review: abc1234
|
|
63
|
+
|
|
64
|
+
<!-- base: 0000000 -->
|
|
65
|
+
|
|
66
|
+
## calc
|
|
67
|
+
|
|
68
|
+
- [ ] ./src/calc.ts#1-1
|
|
69
|
+
renamed to sum
|
|
70
|
+
"""
|
|
71
|
+
And step "build.review.carrying" runs its script
|
|
72
|
+
|
|
73
|
+
Scenario: a design finding and one the fix cap left reach the reviewer, never an escalation
|
|
74
|
+
Given judge step "build.quality.triage" answers:
|
|
75
|
+
"""
|
|
76
|
+
[{"id":"Q1","answer":"mechanical","p":0.9},{"id":"Q2","answer":"design","p":0.9}]
|
|
77
|
+
"""
|
|
78
|
+
And step "build.fix.quality.fixing" changes nothing
|
|
79
|
+
When gtd opens door "fix"
|
|
80
|
+
And the process is driven
|
|
81
|
+
Then the process rests at "build.review.await-review"
|
|
82
|
+
And ".gtd/QUALITY.md" does not exist
|
|
83
|
+
And no commit body contains "build.health.describe"
|
|
84
|
+
And ".gtd/REVIEW.md" contains:
|
|
85
|
+
"""
|
|
86
|
+
## sum has no negative test
|
|
87
|
+
|
|
88
|
+
_From lens correctness._
|
|
89
|
+
|
|
90
|
+
_The fixer left this unresolved._
|
|
91
|
+
"""
|
|
92
|
+
And ".gtd/REVIEW.md" contains:
|
|
93
|
+
"""
|
|
94
|
+
## sum should be named plus
|
|
95
|
+
|
|
96
|
+
_From lens correctness._
|
|
97
|
+
|
|
98
|
+
The glossary says plus.
|
|
99
|
+
"""
|
|
100
|
+
And the commit subjects end with:
|
|
101
|
+
"""
|
|
102
|
+
gtd(agent): build.quality.correctness.reviewing → build.quality.triage
|
|
103
|
+
gtd(judge): build.quality.triage → build.quality.sorting
|
|
104
|
+
gtd(check): build.quality.sorting → build.fix.quality.fixing
|
|
105
|
+
gtd(agent): build.fix.quality.fixing
|
|
106
|
+
gtd(agent): build.fix.quality.fixing
|
|
107
|
+
gtd(agent): build.fix.quality.fixing → build.health.check
|
|
108
|
+
gtd(check): build.health.check → build.quality.closing
|
|
109
|
+
gtd(check): build.quality.closing → build.review.reviewing
|
|
110
|
+
gtd(agent): build.review.reviewing → build.review.carrying
|
|
111
|
+
gtd(check): build.review.carrying → build.review.await-review
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
Scenario: a fixed finding and a noise one are listed in the review
|
|
115
|
+
Given judge step "build.quality.triage" answers:
|
|
116
|
+
"""
|
|
117
|
+
[{"id":"Q1","answer":"mechanical","p":0.9},{"id":"Q2","answer":"noise","p":0.9}]
|
|
118
|
+
"""
|
|
119
|
+
And step "build.fix.quality.fixing" writes "src/calc.test.ts" with:
|
|
120
|
+
"""
|
|
121
|
+
it("sums negative numbers", () => {})
|
|
122
|
+
"""
|
|
123
|
+
And step "build.fix.quality.fixing" deletes ".gtd/QUALITY.md"
|
|
124
|
+
When gtd opens door "fix"
|
|
125
|
+
And the process is driven
|
|
126
|
+
Then the process rests at "build.review.await-review"
|
|
127
|
+
And no commit body contains "build.quality.closing"
|
|
128
|
+
And ".gtd/REVIEW.md" contains "## Fixed automatically"
|
|
129
|
+
And ".gtd/REVIEW.md" contains "- sum has no negative test ("
|
|
130
|
+
And ".gtd/REVIEW.md" contains the short hash of the commit "gtd(agent): build.fix.quality.fixing → build.health.check"
|
|
131
|
+
And ".gtd/REVIEW.md" contains "- sum should be named plus (p=0.9, lens correctness)"
|