@pmelab/gtd 49.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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +16 -3
- package/dist/chunks/{reserved-CYZ1bSBy.mjs → reserved-CUkk9XgL.mjs} +151 -5
- package/dist/chunks/{review-BewyJflS.mjs → review-BaCXlIT2.mjs} +84 -20
- package/dist/chunks/{steering-BXwjmocB.mjs → steering-DtUVUz9l.mjs} +44 -14
- package/dist/chunks/{tail-C1Oi2YEy.mjs → tail-CX6Pdsql.mjs} +1 -1
- package/dist/gtd.bundle.mjs +22 -5
- package/dist/steering.mjs +2 -2
- package/dist/testkit.mjs +1 -1
- package/dist/web/main.js +27 -3
- package/dist/workflow-kit.mjs +1 -1
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +144 -11
- 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 +415 -42
- package/package.json +1 -1
- package/src/steering/index.ts +1 -0
- package/src/steering/review.test.ts +60 -2
- package/src/steering/review.ts +66 -9
- package/src/workflows/feature/coverage.feature +1 -0
- package/src/workflows/feature/fast-path.feature +1 -0
- package/src/workflows/feature/feature.ts +4 -1
- package/src/workflows/feature/flag-mode.feature +1 -1
- package/src/workflows/feature/lens-models.feature +1 -0
- package/src/workflows/feature/lens-review.feature +17 -4
- package/src/workflows/feature/one-step-land.feature +1 -0
- package/src/workflows/feature/prototype-rounds.feature +152 -0
- package/src/workflows/feature/prototype.feature +419 -0
- package/src/workflows/feature/prototype.test.ts +43 -0
- package/src/workflows/feature/prototype.ts +181 -0
- package/src/workflows/feature/refactor-absorbs.feature +9 -1
- package/src/workflows/feature/refactor-first.feature +13 -3
- package/src/workflows/feature/refactor-rebuild.feature +29 -5
- package/src/workflows/feature/refactor-review.feature +111 -13
- package/src/workflows/feature/refactor.test.ts +14 -4
- package/src/workflows/feature/refactor.ts +2 -0
- package/src/workflows/feature/review-answers.feature +9 -1
- package/src/workflows/feature/review-gate.feature +21 -3
- package/src/workflows/feature/reviewer-walkthrough.feature +15 -3
- package/src/workflows/feature/seam-rejection.feature +1 -0
- package/src/workflows/feature/seam.feature +1 -0
- package/src/workflows/feature/seam.test.ts +3 -2
- package/src/workflows/feature/seam.ts +19 -5
- package/src/workflows/feature/severity-budget.feature +112 -0
- package/src/workflows/feature/severity-sorted-review.feature +198 -0
- package/src/workflows/feature/slice-findings.feature +15 -2
- package/src/workflows/feature/slicing-cap.feature +9 -0
- package/src/workflows/feature/slicing.feature +1 -0
- package/src/workflows/feature/standards.feature +5 -0
- package/src/workflows/shared/options.ts +1 -0
- package/src/workflows/shared/prose.ts +6 -0
- package/src/workflows/shared/review.ts +124 -18
- package/src/workflows/shared/skills.test.ts +4 -0
- package/src/workflows/shared/skills.ts +5 -0
- package/src/workflows/shared/steps.ts +1 -0
- package/src/workflows/shared/text.test.ts +90 -0
- package/src/workflows/shared/text.ts +181 -4
- package/src/workflows/shared/vars.ts +3 -0
package/package.json
CHANGED
package/src/steering/index.ts
CHANGED
|
@@ -1276,6 +1276,33 @@ describe("review.view", () => {
|
|
|
1276
1276
|
expect(view.nodes[1]!.detail).toBe("")
|
|
1277
1277
|
})
|
|
1278
1278
|
|
|
1279
|
+
it("shows a chunk's leading severity line as its status, not its prose", () => {
|
|
1280
|
+
const view = review.view(
|
|
1281
|
+
[
|
|
1282
|
+
"# Review: abc1234",
|
|
1283
|
+
"",
|
|
1284
|
+
"## Ranked",
|
|
1285
|
+
"",
|
|
1286
|
+
"_critical: security, one-way door_",
|
|
1287
|
+
"",
|
|
1288
|
+
"The new export.",
|
|
1289
|
+
"",
|
|
1290
|
+
"- [ ] ./a.ts#1-1",
|
|
1291
|
+
"",
|
|
1292
|
+
"## Unranked",
|
|
1293
|
+
"",
|
|
1294
|
+
"_emphasis, not a tier_",
|
|
1295
|
+
"",
|
|
1296
|
+
"- [ ] ./a.ts#2-2",
|
|
1297
|
+
"",
|
|
1298
|
+
].join("\n"),
|
|
1299
|
+
)
|
|
1300
|
+
expect(view.nodes.map((c) => [c.status, c.detail])).toEqual([
|
|
1301
|
+
["critical: security, one-way door", "The new export."],
|
|
1302
|
+
[undefined, "_emphasis, not a tier_"],
|
|
1303
|
+
])
|
|
1304
|
+
})
|
|
1305
|
+
|
|
1279
1306
|
it("is built from one parse of the document, not one per element", () => {
|
|
1280
1307
|
const uniqueContent = [
|
|
1281
1308
|
"# Review: def4567",
|
|
@@ -1706,6 +1733,29 @@ describe("reviewNotes", async () => {
|
|
|
1706
1733
|
expect(reviewNotes(base, base)).toEqual([])
|
|
1707
1734
|
})
|
|
1708
1735
|
|
|
1736
|
+
it("an edited tier line is a note", () => {
|
|
1737
|
+
const was = doc("_could_\n\nProse.")
|
|
1738
|
+
const after = doc("_critical: this leaks the token_\n\nProse.")
|
|
1739
|
+
expect(reviewNotes(was, after)).toMatchObject([
|
|
1740
|
+
{
|
|
1741
|
+
kind: "chunk",
|
|
1742
|
+
before: "Prose.\n\n_could_",
|
|
1743
|
+
text: "Prose.\n\n_critical: this leaks the token_",
|
|
1744
|
+
},
|
|
1745
|
+
])
|
|
1746
|
+
})
|
|
1747
|
+
|
|
1748
|
+
it("prose added under a tier is the human's alone, and a leading ! still forces", () => {
|
|
1749
|
+
const was = doc("_could_\n\nProse.")
|
|
1750
|
+
expect(reviewNotes(was, doc("_could_\n\n! Not here.\n\nProse."))).toMatchObject([
|
|
1751
|
+
{ kind: "chunk", text: "! Not here. Prose.", forced: true },
|
|
1752
|
+
])
|
|
1753
|
+
})
|
|
1754
|
+
|
|
1755
|
+
it("a deleted tier line asks nothing", () => {
|
|
1756
|
+
expect(reviewNotes(doc("_could_\n\nProse."), base)).toEqual([])
|
|
1757
|
+
})
|
|
1758
|
+
|
|
1709
1759
|
it("a pointer note added", () => {
|
|
1710
1760
|
const after = doc("Prose.").replace("#1-3", "#1-3 rename this")
|
|
1711
1761
|
expect(reviewNotes(base, after)).toEqual([
|
|
@@ -1863,8 +1913,16 @@ describe("reviewChunks", async () => {
|
|
|
1863
1913
|
"- [ ] _your answer_",
|
|
1864
1914
|
].join("\n")
|
|
1865
1915
|
expect(reviewChunks(content)).toEqual([
|
|
1866
|
-
{
|
|
1867
|
-
|
|
1916
|
+
{
|
|
1917
|
+
title: "Walkthrough",
|
|
1918
|
+
line: 4,
|
|
1919
|
+
pointers: 2,
|
|
1920
|
+
files: [
|
|
1921
|
+
{ path: "./src/a.ts", line: 1, rangeEnd: 3 },
|
|
1922
|
+
{ path: "./src/b.ts", line: 4, rangeEnd: 6 },
|
|
1923
|
+
],
|
|
1924
|
+
},
|
|
1925
|
+
{ title: "Fixed automatically", line: 9, pointers: 0, files: [] },
|
|
1868
1926
|
])
|
|
1869
1927
|
})
|
|
1870
1928
|
})
|
package/src/steering/review.ts
CHANGED
|
@@ -77,6 +77,8 @@ interface Changeset {
|
|
|
77
77
|
readonly headingLine: number
|
|
78
78
|
/** The chunk's answer boxes, when gtd gave it some. */
|
|
79
79
|
readonly answers?: readonly AnswerOption[]
|
|
80
|
+
/** The tier and reason gtd ranked the chunk with, from its leading severity line. */
|
|
81
|
+
readonly severity?: { readonly label: string; readonly line: number }
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
interface AnswerOption {
|
|
@@ -89,6 +91,10 @@ export const ANSWER_LEAD = "Tick exactly one:"
|
|
|
89
91
|
export const ANSWER_FIX = "fix as suggested"
|
|
90
92
|
export const ANSWER_WONT_FIX = "won't fix"
|
|
91
93
|
|
|
94
|
+
/** A chunk's leading `_critical: security_` paragraph: the tier gtd ranked it with, and why. */
|
|
95
|
+
const SEVERITY_LINE_RE = /^_((?:critical|should|could)(?::[^_\n]*)?)_$/
|
|
96
|
+
export const severityLine = (label: string): string => `_${label}_`
|
|
97
|
+
|
|
92
98
|
interface ReviewDoc {
|
|
93
99
|
readonly shortHash?: string
|
|
94
100
|
readonly fullHash?: string
|
|
@@ -521,6 +527,26 @@ const answerListOf = (
|
|
|
521
527
|
return { lead, list, options }
|
|
522
528
|
}
|
|
523
529
|
|
|
530
|
+
/** A leading severity line is the chunk's tier, never its prose. */
|
|
531
|
+
const splitSeverity = (
|
|
532
|
+
content: string,
|
|
533
|
+
run: readonly RootContent[],
|
|
534
|
+
): {
|
|
535
|
+
readonly severity?: { readonly label: string; readonly line: number } | undefined
|
|
536
|
+
readonly leadingRun: readonly RootContent[]
|
|
537
|
+
} => {
|
|
538
|
+
const first = run[0]
|
|
539
|
+
const label =
|
|
540
|
+
first?.type === "paragraph"
|
|
541
|
+
? SEVERITY_LINE_RE.exec(sourceText(content, first).trim())?.[1]
|
|
542
|
+
: undefined
|
|
543
|
+
if (label === undefined) return { leadingRun: run }
|
|
544
|
+
return {
|
|
545
|
+
severity: { label, line: toLspPosition(first!.position!.start).line },
|
|
546
|
+
leadingRun: run.slice(1),
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
524
550
|
/** Splits one chunk's body nodes into its file pointers (hunk pointers are task items, collected recursively at ANY nesting depth via `taskItems` — a nested hunk is the same kind of hunk as a top-level one) and description prose. The description is the chunk's own leading run and never a node that contains a hunk pointer: it stops at the first node whose `taskItems` include a real pointer (at any depth — a blockquote wrapping a pointer list stops it just as a top-level list would). Every node kind in what's left before that point contributes — a `footnoteDefinition` is the one exception, excluded by `blockNodesOfRun` itself (it's a note, never document content). */
|
|
525
551
|
const parseChunkBody = (
|
|
526
552
|
content: string,
|
|
@@ -534,6 +560,7 @@ const parseChunkBody = (
|
|
|
534
560
|
readonly files: readonly ReviewFile[]
|
|
535
561
|
readonly errors: readonly SteeringFinding[]
|
|
536
562
|
readonly answers?: readonly AnswerOption[]
|
|
563
|
+
readonly severity?: { readonly label: string; readonly line: number } | undefined
|
|
537
564
|
/** Whether a task box outside the answer list exists. */
|
|
538
565
|
readonly boxed: boolean
|
|
539
566
|
} => {
|
|
@@ -543,9 +570,10 @@ const parseChunkBody = (
|
|
|
543
570
|
const firstPointerIndex = body.findIndex(
|
|
544
571
|
(n) => !isAnswer(n) && taskItems(n).some((item) => hasPointerToken(content, item)),
|
|
545
572
|
)
|
|
546
|
-
const
|
|
573
|
+
const run = (firstPointerIndex === -1 ? body : body.slice(0, firstPointerIndex)).filter(
|
|
547
574
|
(n) => !isAnswer(n),
|
|
548
575
|
)
|
|
576
|
+
const { severity, leadingRun } = splitSeverity(content, run)
|
|
549
577
|
const descriptionNodes = blockNodesOfRun(content, leadingRun)
|
|
550
578
|
const descriptionInline = collapseImages(blockRunInline(content, leadingRun))
|
|
551
579
|
const description = leadingRun
|
|
@@ -569,6 +597,7 @@ const parseChunkBody = (
|
|
|
569
597
|
files,
|
|
570
598
|
errors,
|
|
571
599
|
...(answer === undefined ? {} : { answers: answer.options }),
|
|
600
|
+
...(severity === undefined ? {} : { severity }),
|
|
572
601
|
boxed: body.some((n) => !isAnswer(n) && taskItems(n).length > 0),
|
|
573
602
|
}
|
|
574
603
|
}
|
|
@@ -618,6 +647,7 @@ const parseChangesets = (
|
|
|
618
647
|
descriptionInline,
|
|
619
648
|
files,
|
|
620
649
|
answers,
|
|
650
|
+
severity,
|
|
621
651
|
boxed,
|
|
622
652
|
errors: bodyErrors,
|
|
623
653
|
} = parseChunkBody(content, lines, title, body)
|
|
@@ -638,6 +668,7 @@ const parseChangesets = (
|
|
|
638
668
|
files,
|
|
639
669
|
headingLine,
|
|
640
670
|
...(answers === undefined ? {} : { answers }),
|
|
671
|
+
...(severity === undefined ? {} : { severity }),
|
|
641
672
|
})
|
|
642
673
|
}
|
|
643
674
|
if (changesets.length === 0) errors.push({ message: "REVIEW.md has no '##' chunks" })
|
|
@@ -997,6 +1028,7 @@ const reviewView = (content: string): SteeringView => {
|
|
|
997
1028
|
detail: chunk.description,
|
|
998
1029
|
detailInline: chunk.descriptionInline,
|
|
999
1030
|
body: chunk.descriptionNodes,
|
|
1031
|
+
...(chunk.severity === undefined ? {} : { status: chunk.severity.label }),
|
|
1000
1032
|
anchor: { kind: "chunk", index: chunkIndex },
|
|
1001
1033
|
...noteAt(chunk.headingLine),
|
|
1002
1034
|
children: chunk.files.map((file, index) => ({
|
|
@@ -1177,11 +1209,22 @@ const pointerLabel = (f: ReviewFile): string =>
|
|
|
1177
1209
|
|
|
1178
1210
|
type Found = { line: number; note: Omit<ReviewNote, "id" | "forced"> }
|
|
1179
1211
|
|
|
1180
|
-
//
|
|
1181
|
-
//
|
|
1182
|
-
const
|
|
1183
|
-
|
|
1184
|
-
|
|
1212
|
+
// Rewording a tier disputes it, so a changed tier joins the note — after the
|
|
1213
|
+
// prose, so a leading `!` still forces; deleting one asks nothing.
|
|
1214
|
+
const chunkProse = (chunk: Changeset | undefined, retiered: boolean): string =>
|
|
1215
|
+
[
|
|
1216
|
+
chunk?.description ?? "",
|
|
1217
|
+
retiered && chunk?.severity !== undefined ? severityLine(chunk.severity.label) : "",
|
|
1218
|
+
]
|
|
1219
|
+
.filter((p) => p !== "")
|
|
1220
|
+
.join("\n\n")
|
|
1221
|
+
|
|
1222
|
+
const chunkNote = (chunk: Changeset, prior: Changeset | undefined): Found[] => {
|
|
1223
|
+
const retiered = chunk.severity !== undefined && chunk.severity.label !== prior?.severity?.label
|
|
1224
|
+
// A chunk added with its own answer boxes (a driver's imported note) is
|
|
1225
|
+
// answered by its tick, never judged as prose as well.
|
|
1226
|
+
return chunkProse(chunk, retiered) === chunkProse(prior, retiered) ||
|
|
1227
|
+
(prior === undefined && chunk.answers !== undefined)
|
|
1185
1228
|
? []
|
|
1186
1229
|
: [
|
|
1187
1230
|
{
|
|
@@ -1189,11 +1232,12 @@ const chunkNote = (chunk: Changeset, prior: Changeset | undefined): Found[] =>
|
|
|
1189
1232
|
note: {
|
|
1190
1233
|
kind: "chunk",
|
|
1191
1234
|
anchor: chunk.title,
|
|
1192
|
-
before: prior
|
|
1193
|
-
text: chunk
|
|
1235
|
+
before: chunkProse(prior, retiered),
|
|
1236
|
+
text: chunkProse(chunk, retiered),
|
|
1194
1237
|
},
|
|
1195
1238
|
},
|
|
1196
1239
|
]
|
|
1240
|
+
}
|
|
1197
1241
|
|
|
1198
1242
|
const pointerNotes = (chunk: Changeset, prior: Changeset | undefined): Found[] =>
|
|
1199
1243
|
chunk.files.flatMap((file): Found[] => {
|
|
@@ -1342,9 +1386,22 @@ export const reviewDecisions = (before: string, after: string): readonly ReviewD
|
|
|
1342
1386
|
/** Each `##` chunk in document order: its title, 0-based heading line and how many hunk pointers it holds. */
|
|
1343
1387
|
export const reviewChunks = (
|
|
1344
1388
|
content: string,
|
|
1345
|
-
): readonly {
|
|
1389
|
+
): readonly {
|
|
1390
|
+
title: string
|
|
1391
|
+
line: number
|
|
1392
|
+
pointers: number
|
|
1393
|
+
/** 0-based line of the chunk's severity line, when it has one. */
|
|
1394
|
+
severityLine?: number
|
|
1395
|
+
files: readonly { path: string; line?: number; rangeEnd?: number }[]
|
|
1396
|
+
}[] =>
|
|
1346
1397
|
parseReviewDoc(content).changesets.map((c) => ({
|
|
1347
1398
|
title: c.title,
|
|
1348
1399
|
line: c.headingLine,
|
|
1349
1400
|
pointers: c.files.length,
|
|
1401
|
+
...(c.severity === undefined ? {} : { severityLine: c.severity.line }),
|
|
1402
|
+
files: c.files.map((f) => ({
|
|
1403
|
+
path: f.path,
|
|
1404
|
+
...(f.line === undefined ? {} : { line: f.line }),
|
|
1405
|
+
...(f.rangeEnd === undefined ? {} : { rangeEnd: f.rangeEnd }),
|
|
1406
|
+
})),
|
|
1350
1407
|
}))
|
|
@@ -9,6 +9,7 @@ Feature: a feature is done only when every acceptance criterion has a passing te
|
|
|
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 ""
|
|
13
14
|
And an environment variable "GTD_FLAGMODE" set to "false"
|
|
14
15
|
And a file "src/calc.ts" with:
|
|
@@ -8,6 +8,7 @@ Feature: a trivial change takes the same few beats at either risk tier
|
|
|
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 "correctness"
|
|
12
13
|
And an environment variable "GTD_FLAG_RISK_TIER" set to "1"
|
|
13
14
|
And a file "src/greeting.ts" with:
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
} from "./criteria.js"
|
|
29
29
|
import { design } from "./planning.js"
|
|
30
30
|
import { buildTail, humanReview, isCode, reUnwind, type ReviewOutcome } from "../shared/review.js"
|
|
31
|
+
import { prototypeRounds } from "./prototype.js"
|
|
31
32
|
import { implement, implementing, seamBuild } from "./seam.js"
|
|
32
33
|
import { sliceCap, sliceCriteria, slicing } from "./slicing.js"
|
|
33
34
|
import { recordRisk, riskTierOn, type RiskTier } from "./tier.js"
|
|
@@ -36,7 +37,8 @@ import { recordRisk, riskTierOn, type RiskTier } from "./tier.js"
|
|
|
36
37
|
// `unwind` reverts the sketch (its intent survives in history) → a
|
|
37
38
|
// green-baseline gate → design → per slice of the criteria, a naive pass,
|
|
38
39
|
// judged with `flagMode` on: it lands as the implementation, or (refactoring
|
|
39
|
-
// first when the judge asks) is built fresh from → the health loop →
|
|
40
|
+
// first when the judge asks) is built fresh from → the health loop → a
|
|
41
|
+
// hands-on test of every criterion, fixed and re-tested → the
|
|
40
42
|
// triaged quality lap, whose leftovers are carried on → after the last slice,
|
|
41
43
|
// a reviewer's walkthrough of the whole change, every slice's findings
|
|
42
44
|
// among its chunks, for the human, which
|
|
@@ -227,6 +229,7 @@ export const ordinaryStart = async (): Promise<void> => {
|
|
|
227
229
|
// Read right after idle, so a bad value refuses the landing that would pin it.
|
|
228
230
|
flagModeOn()
|
|
229
231
|
sliceCap()
|
|
232
|
+
prototypeRounds()
|
|
230
233
|
const stated = statedCriteria(changes(), head())
|
|
231
234
|
await unwind()
|
|
232
235
|
if (changes(FEEDBACK).some((c) => c.status !== "deleted")) {
|
|
@@ -11,7 +11,7 @@ Feature: flag mode off lands the naive pass with no release flag, seam judge or
|
|
|
11
11
|
"""
|
|
12
12
|
import { feature } from "@pmelab/gtd/workflows/feature"
|
|
13
13
|
export { summary, steering, skills, access } from "@pmelab/gtd/workflows/feature"
|
|
14
|
-
export default feature({ flagMode: false })
|
|
14
|
+
export default feature({ flagMode: false, prototypeRounds: "0" })
|
|
15
15
|
"""
|
|
16
16
|
And an environment variable "GTD_QUALITYREVIEWS" set to "correctness"
|
|
17
17
|
And a file "src/calc.ts" with:
|
|
@@ -12,6 +12,7 @@ Feature: each quality lens runs on its own model hint
|
|
|
12
12
|
export { summary, steering, skills, access } from "@pmelab/gtd/workflows/feature"
|
|
13
13
|
export default feature({
|
|
14
14
|
flagMode: false,
|
|
15
|
+
prototypeRounds: "0",
|
|
15
16
|
qualityReviews: "correctness, conventions, test-audit=opus",
|
|
16
17
|
})
|
|
17
18
|
"""
|
|
@@ -9,6 +9,7 @@ Feature: the quality lap's findings are triaged before the review
|
|
|
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 a file "src/calc.ts" with:
|
|
@@ -75,6 +76,10 @@ Feature: the quality lap's findings are triaged before the review
|
|
|
75
76
|
And step "build.quality.sorting" runs its script
|
|
76
77
|
And step "build.quality.closing" runs its script
|
|
77
78
|
And step "build.review.carrying" runs its script
|
|
79
|
+
And judge step "build.review.ranking" answers:
|
|
80
|
+
"""
|
|
81
|
+
[]
|
|
82
|
+
"""
|
|
78
83
|
|
|
79
84
|
Scenario: a mechanical finding is fixed and listed, noise dropped and listed, a design one noted
|
|
80
85
|
Given judge step "build.quality.triage" answers:
|
|
@@ -110,6 +115,8 @@ Feature: the quality lap's findings are triaged before the review
|
|
|
110
115
|
"""
|
|
111
116
|
## AC-1 ignores floating point
|
|
112
117
|
|
|
118
|
+
_critical: judge unsure_
|
|
119
|
+
|
|
113
120
|
Should subtract round? The criterion does not say.
|
|
114
121
|
|
|
115
122
|
In the code:
|
|
@@ -147,7 +154,8 @@ Feature: the quality lap's findings are triaged before the review
|
|
|
147
154
|
gtd(check): build.quality.sorting → build.fix.quality.fixing
|
|
148
155
|
gtd(agent): build.fix.quality.fixing → build.health.check
|
|
149
156
|
gtd(check): build.health.check → build.review.reviewing
|
|
150
|
-
gtd(agent): build.review.reviewing → build.review.
|
|
157
|
+
gtd(agent): build.review.reviewing → build.review.ranking
|
|
158
|
+
gtd(judge): build.review.ranking → build.review.carrying
|
|
151
159
|
gtd(check): build.review.carrying → build.review.await-review
|
|
152
160
|
"""
|
|
153
161
|
|
|
@@ -189,7 +197,8 @@ Feature: the quality lap's findings are triaged before the review
|
|
|
189
197
|
"""
|
|
190
198
|
gtd(judge): build.quality.triage → build.quality.sorting
|
|
191
199
|
gtd(check): build.quality.sorting → build.review.reviewing
|
|
192
|
-
gtd(agent): build.review.reviewing → build.review.
|
|
200
|
+
gtd(agent): build.review.reviewing → build.review.ranking
|
|
201
|
+
gtd(judge): build.review.ranking → build.review.carrying
|
|
193
202
|
gtd(check): build.review.carrying → build.review.await-review
|
|
194
203
|
"""
|
|
195
204
|
|
|
@@ -228,6 +237,8 @@ Feature: the quality lap's findings are triaged before the review
|
|
|
228
237
|
"""
|
|
229
238
|
## add could be a method
|
|
230
239
|
|
|
240
|
+
_critical: judge unsure_
|
|
241
|
+
|
|
231
242
|
_The fixer left this unresolved._
|
|
232
243
|
|
|
233
244
|
A class would read better.
|
|
@@ -242,7 +253,8 @@ Feature: the quality lap's findings are triaged before the review
|
|
|
242
253
|
gtd(agent): build.fix.quality.fixing → build.health.check
|
|
243
254
|
gtd(check): build.health.check → build.quality.closing
|
|
244
255
|
gtd(check): build.quality.closing → build.review.reviewing
|
|
245
|
-
gtd(agent): build.review.reviewing → build.review.
|
|
256
|
+
gtd(agent): build.review.reviewing → build.review.ranking
|
|
257
|
+
gtd(judge): build.review.ranking → build.review.carrying
|
|
246
258
|
gtd(check): build.review.carrying → build.review.await-review
|
|
247
259
|
"""
|
|
248
260
|
|
|
@@ -344,7 +356,8 @@ Feature: the quality lap's findings are triaged before the review
|
|
|
344
356
|
And ".gtd/REVIEW.md" does not exist
|
|
345
357
|
And the commit subjects end with:
|
|
346
358
|
"""
|
|
347
|
-
gtd(agent): build.review.reviewing → build.review.
|
|
359
|
+
gtd(agent): build.review.reviewing → build.review.ranking
|
|
360
|
+
gtd(judge): build.review.ranking → build.review.carrying
|
|
348
361
|
gtd(check): build.review.carrying → build.review.await-review
|
|
349
362
|
gtd(human): build.review.await-review → build.review.closing
|
|
350
363
|
gtd(check): build.review.closing → idle
|
|
@@ -10,6 +10,7 @@ Feature: a naive pass the judge rules land is itself the implementation
|
|
|
10
10
|
|
|
11
11
|
Background:
|
|
12
12
|
Given the shipped workflows, as gtd install scaffolds them
|
|
13
|
+
And an environment variable "GTD_PROTOTYPEROUNDS" set to "0"
|
|
13
14
|
And an environment variable "GTD_QUALITYREVIEWS" set to "correctness"
|
|
14
15
|
And an environment variable "GTD_FLAG_RISK_TIER" set to "0"
|
|
15
16
|
And a file "src/greeting.ts" with:
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
@inmem
|
|
2
|
+
Feature: past its round cap the hands-on test rests at a person's gate
|
|
3
|
+
|
|
4
|
+
`prototypeRounds` caps the fix turns a slice's hands-on test gets. Once they
|
|
5
|
+
are spent and the report still has open items, the process rests at
|
|
6
|
+
implement.prototype.open naming them; a person's fix there is tested again
|
|
7
|
+
with a fresh budget.
|
|
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 an environment variable "GTD_FLAGMODE" set to "false"
|
|
13
|
+
And an environment variable "GTD_PROTOTYPEROUNDS" set to "1"
|
|
14
|
+
And a file "src/calc.ts" with:
|
|
15
|
+
"""
|
|
16
|
+
export const add = (a: number, b: number) => a + b
|
|
17
|
+
"""
|
|
18
|
+
And the working tree is committed as "feat: add calculator"
|
|
19
|
+
And a file "TODO.md" with:
|
|
20
|
+
"""
|
|
21
|
+
# Add subtract
|
|
22
|
+
|
|
23
|
+
subtract(a, b) returns the difference.
|
|
24
|
+
"""
|
|
25
|
+
And step "unwind" changes nothing
|
|
26
|
+
And step "start-gate.check" changes nothing
|
|
27
|
+
And step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
|
|
28
|
+
"""
|
|
29
|
+
# Requirements
|
|
30
|
+
|
|
31
|
+
## Acceptance Criteria
|
|
32
|
+
|
|
33
|
+
- AC-1: subtract(a, b) returns a - b.
|
|
34
|
+
- AC-2: add is unchanged.
|
|
35
|
+
"""
|
|
36
|
+
And step "design.gate.answer" changes nothing
|
|
37
|
+
And step "seam.naive" writes "src/calc.ts" with:
|
|
38
|
+
"""
|
|
39
|
+
export const add = (a: number, b: number) => a + b
|
|
40
|
+
export const subtract = (a: number, b: number) => a + b
|
|
41
|
+
"""
|
|
42
|
+
And step "seam.naive" writes "src/calc.test.ts" with:
|
|
43
|
+
"""
|
|
44
|
+
it("[AC-1] subtracts", () => expect(subtract(3, 1)).toBe(2))
|
|
45
|
+
it("[AC-2] adds", () => expect(add(1, 1)).toBe(2))
|
|
46
|
+
"""
|
|
47
|
+
And step "seam.check" changes nothing
|
|
48
|
+
And step "implement.applying" runs its script
|
|
49
|
+
And step "implement.health.check" changes nothing
|
|
50
|
+
And judge step "implement.coverage" answers:
|
|
51
|
+
"""
|
|
52
|
+
[{"id":"AC-1","answer":"yes","p":0.9},{"id":"AC-2","answer":"yes","p":0.9}]
|
|
53
|
+
"""
|
|
54
|
+
And step "implement.closing" runs its script
|
|
55
|
+
|
|
56
|
+
Scenario: past the round cap a person is asked, naming the open items
|
|
57
|
+
Given step "implement.prototype.testing", when prompted with "Find how this project", writes ".gtd/PROTOTYPE.md" with:
|
|
58
|
+
"""
|
|
59
|
+
## AC-1: fail
|
|
60
|
+
|
|
61
|
+
`subtract(3, 1)` printed 4. Expected 2.
|
|
62
|
+
|
|
63
|
+
## AC-2: pass
|
|
64
|
+
|
|
65
|
+
Printed 2.
|
|
66
|
+
"""
|
|
67
|
+
And step "implement.prototype.testing", when prompted with "This is a re-test", writes ".gtd/PROTOTYPE.md" with:
|
|
68
|
+
"""
|
|
69
|
+
## AC-1: fail
|
|
70
|
+
|
|
71
|
+
Re-tested: `subtract(3, 1)` printed -2. Expected 2.
|
|
72
|
+
|
|
73
|
+
## AC-2: pass
|
|
74
|
+
|
|
75
|
+
Printed 2.
|
|
76
|
+
"""
|
|
77
|
+
And step "implement.prototype.fix.fixing" writes "src/calc.ts" with:
|
|
78
|
+
"""
|
|
79
|
+
export const add = (a: number, b: number) => a + b
|
|
80
|
+
export const subtract = (a: number, b: number) => b - a
|
|
81
|
+
"""
|
|
82
|
+
And step "implement.prototype.health.check" changes nothing
|
|
83
|
+
When the process is driven
|
|
84
|
+
Then the process rests at "implement.prototype.open"
|
|
85
|
+
And the resting step's content contains "- AC-1: fail"
|
|
86
|
+
And the resting step's content contains "after 1 fix round"
|
|
87
|
+
And the resting step's content does not contain "AC-2"
|
|
88
|
+
|
|
89
|
+
Scenario: a person's change at the gate is tested again
|
|
90
|
+
Given step "implement.prototype.testing", when prompted with "Find how this project", writes ".gtd/PROTOTYPE.md" with:
|
|
91
|
+
"""
|
|
92
|
+
## AC-1: blocked
|
|
93
|
+
|
|
94
|
+
`subtract` is not exported from any entry point I could run.
|
|
95
|
+
|
|
96
|
+
## AC-2: pass
|
|
97
|
+
|
|
98
|
+
Printed 2.
|
|
99
|
+
"""
|
|
100
|
+
And step "implement.prototype.open" writes "src/calc.ts" with:
|
|
101
|
+
"""
|
|
102
|
+
export const add = (a: number, b: number) => a + b
|
|
103
|
+
export const subtract = (a: number, b: number) => a - b
|
|
104
|
+
"""
|
|
105
|
+
And step "implement.prototype.health.check" changes nothing
|
|
106
|
+
And step "implement.prototype.testing", when prompted with "This is a re-test", writes ".gtd/PROTOTYPE.md" with:
|
|
107
|
+
"""
|
|
108
|
+
## AC-1: pass
|
|
109
|
+
|
|
110
|
+
`subtract(3, 1)` printed 2.
|
|
111
|
+
|
|
112
|
+
## AC-2: pass
|
|
113
|
+
|
|
114
|
+
Printed 2.
|
|
115
|
+
"""
|
|
116
|
+
When the process is driven
|
|
117
|
+
Then the process rests at "build.quality.correctness.reviewing"
|
|
118
|
+
And the commit subjects end with:
|
|
119
|
+
"""
|
|
120
|
+
gtd(agent): implement.prototype.testing → implement.prototype.open
|
|
121
|
+
gtd(human): implement.prototype.open → implement.prototype.health.check
|
|
122
|
+
gtd(check): implement.prototype.health.check → implement.prototype.testing
|
|
123
|
+
gtd(agent): implement.prototype.testing → implement.closing
|
|
124
|
+
gtd(check): implement.closing → build.quality.correctness.reviewing
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
Scenario: a re-test that sees exactly what the last round saw still ends the round
|
|
128
|
+
Given step "implement.prototype.testing" writes ".gtd/PROTOTYPE.md" with:
|
|
129
|
+
"""
|
|
130
|
+
## AC-1: fail
|
|
131
|
+
|
|
132
|
+
`subtract(3, 1)` printed 4. Expected 2.
|
|
133
|
+
|
|
134
|
+
## AC-2: pass
|
|
135
|
+
|
|
136
|
+
Printed 2.
|
|
137
|
+
"""
|
|
138
|
+
And step "implement.prototype.fix.fixing" writes "src/calc.ts" with:
|
|
139
|
+
"""
|
|
140
|
+
export const add = (a: number, b: number) => a + b
|
|
141
|
+
export const subtract = (a: number, b: number) => b - a
|
|
142
|
+
"""
|
|
143
|
+
And step "implement.prototype.health.check" changes nothing
|
|
144
|
+
When the process is driven
|
|
145
|
+
Then the process rests at "implement.prototype.open"
|
|
146
|
+
And the commit subjects end with:
|
|
147
|
+
"""
|
|
148
|
+
gtd(agent): implement.prototype.testing → implement.prototype.fix.fixing
|
|
149
|
+
gtd(agent): implement.prototype.fix.fixing → implement.prototype.health.check
|
|
150
|
+
gtd(check): implement.prototype.health.check → implement.prototype.testing
|
|
151
|
+
gtd(agent): implement.prototype.testing → implement.prototype.open
|
|
152
|
+
"""
|