@pmelab/gtd 30.1.0 → 32.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 +29 -23
- package/claude/hooks/ship.ts +7 -3
- package/dist/chunks/{reserved-DDiiAZ8U.mjs → reserved-CHTJTzpA.mjs} +52 -3
- package/dist/chunks/{tail-ILPlELuy.mjs → tail-CCsDBbZe.mjs} +1 -1
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +137 -8
- package/dist/workflows/fix.mjs +2 -2
- package/dist/workflows/review.mjs +2 -2
- package/dist/workflows/rollout.mjs +1 -1
- package/dist/workflows.mjs +188 -10
- package/package.json +1 -1
- package/src/workflows/feature/acceptance-criteria-tier.feature +25 -0
- package/src/workflows/feature/acceptance-criteria.feature +189 -0
- package/src/workflows/feature/criteria.test.ts +106 -0
- package/src/workflows/feature/criteria.ts +168 -0
- package/src/workflows/feature/fast-path.feature +4 -0
- package/src/workflows/feature/feature.test.ts +41 -11
- package/src/workflows/feature/feature.ts +7 -4
- package/src/workflows/feature/flag-mode.feature +8 -0
- package/src/workflows/feature/index.ts +1 -0
- package/src/workflows/feature/one-step-land.feature +4 -0
- package/src/workflows/feature/planning.ts +2 -0
- package/src/workflows/feature/refactor-absorbs.feature +4 -0
- package/src/workflows/feature/refactor-first.feature +4 -0
- package/src/workflows/feature/refactor-review.feature +24 -0
- package/src/workflows/feature/risk-tier.feature +32 -0
- package/src/workflows/feature/seam-rejection.feature +8 -0
- package/src/workflows/feature/seam.feature +8 -0
- package/src/workflows/feature/seam.ts +12 -7
- package/src/workflows/feature/tier-pin.feature +4 -0
- package/src/workflows/index.ts +1 -0
- package/src/workflows/shared/text.ts +77 -2
package/src/workflows/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ export {
|
|
|
21
21
|
} from "./shared/tail.js"
|
|
22
22
|
export { lensesOf } from "../workflow-kit/index.js"
|
|
23
23
|
export * from "./feature/steps.js"
|
|
24
|
+
export * from "./feature/criteria.js"
|
|
24
25
|
export * from "./feature/planning.js"
|
|
25
26
|
export * from "./feature/refactor.js"
|
|
26
27
|
export * from "./feature/seam.js"
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
skillsFor,
|
|
10
10
|
start,
|
|
11
11
|
type AgentOptions,
|
|
12
|
+
type JudgeQuestion,
|
|
12
13
|
type SummaryContext,
|
|
13
14
|
} from "../../flows/index.js"
|
|
14
15
|
import { escapeRegExp } from "./registry.js"
|
|
@@ -115,6 +116,22 @@ const codeThreadReplies = (): string => {
|
|
|
115
116
|
${list}`
|
|
116
117
|
}
|
|
117
118
|
|
|
119
|
+
const acceptanceCriteriaBlock = `
|
|
120
|
+
## Acceptance criteria
|
|
121
|
+
|
|
122
|
+
Whichever lap this is, \`.gtd/REQUIREMENTS.md\` carries a
|
|
123
|
+
\`## Acceptance Criteria\` section: one \`- AC-<n>: <criterion>\` line
|
|
124
|
+
per criterion, numbered from \`AC-1\`. They bind every later step —
|
|
125
|
+
the build is done when each one holds — so each must be checkable: a
|
|
126
|
+
test or a command shows it holds or not. A later lap keeps every ID:
|
|
127
|
+
reword a criterion in place, append a new one with the next unused
|
|
128
|
+
number, delete a dropped one — never renumber, never reuse an ID. When
|
|
129
|
+
the file holds no list (a review loop-back rewrites it), recover the
|
|
130
|
+
last settled one from history (\`git log -p -- .gtd/REQUIREMENTS.md\`)
|
|
131
|
+
and carry its IDs over. Landing is refused while a criterion has no ID or
|
|
132
|
+
two share one.
|
|
133
|
+
`
|
|
134
|
+
|
|
118
135
|
const riskTierBlock = `
|
|
119
136
|
## Risk tier and production signals
|
|
120
137
|
|
|
@@ -160,7 +177,7 @@ ${footnoteFoldIn}${codeThreadReplies()}
|
|
|
160
177
|
or both
|
|
161
178
|
- Whichever lap this is, leave \`.gtd/REQUIREMENTS.md\`
|
|
162
179
|
uncommitted and finish once it reflects this lap's own work
|
|
163
|
-
|
|
180
|
+
${acceptanceCriteriaBlock}
|
|
164
181
|
## First lap
|
|
165
182
|
|
|
166
183
|
\`.gtd/REQUIREMENTS.md\` does not exist yet (or holds whatever the
|
|
@@ -216,6 +233,47 @@ ${questionBarReturn}
|
|
|
216
233
|
everything else — the build assumes the suite started green
|
|
217
234
|
${riskTier ? riskTierBlock : ""}`
|
|
218
235
|
|
|
236
|
+
export const clearCriteriaQuestion: JudgeQuestion = {
|
|
237
|
+
id: "clear",
|
|
238
|
+
primitive: "noul",
|
|
239
|
+
instructions:
|
|
240
|
+
"state.criteria lists the acceptance criteria the human's sketch (state.sketch) stated, as gtd numbered them. Decide whether they can stand as the binding definition of done, with no design conversation.",
|
|
241
|
+
criteria:
|
|
242
|
+
"yes: every criterion is clear and checkable — a test or a command can show it holds or not — and together they say what to build. no: any one criterion is vague, subjective or untestable, or the sketch leaves an open product question. Unsure means no.",
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export const clearCriteriaMessage = (): string =>
|
|
246
|
+
`The sketch stated its own acceptance criteria. Run \`gtd judge answer\` and
|
|
247
|
+
pipe \`yes\` when every one is clear and checkable, so design is skipped and
|
|
248
|
+
they are recorded as they are — or land without a verdict to run the
|
|
249
|
+
ordinary design conversation.
|
|
250
|
+
`
|
|
251
|
+
|
|
252
|
+
export const criterionLine = (c: { readonly id: string; readonly text: string }): string =>
|
|
253
|
+
`- ${c.id}: ${c.text.replace(/\s+/g, " ")}`
|
|
254
|
+
|
|
255
|
+
/** Writes \`.gtd/REQUIREMENTS.md\` from criteria the sketch stated, wrapped the way oxfmt writes it. */
|
|
256
|
+
export const normaliseCriteriaScript = (stated: {
|
|
257
|
+
readonly path: string
|
|
258
|
+
readonly commit: string
|
|
259
|
+
readonly criteria: readonly { readonly id: string; readonly text: string }[]
|
|
260
|
+
}): string => {
|
|
261
|
+
const lines = [
|
|
262
|
+
"# Requirements",
|
|
263
|
+
"",
|
|
264
|
+
`Stated in \`${stated.path}\` by the sketch:`,
|
|
265
|
+
"",
|
|
266
|
+
`\`git show ${stated.commit}:${stated.path}\``,
|
|
267
|
+
"",
|
|
268
|
+
"## Acceptance Criteria",
|
|
269
|
+
"",
|
|
270
|
+
...stated.criteria.flatMap((c) => wrapItem(criterionLine(c))),
|
|
271
|
+
]
|
|
272
|
+
return `mkdir -p .gtd
|
|
273
|
+
printf '%s\\n' ${lines.map(quote).join(" ")} > ${quote(".gtd/REQUIREMENTS.md")}
|
|
274
|
+
`
|
|
275
|
+
}
|
|
276
|
+
|
|
219
277
|
export const designSystem = (): string =>
|
|
220
278
|
`${designPersona}
|
|
221
279
|
|
|
@@ -650,7 +708,17 @@ What each change does next (then run \`gtd land\`):
|
|
|
650
708
|
const wrapItem = (item: string): string[] => {
|
|
651
709
|
const out: string[] = []
|
|
652
710
|
let line = ""
|
|
653
|
-
|
|
711
|
+
// A word that would read as a list marker or heading at a line start stays on the line before.
|
|
712
|
+
const words = item
|
|
713
|
+
.split(" ")
|
|
714
|
+
.reduce<string[]>(
|
|
715
|
+
(acc, word) =>
|
|
716
|
+
acc.length > 0 && /^([-+*>#]|\d+[.)])$/.test(word)
|
|
717
|
+
? [...acc.slice(0, -1), `${acc.at(-1)!} ${word}`]
|
|
718
|
+
: [...acc, word],
|
|
719
|
+
[],
|
|
720
|
+
)
|
|
721
|
+
for (const word of words) {
|
|
654
722
|
if (line !== "" && line.length + 1 + word.length > 80) {
|
|
655
723
|
out.push(line)
|
|
656
724
|
line = ` ${word}`
|
|
@@ -1066,9 +1134,15 @@ export interface SeamRejection {
|
|
|
1066
1134
|
readonly p: number
|
|
1067
1135
|
}
|
|
1068
1136
|
|
|
1137
|
+
const criteriaBinding = (refused: boolean): string =>
|
|
1138
|
+
`- Its \`## Acceptance Criteria\` (\`AC-<n>\`) bind this turn: re-read them from
|
|
1139
|
+
the file, never from memory, and stop only when each one holds. They are
|
|
1140
|
+
settled — ${refused ? "a turn that changes them is refused" : "leave them as they are"}`
|
|
1141
|
+
|
|
1069
1142
|
export const naivePassPrompt = (rejection?: SeamRejection): string =>
|
|
1070
1143
|
`${stateFileRules}
|
|
1071
1144
|
- Read \`.gtd/REQUIREMENTS.md\`: the settled requirements for this change
|
|
1145
|
+
${criteriaBinding(false)}
|
|
1072
1146
|
- Implement them the quickest way that works — the naive version. Do not
|
|
1073
1147
|
restructure, abstract or refactor around the change; a later step judges
|
|
1074
1148
|
the shape of your diff and a reviewer makes it fit
|
|
@@ -1095,6 +1169,7 @@ export const implementBuildingPrompt = (
|
|
|
1095
1169
|
): string =>
|
|
1096
1170
|
`${stateFileRules}
|
|
1097
1171
|
- Read \`.gtd/REQUIREMENTS.md\`: the settled requirements for this change
|
|
1172
|
+
${criteriaBinding(true)}
|
|
1098
1173
|
${
|
|
1099
1174
|
afterRefactor === true
|
|
1100
1175
|
? `- A reviewed refactor has just landed and may already carry part of the change,
|