@pmelab/gtd 51.0.1 → 52.1.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 +46 -40
- package/dist/chunks/{reserved-CUkk9XgL.mjs → reserved-DxB5CROj.mjs} +73 -38
- package/dist/chunks/{review-BaCXlIT2.mjs → review-CKMSdvgW.mjs} +11 -9
- package/dist/chunks/{steering-DtUVUz9l.mjs → steering-CLhu8C7q.mjs} +262 -52
- package/dist/chunks/{tail-CX6Pdsql.mjs → tail-QZc7Uz2U.mjs} +1 -1
- package/dist/gtd.bundle.mjs +198 -43
- package/dist/steering.mjs +2 -2
- package/dist/testkit.mjs +1 -1
- package/dist/web/main.css +1 -1
- package/dist/web/main.js +510 -147
- package/dist/workflow-kit.mjs +1 -1
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +228 -222
- 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 +570 -317
- package/package.json +5 -4
- package/src/steering/FORMATS.md +4 -1
- package/src/steering/SteeringFormat.ts +7 -1
- package/src/steering/contract.test.ts +2 -1
- package/src/steering/fixtures/review/sectioned.md +31 -0
- package/src/steering/fixtures/review/sectioned.read.json +119 -0
- package/src/steering/index.ts +4 -0
- package/src/steering/qa.ts +1 -1
- package/src/steering/review.test.ts +270 -4
- package/src/steering/review.ts +328 -69
- package/src/testkit/steps.ts +13 -0
- package/src/workflows/feature/acceptance-criteria.feature +159 -3
- package/src/workflows/feature/continuation-cap.feature +4 -0
- package/src/workflows/feature/continuation-rebuild.feature +4 -0
- package/src/workflows/feature/continuation-slices.feature +4 -0
- package/src/workflows/feature/continuation.feature +4 -0
- package/src/workflows/feature/criteria.test.ts +60 -1
- package/src/workflows/feature/criteria.ts +87 -8
- package/src/workflows/feature/fast-path.feature +5 -6
- package/src/workflows/feature/feature.test.ts +6 -5
- package/src/workflows/feature/feature.ts +1 -2
- package/src/workflows/feature/flag-mode.feature +5 -6
- package/src/workflows/feature/green.ts +22 -0
- package/src/workflows/feature/hands-on-skip.feature +384 -0
- package/src/workflows/feature/lens-models.feature +4 -4
- package/src/workflows/feature/lens-review.feature +10 -8
- package/src/workflows/feature/naive-acceptance.feature +4 -0
- package/src/workflows/feature/one-step-land.feature +5 -6
- package/src/workflows/feature/planning.ts +2 -1
- package/src/workflows/feature/prototype-rounds.feature +5 -1
- package/src/workflows/feature/prototype.feature +18 -9
- package/src/workflows/feature/prototype.ts +48 -17
- package/src/workflows/feature/refactor-absorbs.feature +7 -8
- package/src/workflows/feature/refactor-first.feature +7 -8
- package/src/workflows/feature/refactor-lens-models.feature +4 -0
- package/src/workflows/feature/refactor-rebuild.feature +11 -12
- package/src/workflows/feature/refactor-review.feature +72 -72
- package/src/workflows/feature/review-answers.feature +94 -5
- package/src/workflows/feature/review-gate.feature +23 -53
- package/src/workflows/feature/reviewer-walkthrough.feature +33 -12
- package/src/workflows/feature/risk-tier.feature +32 -0
- package/src/workflows/feature/seam-rejection.feature +9 -6
- package/src/workflows/feature/seam.feature +9 -6
- package/src/workflows/feature/seam.test.ts +5 -3
- package/src/workflows/feature/seam.ts +2 -3
- package/src/workflows/feature/severity-budget.feature +11 -7
- package/src/workflows/feature/severity-sorted-review.feature +54 -54
- package/src/workflows/feature/slice-findings.feature +9 -15
- package/src/workflows/feature/slicing.feature +36 -24
- package/src/workflows/feature/standards.feature +4 -4
- package/src/workflows/feature/tier-pin.feature +4 -0
- package/src/workflows/shared/review.ts +26 -6
- package/src/workflows/shared/text.test.ts +85 -11
- package/src/workflows/shared/text.ts +95 -42
- package/src/workflows/feature/coverage.feature +0 -165
- package/src/workflows/feature/coverage.ts +0 -120
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
import { changes, human, read, refuse, scope, vars } from "../../flows/index.js"
|
|
1
|
+
import { answered, changes, human, judge, read, refuse, scope, vars } from "../../flows/index.js"
|
|
2
2
|
import { HEADING, markFenced } from "../shared/fences.js"
|
|
3
3
|
import { coder, PROTOTYPE, REQUIREMENTS } from "../shared/steps.js"
|
|
4
4
|
import * as t from "../shared/text.js"
|
|
5
5
|
import { defaults } from "../shared/vars.js"
|
|
6
6
|
import type { Guard } from "../shared/health.js"
|
|
7
|
-
import { green } from "./
|
|
8
|
-
import {
|
|
7
|
+
import { green } from "./green.js"
|
|
8
|
+
import {
|
|
9
|
+
criteriaOf,
|
|
10
|
+
criterionIds,
|
|
11
|
+
handsOnIds,
|
|
12
|
+
requireCriteriaFrozen,
|
|
13
|
+
sliceIds,
|
|
14
|
+
type CriteriaSlice,
|
|
15
|
+
} from "./criteria.js"
|
|
16
|
+
|
|
17
|
+
/** Only a `no` at this or above skips a criterion: no verdict, or an unsure one, sends it to the hands-on test. */
|
|
18
|
+
const UNOBSERVABLE_MIN_P = 0.5
|
|
9
19
|
|
|
10
20
|
/** The `prototypeRounds` setting. Anything but a non-negative integer fails rather than guess a cap. */
|
|
11
21
|
export const prototypeRounds = (): number => {
|
|
@@ -55,11 +65,11 @@ export const reportOf = (text: string): Report => {
|
|
|
55
65
|
|
|
56
66
|
/**
|
|
57
67
|
* The report the testing turn left, refused unless it is the only change and
|
|
58
|
-
* names
|
|
59
|
-
*
|
|
68
|
+
* names each of `ids` once, with its open items: any other criterion is not
|
|
69
|
+
* this test's to fix.
|
|
60
70
|
*/
|
|
61
71
|
const requireReport = (
|
|
62
|
-
|
|
72
|
+
ids: readonly string[],
|
|
63
73
|
retest: boolean,
|
|
64
74
|
): { readonly unguided: boolean; readonly open: readonly Item[] } => {
|
|
65
75
|
const others = changes()
|
|
@@ -79,27 +89,45 @@ const requireReport = (
|
|
|
79
89
|
// Only a first test with nothing else in it: a chunk beside it was tested after all, and a
|
|
80
90
|
// re-test's open items must reach a person, not slip past as untested.
|
|
81
91
|
if (report.unguided && report.items.length === 0 && !retest) return { unguided: true, open: [] }
|
|
82
|
-
const
|
|
83
|
-
const ids = criterionIds(requirements, slice)
|
|
84
|
-
const elsewhere = criterionIds(requirements).filter((id) => !ids.includes(id))
|
|
92
|
+
const elsewhere = criterionIds(read(REQUIREMENTS) ?? "").filter((id) => !ids.includes(id))
|
|
85
93
|
const count = (id: string): number => report.items.filter((i) => i.subject === id).length
|
|
86
94
|
const wrong = ids.filter((id) => count(id) !== 1)
|
|
87
95
|
if (wrong.length > 0)
|
|
88
96
|
refuse(
|
|
89
97
|
`gtd land: prototype-report: ${PROTOTYPE} needs exactly one chunk headed \`## <ID>: pass|fail|blocked\` for ${wrong.join(", ")}`,
|
|
90
98
|
)
|
|
91
|
-
// A mistyped ID stays open: only
|
|
99
|
+
// A mistyped ID stays open: only a known criterion outside `ids` is not this test's to fix.
|
|
92
100
|
const open = report.items.filter((i) => i.verdict !== "pass" && !elsewhere.includes(i.subject))
|
|
93
101
|
return { unguided: false, open }
|
|
94
102
|
}
|
|
95
103
|
|
|
104
|
+
/**
|
|
105
|
+
* The slice's criteria a person can observe by using the change, by the
|
|
106
|
+
* judge: those tagged `(no hands-on)` are never asked about.
|
|
107
|
+
*/
|
|
108
|
+
const observable = async (slice: CriteriaSlice | undefined): Promise<string[]> => {
|
|
109
|
+
const requirements = read(REQUIREMENTS) ?? ""
|
|
110
|
+
const own = handsOnIds(requirements, slice)
|
|
111
|
+
const criteria = (criteriaOf(requirements) ?? []).filter((c) => own.includes(c.id))
|
|
112
|
+
if (criteria.length === 0) return []
|
|
113
|
+
const { answers } = await judge("observable", {
|
|
114
|
+
questions: criteria.map(t.observableQuestion),
|
|
115
|
+
evidence: { requirements },
|
|
116
|
+
message: t.observableMessage(),
|
|
117
|
+
label: "Judging which criteria to test by hand",
|
|
118
|
+
})
|
|
119
|
+
return own.filter((id) => !answered(answers[id], "no", UNOBSERVABLE_MIN_P))
|
|
120
|
+
}
|
|
121
|
+
|
|
96
122
|
const frozen = (): void => requireCriteriaFrozen(REQUIREMENTS)
|
|
97
123
|
|
|
98
124
|
const testing = async (
|
|
99
125
|
slice: CriteriaSlice | undefined,
|
|
126
|
+
ids: readonly string[],
|
|
100
127
|
retest: boolean,
|
|
101
128
|
): Promise<ReturnType<typeof requireReport>> => {
|
|
102
|
-
|
|
129
|
+
const skipped = criterionIds(read(REQUIREMENTS) ?? "", slice).filter((id) => !ids.includes(id))
|
|
130
|
+
await t.agentWithSkills("testing", t.prototypeTestingPrompt(sliceIds(slice), skipped, retest), {
|
|
103
131
|
label: retest ? "Re-testing the slice by hand" : "Testing the slice by hand",
|
|
104
132
|
file: PROTOTYPE,
|
|
105
133
|
model: coder(),
|
|
@@ -107,7 +135,7 @@ const testing = async (
|
|
|
107
135
|
// A test that saw exactly what the last round saw leaves the report as it was.
|
|
108
136
|
allowEmpty: true,
|
|
109
137
|
})
|
|
110
|
-
return requireReport(
|
|
138
|
+
return requireReport(ids, retest)
|
|
111
139
|
}
|
|
112
140
|
|
|
113
141
|
const fixing = async (slice: CriteriaSlice | undefined, guard?: Guard): Promise<void> => {
|
|
@@ -138,10 +166,11 @@ const askPerson = async (name: string, message: string, label: string): Promise<
|
|
|
138
166
|
}
|
|
139
167
|
|
|
140
168
|
/**
|
|
141
|
-
* Use the slice by hand: a testing turn reports on every criterion
|
|
142
|
-
* takes each failure, and the slice is tested again —
|
|
143
|
-
* clean, or `prototypeRounds` fixes are spent and a person
|
|
144
|
-
*
|
|
169
|
+
* Use the slice by hand: a testing turn reports on every criterion a person
|
|
170
|
+
* can observe, a fix turn takes each failure, and the slice is tested again —
|
|
171
|
+
* until the report is clean, or `prototypeRounds` fixes are spent and a person
|
|
172
|
+
* decides. A slice with no such criterion is not tested by hand. `guard` holds
|
|
173
|
+
* every fix turn, as it holds the implementation's.
|
|
145
174
|
*/
|
|
146
175
|
export const prototypeTest = async (
|
|
147
176
|
slice: CriteriaSlice | undefined,
|
|
@@ -150,10 +179,12 @@ export const prototypeTest = async (
|
|
|
150
179
|
const cap = prototypeRounds()
|
|
151
180
|
if (cap === 0) return
|
|
152
181
|
await scope("prototype", async () => {
|
|
182
|
+
const ids = await observable(slice)
|
|
183
|
+
if (ids.length === 0) return
|
|
153
184
|
let fixes = 0
|
|
154
185
|
let retest = false
|
|
155
186
|
for (;;) {
|
|
156
|
-
const { unguided, open } = await testing(slice, retest)
|
|
187
|
+
const { unguided, open } = await testing(slice, ids, retest)
|
|
157
188
|
retest = !unguided
|
|
158
189
|
if (unguided) {
|
|
159
190
|
const instructed = await askPerson(
|
|
@@ -37,6 +37,10 @@ Feature: a refactor that carried the whole feature still gets a quality lap over
|
|
|
37
37
|
|
|
38
38
|
- `subtract(a, b)` returns `a - b`.
|
|
39
39
|
|
|
40
|
+
## Impact
|
|
41
|
+
|
|
42
|
+
- the greeting module
|
|
43
|
+
|
|
40
44
|
## Acceptance Criteria
|
|
41
45
|
|
|
42
46
|
- AC-1: the change behaves as described.
|
|
@@ -96,9 +100,9 @@ Feature: a refactor that carried the whole feature still gets a quality lap over
|
|
|
96
100
|
|
|
97
101
|
<!-- base: 0000000 -->
|
|
98
102
|
|
|
99
|
-
##
|
|
103
|
+
## Walkthrough
|
|
100
104
|
|
|
101
|
-
|
|
105
|
+
### calc
|
|
102
106
|
|
|
103
107
|
- [x] ./src/calc.ts#1-2
|
|
104
108
|
subtract next to add
|
|
@@ -109,10 +113,6 @@ Feature: a refactor that carried the whole feature still gets a quality lap over
|
|
|
109
113
|
"""
|
|
110
114
|
And step "implement.health.check" changes nothing
|
|
111
115
|
And step "implement.closing" runs its script
|
|
112
|
-
And judge step "implement.coverage" answers:
|
|
113
|
-
"""
|
|
114
|
-
[{"id":"AC-1","answer":"yes","p":0.9}]
|
|
115
|
-
"""
|
|
116
116
|
When gtd lands
|
|
117
117
|
And the process is driven
|
|
118
118
|
Then the process rests at "build.quality.correctness.reviewing"
|
|
@@ -128,8 +128,7 @@ Feature: a refactor that carried the whole feature still gets a quality lap over
|
|
|
128
128
|
gtd(human): refactor.build.review.await-review → refactor.build.review.closing
|
|
129
129
|
gtd(check): refactor.build.review.closing → implement.building
|
|
130
130
|
gtd(agent): implement.building → implement.health.check
|
|
131
|
-
gtd(check): implement.health.check → implement.
|
|
132
|
-
gtd(judge): implement.coverage → implement.closing
|
|
131
|
+
gtd(check): implement.health.check → implement.closing
|
|
133
132
|
gtd(check): implement.closing → build.quality.correctness.reviewing
|
|
134
133
|
gtd(agent): build.quality.correctness.reviewing → build.review.reviewing
|
|
135
134
|
"""
|
|
@@ -36,6 +36,10 @@ Feature: a refactor-first judgment walks one fixed path to the review gate
|
|
|
36
36
|
|
|
37
37
|
- `subtract(a, b)` returns `a - b`.
|
|
38
38
|
|
|
39
|
+
## Impact
|
|
40
|
+
|
|
41
|
+
- the greeting module
|
|
42
|
+
|
|
39
43
|
## Acceptance Criteria
|
|
40
44
|
|
|
41
45
|
- AC-1: the change behaves as described.
|
|
@@ -100,9 +104,9 @@ Feature: a refactor-first judgment walks one fixed path to the review gate
|
|
|
100
104
|
|
|
101
105
|
<!-- base: 0000000 -->
|
|
102
106
|
|
|
103
|
-
##
|
|
107
|
+
## Walkthrough
|
|
104
108
|
|
|
105
|
-
|
|
109
|
+
### calc
|
|
106
110
|
|
|
107
111
|
- [x] ./src/calc.ts#1-2
|
|
108
112
|
add built on the shared op
|
|
@@ -124,10 +128,6 @@ Feature: a refactor-first judgment walks one fixed path to the review gate
|
|
|
124
128
|
"""
|
|
125
129
|
And step "implement.health.check" changes nothing
|
|
126
130
|
And step "implement.closing" runs its script
|
|
127
|
-
And judge step "implement.coverage" answers:
|
|
128
|
-
"""
|
|
129
|
-
[{"id":"AC-1","answer":"yes","p":0.9}]
|
|
130
|
-
"""
|
|
131
131
|
When the process is driven
|
|
132
132
|
Then the process rests at "build.review.reviewing"
|
|
133
133
|
And "src/calc.ts" contains "op(a, b, 1)"
|
|
@@ -153,8 +153,7 @@ Feature: a refactor-first judgment walks one fixed path to the review gate
|
|
|
153
153
|
gtd(human): refactor.build.review.await-review → refactor.build.review.closing
|
|
154
154
|
gtd(check): refactor.build.review.closing → implement.building
|
|
155
155
|
gtd(agent): implement.building → implement.health.check
|
|
156
|
-
gtd(check): implement.health.check → implement.
|
|
157
|
-
gtd(judge): implement.coverage → implement.closing
|
|
156
|
+
gtd(check): implement.health.check → implement.closing
|
|
158
157
|
gtd(check): implement.closing → build.review.reviewing
|
|
159
158
|
"""
|
|
160
159
|
And the commit "gtd(human): idle → unwind" has a body containing "Gtd-Var: flag_risk_tier=0"
|
|
@@ -37,6 +37,10 @@ Feature: refactor-first writes a refactor note, refactors on its own, then rebui
|
|
|
37
37
|
|
|
38
38
|
- `subtract(a, b)` returns `a - b`.
|
|
39
39
|
|
|
40
|
+
## Impact
|
|
41
|
+
|
|
42
|
+
- the greeting module
|
|
43
|
+
|
|
40
44
|
## Acceptance Criteria
|
|
41
45
|
|
|
42
46
|
- AC-1: subtract returns the difference.
|
|
@@ -110,9 +114,9 @@ Feature: refactor-first writes a refactor note, refactors on its own, then rebui
|
|
|
110
114
|
|
|
111
115
|
<!-- base: 0000000 -->
|
|
112
116
|
|
|
113
|
-
##
|
|
117
|
+
## Walkthrough
|
|
114
118
|
|
|
115
|
-
|
|
119
|
+
### calc
|
|
116
120
|
|
|
117
121
|
- [x] ./src/calc.ts#1-2
|
|
118
122
|
add built on the shared op
|
|
@@ -181,9 +185,9 @@ Feature: refactor-first writes a refactor note, refactors on its own, then rebui
|
|
|
181
185
|
|
|
182
186
|
<!-- base: 0000000 -->
|
|
183
187
|
|
|
184
|
-
##
|
|
188
|
+
## Walkthrough
|
|
185
189
|
|
|
186
|
-
|
|
190
|
+
### calc
|
|
187
191
|
|
|
188
192
|
- [x] ./src/calc.ts#1-2
|
|
189
193
|
add built on the shared op
|
|
@@ -234,9 +238,9 @@ Feature: refactor-first writes a refactor note, refactors on its own, then rebui
|
|
|
234
238
|
|
|
235
239
|
<!-- base: 0000000 -->
|
|
236
240
|
|
|
237
|
-
##
|
|
241
|
+
## Walkthrough
|
|
238
242
|
|
|
239
|
-
|
|
243
|
+
### calc
|
|
240
244
|
|
|
241
245
|
- [x] ./src/calc.ts#1-2
|
|
242
246
|
add built on the shared op
|
|
@@ -259,10 +263,6 @@ Feature: refactor-first writes a refactor note, refactors on its own, then rebui
|
|
|
259
263
|
"""
|
|
260
264
|
And step "implement.health.check" changes nothing
|
|
261
265
|
And step "implement.closing" runs its script
|
|
262
|
-
And judge step "implement.coverage" answers:
|
|
263
|
-
"""
|
|
264
|
-
[{"id":"AC-1","answer":"yes","p":0.9}]
|
|
265
|
-
"""
|
|
266
266
|
When the process is driven
|
|
267
267
|
Then the process rests at "build.review.reviewing"
|
|
268
268
|
And "src/calc.ts" contains "op(a, b, -1)"
|
|
@@ -282,8 +282,7 @@ Feature: refactor-first writes a refactor note, refactors on its own, then rebui
|
|
|
282
282
|
gtd(human): refactor.build.review.await-review → refactor.build.review.closing
|
|
283
283
|
gtd(check): refactor.build.review.closing → implement.building
|
|
284
284
|
gtd(agent): implement.building → implement.health.check
|
|
285
|
-
gtd(check): implement.health.check → implement.
|
|
286
|
-
gtd(judge): implement.coverage → implement.closing
|
|
285
|
+
gtd(check): implement.health.check → implement.closing
|
|
287
286
|
gtd(check): implement.closing → build.review.reviewing
|
|
288
287
|
"""
|
|
289
288
|
And the output lines of gtd "slices" start with:
|