@pmelab/gtd 58.2.0 → 59.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 +115 -390
- package/dist/chunks/{Schema-B3o6eRNU.mjs → testkit-CMBAAyRK.mjs} +13409 -3351
- package/dist/gtd.bundle.mjs +76 -59
- package/dist/testkit/gherkin.mjs +1 -1
- package/dist/testkit.mjs +1 -1
- package/package.json +3 -42
- package/skills/authoring/SKILL.md +31 -25
- package/src/cli/Cli.test.ts +15 -19
- package/src/cli/Cli.ts +2 -4
- package/src/cli/Program.test.ts +60 -29
- package/src/cli/Program.ts +5 -7
- package/src/edge/Edge.ts +3 -3
- package/src/flags/index.test.ts +0 -13
- package/src/flags/index.ts +1 -3
- 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/twins/FlowScripts.ts +1 -1
- package/src/workflow/index.ts +1 -0
- package/src/workflow/load.test.ts +138 -113
- package/src/workflow/load.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,267 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
answered,
|
|
3
|
-
changes,
|
|
4
|
-
head,
|
|
5
|
-
judge,
|
|
6
|
-
read,
|
|
7
|
-
refuse,
|
|
8
|
-
requireProgress,
|
|
9
|
-
run,
|
|
10
|
-
scope,
|
|
11
|
-
type Change,
|
|
12
|
-
} from "../../flows/index.js"
|
|
13
|
-
import { HEADING, ITEM, markFenced } from "../shared/fences.js"
|
|
14
|
-
import { planner, REQUIREMENTS } from "../shared/steps.js"
|
|
15
|
-
import * as t from "../shared/text.js"
|
|
16
|
-
import { requireReleaseFlag } from "./release-flag.js"
|
|
17
|
-
|
|
18
|
-
export interface Criterion {
|
|
19
|
-
readonly id: string
|
|
20
|
-
readonly text: string
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** Acceptance criteria the human's sketch stated outright, ready to normalise. */
|
|
24
|
-
export interface StatedCriteria {
|
|
25
|
-
readonly path: string
|
|
26
|
-
readonly commit: string
|
|
27
|
-
/** The file as the sketch left it: `unwind` reverts it out of the tree. */
|
|
28
|
-
readonly text: string
|
|
29
|
-
readonly criteria: readonly Criterion[]
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** Below this the judge's `yes` is not trusted: a wrong skip builds against criteria nobody can check. */
|
|
33
|
-
const CLEAR_MIN_P = 0.8
|
|
34
|
-
|
|
35
|
-
const ID = /^(AC-\d+):\s*(.*)$/
|
|
36
|
-
|
|
37
|
-
const criterion = (id: RegExpExecArray, as = id[1]!): Criterion => ({ id: as, text: id[2]! })
|
|
38
|
-
|
|
39
|
-
/** The list items under the first heading `title` matches (at `level` when given), up to the next heading; wrapped lines joined, fenced lines ignored. */
|
|
40
|
-
const listUnder = (text: string, title: RegExp, level?: number): string[] | undefined => {
|
|
41
|
-
const lines = markFenced(text)
|
|
42
|
-
const at = lines.findIndex((l) => {
|
|
43
|
-
const heading = l.fenced ? null : HEADING.exec(l.line)
|
|
44
|
-
return (
|
|
45
|
-
heading !== null &&
|
|
46
|
-
(level === undefined || heading[1]!.length === level) &&
|
|
47
|
-
title.test(heading[2]!)
|
|
48
|
-
)
|
|
49
|
-
})
|
|
50
|
-
if (at === -1) return undefined
|
|
51
|
-
const items: string[] = []
|
|
52
|
-
let open = false
|
|
53
|
-
for (const { line, fenced } of lines.slice(at + 1)) {
|
|
54
|
-
if (fenced) {
|
|
55
|
-
open = false
|
|
56
|
-
continue
|
|
57
|
-
}
|
|
58
|
-
if (HEADING.test(line)) break
|
|
59
|
-
const item = ITEM.exec(line)
|
|
60
|
-
if (item !== null) {
|
|
61
|
-
items.push(item[1]!.trim())
|
|
62
|
-
open = true
|
|
63
|
-
} else if (open && line.trim() !== "") {
|
|
64
|
-
items[items.length - 1] = `${items.at(-1)!} ${line.trim()}`.trim()
|
|
65
|
-
} else open = false
|
|
66
|
-
}
|
|
67
|
-
return items
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** The `- AC-<n>: …` list of `## Acceptance Criteria`, or `undefined` when the section is missing. */
|
|
71
|
-
export const criteriaOf = (text: string): readonly Criterion[] | undefined =>
|
|
72
|
-
listUnder(text, /^acceptance criteria$/i, 2)?.map((item) => {
|
|
73
|
-
const id = ID.exec(item)
|
|
74
|
-
return id === null ? { id: "", text: item } : criterion(id)
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
/** The ID'd criteria of `text`. */
|
|
78
|
-
export const criterionIds = (text: string): string[] =>
|
|
79
|
-
(criteriaOf(text) ?? []).map((c) => c.id).filter((id) => id !== "")
|
|
80
|
-
|
|
81
|
-
/** Items numbered `AC-1`…, unless every one already carries its own distinct ID. */
|
|
82
|
-
const numbered = (items: readonly string[]): Criterion[] => {
|
|
83
|
-
const ids = items.map((item) => ID.exec(item))
|
|
84
|
-
const own = ids.every((id) => id !== null) && new Set(ids.map((id) => id![1])).size === ids.length
|
|
85
|
-
return items.map((item, i) => {
|
|
86
|
-
const id = ids[i]
|
|
87
|
-
if (own) return criterion(id!)
|
|
88
|
-
return id === null ? { id: `AC-${i + 1}`, text: item } : criterion(id!, `AC-${i + 1}`)
|
|
89
|
-
})
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const STATED = /^acceptance criteria:?$/i
|
|
93
|
-
|
|
94
|
-
const statedItems = (text: string | undefined): string[] =>
|
|
95
|
-
text === undefined ? [] : (listUnder(text, STATED) ?? []).filter((item) => item !== "")
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* The criteria a `TODO.md` in the sketch `sketch` (the idle turn's changes)
|
|
99
|
-
* added or changed under an `Acceptance criteria` heading. More than one such
|
|
100
|
-
* file is ambiguous, and any other change in the sketch is input only triage
|
|
101
|
-
* folds in, so none.
|
|
102
|
-
*/
|
|
103
|
-
export const statedCriteria = (
|
|
104
|
-
sketch: readonly Change[],
|
|
105
|
-
commit: string,
|
|
106
|
-
): StatedCriteria | undefined => {
|
|
107
|
-
const found = sketch.flatMap((change) => {
|
|
108
|
-
if (!/(^|\/)TODO\.md$/.test(change.path) || change.after === undefined) return []
|
|
109
|
-
const items = statedItems(change.after)
|
|
110
|
-
if (items.length === 0 || items.join("\n") === statedItems(change.before).join("\n")) return []
|
|
111
|
-
return [{ path: change.path, commit, text: change.after, criteria: numbered(items) }]
|
|
112
|
-
})
|
|
113
|
-
return found.length === 1 && sketch.length === 1 ? found[0] : undefined
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const listing = (criteria: readonly Criterion[]): string => criteria.map(t.criterionLine).join("\n")
|
|
117
|
-
|
|
118
|
-
/** Refuse a turn that leaves `file` without ID'd acceptance criteria. A deleted file is `requireProgress`'s call. */
|
|
119
|
-
export const requireCriteria = (file: string): void => {
|
|
120
|
-
const text = read(file)
|
|
121
|
-
if (text === undefined) return
|
|
122
|
-
const criteria = criteriaOf(text) ?? []
|
|
123
|
-
const unnamed = criteria.filter((c) => c.id === "")
|
|
124
|
-
const ids = criteria.map((c) => c.id).filter((id) => id !== "")
|
|
125
|
-
const twice = ids.filter((id, i) => ids.indexOf(id) !== i)
|
|
126
|
-
if (criteria.length > 0 && unnamed.length === 0 && twice.length === 0) return
|
|
127
|
-
const why =
|
|
128
|
-
criteria.length === 0
|
|
129
|
-
? `no "## Acceptance Criteria" list`
|
|
130
|
-
: unnamed.length > 0
|
|
131
|
-
? `${unnamed.length} criterion without an ID:\n${unnamed.map((c) => ` - ${c.text}`).join("\n")}`
|
|
132
|
-
: `duplicate IDs: ${[...new Set(twice)].join(", ")}`
|
|
133
|
-
refuse(
|
|
134
|
-
`gtd land: acceptance-criteria: ${file} has ${why} — list every criterion under "## Acceptance Criteria" as "- AC-<n>: <checkable criterion>", each ID unique and never reused`,
|
|
135
|
-
)
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const isImpactHeading = (h: RegExpExecArray | null): boolean =>
|
|
139
|
-
h !== null && h[1]!.length === 2 && /^impact$/i.test(h[2]!.trim())
|
|
140
|
-
|
|
141
|
-
// The skip path's impact turn may write both: a sketch can ask for a flag.
|
|
142
|
-
const isImpactTurnHeading = (h: RegExpExecArray | null): boolean =>
|
|
143
|
-
h !== null && h[1]!.length === 2 && /^(impact|release flag)$/i.test(h[2]!.trim())
|
|
144
|
-
|
|
145
|
-
/** Whether `text` has an unfenced, level-2 `## Impact` heading. */
|
|
146
|
-
export const hasImpact = (text: string): boolean =>
|
|
147
|
-
markFenced(text).some((l) => !l.fenced && isImpactHeading(HEADING.exec(l.line)))
|
|
148
|
-
|
|
149
|
-
/** `text` without its `## Impact` and `## Release Flag` sections, whitespace collapsed. */
|
|
150
|
-
const withoutImpact = (text: string): string => {
|
|
151
|
-
let inImpact = false
|
|
152
|
-
const kept = markFenced(text).filter((l) => {
|
|
153
|
-
const h = l.fenced ? null : HEADING.exec(l.line)
|
|
154
|
-
if (h !== null && h[1]!.length <= 2) inImpact = isImpactTurnHeading(h)
|
|
155
|
-
return !inImpact
|
|
156
|
-
})
|
|
157
|
-
return kept
|
|
158
|
-
.map((l) => l.line)
|
|
159
|
-
.join(" ")
|
|
160
|
-
.replace(/\s+/g, " ")
|
|
161
|
-
.trim()
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Refuse a turn that leaves `file` without a `## Impact` section. Only the
|
|
166
|
-
* pending turn (`head()` is `""`): a triage lap recorded before Impact existed
|
|
167
|
-
* replays without one, and refusing there would fail every in-flight process.
|
|
168
|
-
* A human's edit at a later gate is not checked — the human owns that file then.
|
|
169
|
-
*/
|
|
170
|
-
export const requireImpact = (file: string): void => {
|
|
171
|
-
if (head() !== "") return
|
|
172
|
-
const text = read(file)
|
|
173
|
-
if (text === undefined) return
|
|
174
|
-
if (hasImpact(text)) return
|
|
175
|
-
refuse(
|
|
176
|
-
`gtd land: impact: ${file} has no "## Impact" section — explore the codebase and add "## Impact" naming the units, public surfaces and surprises this change touches`,
|
|
177
|
-
)
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/** Refuse a turn that changed `file` beyond its `## Impact` and `## Release Flag` sections. */
|
|
181
|
-
export const requireImpactOnly = (file: string): void => {
|
|
182
|
-
const change = changes().get(file)
|
|
183
|
-
if (change === undefined) return
|
|
184
|
-
if (withoutImpact(change.before ?? "") === withoutImpact(change.after ?? "")) return
|
|
185
|
-
refuse(
|
|
186
|
-
`gtd land: impact-only: this turn may only add "## Impact" (and "## Release Flag") to ${file} — restore everything else`,
|
|
187
|
-
)
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/** The IDs a review's feedback lap must respect. */
|
|
191
|
-
export interface StableIds {
|
|
192
|
-
/** Listed when the build closed: each may stay, reworded or not, or go. */
|
|
193
|
-
readonly closed: readonly string[]
|
|
194
|
-
/** Every ID handed out so far: any other is numbered above all of them. */
|
|
195
|
-
readonly used: readonly string[]
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const idNumber = (id: string): number => Number(id.slice("AC-".length))
|
|
199
|
-
|
|
200
|
-
/** Why `listed` renumbers or reuses an earlier ID; `undefined` when it does neither. */
|
|
201
|
-
export const unstableIds = (listed: readonly string[], ids: StableIds): string | undefined => {
|
|
202
|
-
const top = Math.max(0, ...ids.used.map(idNumber))
|
|
203
|
-
const reused = listed.filter((id) => !ids.closed.includes(id) && idNumber(id) <= top)
|
|
204
|
-
if (reused.length === 0) return undefined
|
|
205
|
-
return `numbers ${reused.join(", ")} at or below AC-${top}, an ID already handed out — keep a built criterion's ID (reword it in place when the review changes it), and number new ones from AC-${top + 1}`
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/** Refuse criteria in `file` that renumber or reuse an earlier ID. */
|
|
209
|
-
export const requireStableIds = (file: string, ids: StableIds): void => {
|
|
210
|
-
const why = unstableIds(criterionIds(read(file) ?? ""), ids)
|
|
211
|
-
if (why !== undefined) refuse(`gtd land: criteria-ids: ${file} ${why}`)
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/** Refuse a turn that changed the acceptance criteria in `file`: after the design gate only another design lap may. */
|
|
215
|
-
export const requireCriteriaFrozen = (file: string): void => {
|
|
216
|
-
const change = changes().get(file)
|
|
217
|
-
if (change === undefined) return
|
|
218
|
-
const before = listing(criteriaOf(change.before ?? "") ?? [])
|
|
219
|
-
if (before === listing(criteriaOf(change.after ?? "") ?? [])) return
|
|
220
|
-
refuse(
|
|
221
|
-
`gtd land: criteria-frozen: the acceptance criteria in ${file} are settled — restore them; only a design lap changes them`,
|
|
222
|
-
)
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/** Whether the judge trusts `stated` as clear and checkable, every criterion of it. Truncated evidence never is. */
|
|
226
|
-
const clear = async (stated: StatedCriteria): Promise<boolean> => {
|
|
227
|
-
const { answers, truncated } = await judge("clear", {
|
|
228
|
-
questions: [t.clearCriteriaQuestion],
|
|
229
|
-
evidence: { criteria: listing(stated.criteria), sketch: stated.text },
|
|
230
|
-
message: t.clearCriteriaMessage(),
|
|
231
|
-
label: "Judging the stated acceptance criteria",
|
|
232
|
-
})
|
|
233
|
-
return truncated.length === 0 && answered(answers.clear, "yes", CLEAR_MIN_P)
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/** The fast path's impact-only turn: add `## Impact`, leave the criteria alone. */
|
|
237
|
-
const impact = async (): Promise<void> => {
|
|
238
|
-
await t.agentWithSkills("impact", t.designImpactPrompt(), {
|
|
239
|
-
label: "Mapping the change's impact",
|
|
240
|
-
file: REQUIREMENTS,
|
|
241
|
-
model: planner(),
|
|
242
|
-
system: t.designSystem(),
|
|
243
|
-
})
|
|
244
|
-
requireProgress(REQUIREMENTS)
|
|
245
|
-
requireCriteriaFrozen(REQUIREMENTS)
|
|
246
|
-
requireImpact(REQUIREMENTS)
|
|
247
|
-
requireImpactOnly(REQUIREMENTS)
|
|
248
|
-
requireReleaseFlag(REQUIREMENTS)
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* The skip path: when the sketch stated clear, checkable criteria, write them
|
|
253
|
-
* to `.gtd/REQUIREMENTS.md` as they are and resolve `true` — no triage, no
|
|
254
|
-
* design gate. `false` when design must run.
|
|
255
|
-
*/
|
|
256
|
-
export const skipDesign = (stated: StatedCriteria | undefined): Promise<boolean> =>
|
|
257
|
-
stated === undefined
|
|
258
|
-
? Promise.resolve(false)
|
|
259
|
-
: scope("design", async () => {
|
|
260
|
-
if (!(await clear(stated))) return false
|
|
261
|
-
await run("normalise", t.normaliseCriteriaScript(stated), {
|
|
262
|
-
label: "Recording the stated acceptance criteria",
|
|
263
|
-
file: REQUIREMENTS,
|
|
264
|
-
})
|
|
265
|
-
await impact()
|
|
266
|
-
return true
|
|
267
|
-
})
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
@inmem
|
|
2
|
-
Feature: a trivial change takes the same few beats at either risk tier
|
|
3
|
-
|
|
4
|
-
With GTD_FLAG_RISK_TIER on, a one-line copy fix goes
|
|
5
|
-
from idle through the naive pass, the seam judge's land, the applied pass and
|
|
6
|
-
the quality lap to the review gate. Low tier walks exactly the steps high does; only
|
|
7
|
-
the summary differs.
|
|
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_FLAG_RISK_TIER" set to "1"
|
|
13
|
-
And a file "src/greeting.ts" with:
|
|
14
|
-
"""
|
|
15
|
-
export const greeting = "Helo, world"
|
|
16
|
-
"""
|
|
17
|
-
And the working tree is committed as "feat: add greeting"
|
|
18
|
-
And a file "TODO.md" with:
|
|
19
|
-
"""
|
|
20
|
-
# Fix the typo
|
|
21
|
-
|
|
22
|
-
greeting says "Helo"; make it "Hello".
|
|
23
|
-
"""
|
|
24
|
-
And step "unwind" changes nothing
|
|
25
|
-
And step "start-gate.check" changes nothing
|
|
26
|
-
And step "design.gate.answer" changes nothing
|
|
27
|
-
And step "implement.health.check" changes nothing
|
|
28
|
-
And step "implement.closing" runs its script
|
|
29
|
-
And step "build.quality.correctness.reviewing" changes nothing
|
|
30
|
-
|
|
31
|
-
Scenario Outline: a one-line fix runs from idle to the review gate with the same beats at either tier
|
|
32
|
-
Given step "design.triage" writes ".gtd/REQUIREMENTS.md" with:
|
|
33
|
-
"""
|
|
34
|
-
# Requirements
|
|
35
|
-
|
|
36
|
-
## Risk Tier
|
|
37
|
-
|
|
38
|
-
<tier> — one-line copy fix
|
|
39
|
-
|
|
40
|
-
## Impact
|
|
41
|
-
|
|
42
|
-
- the greeting module
|
|
43
|
-
|
|
44
|
-
## Release Flag
|
|
45
|
-
|
|
46
|
-
new
|
|
47
|
-
|
|
48
|
-
## Acceptance Criteria
|
|
49
|
-
|
|
50
|
-
- AC-1: the change behaves as described.
|
|
51
|
-
"""
|
|
52
|
-
When the process is driven
|
|
53
|
-
Then the process rests at "seam.naive"
|
|
54
|
-
And the last commit subject is "gtd(human): design.gate.answer → seam.naive"
|
|
55
|
-
|
|
56
|
-
Given step "seam.check" changes nothing
|
|
57
|
-
And step "seam.naive" writes "src/acceptance.test.ts" with:
|
|
58
|
-
"""
|
|
59
|
-
it("[AC-1] holds", () => {})
|
|
60
|
-
"""
|
|
61
|
-
And step "seam.naive" writes "src/greeting.ts" with:
|
|
62
|
-
"""
|
|
63
|
-
export const greeting = "Hello, world"
|
|
64
|
-
"""
|
|
65
|
-
And judge step "seam.judge" answers:
|
|
66
|
-
"""
|
|
67
|
-
[{"id":"shape","answer":"land","p":0.9}]
|
|
68
|
-
"""
|
|
69
|
-
And step "implement.applying" runs its script
|
|
70
|
-
When the process is driven
|
|
71
|
-
Then the process rests at "build.review.reviewing"
|
|
72
|
-
And "src/greeting.ts" contains "Hello, world"
|
|
73
|
-
And the commit subjects end with:
|
|
74
|
-
"""
|
|
75
|
-
gtd(human): idle → unwind
|
|
76
|
-
gtd(check): unwind → start-gate.check
|
|
77
|
-
gtd(check): start-gate.check → design.triage
|
|
78
|
-
gtd(agent): design.triage → design.gate.answer
|
|
79
|
-
gtd(human): design.gate.answer → seam.naive
|
|
80
|
-
gtd(agent): seam.naive → seam.check
|
|
81
|
-
gtd(check): seam.check → seam.judge
|
|
82
|
-
gtd(judge): seam.judge → implement.applying
|
|
83
|
-
gtd(check): implement.applying → implement.health.check
|
|
84
|
-
gtd(check): implement.health.check → implement.closing
|
|
85
|
-
gtd(check): implement.closing → build.quality.correctness.reviewing
|
|
86
|
-
gtd(agent): build.quality.correctness.reviewing → build.review.reviewing
|
|
87
|
-
"""
|
|
88
|
-
And the output of gtd "summary" <summary> "needs a human reviewer"
|
|
89
|
-
|
|
90
|
-
Examples:
|
|
91
|
-
| tier | summary |
|
|
92
|
-
| low | does not contain |
|
|
93
|
-
| high | contains |
|