@pmelab/gtd 50.0.0 → 51.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 +32 -24
- package/dist/chunks/{reserved-CdOzKFVX.mjs → reserved-CUkk9XgL.mjs} +93 -4
- package/dist/chunks/{review-BChXEl2Y.mjs → review-BaCXlIT2.mjs} +2 -2
- package/dist/chunks/{tail-DRM2eC1z.mjs → tail-CX6Pdsql.mjs} +1 -1
- package/dist/gtd.bundle.mjs +26 -25
- package/dist/testkit.mjs +26 -25
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +144 -11
- package/dist/workflows/fix.mjs +2 -2
- package/dist/workflows/review.mjs +3 -3
- package/dist/workflows/rollout.mjs +1 -1
- package/dist/workflows.mjs +234 -12
- package/package.json +1 -1
- package/src/edge/Edge.ts +1 -30
- package/src/replay/Replay.test.ts +4 -8
- package/src/replay/Replay.ts +35 -3
- package/src/replay/index.ts +7 -1
- 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 +1 -0
- 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 +1 -0
- package/src/workflows/feature/review-gate.feature +1 -0
- package/src/workflows/feature/reviewer-walkthrough.feature +1 -0
- 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 +1 -0
- package/src/workflows/feature/severity-sorted-review.feature +1 -0
- package/src/workflows/feature/slice-findings.feature +1 -0
- package/src/workflows/feature/slicing-cap.feature +9 -0
- package/src/workflows/feature/slicing.feature +1 -0
- package/src/workflows/feature/standards.feature +1 -0
- package/src/workflows/shared/options.ts +1 -0
- package/src/workflows/shared/prose.ts +6 -0
- 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.ts +104 -3
- package/src/workflows/shared/vars.ts +3 -0
package/src/edge/Edge.ts
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
isRecordableApproval,
|
|
38
38
|
parseCommitMessage,
|
|
39
39
|
replay,
|
|
40
|
+
scopeRunStart,
|
|
40
41
|
sliceRanges,
|
|
41
42
|
treeFromRecord,
|
|
42
43
|
type EpisodeCommit,
|
|
@@ -791,36 +792,6 @@ const stepDefOf = (step: ReachedStep): StepDef => {
|
|
|
791
792
|
}
|
|
792
793
|
}
|
|
793
794
|
|
|
794
|
-
/** Is `name` inside `scope`'s subtree? The root scope `""` holds everything. */
|
|
795
|
-
const inScope = (name: string, scope: string): boolean =>
|
|
796
|
-
scope === "" || name === scope || name.startsWith(`${scope}.`)
|
|
797
|
-
|
|
798
|
-
/**
|
|
799
|
-
* Where the current unbroken run of rests inside `scope`'s subtree began —
|
|
800
|
-
* a dip into a descendant scope does not break it, a sibling or ancestor
|
|
801
|
-
* does, and so does a fresh `scope()` call of the same scope.
|
|
802
|
-
*/
|
|
803
|
-
const scopeRunStart = (trace: readonly ReachedStep[], scope: string): number => {
|
|
804
|
-
let start = -1
|
|
805
|
-
for (let k = 0; k < trace.length; k++) {
|
|
806
|
-
if (!inScope(trace[k]!.memoryScope, scope)) continue
|
|
807
|
-
const previous = trace[k - 1]
|
|
808
|
-
const unbroken =
|
|
809
|
-
previous !== undefined &&
|
|
810
|
-
inScope(previous.memoryScope, scope) &&
|
|
811
|
-
scopeCallOf(previous, scope) === scopeCallOf(trace[k]!, scope)
|
|
812
|
-
if (!unbroken) start = k
|
|
813
|
-
}
|
|
814
|
-
return start
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
/** The innermost `scope()` call enclosing `scope` at `step` — a new call is a new conversation. */
|
|
818
|
-
const scopeCallOf = (step: ReachedStep, scope: string): number | undefined => {
|
|
819
|
-
let call: number | undefined
|
|
820
|
-
for (const entry of step.scopeCalls) if (inScope(scope, entry.prefix)) call = entry.call
|
|
821
|
-
return call
|
|
822
|
-
}
|
|
823
|
-
|
|
824
795
|
// Shown for the root scope, which has no name of its own.
|
|
825
796
|
const ROOT_MEMORY_SCOPE_NAME = "root"
|
|
826
797
|
|
|
@@ -940,7 +940,7 @@ describe("replay: skills resolution", () => {
|
|
|
940
940
|
expect(await wireSkills(inScope({ name: "a" }, step), { skills: { a: [] } })).toBeUndefined()
|
|
941
941
|
})
|
|
942
942
|
|
|
943
|
-
it("
|
|
943
|
+
it("runs two scope() calls of one memory scope with different lists as two conversations", async () => {
|
|
944
944
|
const flow: Flow = async () => {
|
|
945
945
|
await scope({ name: "a", skills: ["one"] }, async () => {
|
|
946
946
|
await agent("first", "prompt")
|
|
@@ -959,9 +959,7 @@ describe("replay: skills resolution", () => {
|
|
|
959
959
|
start: hashOf(0),
|
|
960
960
|
budgetBytes: 1024,
|
|
961
961
|
})
|
|
962
|
-
expect(outcome.kind
|
|
963
|
-
"different model, system prompt, skills or file access",
|
|
964
|
-
)
|
|
962
|
+
expect(outcome.kind).toBe("rest")
|
|
965
963
|
})
|
|
966
964
|
})
|
|
967
965
|
|
|
@@ -1084,7 +1082,7 @@ describe("replay: access resolution", () => {
|
|
|
1084
1082
|
expect(outcome.rest.access).toEqual({ read: null, write: ["src/a.ts"] })
|
|
1085
1083
|
})
|
|
1086
1084
|
|
|
1087
|
-
it("
|
|
1085
|
+
it("runs two scope() calls of one memory scope with different access as two conversations", async () => {
|
|
1088
1086
|
const flow: Flow = async () => {
|
|
1089
1087
|
await scope({ name: "a", access: { write: ["one"] } }, () => agent("first", "prompt"))
|
|
1090
1088
|
await scope({ name: "a", access: { write: ["two"] } }, () => agent("second", "prompt"))
|
|
@@ -1092,9 +1090,7 @@ describe("replay: access resolution", () => {
|
|
|
1092
1090
|
const history = new History()
|
|
1093
1091
|
history.land("agent", "a.first", 1, "a.second", { "x.txt": "x" })
|
|
1094
1092
|
const outcome = await restOf(flow, {}, history)
|
|
1095
|
-
expect(outcome.kind
|
|
1096
|
-
"different model, system prompt, skills or file access",
|
|
1097
|
-
)
|
|
1093
|
+
expect(outcome.kind).toBe("rest")
|
|
1098
1094
|
})
|
|
1099
1095
|
|
|
1100
1096
|
it("does not conflict two steps that differ only in steering file", async () => {
|
package/src/replay/Replay.ts
CHANGED
|
@@ -139,6 +139,36 @@ const memoryScopeOf = (name: string): string => {
|
|
|
139
139
|
return dot === -1 ? "" : name.slice(0, dot)
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
/** Is `name` inside `scope`'s subtree? The root scope `""` holds everything. */
|
|
143
|
+
const inScope = (name: string, scope: string): boolean =>
|
|
144
|
+
scope === "" || name === scope || name.startsWith(`${scope}.`)
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Where the current unbroken run of rests inside `scope`'s subtree began —
|
|
148
|
+
* a dip into a descendant scope does not break it, a sibling or ancestor
|
|
149
|
+
* does, and so does a fresh `scope()` call of the same scope.
|
|
150
|
+
*/
|
|
151
|
+
export const scopeRunStart = (trace: readonly ReachedStep[], scope: string): number => {
|
|
152
|
+
let start = -1
|
|
153
|
+
for (let k = 0; k < trace.length; k++) {
|
|
154
|
+
if (!inScope(trace[k]!.memoryScope, scope)) continue
|
|
155
|
+
const previous = trace[k - 1]
|
|
156
|
+
const unbroken =
|
|
157
|
+
previous !== undefined &&
|
|
158
|
+
inScope(previous.memoryScope, scope) &&
|
|
159
|
+
scopeCallOf(previous, scope) === scopeCallOf(trace[k]!, scope)
|
|
160
|
+
if (!unbroken) start = k
|
|
161
|
+
}
|
|
162
|
+
return start
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** The innermost `scope()` call enclosing `scope` at `step` — a new call is a new conversation. */
|
|
166
|
+
const scopeCallOf = (step: ReachedStep, scope: string): number | undefined => {
|
|
167
|
+
let call: number | undefined
|
|
168
|
+
for (const entry of step.scopeCalls) if (inScope(scope, entry.prefix)) call = entry.call
|
|
169
|
+
return call
|
|
170
|
+
}
|
|
171
|
+
|
|
142
172
|
class Stop extends Error {}
|
|
143
173
|
|
|
144
174
|
const short = (hash: string): string => hash.slice(0, 7)
|
|
@@ -505,14 +535,16 @@ export const replay = async (input: ReplayInput): Promise<ReplayOutcome> => {
|
|
|
505
535
|
skills: step.skills,
|
|
506
536
|
access: unfoldedAccess.get(step),
|
|
507
537
|
}
|
|
508
|
-
|
|
538
|
+
// Keyed like the memory key: a fresh scope() call is a fresh conversation, free to differ.
|
|
539
|
+
const conversation = `${step.memoryScope}#${scopeRunStart(trace, step.memoryScope)}`
|
|
540
|
+
const seen = personas.get(conversation)
|
|
509
541
|
if (seen === undefined) {
|
|
510
|
-
personas.set(
|
|
542
|
+
personas.set(conversation, identity)
|
|
511
543
|
return undefined
|
|
512
544
|
}
|
|
513
545
|
return samePersona(seen, identity)
|
|
514
546
|
? undefined
|
|
515
|
-
: `gtd: "${step.name}" runs with a different model, system prompt, skills or file access than an earlier agent step in memory scope "${step.memoryScope || "root"}" —
|
|
547
|
+
: `gtd: "${step.name}" runs with a different model, system prompt, skills or file access than an earlier agent step in its conversation in memory scope "${step.memoryScope || "root"}" — a conversation has one identity`
|
|
516
548
|
}
|
|
517
549
|
|
|
518
550
|
const DIVERGED = "the workflow changed under this process; run `gtd abandon` to start over"
|
package/src/replay/index.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {
|
|
2
|
+
replay,
|
|
3
|
+
scopeRunStart,
|
|
4
|
+
type EpisodeCommit,
|
|
5
|
+
type ReachedStep,
|
|
6
|
+
type ReplayOutcome,
|
|
7
|
+
} from "./Replay.js"
|
|
2
8
|
export { treeFromRecord, type TreeView } from "./Tree.js"
|
|
3
9
|
export {
|
|
4
10
|
formatCommitMessage,
|
|
@@ -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:
|
|
@@ -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
|
+
"""
|