@pmelab/gtd 58.2.0 → 59.0.1
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 +120 -395
- package/dist/chunks/{Schema-B3o6eRNU.mjs → testkit-07Z_ENch.mjs} +13503 -3460
- package/dist/chunks/{wire-C12dahrH.mjs → wire-BTUhRGXj.mjs} +1 -1
- package/dist/flows.mjs +1 -1
- package/dist/gtd.bundle.mjs +484 -497
- package/dist/testkit/gherkin.mjs +145 -65
- package/dist/testkit.mjs +1 -1
- package/dist/wire.mjs +1 -1
- package/package.json +3 -42
- package/skills/authoring/SKILL.md +34 -27
- package/src/cli/Cli.test.ts +148 -71
- package/src/cli/Cli.ts +384 -463
- package/src/cli/Help.test.ts +47 -0
- package/src/cli/Help.ts +70 -0
- package/src/cli/Program.test.ts +127 -33
- package/src/cli/Program.ts +70 -25
- package/src/edge/Edge.ts +4 -4
- package/src/emit/Emit.test.ts +1 -1
- package/src/emit/Emit.ts +1 -1
- package/src/flags/index.test.ts +0 -13
- package/src/flags/index.ts +1 -3
- package/src/flows/factory.ts +3 -1
- package/src/gherkin/index.ts +135 -71
- package/src/gherkin/messages.test.ts +90 -0
- package/src/gherkin/messages.ts +84 -0
- package/src/install/Install.test.ts +14 -11
- package/src/install/Install.ts +38 -15
- package/src/install/index.ts +1 -2
- package/src/replay/Replay.test.ts +1 -1
- package/src/replay/index.ts +1 -1
- package/src/step/planStart.ts +6 -1
- package/src/testkit/TestRepo.test.ts +47 -2
- package/src/testkit/TestRepo.ts +51 -6
- package/src/twins/EmittedScriptRecognizer.ts +5 -8
- package/src/twins/FlowScripts.ts +1 -1
- package/src/wire/Land.ts +1 -1
- package/src/workflow/index.ts +1 -0
- package/src/workflow/load.test.ts +138 -113
- package/src/workflow/load.ts +7 -7
- package/src/workflow/skills.test.ts +5 -5
- package/src/workflow/skills.ts +1 -1
- package/src/workflow/vars.test.ts +30 -3
- package/src/workflow/vars.ts +34 -2
- package/src/workflow-kit/index.ts +1 -1
- package/dist/chunks/reserved-lMYCaapY.mjs +0 -1990
- package/dist/chunks/review-Bfaror38.mjs +0 -427
- package/dist/chunks/tail-Bd7L940p.mjs +0 -355
- package/dist/chunks/testkit-JDtpNKyi.mjs +0 -10410
- package/dist/workflows/cleanup.mjs +0 -36
- package/dist/workflows/feature.mjs +0 -1018
- package/dist/workflows/fix.mjs +0 -41
- package/dist/workflows/review.mjs +0 -45
- package/dist/workflows/rollout.mjs +0 -48
- package/dist/workflows.mjs +0 -40651
- package/src/install/ConfigScaffold.ts +0 -22
- package/src/workflows/cleanup/cleanup-path.feature +0 -94
- package/src/workflows/cleanup/cleanup.feature +0 -315
- package/src/workflows/cleanup/cleanup.test.ts +0 -276
- package/src/workflows/cleanup/cleanup.ts +0 -52
- package/src/workflows/cleanup/index.ts +0 -6
- package/src/workflows/feature/acceptance-criteria-tier.feature +0 -25
- package/src/workflows/feature/acceptance-criteria.feature +0 -389
- package/src/workflows/feature/criteria-coverage.feature +0 -102
- package/src/workflows/feature/criteria.test.ts +0 -147
- package/src/workflows/feature/criteria.ts +0 -267
- package/src/workflows/feature/fast-path.feature +0 -93
- package/src/workflows/feature/feature.test.ts +0 -338
- package/src/workflows/feature/feature.ts +0 -156
- package/src/workflows/feature/green.ts +0 -19
- package/src/workflows/feature/index.ts +0 -18
- package/src/workflows/feature/lens-models.feature +0 -69
- package/src/workflows/feature/lens-review.feature +0 -566
- package/src/workflows/feature/naive-acceptance.feature +0 -147
- package/src/workflows/feature/naive-test.feature +0 -263
- package/src/workflows/feature/one-step-land.feature +0 -93
- package/src/workflows/feature/planning.ts +0 -41
- package/src/workflows/feature/refactor-absorbs.feature +0 -131
- package/src/workflows/feature/refactor-first.feature +0 -158
- package/src/workflows/feature/refactor-lens-models.feature +0 -79
- package/src/workflows/feature/refactor-rebuild.feature +0 -273
- package/src/workflows/feature/refactor-review.feature +0 -1528
- package/src/workflows/feature/refactor.test.ts +0 -1220
- package/src/workflows/feature/refactor.ts +0 -368
- package/src/workflows/feature/release-flag.feature +0 -167
- package/src/workflows/feature/release-flag.test.ts +0 -49
- package/src/workflows/feature/release-flag.ts +0 -46
- package/src/workflows/feature/review-gate.feature +0 -241
- package/src/workflows/feature/reviewer-walkthrough.feature +0 -142
- package/src/workflows/feature/risk-tier.feature +0 -351
- package/src/workflows/feature/seam-rejection.feature +0 -146
- package/src/workflows/feature/seam.feature +0 -297
- package/src/workflows/feature/seam.test.ts +0 -527
- package/src/workflows/feature/seam.ts +0 -426
- package/src/workflows/feature/standards.feature +0 -354
- package/src/workflows/feature/steps.test.ts +0 -65
- package/src/workflows/feature/steps.ts +0 -45
- package/src/workflows/feature/tier-pin.feature +0 -66
- package/src/workflows/feature/tier.test.ts +0 -166
- package/src/workflows/feature/tier.ts +0 -56
- package/src/workflows/fix/fix-lens-origin.feature +0 -114
- package/src/workflows/fix/fix-quality.feature +0 -212
- package/src/workflows/fix/fix.feature +0 -79
- package/src/workflows/fix/fix.test.ts +0 -74
- package/src/workflows/fix/fix.ts +0 -52
- package/src/workflows/fix/index.ts +0 -6
- package/src/workflows/index.ts +0 -47
- package/src/workflows/review/index.ts +0 -6
- package/src/workflows/review/review-door.feature +0 -199
- package/src/workflows/review/review.test.ts +0 -27
- package/src/workflows/review/review.ts +0 -60
- package/src/workflows/rollout/index.ts +0 -6
- package/src/workflows/rollout/rollout.feature +0 -510
- package/src/workflows/rollout/rollout.test.ts +0 -263
- package/src/workflows/rollout/rollout.ts +0 -82
- package/src/workflows/shared/__fixtures__/feature-collecting-prompt.txt +0 -98
- package/src/workflows/shared/access.test.ts +0 -62
- package/src/workflows/shared/access.ts +0 -24
- package/src/workflows/shared/config.fixture.ts +0 -12
- package/src/workflows/shared/fences.ts +0 -24
- package/src/workflows/shared/health.test.ts +0 -148
- package/src/workflows/shared/health.ts +0 -191
- package/src/workflows/shared/learn.test.ts +0 -140
- package/src/workflows/shared/learn.ts +0 -201
- package/src/workflows/shared/lenses.test.ts +0 -39
- package/src/workflows/shared/lenses.ts +0 -294
- package/src/workflows/shared/options.test.ts +0 -157
- package/src/workflows/shared/options.ts +0 -92
- package/src/workflows/shared/progress.feature +0 -124
- package/src/workflows/shared/prose.ts +0 -263
- package/src/workflows/shared/recheck.feature +0 -60
- package/src/workflows/shared/registry.test.ts +0 -345
- package/src/workflows/shared/registry.ts +0 -183
- package/src/workflows/shared/reserved.ts +0 -11
- package/src/workflows/shared/review.test.ts +0 -1062
- package/src/workflows/shared/review.ts +0 -446
- package/src/workflows/shared/skills.test.ts +0 -121
- package/src/workflows/shared/skills.ts +0 -49
- package/src/workflows/shared/standards.test.ts +0 -90
- package/src/workflows/shared/standards.ts +0 -156
- package/src/workflows/shared/steps.test.ts +0 -219
- package/src/workflows/shared/steps.ts +0 -201
- package/src/workflows/shared/tail.test.ts +0 -454
- package/src/workflows/shared/tail.ts +0 -264
- package/src/workflows/shared/text.fixture.ts +0 -128
- package/src/workflows/shared/text.test.ts +0 -1430
- package/src/workflows/shared/text.ts +0 -1700
- package/src/workflows/shared/threads.test.ts +0 -129
- package/src/workflows/shared/vars.ts +0 -47
- package/src/workflows/shared/workflows.steps.ts +0 -54
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it } from "vitest"
|
|
2
|
-
import { installContext, type FlowContext } from "../../flows/index.js"
|
|
3
|
-
import { fixtureContext } from "../shared/text.fixture.js"
|
|
4
|
-
import { productionSignals, readRiskTier, recordRisk, riskTierOn } from "./tier.js"
|
|
5
|
-
|
|
6
|
-
afterEach(() => installContext(undefined))
|
|
7
|
-
|
|
8
|
-
const withVars = (vars: Record<string, string>) => installContext(fixtureContext({ vars }))
|
|
9
|
-
|
|
10
|
-
describe("riskTierOn", () => {
|
|
11
|
-
it.each([
|
|
12
|
-
[{ flag_risk_tier: "1" }, true],
|
|
13
|
-
[{ flag_risk_tier: "0" }, false],
|
|
14
|
-
[{}, false],
|
|
15
|
-
])("%j is %s", (vars, expected) => {
|
|
16
|
-
withVars(vars)
|
|
17
|
-
expect(riskTierOn()).toBe(expected)
|
|
18
|
-
})
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
describe("readRiskTier", () => {
|
|
22
|
-
const doc = (body: string) =>
|
|
23
|
-
`# Requirements\n\n## Risk Tier\n\n${body}\n\n## Answered Questions\n`
|
|
24
|
-
|
|
25
|
-
it.each(["low", "`low`", "**Low** — trivial copy", "LOW - copy", "_low_ — x"])(
|
|
26
|
-
"reads %j as low",
|
|
27
|
-
(body) => expect(readRiskTier(doc(body))).toBe("low"),
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
it.each(["high", "medium", "lower", "", " \n\n"])("reads %j as high", (body) =>
|
|
31
|
-
expect(readRiskTier(doc(body))).toBe("high"),
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
it("reads a missing section and undefined as high", () => {
|
|
35
|
-
expect(readRiskTier("# Requirements\n\n## Other\n\nlow\n")).toBe("high")
|
|
36
|
-
expect(readRiskTier(undefined)).toBe("high")
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
it("takes the first non-blank line only and stops at the next section", () => {
|
|
40
|
-
expect(readRiskTier("## Risk Tier\n\nhigh — why\nlow\n")).toBe("high")
|
|
41
|
-
expect(readRiskTier("## Risk Tier\n\n## Other\n\nlow\n")).toBe("high")
|
|
42
|
-
})
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
describe("readRiskTier fences", () => {
|
|
46
|
-
it("ignores a backtick-fenced example above the real section", () => {
|
|
47
|
-
const text = "```\n## Risk Tier\n\nlow\n```\n\n## Risk Tier\n\nhigh\n"
|
|
48
|
-
expect(readRiskTier(text)).toBe("high")
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
it("reads a fenced example with no real section as high", () => {
|
|
52
|
-
expect(readRiskTier("```md\n## Risk Tier\n\nlow\n```\n")).toBe("high")
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
it("keeps the signals section open across a fence and skips fenced items", () => {
|
|
56
|
-
const signals = "## Production Signals\n- before\n```\n# x\n- fake\n```\n- after\n"
|
|
57
|
-
expect(productionSignals(signals)).toEqual(["before", "after"])
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it("ignores a tilde-fenced example above the real section", () => {
|
|
61
|
-
const text = "~~~\n## Risk Tier\n\nlow\n~~~\n\n## Risk Tier\n\nhigh\n"
|
|
62
|
-
expect(readRiskTier(text)).toBe("high")
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
it("does not read a fence opener's info string as the tier", () => {
|
|
66
|
-
expect(readRiskTier("## Risk Tier\n\n```low\nhigh\n```\n")).toBe("high")
|
|
67
|
-
expect(readRiskTier("## Risk Tier\n\n~~~low\nlow\n~~~\n")).toBe("high")
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it("fails closed on an unclosed fence before the real section", () => {
|
|
71
|
-
expect(readRiskTier("```\nstray\n\n## Risk Tier\n\nlow\n")).toBe("high")
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
it("does not close a 4-backtick fence with 3 backticks", () => {
|
|
75
|
-
const text = "````\n```\n## Risk Tier\n\nlow\n````\n"
|
|
76
|
-
expect(readRiskTier(text)).toBe("high")
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
it("does not open a fence on a backtick line indented 4 spaces", () => {
|
|
80
|
-
expect(readRiskTier(" ```\n## Risk Tier\n\nlow\n")).toBe("low")
|
|
81
|
-
})
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
describe("readRiskTier fence closers", () => {
|
|
85
|
-
it.each([
|
|
86
|
-
["a tilde line does not close a backtick fence", "```\n~~~\n## Risk Tier\n\nlow\n```\n"],
|
|
87
|
-
["a closer with an info string does not close", "```\n```js\n## Risk Tier\n\nlow\n```\n"],
|
|
88
|
-
])("%s", (_, text) => {
|
|
89
|
-
expect(readRiskTier(text)).toBe("high")
|
|
90
|
-
})
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
describe("productionSignals", () => {
|
|
94
|
-
it("lists top-level items only, skipping blanks and indented lines", () => {
|
|
95
|
-
const text = `## Production Signals
|
|
96
|
-
|
|
97
|
-
- error rate on /pay
|
|
98
|
-
not a signal
|
|
99
|
-
- nested detail
|
|
100
|
-
-
|
|
101
|
-
- spaced out\t signal
|
|
102
|
-
* star item
|
|
103
|
-
|
|
104
|
-
## Answered Questions
|
|
105
|
-
|
|
106
|
-
- not a signal
|
|
107
|
-
`
|
|
108
|
-
expect(productionSignals(text)).toEqual([
|
|
109
|
-
"error rate on /pay",
|
|
110
|
-
"spaced out signal",
|
|
111
|
-
"star item",
|
|
112
|
-
])
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
it("skips a fenced Production Signals example", () => {
|
|
116
|
-
const text = "```\n## Production Signals\n- fake\n```\n\n## Production Signals\n- real\n"
|
|
117
|
-
expect(productionSignals(text)).toEqual(["real"])
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
it("drops control characters", () => {
|
|
121
|
-
expect(productionSignals("## Production Signals\n- a\u001bb\u0085c\n")).toEqual(["abc"])
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
it("is empty for a missing section or undefined", () => {
|
|
125
|
-
expect(productionSignals("## Risk Tier\nlow\n")).toEqual([])
|
|
126
|
-
expect(productionSignals(undefined)).toEqual([])
|
|
127
|
-
})
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
describe("recordRisk", () => {
|
|
131
|
-
const record = (requirements: string | undefined) => {
|
|
132
|
-
const marks: [string, string][] = []
|
|
133
|
-
installContext({
|
|
134
|
-
...fixtureContext({ read: () => requirements }),
|
|
135
|
-
mark: (name: string, value: string) => marks.push([name, value]),
|
|
136
|
-
} as unknown as FlowContext)
|
|
137
|
-
return { tier: recordRisk(), marks }
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
it("marks the tier first, then one production-signal each", () => {
|
|
141
|
-
const { tier, marks } = record(
|
|
142
|
-
"## Risk Tier\nhigh — payments\n\n## Production Signals\n- a\n- b\n",
|
|
143
|
-
)
|
|
144
|
-
expect(tier).toBe("high")
|
|
145
|
-
expect(marks).toEqual([
|
|
146
|
-
["risk-tier", "high"],
|
|
147
|
-
["production-signal", "a"],
|
|
148
|
-
["production-signal", "b"],
|
|
149
|
-
])
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
it("marks risk-tier=low too", () => {
|
|
153
|
-
const { tier, marks } = record("## Risk Tier\nlow — copy\n\n## Production Signals\n- a\n")
|
|
154
|
-
expect(tier).toBe("low")
|
|
155
|
-
expect(marks).toEqual([
|
|
156
|
-
["risk-tier", "low"],
|
|
157
|
-
["production-signal", "a"],
|
|
158
|
-
])
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
it("treats a missing file as high", () => {
|
|
162
|
-
const { tier, marks } = record(undefined)
|
|
163
|
-
expect(tier).toBe("high")
|
|
164
|
-
expect(marks).toEqual([["risk-tier", "high"]])
|
|
165
|
-
})
|
|
166
|
-
})
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { mark, read, vars } from "../../flows/index.js"
|
|
2
|
-
import { markFenced } from "../shared/fences.js"
|
|
3
|
-
import { REQUIREMENTS } from "../shared/steps.js"
|
|
4
|
-
|
|
5
|
-
export type RiskTier = "low" | "high"
|
|
6
|
-
|
|
7
|
-
/** GTD_FLAG_RISK_TIER: pinned at process start, so a mid-process flip never moves a process. */
|
|
8
|
-
export const riskTierOn = (): boolean => vars.flag_risk_tier === "1"
|
|
9
|
-
|
|
10
|
-
const sectionLines = (
|
|
11
|
-
text: string | undefined,
|
|
12
|
-
title: string,
|
|
13
|
-
): readonly { line: string; fenced: boolean }[] => {
|
|
14
|
-
if (text === undefined) return []
|
|
15
|
-
const heading = new RegExp(`^##[ \\t]+${title}[ \\t]*$`, "i")
|
|
16
|
-
// An unclosed fence hides the rest, so the tier reads `high` (fail-closed).
|
|
17
|
-
const lines = markFenced(text)
|
|
18
|
-
const start = lines.findIndex((l) => !l.fenced && heading.test(l.line))
|
|
19
|
-
if (start < 0) return []
|
|
20
|
-
const rest = lines.slice(start + 1)
|
|
21
|
-
const end = rest.findIndex((l) => !l.fenced && /^#{1,2}[ \t]/.test(l.line))
|
|
22
|
-
return end < 0 ? rest : rest.slice(0, end)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** Anything but a legible `low` is `high`: under-tiering is the risky direction. */
|
|
26
|
-
export const readRiskTier = (requirements: string | undefined): RiskTier => {
|
|
27
|
-
const first = sectionLines(requirements, "Risk Tier").find(
|
|
28
|
-
(l) => !l.fenced && l.line.trim() !== "",
|
|
29
|
-
)?.line
|
|
30
|
-
const word = first?.trim().split(/\s+/)[0]?.replace(/[`*_]/g, "").toLowerCase()
|
|
31
|
-
return word === "low" ? "low" : "high"
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// eslint-disable-next-line no-control-regex -- the control range IS the point
|
|
35
|
-
const CONTROL = /[\x00-\x1f\x7f-\x9f\u2028\u2029]/g
|
|
36
|
-
|
|
37
|
-
const tidy = (text: string): string => text.replace(/\s+/g, " ").replace(CONTROL, "").trim()
|
|
38
|
-
|
|
39
|
-
export const productionSignals = (requirements: string | undefined): readonly string[] => {
|
|
40
|
-
const items: string[] = []
|
|
41
|
-
for (const { line, fenced } of sectionLines(requirements, "Production Signals")) {
|
|
42
|
-
if (fenced) continue
|
|
43
|
-
const top = /^[-*+][ \t]+(.*)$/.exec(line)
|
|
44
|
-
if (top !== null) items.push(top[1]!)
|
|
45
|
-
}
|
|
46
|
-
return items.map(tidy).filter((item) => item !== "")
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** The tier is always marked, first, so the last `risk-tier` mark opens the current lap's marks. */
|
|
50
|
-
export const recordRisk = (): RiskTier => {
|
|
51
|
-
const requirements = read(REQUIREMENTS)
|
|
52
|
-
const tier = readRiskTier(requirements)
|
|
53
|
-
mark("risk-tier", tier)
|
|
54
|
-
for (const signal of productionSignals(requirements)) mark("production-signal", signal)
|
|
55
|
-
return tier
|
|
56
|
-
}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
@inmem
|
|
2
|
-
Feature: each finding at the finding gate 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
|
-
|
|
7
|
-
Background:
|
|
8
|
-
Given the shipped workflows, as gtd install scaffolds them
|
|
9
|
-
And an environment variable "GTD_QUALITYREVIEWS" set to "correctness,tests"
|
|
10
|
-
And a file "src/calc.ts" with:
|
|
11
|
-
"""
|
|
12
|
-
export const add = (a: number, b: number) => a + b
|
|
13
|
-
"""
|
|
14
|
-
And the working tree is committed as "feat: add calculator"
|
|
15
|
-
And a file ".gtd/REVIEW.orig.md" with:
|
|
16
|
-
"""
|
|
17
|
-
# Review: abc1234
|
|
18
|
-
|
|
19
|
-
<!-- base: 0000000 -->
|
|
20
|
-
|
|
21
|
-
## calc
|
|
22
|
-
|
|
23
|
-
- [ ] ./src/calc.ts#1-1
|
|
24
|
-
new add function
|
|
25
|
-
"""
|
|
26
|
-
And a file ".gtd/REVIEW.md" with:
|
|
27
|
-
"""
|
|
28
|
-
# Review: abc1234
|
|
29
|
-
|
|
30
|
-
<!-- base: 0000000 -->
|
|
31
|
-
|
|
32
|
-
## calc
|
|
33
|
-
|
|
34
|
-
- [ ] ./src/calc.ts#1-1
|
|
35
|
-
new add function — rename it to sum
|
|
36
|
-
"""
|
|
37
|
-
And the working tree is committed as "chore: hand off a review"
|
|
38
|
-
And step "notes.fixing" writes "src/calc.ts" with:
|
|
39
|
-
"""
|
|
40
|
-
export const sum = (a: number, b: number) => a + b
|
|
41
|
-
"""
|
|
42
|
-
And step "notes.closing" runs its script
|
|
43
|
-
And step "build.health.check" changes nothing
|
|
44
|
-
And step "build.quality.correctness.reviewing" writes ".gtd/QUALITY.md" with:
|
|
45
|
-
"""
|
|
46
|
-
# Quality
|
|
47
|
-
|
|
48
|
-
## sum is untested
|
|
49
|
-
|
|
50
|
-
From correctness.
|
|
51
|
-
"""
|
|
52
|
-
And step "build.quality.tests.reviewing" writes ".gtd/QUALITY.md" with:
|
|
53
|
-
"""
|
|
54
|
-
# Quality
|
|
55
|
-
|
|
56
|
-
## sum is untested
|
|
57
|
-
|
|
58
|
-
From correctness.
|
|
59
|
-
|
|
60
|
-
## sum is untested
|
|
61
|
-
|
|
62
|
-
From tests.
|
|
63
|
-
"""
|
|
64
|
-
And step "build.quality.gating" runs its script
|
|
65
|
-
And step "build.quality.closing" runs its script
|
|
66
|
-
And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
|
|
67
|
-
"""
|
|
68
|
-
# Review: abc1234
|
|
69
|
-
|
|
70
|
-
<!-- base: 0000000 -->
|
|
71
|
-
|
|
72
|
-
## calc
|
|
73
|
-
|
|
74
|
-
- [ ] ./src/calc.ts#1-1
|
|
75
|
-
renamed to sum
|
|
76
|
-
"""
|
|
77
|
-
And step "build.review.carrying" runs its script
|
|
78
|
-
|
|
79
|
-
Scenario: two lenses' findings with one title each name their own lens
|
|
80
|
-
Given judge step "build.quality.triage" answers:
|
|
81
|
-
"""
|
|
82
|
-
[{"id":"Q1","answer":"design","p":0.9},{"id":"Q2","answer":"design","p":0.9}]
|
|
83
|
-
"""
|
|
84
|
-
When gtd opens door "fix"
|
|
85
|
-
And the process is driven
|
|
86
|
-
Then the process rests at "build.quality.gate"
|
|
87
|
-
And ".gtd/QUALITY.md" contains:
|
|
88
|
-
"""
|
|
89
|
-
## sum is untested
|
|
90
|
-
|
|
91
|
-
_From lens correctness._
|
|
92
|
-
|
|
93
|
-
From correctness.
|
|
94
|
-
|
|
95
|
-
- [ ] fix
|
|
96
|
-
|
|
97
|
-
## sum is untested
|
|
98
|
-
|
|
99
|
-
_From lens tests._
|
|
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)
|
|
114
|
-
"""
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
@inmem
|
|
2
|
-
Feature: fix's quality lap is triaged, and its leftovers are decided at the finding gate
|
|
3
|
-
|
|
4
|
-
A judge sorts the lap's findings: mechanical ones are fixed for up to three
|
|
5
|
-
turns, and the design ones — with whatever the fix turns left, and the
|
|
6
|
-
noise — wait at the finding gate, never at an escalation. A ticked one is
|
|
7
|
-
fixed on the spot; the hand-off review lists what became of each in its
|
|
8
|
-
summary and carries no finding on to the next `fix`. With no requirements
|
|
9
|
-
to check against, the `criteria-coverage` lens runs no turn.
|
|
10
|
-
|
|
11
|
-
Background:
|
|
12
|
-
Given the shipped workflows, as gtd install scaffolds them
|
|
13
|
-
And an environment variable "GTD_QUALITYREVIEWS" set to "criteria-coverage, correctness"
|
|
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 ".gtd/REVIEW.orig.md" with:
|
|
20
|
-
"""
|
|
21
|
-
# Review: abc1234
|
|
22
|
-
|
|
23
|
-
<!-- base: 0000000 -->
|
|
24
|
-
|
|
25
|
-
## calc
|
|
26
|
-
|
|
27
|
-
- [ ] ./src/calc.ts#1-1
|
|
28
|
-
new add function
|
|
29
|
-
"""
|
|
30
|
-
And a file ".gtd/REVIEW.md" with:
|
|
31
|
-
"""
|
|
32
|
-
# Review: abc1234
|
|
33
|
-
|
|
34
|
-
<!-- base: 0000000 -->
|
|
35
|
-
|
|
36
|
-
## calc
|
|
37
|
-
|
|
38
|
-
- [ ] ./src/calc.ts#1-1
|
|
39
|
-
new add function — rename it to sum
|
|
40
|
-
"""
|
|
41
|
-
And the working tree is committed as "chore: hand off a review"
|
|
42
|
-
And step "notes.fixing" writes "src/calc.ts" with:
|
|
43
|
-
"""
|
|
44
|
-
export const sum = (a: number, b: number) => a + b
|
|
45
|
-
"""
|
|
46
|
-
And step "notes.closing" runs its script
|
|
47
|
-
And step "build.health.check" changes nothing
|
|
48
|
-
And step "build.quality.correctness.reviewing" writes ".gtd/QUALITY.md" with:
|
|
49
|
-
"""
|
|
50
|
-
# Quality
|
|
51
|
-
|
|
52
|
-
## sum has no negative test
|
|
53
|
-
|
|
54
|
-
Add a test summing negative numbers.
|
|
55
|
-
|
|
56
|
-
## sum should be named plus
|
|
57
|
-
|
|
58
|
-
The glossary says plus.
|
|
59
|
-
"""
|
|
60
|
-
And step "build.quality.sorting" runs its script
|
|
61
|
-
And step "build.quality.gating" runs its script
|
|
62
|
-
And step "build.quality.picking" runs its script
|
|
63
|
-
And step "build.quality.closing" runs its script
|
|
64
|
-
And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
|
|
65
|
-
"""
|
|
66
|
-
# Review: abc1234
|
|
67
|
-
|
|
68
|
-
<!-- base: 0000000 -->
|
|
69
|
-
|
|
70
|
-
## calc
|
|
71
|
-
|
|
72
|
-
- [ ] ./src/calc.ts#1-1
|
|
73
|
-
renamed to sum
|
|
74
|
-
"""
|
|
75
|
-
And step "build.review.carrying" runs its script
|
|
76
|
-
|
|
77
|
-
Scenario: a design finding and one the fix cap left reach the finding gate, and a ticked one is fixed there
|
|
78
|
-
Given judge step "build.quality.triage" answers:
|
|
79
|
-
"""
|
|
80
|
-
[{"id":"Q1","answer":"mechanical","p":0.9},{"id":"Q2","answer":"design","p":0.9}]
|
|
81
|
-
"""
|
|
82
|
-
And step "build.fix.quality.fixing" changes nothing
|
|
83
|
-
When gtd opens door "fix"
|
|
84
|
-
And the process is driven
|
|
85
|
-
Then the process rests at "build.quality.gate"
|
|
86
|
-
And no commit body contains "build.health.describe"
|
|
87
|
-
And no commit body contains "build.quality.criteria-coverage"
|
|
88
|
-
And ".gtd/QUALITY.md" contains:
|
|
89
|
-
"""
|
|
90
|
-
## sum should be named plus
|
|
91
|
-
|
|
92
|
-
_From lens correctness._
|
|
93
|
-
|
|
94
|
-
The glossary says plus.
|
|
95
|
-
|
|
96
|
-
- [ ] fix
|
|
97
|
-
|
|
98
|
-
## sum has no negative test
|
|
99
|
-
|
|
100
|
-
_From lens correctness. The fixer left it unresolved._
|
|
101
|
-
|
|
102
|
-
Add a test summing negative numbers.
|
|
103
|
-
|
|
104
|
-
- [ ] fix
|
|
105
|
-
"""
|
|
106
|
-
Given a file ".gtd/QUALITY.md" with:
|
|
107
|
-
"""
|
|
108
|
-
# Quality
|
|
109
|
-
|
|
110
|
-
## sum should be named plus
|
|
111
|
-
|
|
112
|
-
The glossary says plus.
|
|
113
|
-
|
|
114
|
-
- [x] fix
|
|
115
|
-
|
|
116
|
-
## sum has no negative test
|
|
117
|
-
|
|
118
|
-
Add a test summing negative numbers.
|
|
119
|
-
|
|
120
|
-
- [ ] fix
|
|
121
|
-
"""
|
|
122
|
-
And step "build.fix.quality.fixing" is no longer scripted
|
|
123
|
-
And step "build.fix.quality.fixing" writes "src/calc.ts" with:
|
|
124
|
-
"""
|
|
125
|
-
export const plus = (a: number, b: number) => a + b
|
|
126
|
-
"""
|
|
127
|
-
And step "build.fix.quality.fixing" deletes ".gtd/QUALITY.md"
|
|
128
|
-
When gtd lands
|
|
129
|
-
And the process is driven
|
|
130
|
-
Then the process rests at "build.review.await-review"
|
|
131
|
-
And ".gtd/QUALITY.md" does not exist
|
|
132
|
-
And ".gtd/REVIEW.md" contains:
|
|
133
|
-
"""
|
|
134
|
-
Fixed at the finding gate:
|
|
135
|
-
|
|
136
|
-
- sum should be named plus (
|
|
137
|
-
"""
|
|
138
|
-
And ".gtd/REVIEW.md" contains:
|
|
139
|
-
"""
|
|
140
|
-
Dropped at the finding gate:
|
|
141
|
-
|
|
142
|
-
- sum has no negative test (lens correctness)
|
|
143
|
-
"""
|
|
144
|
-
And ".gtd/REVIEW.md" does not contain "Tick exactly one:"
|
|
145
|
-
And the commit subjects end with:
|
|
146
|
-
"""
|
|
147
|
-
gtd(human): build.quality.gate → build.quality.picking
|
|
148
|
-
gtd(check): build.quality.picking → build.fix.quality.fixing
|
|
149
|
-
gtd(agent): build.fix.quality.fixing → build.health.check
|
|
150
|
-
gtd(check): build.health.check → build.review.reviewing
|
|
151
|
-
gtd(agent): build.review.reviewing → build.review.carrying
|
|
152
|
-
gtd(check): build.review.carrying → build.review.await-review
|
|
153
|
-
"""
|
|
154
|
-
|
|
155
|
-
Scenario: a fixed finding and a noise one are listed in the review
|
|
156
|
-
Given judge step "build.quality.triage" answers:
|
|
157
|
-
"""
|
|
158
|
-
[{"id":"Q1","answer":"mechanical","p":0.9},{"id":"Q2","answer":"noise","p":0.9}]
|
|
159
|
-
"""
|
|
160
|
-
And step "build.fix.quality.fixing" writes "src/calc.test.ts" with:
|
|
161
|
-
"""
|
|
162
|
-
it("sums negative numbers", () => {})
|
|
163
|
-
"""
|
|
164
|
-
And step "build.fix.quality.fixing" deletes ".gtd/QUALITY.md"
|
|
165
|
-
And step "build.quality.gate" changes nothing
|
|
166
|
-
When gtd opens door "fix"
|
|
167
|
-
And the process is driven
|
|
168
|
-
Then the process rests at "build.review.await-review"
|
|
169
|
-
And ".gtd/REVIEW.md" contains "Fixed automatically:"
|
|
170
|
-
And ".gtd/REVIEW.md" contains "- sum has no negative test ("
|
|
171
|
-
And ".gtd/REVIEW.md" contains the short hash of the commit "gtd(agent): build.fix.quality.fixing → build.health.check"
|
|
172
|
-
And ".gtd/REVIEW.md" contains "- sum should be named plus (p=0.9, lens correctness)"
|
|
173
|
-
|
|
174
|
-
Scenario: a risk the hand-off reviewer marks is fixed and re-reviewed in its own draft, the summary added once
|
|
175
|
-
Given judge step "build.quality.triage" answers:
|
|
176
|
-
"""
|
|
177
|
-
[{"id":"Q1","answer":"mechanical","p":0.9},{"id":"Q2","answer":"noise","p":0.9}]
|
|
178
|
-
"""
|
|
179
|
-
And step "build.fix.quality.fixing" deletes ".gtd/QUALITY.md"
|
|
180
|
-
And step "build.quality.gate" changes nothing
|
|
181
|
-
And step "build.review.reviewing" is no longer scripted
|
|
182
|
-
And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
|
|
183
|
-
"""
|
|
184
|
-
# Review: abc1234
|
|
185
|
-
|
|
186
|
-
<!-- base: 0000000 -->
|
|
187
|
-
|
|
188
|
-
## calc
|
|
189
|
-
|
|
190
|
-
- [ ] ./src/calc.ts#1-1 — Risk: overflows on large numbers
|
|
191
|
-
"""
|
|
192
|
-
When gtd opens door "fix"
|
|
193
|
-
And the process is driven
|
|
194
|
-
Then the process rests at "build.review.fix.risks.fixing"
|
|
195
|
-
And ".gtd/REVIEW.md" does not contain "## Quality lap"
|
|
196
|
-
Given step "build.review.reviewing" is no longer scripted
|
|
197
|
-
And step "build.review.reviewing" replaces the text "Risk: overflows on large numbers" in ".gtd/REVIEW.md" with "sum, now guarded"
|
|
198
|
-
And step "build.review.fix.risks.fixing" changes nothing
|
|
199
|
-
When the process is driven
|
|
200
|
-
Then the process rests at "build.review.await-review"
|
|
201
|
-
And ".gtd/REVIEW.md" contains "sum, now guarded"
|
|
202
|
-
And ".gtd/REVIEW.md" contains "## Quality lap" exactly once
|
|
203
|
-
And ".gtd/REVIEW.md" contains "## calc" exactly once
|
|
204
|
-
And the commit subjects end with:
|
|
205
|
-
"""
|
|
206
|
-
gtd(check): build.quality.closing → build.review.reviewing
|
|
207
|
-
gtd(agent): build.review.reviewing → build.review.fix.risks.fixing
|
|
208
|
-
gtd(agent): build.review.fix.risks.fixing → build.health.check
|
|
209
|
-
gtd(check): build.health.check → build.review.reviewing
|
|
210
|
-
gtd(agent): build.review.reviewing → build.review.carrying
|
|
211
|
-
gtd(check): build.review.carrying → build.review.await-review
|
|
212
|
-
"""
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
Feature: fix fixes the notes a hand-off leaves it
|
|
2
|
-
|
|
3
|
-
A hand-off is `.gtd/REVIEW.md` with the human's notes beside the reviewer's
|
|
4
|
-
own `.gtd/REVIEW.orig.md`; `gtd door fix` picks both up.
|
|
5
|
-
|
|
6
|
-
Background:
|
|
7
|
-
Given the shipped workflows, as gtd install scaffolds them
|
|
8
|
-
And an environment variable "GTD_QUALITYREVIEWS" set to ""
|
|
9
|
-
And a file "src/calc.ts" with:
|
|
10
|
-
"""
|
|
11
|
-
export const add = (a: number, b: number) => a + b
|
|
12
|
-
"""
|
|
13
|
-
And the working tree is committed as "feat: add calculator"
|
|
14
|
-
|
|
15
|
-
Scenario: a handed-off note is fixed, and both review files are removed
|
|
16
|
-
Given 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 "build.health.check" changes nothing
|
|
40
|
-
And step "build.review.reviewing" writes ".gtd/REVIEW.md" with:
|
|
41
|
-
"""
|
|
42
|
-
# Review: abc1234
|
|
43
|
-
|
|
44
|
-
<!-- base: 0000000 -->
|
|
45
|
-
|
|
46
|
-
## calc
|
|
47
|
-
|
|
48
|
-
- [ ] ./src/calc.ts#1-1
|
|
49
|
-
new add function
|
|
50
|
-
"""
|
|
51
|
-
And step "notes.fixing" writes "src/calc.ts" with:
|
|
52
|
-
"""
|
|
53
|
-
export const sum = (a: number, b: number) => a + b
|
|
54
|
-
"""
|
|
55
|
-
And step "notes.closing" runs its script
|
|
56
|
-
When gtd opens door "fix"
|
|
57
|
-
Then the resting step's content contains "rename it to sum"
|
|
58
|
-
# The fix goes to its own review, which the scripted reviewer writes afresh.
|
|
59
|
-
When the process is driven
|
|
60
|
-
Then the process rests at "build.review.await-review"
|
|
61
|
-
And the commit subjects end with:
|
|
62
|
-
"""
|
|
63
|
-
gtd(human): notes.fixing
|
|
64
|
-
gtd(agent): notes.fixing → notes.closing
|
|
65
|
-
gtd(check): notes.closing → build.health.check
|
|
66
|
-
gtd(check): build.health.check → build.review.reviewing
|
|
67
|
-
gtd(agent): build.review.reviewing → build.review.await-review
|
|
68
|
-
"""
|
|
69
|
-
And ".gtd/REVIEW.orig.md" does not exist
|
|
70
|
-
And ".gtd/REVIEW.md" contains "new add function"
|
|
71
|
-
And "src/calc.ts" contains "sum"
|
|
72
|
-
|
|
73
|
-
Scenario: a reviewer's copy with no review is refused
|
|
74
|
-
Given a file ".gtd/REVIEW.orig.md" with:
|
|
75
|
-
"""
|
|
76
|
-
# Review: abc1234
|
|
77
|
-
"""
|
|
78
|
-
And the working tree is committed as "chore: stray copy"
|
|
79
|
-
Then opening door "fix" is refused with "has no .gtd/REVIEW.md"
|