@pmelab/gtd 54.0.0 → 56.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 +109 -104
- package/dist/chunks/{reserved-CQ7amcKG.mjs → reserved-B8EC-WE8.mjs} +124 -230
- package/dist/chunks/{review-CXzUXJHL.mjs → review-HdrX0FJ7.mjs} +30 -125
- package/dist/chunks/{steering-CLhu8C7q.mjs → steering-CCNvngSH.mjs} +256 -65
- package/dist/chunks/{tail-JZ9PAOwp.mjs → tail-BSLah2mA.mjs} +155 -63
- package/dist/gtd.bundle.mjs +259 -22
- package/dist/steering.mjs +2 -2
- package/dist/testkit.mjs +6 -6
- package/dist/web/main.css +1 -1
- package/dist/web/main.js +551 -66
- package/dist/workflow-kit.mjs +1 -1
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +47 -102
- package/dist/workflows/fix.mjs +3 -3
- package/dist/workflows/review.mjs +3 -3
- package/dist/workflows/rollout.mjs +1 -1
- package/dist/workflows.mjs +614 -574
- package/package.json +1 -1
- package/skills/authoring/SKILL.md +3 -3
- package/src/cli/Cli.ts +5 -5
- package/src/edge/Edge.test.ts +1 -1
- package/src/steering/FORMATS.md +5 -1
- package/src/steering/SteeringFormats.test.ts +2 -2
- package/src/steering/contract.test.ts +1 -0
- package/src/steering/findings.test.ts +47 -0
- package/src/steering/findings.ts +350 -0
- package/src/steering/fixtures/findings/gate.md +34 -0
- package/src/steering/fixtures/findings/gate.read.json +87 -0
- package/src/steering/index.test.ts +2 -2
- package/src/steering/index.ts +4 -3
- package/src/steering/review.test.ts +1 -75
- package/src/steering/review.ts +1 -74
- package/src/workflow/compile.test.ts +1 -0
- package/src/workflows/feature/criteria-coverage.feature +104 -0
- package/src/workflows/feature/feature.test.ts +1 -1
- package/src/workflows/feature/feature.ts +17 -20
- package/src/workflows/feature/green.ts +2 -5
- package/src/workflows/feature/lens-review.feature +323 -171
- package/src/workflows/feature/naive-acceptance.feature +31 -32
- package/src/workflows/feature/prototype.ts +5 -8
- package/src/workflows/feature/refactor-absorbs.feature +1 -7
- package/src/workflows/feature/refactor-first.feature +2 -10
- package/src/workflows/feature/refactor-rebuild.feature +20 -36
- package/src/workflows/feature/refactor-review.feature +33 -117
- package/src/workflows/feature/refactor.test.ts +0 -2
- package/src/workflows/feature/refactor.ts +0 -1
- package/src/workflows/feature/review-gate.feature +6 -22
- package/src/workflows/feature/reviewer-walkthrough.feature +16 -103
- package/src/workflows/feature/seam.test.ts +19 -101
- package/src/workflows/feature/seam.ts +13 -89
- package/src/workflows/feature/standards.feature +75 -34
- package/src/workflows/fix/fix-lens-origin.feature +20 -7
- package/src/workflows/fix/fix-quality.feature +104 -23
- package/src/workflows/review/review-door.feature +62 -59
- package/src/workflows/shared/access.test.ts +1 -0
- package/src/workflows/shared/learn.test.ts +0 -14
- package/src/workflows/shared/learn.ts +1 -6
- package/src/workflows/shared/lenses.ts +149 -43
- package/src/workflows/shared/review.test.ts +30 -27
- package/src/workflows/shared/review.ts +29 -196
- package/src/workflows/shared/skills.test.ts +3 -1
- package/src/workflows/shared/steps.test.ts +9 -6
- package/src/workflows/shared/steps.ts +12 -3
- package/src/workflows/shared/tail.test.ts +20 -2
- package/src/workflows/shared/tail.ts +38 -16
- package/src/workflows/shared/text.test.ts +122 -294
- package/src/workflows/shared/text.ts +181 -358
- package/src/workflows/shared/vars.ts +1 -1
- package/src/workflows/feature/review-answers.feature +0 -324
- package/src/workflows/feature/severity-budget.feature +0 -116
- package/src/workflows/feature/severity-sorted-review.feature +0 -198
|
@@ -14,8 +14,6 @@ import {
|
|
|
14
14
|
naivePass,
|
|
15
15
|
acceptanceFault,
|
|
16
16
|
naiveAcceptance,
|
|
17
|
-
requireAcceptance,
|
|
18
|
-
requireAcceptanceKept,
|
|
19
17
|
seamBuild,
|
|
20
18
|
type SeamOutcome,
|
|
21
19
|
} from "./seam.js"
|
|
@@ -108,71 +106,24 @@ describe("naivePass", () => {
|
|
|
108
106
|
})
|
|
109
107
|
})
|
|
110
108
|
|
|
111
|
-
describe("naiveAcceptance
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const file = (path: string, before: string | undefined, after: string): Change => ({
|
|
115
|
-
path,
|
|
116
|
-
status: before === undefined ? "added" : "modified",
|
|
117
|
-
before,
|
|
118
|
-
after,
|
|
119
|
-
})
|
|
120
|
-
const install = (since: Change[] = []) =>
|
|
121
|
-
installContext({
|
|
122
|
-
...fixtureContext({}),
|
|
123
|
-
matches: globMatches,
|
|
124
|
-
read: (path: string) => (path === ".gtd/REQUIREMENTS.md" ? requirements : undefined),
|
|
125
|
-
changesSince: () => since,
|
|
126
|
-
changes: () => since,
|
|
127
|
-
refuse: (message: string) => {
|
|
128
|
-
throw new Error(message)
|
|
129
|
-
},
|
|
130
|
-
} as unknown as FlowContext)
|
|
131
|
-
|
|
132
|
-
it("takes the IDs the pass's tests name on an added line, and only those test files", () => {
|
|
133
|
-
install()
|
|
109
|
+
describe("naiveAcceptance", () => {
|
|
110
|
+
it("carries every test file the pass added or changed, named or not", () => {
|
|
111
|
+
installContext({ ...fixtureContext({}), matches: globMatches } as unknown as FlowContext)
|
|
134
112
|
expect(
|
|
135
113
|
naiveAcceptance(
|
|
136
114
|
tip(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
115
|
+
change("src/a.test.ts", "modified"),
|
|
116
|
+
change("tests/b.feature", "added"),
|
|
117
|
+
change("src/gone.test.ts", "deleted"),
|
|
118
|
+
change("src/calc.ts", "modified"),
|
|
119
|
+
change(".gtd/x.test.ts", "added"),
|
|
140
120
|
),
|
|
141
121
|
),
|
|
142
|
-
).toEqual({ hash: "abc", tests: ["src/a.test.ts"
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
it("refuses a rebuild whose tests leave out an ID, naming the naive tests to carry over", () => {
|
|
146
|
-
install([file("src/calc.test.ts", undefined, 'it("[AC-1] adds")')])
|
|
147
|
-
expect(() =>
|
|
148
|
-
requireAcceptance({ hash: "abc", tests: ["src/a.test.ts"], ids: ["AC-1", "AC-2"] }, "c0"),
|
|
149
|
-
).toThrow(
|
|
150
|
-
/^gtd land: rebuild-acceptance: .*leave out AC-2 — .*git show abc -- 'src\/a\.test\.ts'/,
|
|
151
|
-
)
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
it("refuses a fix turn that itself drops a carried ID, never one that leaves it alone", () => {
|
|
155
|
-
const acceptance = { hash: "abc", tests: [], ids: ["AC-1"] }
|
|
156
|
-
install([file("src/calc.test.ts", 'it("[AC-1] adds")', 'it("adds")')])
|
|
157
|
-
expect(() => requireAcceptanceKept(acceptance)).toThrow(/drops AC-1/)
|
|
158
|
-
install([file("src/calc.ts", "a", "b")])
|
|
159
|
-
expect(() => requireAcceptanceKept(acceptance)).not.toThrow()
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
it("passes a rebuild whose tests name every ID, wherever they live", () => {
|
|
163
|
-
install([
|
|
164
|
-
file("tests/x.test.ts", undefined, 'it("[AC-1] adds")'),
|
|
165
|
-
file("tests/y.feature", "", "Scenario: [AC-2] subtracts"),
|
|
166
|
-
])
|
|
167
|
-
expect(() =>
|
|
168
|
-
requireAcceptance({ hash: "abc", tests: ["src/a.test.ts"], ids: ["AC-1", "AC-2"] }, "c0"),
|
|
169
|
-
).not.toThrow()
|
|
122
|
+
).toEqual({ hash: "abc", tests: ["src/a.test.ts", "tests/b.feature"] })
|
|
170
123
|
})
|
|
171
124
|
})
|
|
172
125
|
|
|
173
126
|
describe("acceptanceFault", () => {
|
|
174
|
-
const requirements =
|
|
175
|
-
"# R\n\n## Impact\n\n- the module\n\n## Acceptance Criteria\n\n- AC-1: adds.\n- AC-2: subtracts.\n"
|
|
176
127
|
const file = (path: string, status: Change["status"], after: string): Change => ({
|
|
177
128
|
path,
|
|
178
129
|
status,
|
|
@@ -180,30 +131,10 @@ describe("acceptanceFault", () => {
|
|
|
180
131
|
after,
|
|
181
132
|
})
|
|
182
133
|
const refusal = (...changes: Change[]): string | undefined => {
|
|
183
|
-
installContext({
|
|
184
|
-
|
|
185
|
-
matches: globMatches,
|
|
186
|
-
read: (path: string) => (path === ".gtd/REQUIREMENTS.md" ? requirements : undefined),
|
|
187
|
-
} as unknown as FlowContext)
|
|
188
|
-
return acceptanceFault(".gtd/REQUIREMENTS.md", tip(...changes))
|
|
134
|
+
installContext({ ...fixtureContext({}), matches: globMatches } as unknown as FlowContext)
|
|
135
|
+
return acceptanceFault(tip(...changes))
|
|
189
136
|
}
|
|
190
|
-
const tagged = 'import { add } from "../src/calc.js"\nit("
|
|
191
|
-
|
|
192
|
-
it("passes when every ID is named in a test the pass wrote", () => {
|
|
193
|
-
expect(
|
|
194
|
-
refusal(file("src/calc.ts", "modified", "x"), file("tests/calc.test.ts", "added", tagged)),
|
|
195
|
-
).toBe(undefined)
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
it("names every ID no test of the pass mentions", () => {
|
|
199
|
-
const message = refusal(file("tests/calc.test.ts", "added", 'it("[AC-12] x")'))
|
|
200
|
-
expect(message).toMatch(/^acceptance-tests: /)
|
|
201
|
-
expect(message).toContain("AC-1, AC-2")
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
it("does not count an ID in a non-test file", () => {
|
|
205
|
-
expect(refusal(file("src/calc.ts", "modified", "AC-1 AC-2"))).toContain("AC-1, AC-2")
|
|
206
|
-
})
|
|
137
|
+
const tagged = 'import { add } from "../src/calc.js"\nit("adds")'
|
|
207
138
|
|
|
208
139
|
it("refuses a test importing a file the pass added, resolved from the test's directory", () => {
|
|
209
140
|
const message = refusal(
|
|
@@ -224,17 +155,6 @@ describe("acceptanceFault", () => {
|
|
|
224
155
|
).toContain("acceptance-internals")
|
|
225
156
|
})
|
|
226
157
|
|
|
227
|
-
it("counts only the lines the pass added: an ID an earlier feature left does not", () => {
|
|
228
|
-
const old = 'it("[AC-1] old")\nit("[AC-2] old")'
|
|
229
|
-
const message = refusal({
|
|
230
|
-
path: "tests/calc.test.ts",
|
|
231
|
-
status: "modified",
|
|
232
|
-
before: old,
|
|
233
|
-
after: `${old}\nit("[AC-2] new")`,
|
|
234
|
-
})
|
|
235
|
-
expect(message).toContain("names AC-1 —")
|
|
236
|
-
})
|
|
237
|
-
|
|
238
158
|
it("refuses a bare . or .. import of an added index", () => {
|
|
239
159
|
expect(
|
|
240
160
|
refusal(
|
|
@@ -424,7 +344,7 @@ describe("seamBuild", () => {
|
|
|
424
344
|
reference: "abc",
|
|
425
345
|
base: "after-refactor",
|
|
426
346
|
refactored: true,
|
|
427
|
-
rebuild: { note: "Seam: a shared op", acceptance: { hash: "abc", tests: []
|
|
347
|
+
rebuild: { note: "Seam: a shared op", acceptance: { hash: "abc", tests: [] } },
|
|
428
348
|
})
|
|
429
349
|
expect(h.names.indexOf("seam.note")).toBe(h.names.indexOf("seam.judge") + 1)
|
|
430
350
|
expect(h.names.indexOf("refactor.deriving")).toBe(h.names.indexOf("seam.note") + 1)
|
|
@@ -440,7 +360,7 @@ describe("seamBuild", () => {
|
|
|
440
360
|
expect(h.outcome).toEqual({
|
|
441
361
|
reference: "abc",
|
|
442
362
|
base: "c-base",
|
|
443
|
-
rebuild: { acceptance: { hash: "abc", tests: []
|
|
363
|
+
rebuild: { acceptance: { hash: "abc", tests: ["src/a.test.ts"] } },
|
|
444
364
|
})
|
|
445
365
|
expect(h.names).not.toContain("seam.by-hand")
|
|
446
366
|
expect(h.names.some((n) => n.startsWith("refactor"))).toBe(false)
|
|
@@ -466,7 +386,7 @@ describe("seamBuild", () => {
|
|
|
466
386
|
expect(h.outcome).toEqual({
|
|
467
387
|
reference: "abc",
|
|
468
388
|
base: "c-base",
|
|
469
|
-
rebuild: { acceptance: { hash: "abc", tests: []
|
|
389
|
+
rebuild: { acceptance: { hash: "abc", tests: [] } },
|
|
470
390
|
})
|
|
471
391
|
})
|
|
472
392
|
})
|
|
@@ -543,19 +463,17 @@ describe("implement", () => {
|
|
|
543
463
|
reference: "abc",
|
|
544
464
|
rebuild: {
|
|
545
465
|
note: "# Note\n\nSeam: a shared op",
|
|
546
|
-
acceptance: { hash: "abc", tests: ["src/a.test.ts"]
|
|
466
|
+
acceptance: { hash: "abc", tests: ["src/a.test.ts"] },
|
|
547
467
|
},
|
|
548
|
-
|
|
549
|
-
}).catch(() => undefined),
|
|
468
|
+
}),
|
|
550
469
|
)
|
|
551
470
|
expect(prompt).toContain(" > Seam: a shared op")
|
|
552
|
-
expect(prompt).toContain("acceptance tests for AC-1")
|
|
553
471
|
expect(prompt).toContain("git show abc -- 'src/a.test.ts'")
|
|
554
472
|
})
|
|
555
473
|
|
|
556
|
-
it("a rebuild whose naive
|
|
474
|
+
it("a rebuild whose naive pass wrote no test asks to carry nothing over", async () => {
|
|
557
475
|
const prompt = await building("abc", undefined, undefined, () =>
|
|
558
|
-
implement({ reference: "abc", rebuild: { acceptance: { hash: "abc", tests: []
|
|
476
|
+
implement({ reference: "abc", rebuild: { acceptance: { hash: "abc", tests: [] } } }),
|
|
559
477
|
)
|
|
560
478
|
expect(prompt).not.toContain("Carry over")
|
|
561
479
|
expect(prompt).not.toContain("refactor note")
|
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
agent,
|
|
4
4
|
applyScript,
|
|
5
5
|
changes,
|
|
6
|
-
changesSince,
|
|
7
6
|
check,
|
|
8
7
|
codeChanges,
|
|
9
8
|
env,
|
|
@@ -92,43 +91,19 @@ const reaches = (imports: ReturnType<typeof importsOf>, added: string): boolean
|
|
|
92
91
|
imports.some(({ path, tail }) => name === path || (tail && name.endsWith(`/${path}`))),
|
|
93
92
|
)
|
|
94
93
|
|
|
95
|
-
const named = (id: string, text: string): boolean =>
|
|
96
|
-
new RegExp(`(?<![\\w-])${id}(?!\\d)`).test(text)
|
|
97
|
-
|
|
98
|
-
/** The lines `change` added: an ID an earlier change left in the file does not count. */
|
|
99
|
-
const addedLines = (change: Change): string => {
|
|
100
|
-
const before = new Set((change.before ?? "").split("\n"))
|
|
101
|
-
return change
|
|
102
|
-
.after!.split("\n")
|
|
103
|
-
.filter((line) => !before.has(line))
|
|
104
|
-
.join("\n")
|
|
105
|
-
}
|
|
106
|
-
|
|
107
94
|
const acceptanceTests = (changes: readonly Change[]): readonly Change[] =>
|
|
108
95
|
changes.filter((c) => c.after !== undefined && isCode(c.path) && isTestFile(c.path))
|
|
109
96
|
|
|
110
|
-
const idsOf = (file: string): string[] => criterionIds(read(file) ?? "")
|
|
111
|
-
|
|
112
|
-
// One rule for both gates, so a naive test carried over verbatim always satisfies the rebuild's.
|
|
113
|
-
const unnamed = (ids: readonly string[], tests: readonly Change[]): string[] => {
|
|
114
|
-
const lines = tests.map(addedLines)
|
|
115
|
-
return ids.filter((id) => !lines.some((text) => named(id, text)))
|
|
116
|
-
}
|
|
117
|
-
|
|
118
97
|
/**
|
|
119
|
-
* Why `tip`'s tests fall short, or `undefined`: a
|
|
120
|
-
*
|
|
121
|
-
*
|
|
98
|
+
* Why `tip`'s tests fall short, or `undefined`: a test importing a file the
|
|
99
|
+
* pass added — naive structure a reset and refactor replaces, which the tests
|
|
100
|
+
* must survive.
|
|
122
101
|
*/
|
|
123
|
-
export const acceptanceFault = (
|
|
124
|
-
const tests = acceptanceTests(tip.changes)
|
|
125
|
-
const untested = unnamed(idsOf(file), tests)
|
|
126
|
-
if (untested.length > 0)
|
|
127
|
-
return `acceptance-tests: no test of the naive pass names ${untested.join(", ")} — write a behaviour-level test for each criterion with its ID in the test or scenario name, e.g. "[${untested[0]}] …"`
|
|
102
|
+
export const acceptanceFault = (tip: KeptTip): string | undefined => {
|
|
128
103
|
const added = tip.changes
|
|
129
104
|
.filter((c) => c.status === "added" && isCode(c.path) && !isTestFile(c.path))
|
|
130
105
|
.map((c) => c.path)
|
|
131
|
-
const reaching =
|
|
106
|
+
const reaching = acceptanceTests(tip.changes)
|
|
132
107
|
.filter((test) => holdsCallSites(test.path))
|
|
133
108
|
.flatMap((test) => {
|
|
134
109
|
const imports = importsOf(test)
|
|
@@ -141,48 +116,11 @@ export const acceptanceFault = (file: string, tip: KeptTip): string | undefined
|
|
|
141
116
|
return undefined
|
|
142
117
|
}
|
|
143
118
|
|
|
144
|
-
/** What a rebuild carries over from the naive pass `tip`:
|
|
145
|
-
export const naiveAcceptance = (tip: KeptTip): t.Acceptance => {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return { path: c.path, ids: ids.filter((id) => named(id, lines)) }
|
|
150
|
-
})
|
|
151
|
-
return {
|
|
152
|
-
hash: tip.hash,
|
|
153
|
-
tests: perTest.filter((c) => c.ids.length > 0).map((c) => c.path),
|
|
154
|
-
ids: ids.filter((id) => perTest.some((c) => c.ids.includes(id))),
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Refuse a rebuild whose test changes since `since` leave out an ID the naive
|
|
160
|
-
* pass's tests named. A skipped test still counts: the gate reads text, never runs it.
|
|
161
|
-
*/
|
|
162
|
-
export const requireAcceptance = (acceptance: t.Acceptance, since: string): void => {
|
|
163
|
-
const missing = unnamed(acceptance.ids, acceptanceTests(changesSince(since)))
|
|
164
|
-
if (missing.length === 0) return
|
|
165
|
-
refuse(
|
|
166
|
-
`gtd land: rebuild-acceptance: the rebuild's tests leave out ${missing.join(", ")} — carry over the naive pass's acceptance tests (\`git show ${acceptance.hash} -- ${acceptance.tests.map(quote).join(" ")}\`), each with its ID in the test or scenario name`,
|
|
167
|
-
)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Refuse a fix turn that, on its own, drops an ID the naive pass's tests named
|
|
172
|
-
* from a test file. Turn-local, so a person's edit at an escalation rest never
|
|
173
|
-
* wedges later fix turns.
|
|
174
|
-
*/
|
|
175
|
-
export const requireAcceptanceKept = (acceptance: t.Acceptance): void => {
|
|
176
|
-
const dropped = acceptance.ids.filter((id) =>
|
|
177
|
-
changes()
|
|
178
|
-
.filter((c) => isCode(c.path) && isTestFile(c.path))
|
|
179
|
-
.some((c) => named(id, c.before ?? "") && !named(id, c.after ?? "")),
|
|
180
|
-
)
|
|
181
|
-
if (dropped.length === 0) return
|
|
182
|
-
refuse(
|
|
183
|
-
`gtd land: rebuild-acceptance: this turn drops ${dropped.join(", ")} from the tests — the naive pass's acceptance tests stay; fix the code, not them`,
|
|
184
|
-
)
|
|
185
|
-
}
|
|
119
|
+
/** What a rebuild carries over from the naive pass `tip`: every test file it added or changed. */
|
|
120
|
+
export const naiveAcceptance = (tip: KeptTip): t.Acceptance => ({
|
|
121
|
+
hash: tip.hash,
|
|
122
|
+
tests: acceptanceTests(tip.changes).map((c) => c.path),
|
|
123
|
+
})
|
|
186
124
|
|
|
187
125
|
const restore = (path: string): string =>
|
|
188
126
|
`if git cat-file -e ${quote(`HEAD:${path}`)} 2>/dev/null; then git checkout -q HEAD -- ${quote(path)}; else rm -f -- ${quote(path)}; fi`
|
|
@@ -243,12 +181,6 @@ const applied = async (tip: string): Promise<boolean> => {
|
|
|
243
181
|
return !wrote(FEEDBACK) && codeChanges().length > 0
|
|
244
182
|
}
|
|
245
183
|
|
|
246
|
-
/** `carried` limited to the criteria `.gtd/REQUIREMENTS.md` lists now; a file listing none narrows nothing. */
|
|
247
|
-
const settled = (carried: readonly string[]): readonly string[] => {
|
|
248
|
-
const listed = idsOf(REQUIREMENTS)
|
|
249
|
-
return listed.length === 0 ? carried : carried.filter((id) => listed.includes(id))
|
|
250
|
-
}
|
|
251
|
-
|
|
252
184
|
const build = async (options: ImplementOptions): Promise<void> => {
|
|
253
185
|
await t.agentWithSkills(
|
|
254
186
|
"building",
|
|
@@ -290,19 +222,11 @@ export async function implement(
|
|
|
290
222
|
/** `implement`, resolving with the criterion IDs the requirements listed when it closed. */
|
|
291
223
|
export const implementing = (options: ImplementOptions): Promise<readonly string[]> =>
|
|
292
224
|
scope("implement", async () => {
|
|
293
|
-
const since = head()
|
|
294
|
-
const acceptance = options.rebuild?.acceptance
|
|
295
225
|
const built = options.naive === undefined || !(await applied(options.naive))
|
|
296
226
|
if (built) await build(options)
|
|
297
227
|
else requireCriteriaFrozen(REQUIREMENTS)
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
requireAcceptance({ ...acceptance, ids: settled(acceptance.ids) }, since)
|
|
301
|
-
// Fix turns are held to the carried tests too: deleting a red one must not buy a green suite.
|
|
302
|
-
const keepsTests =
|
|
303
|
-
built && acceptance !== undefined ? () => requireAcceptanceKept(acceptance) : undefined
|
|
304
|
-
await green(keepsTests)
|
|
305
|
-
await prototypeTest(keepsTests)
|
|
228
|
+
await green()
|
|
229
|
+
await prototypeTest()
|
|
306
230
|
const ids = criterionIds(read(REQUIREMENTS) ?? "")
|
|
307
231
|
await run("closing", removeScript([REQUIREMENTS, REFACTOR_NOTE, PROTOTYPE]), {
|
|
308
232
|
label: "Closing out the implementation",
|
|
@@ -360,7 +284,7 @@ interface Vetting {
|
|
|
360
284
|
|
|
361
285
|
/** Sends a pass whose tests fall short or run red back (`undefined`: it goes on, an overruled one as it is). */
|
|
362
286
|
const vetted = async (tip: KeptTip, vetting: Vetting): Promise<Verdict | undefined> => {
|
|
363
|
-
const fault = vetting.waived ? undefined : acceptanceFault(
|
|
287
|
+
const fault = vetting.waived ? undefined : acceptanceFault(tip)
|
|
364
288
|
if (fault !== undefined) {
|
|
365
289
|
const verdict = await vetting.again({ fault, hash: tip.hash })
|
|
366
290
|
if (verdict.kind !== "overruled") return verdict
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
@inmem
|
|
2
2
|
Feature: the review learns standards from the human's answers
|
|
3
3
|
|
|
4
|
-
A won't-fix tick or an own answer at the review gate
|
|
5
|
-
standard. A judge asks whether it is reusable; above `standardMinP` it is
|
|
4
|
+
A won't-fix tick or an own answer at the review gate — on a note the human
|
|
5
|
+
added with its own answer boxes — is a candidate standard. A judge asks whether it is reusable; above `standardMinP` it is
|
|
6
6
|
kept in `.gtd/STANDARDS.md` with no further question, and every later agent
|
|
7
7
|
turn — lenses and coder alike — is told to follow it. One that contradicts
|
|
8
8
|
a standard already kept becomes a note in `.gtd/REVIEW.md` the review cannot
|
|
@@ -12,7 +12,7 @@ Feature: the review learns standards from the human's answers
|
|
|
12
12
|
Background:
|
|
13
13
|
Given the shipped workflows, as gtd install scaffolds them
|
|
14
14
|
And an environment variable "GTD_PROTOTYPEROUNDS" set to "0"
|
|
15
|
-
And an environment variable "GTD_QUALITYREVIEWS" set to "
|
|
15
|
+
And an environment variable "GTD_QUALITYREVIEWS" set to ""
|
|
16
16
|
And an environment variable "GTD_FLAGMODE" set to "false"
|
|
17
17
|
And a file "src/calc.ts" with:
|
|
18
18
|
"""
|
|
@@ -59,15 +59,6 @@ Feature: the review learns standards from the human's answers
|
|
|
59
59
|
And step "implement.applying" runs its script
|
|
60
60
|
And step "implement.health.check" changes nothing
|
|
61
61
|
And step "implement.closing" runs its script
|
|
62
|
-
And step "build.quality.correctness.reviewing" writes ".gtd/QUALITY.md" with:
|
|
63
|
-
"""
|
|
64
|
-
# Quality
|
|
65
|
-
|
|
66
|
-
## subtract has no class
|
|
67
|
-
|
|
68
|
-
Calculators read better as classes.
|
|
69
|
-
"""
|
|
70
|
-
And step "build.quality.sorting" runs its script
|
|
71
62
|
And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
|
|
72
63
|
"""
|
|
73
64
|
# Review: abc1234
|
|
@@ -78,29 +69,7 @@ Feature: the review learns standards from the human's answers
|
|
|
78
69
|
|
|
79
70
|
- [ ] ./src/calc.ts#2-2 — subtract(a, b)
|
|
80
71
|
"""
|
|
81
|
-
And step "build.review.reviewing", when prompted with "- `## subtract has no class`", writes ".gtd/REVIEW.md" with:
|
|
82
|
-
"""
|
|
83
|
-
# Review: abc1234
|
|
84
|
-
|
|
85
|
-
<!-- base: 0000000 -->
|
|
86
|
-
|
|
87
|
-
## Subtract
|
|
88
|
-
|
|
89
|
-
- [ ] ./src/calc.ts#2-2 — subtract(a, b)
|
|
90
|
-
|
|
91
|
-
## subtract has no class
|
|
92
|
-
|
|
93
|
-
- [ ] ./src/calc.ts#2-2 — a plain function
|
|
94
|
-
"""
|
|
95
72
|
And step "build.review.carrying" runs its script
|
|
96
|
-
And judge step "build.review.ranking" answers:
|
|
97
|
-
"""
|
|
98
|
-
[]
|
|
99
|
-
"""
|
|
100
|
-
And judge step "build.quality.triage" answers:
|
|
101
|
-
"""
|
|
102
|
-
[{"id":"Q1","answer":"design","p":0.9}]
|
|
103
|
-
"""
|
|
104
73
|
And step "build.review.closing" runs its script
|
|
105
74
|
And step "build.review.standards.recording" runs its script
|
|
106
75
|
And step "build.review.standards.raising" runs its script
|
|
@@ -111,6 +80,18 @@ Feature: the review learns standards from the human's answers
|
|
|
111
80
|
[{"id":"standard-1","answer":"yes","p":0.9}]
|
|
112
81
|
"""
|
|
113
82
|
When the process is driven
|
|
83
|
+
And ".gtd/REVIEW.md" gets appended:
|
|
84
|
+
"""
|
|
85
|
+
## subtract has no class
|
|
86
|
+
|
|
87
|
+
Calculators read better as classes.
|
|
88
|
+
|
|
89
|
+
Tick exactly one:
|
|
90
|
+
|
|
91
|
+
- [ ] fix as suggested
|
|
92
|
+
- [ ] won't fix
|
|
93
|
+
- [ ] _your answer_
|
|
94
|
+
"""
|
|
114
95
|
And the text "- [ ] won't fix" in ".gtd/REVIEW.md" is replaced with "- [x] won't fix"
|
|
115
96
|
And gtd lands
|
|
116
97
|
And the process is driven
|
|
@@ -143,6 +124,18 @@ Feature: the review learns standards from the human's answers
|
|
|
143
124
|
"""
|
|
144
125
|
And step "build.health.check" changes nothing
|
|
145
126
|
When the process is driven
|
|
127
|
+
And ".gtd/REVIEW.md" gets appended:
|
|
128
|
+
"""
|
|
129
|
+
## subtract has no class
|
|
130
|
+
|
|
131
|
+
Calculators read better as classes.
|
|
132
|
+
|
|
133
|
+
Tick exactly one:
|
|
134
|
+
|
|
135
|
+
- [ ] fix as suggested
|
|
136
|
+
- [ ] won't fix
|
|
137
|
+
- [ ] _your answer_
|
|
138
|
+
"""
|
|
146
139
|
And the text "- [ ] _your answer_" in ".gtd/REVIEW.md" is replaced with "- [x] Calculators are plain functions."
|
|
147
140
|
And gtd lands
|
|
148
141
|
And the process is driven
|
|
@@ -162,6 +155,18 @@ Feature: the review learns standards from the human's answers
|
|
|
162
155
|
[{"id":"standard-1","answer":"yes","p":0.6}]
|
|
163
156
|
"""
|
|
164
157
|
When the process is driven
|
|
158
|
+
And ".gtd/REVIEW.md" gets appended:
|
|
159
|
+
"""
|
|
160
|
+
## subtract has no class
|
|
161
|
+
|
|
162
|
+
Calculators read better as classes.
|
|
163
|
+
|
|
164
|
+
Tick exactly one:
|
|
165
|
+
|
|
166
|
+
- [ ] fix as suggested
|
|
167
|
+
- [ ] won't fix
|
|
168
|
+
- [ ] _your answer_
|
|
169
|
+
"""
|
|
165
170
|
And the text "- [ ] won't fix" in ".gtd/REVIEW.md" is replaced with "- [x] won't fix"
|
|
166
171
|
And gtd lands
|
|
167
172
|
And the process is driven
|
|
@@ -210,6 +215,18 @@ Feature: the review learns standards from the human's answers
|
|
|
210
215
|
[{"id":"answer-1","answer":"praise","p":0.9}]
|
|
211
216
|
"""
|
|
212
217
|
When the process is driven
|
|
218
|
+
And ".gtd/REVIEW.md" gets appended:
|
|
219
|
+
"""
|
|
220
|
+
## subtract has no class
|
|
221
|
+
|
|
222
|
+
Calculators read better as classes.
|
|
223
|
+
|
|
224
|
+
Tick exactly one:
|
|
225
|
+
|
|
226
|
+
- [ ] fix as suggested
|
|
227
|
+
- [ ] won't fix
|
|
228
|
+
- [ ] _your answer_
|
|
229
|
+
"""
|
|
213
230
|
And the text "- [ ] _your answer_" in ".gtd/REVIEW.md" is replaced with "- [x] Calculators are plain functions."
|
|
214
231
|
And gtd lands
|
|
215
232
|
And the process is driven
|
|
@@ -267,6 +284,18 @@ Feature: the review learns standards from the human's answers
|
|
|
267
284
|
[{"id":"standard-1","answer":"yes","p":0.9},{"id":"standard-1.contradicts-1","answer":"yes","p":0.8}]
|
|
268
285
|
"""
|
|
269
286
|
When the process is driven
|
|
287
|
+
And ".gtd/REVIEW.md" gets appended:
|
|
288
|
+
"""
|
|
289
|
+
## subtract has no class
|
|
290
|
+
|
|
291
|
+
Calculators read better as classes.
|
|
292
|
+
|
|
293
|
+
Tick exactly one:
|
|
294
|
+
|
|
295
|
+
- [ ] fix as suggested
|
|
296
|
+
- [ ] won't fix
|
|
297
|
+
- [ ] _your answer_
|
|
298
|
+
"""
|
|
270
299
|
And the text "- [ ] won't fix" in ".gtd/REVIEW.md" is replaced with "- [x] won't fix"
|
|
271
300
|
And gtd lands
|
|
272
301
|
And the process is driven
|
|
@@ -303,6 +332,18 @@ Feature: the review learns standards from the human's answers
|
|
|
303
332
|
[{"id":"standard-1","answer":"yes","p":0.9},{"id":"standard-1.contradicts-1","answer":"yes","p":0.8}]
|
|
304
333
|
"""
|
|
305
334
|
When the process is driven
|
|
335
|
+
And ".gtd/REVIEW.md" gets appended:
|
|
336
|
+
"""
|
|
337
|
+
## subtract has no class
|
|
338
|
+
|
|
339
|
+
Calculators read better as classes.
|
|
340
|
+
|
|
341
|
+
Tick exactly one:
|
|
342
|
+
|
|
343
|
+
- [ ] fix as suggested
|
|
344
|
+
- [ ] won't fix
|
|
345
|
+
- [ ] _your answer_
|
|
346
|
+
"""
|
|
306
347
|
And the text "- [ ] won't fix" in ".gtd/REVIEW.md" is replaced with "- [x] won't fix"
|
|
307
348
|
And gtd lands
|
|
308
349
|
And the process is driven
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
@inmem
|
|
2
|
-
Feature: each finding the
|
|
2
|
+
Feature: each finding at the finding gate names the lens that wrote it
|
|
3
3
|
|
|
4
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.
|
|
5
|
+
whose turn added it — even when two lenses give findings the same title.
|
|
7
6
|
|
|
8
7
|
Background:
|
|
9
8
|
Given the shipped workflows, as gtd install scaffolds them
|
|
@@ -62,7 +61,7 @@ Feature: each finding the reviewer is handed names the lens that wrote it
|
|
|
62
61
|
|
|
63
62
|
From tests.
|
|
64
63
|
"""
|
|
65
|
-
And step "build.quality.
|
|
64
|
+
And step "build.quality.gating" runs its script
|
|
66
65
|
And step "build.quality.closing" runs its script
|
|
67
66
|
And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
|
|
68
67
|
"""
|
|
@@ -84,8 +83,8 @@ Feature: each finding the reviewer is handed names the lens that wrote it
|
|
|
84
83
|
"""
|
|
85
84
|
When gtd opens door "fix"
|
|
86
85
|
And the process is driven
|
|
87
|
-
Then the process rests at "build.
|
|
88
|
-
And ".gtd/
|
|
86
|
+
Then the process rests at "build.quality.gate"
|
|
87
|
+
And ".gtd/QUALITY.md" contains:
|
|
89
88
|
"""
|
|
90
89
|
## sum is untested
|
|
91
90
|
|
|
@@ -93,9 +92,23 @@ Feature: each finding the reviewer is handed names the lens that wrote it
|
|
|
93
92
|
|
|
94
93
|
From correctness.
|
|
95
94
|
|
|
96
|
-
|
|
95
|
+
- [ ] fix
|
|
96
|
+
|
|
97
|
+
## sum is untested
|
|
97
98
|
|
|
98
99
|
_From lens tests._
|
|
99
100
|
|
|
100
101
|
From tests.
|
|
102
|
+
|
|
103
|
+
- [ ] fix
|
|
104
|
+
"""
|
|
105
|
+
When gtd lands
|
|
106
|
+
And the process is driven
|
|
107
|
+
Then the process rests at "build.review.await-review"
|
|
108
|
+
And ".gtd/REVIEW.md" contains:
|
|
109
|
+
"""
|
|
110
|
+
Dropped at the finding gate:
|
|
111
|
+
|
|
112
|
+
- sum is untested (lens correctness)
|
|
113
|
+
- sum is untested (lens tests)
|
|
101
114
|
"""
|