@pmelab/gtd 39.0.0 → 41.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.
Files changed (37) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +57 -55
  3. package/dist/chunks/{reserved-B6tTYjeb.mjs → reserved-Ch4LLrEj.mjs} +15 -9
  4. package/dist/chunks/{tail-_CZTP7Hu.mjs → tail-lY1UmJrU.mjs} +1 -1
  5. package/dist/workflow-kit.mjs +29 -11
  6. package/dist/workflows/cleanup.mjs +2 -2
  7. package/dist/workflows/feature.mjs +98 -37
  8. package/dist/workflows/fix.mjs +2 -2
  9. package/dist/workflows/review.mjs +2 -2
  10. package/dist/workflows/rollout.mjs +1 -1
  11. package/dist/workflows.mjs +140 -55
  12. package/package.json +1 -1
  13. package/skills/authoring/SKILL.md +3 -2
  14. package/src/workflow-kit/health.ts +42 -13
  15. package/src/workflows/feature/coverage.feature +5 -5
  16. package/src/workflows/feature/criteria.test.ts +20 -1
  17. package/src/workflows/feature/criteria.ts +24 -0
  18. package/src/workflows/feature/fast-path.feature +4 -4
  19. package/src/workflows/feature/feature.ts +82 -22
  20. package/src/workflows/feature/flag-mode.feature +2 -2
  21. package/src/workflows/feature/one-step-land.feature +4 -4
  22. package/src/workflows/feature/planning.ts +3 -2
  23. package/src/workflows/feature/quality-unresolved.feature +6 -6
  24. package/src/workflows/feature/refactor-absorbs.feature +3 -3
  25. package/src/workflows/feature/refactor-first.feature +5 -5
  26. package/src/workflows/feature/refactor-rebuild.feature +2 -2
  27. package/src/workflows/feature/refactor-review.feature +8 -8
  28. package/src/workflows/feature/review-gate.feature +271 -0
  29. package/src/workflows/feature/seam-rejection.feature +4 -4
  30. package/src/workflows/feature/seam.feature +2 -2
  31. package/src/workflows/feature/seam.ts +17 -7
  32. package/src/workflows/feature/slicing.feature +63 -4
  33. package/src/workflows/shared/health.test.ts +30 -0
  34. package/src/workflows/shared/health.ts +12 -9
  35. package/src/workflows/shared/progress.feature +124 -0
  36. package/src/workflows/shared/text.ts +7 -5
  37. package/src/workflows/review/review-door.feature +0 -121
@@ -9,13 +9,16 @@ import {
9
9
  slice,
10
10
  start,
11
11
  } from "../../flows/index.js"
12
- import { gate, healthy } from "../shared/health.js"
12
+ import { gate, healthy, type EscalationCount } from "../shared/health.js"
13
13
  import { flagModeOn, shipped } from "../shared/options.js"
14
14
  import { FEEDBACK, fixCheck, QUALITY, REQUIREMENTS } from "../shared/steps.js"
15
15
  import { refusePendingHandOff, tail } from "../shared/tail.js"
16
16
  import * as t from "../shared/text.js"
17
17
  import {
18
+ criterionIds,
18
19
  requireCriteriaFrozen,
20
+ requireStableIds,
21
+ type StableIds,
19
22
  skipDesign,
20
23
  sliceIds,
21
24
  statedCriteria,
@@ -23,8 +26,8 @@ import {
23
26
  type StatedCriteria,
24
27
  } from "./criteria.js"
25
28
  import { design } from "./planning.js"
26
- import { buildTail, isCode, reUnwind, type ReviewOutcome } from "./review.js"
27
- import { closeOut, implement, seamBuild } from "./seam.js"
29
+ import { buildTail, isCode, reUnwind, review, type ReviewOutcome } from "./review.js"
30
+ import { implement, implementing, seamBuild } from "./seam.js"
28
31
  import { sliceCap, sliceCriteria, slicing } from "./slicing.js"
29
32
  import { recordRisk, riskTierOn, type RiskTier } from "./tier.js"
30
33
 
@@ -33,7 +36,9 @@ import { recordRisk, riskTierOn, type RiskTier } from "./tier.js"
33
36
  // green-baseline gate → design → per slice of the criteria, a naive pass,
34
37
  // judged with `flagMode` on: it lands as the implementation, or (refactoring
35
38
  // first when the judge asks) is built fresh from → the health loop → the
36
- // quality lap, and the episode ends back at `idle`. Human review is the `review` door's.
39
+ // quality lap → after the last slice, human review of the whole change, which
40
+ // signs off (the episode ends back at `idle`) or sends a design → implement
41
+ // lap.
37
42
 
38
43
  /** Revert the sketch that started the process out of the working tree; its intent survives in history. */
39
44
  export const unwind = (): Promise<void> => {
@@ -52,40 +57,54 @@ export { reUnwind }
52
57
  export const buildFeature = async (firstBase: string): Promise<void> => {
53
58
  const tier = riskTierOn() ? recordRisk() : undefined
54
59
  const slices = await slicing()
60
+ // Every ID handed out, gathered before a later narrowing can drop one: none is ever reused.
61
+ const handedOut = new Set(listedIds())
55
62
  // A single slice keeps the unnumbered names and builds against every criterion.
56
63
  const one = slices.length === 1
64
+ // A steer may delete every criterion of a later slice: there is nothing to build there.
65
+ const built = (i: number): boolean => sliceIds(sliceCriteria(slices, i))?.length !== 0
57
66
  for (const i of slices.keys()) {
58
- const more = i < slices.length - 1
59
- const criteria = sliceCriteria(slices, i)
60
- // A steer may have deleted every criterion of a later slice: nothing to build.
61
- if (sliceIds(criteria)?.length === 0) {
62
- if (!more) await closeOut()
63
- continue
64
- }
67
+ if (!built(i)) continue
65
68
  const n = one ? "" : `-${i + 1}`
66
69
  await buildSlice(i === 0 ? firstBase : head(), {
67
70
  tier,
68
- criteria,
71
+ criteria: sliceCriteria(slices, i),
69
72
  refactor: `refactor${n}`,
70
73
  feature: `feature${n}`,
71
- more,
74
+ // A steer or a narrowing may empty every later slice.
75
+ more: () => slices.some((_, j) => j > i && built(j)),
76
+ // Every slice but the one is built unreviewed, so the review covers them all.
77
+ reviewBase: one ? undefined : firstBase,
78
+ handedOut,
72
79
  })
73
80
  }
74
81
  }
75
82
 
83
+ const listedIds = (): readonly string[] => criterionIds(read(REQUIREMENTS) ?? "")
84
+
85
+ const gather = (handedOut: Set<string>): readonly string[] => {
86
+ for (const id of listedIds()) handedOut.add(id)
87
+ return [...handedOut]
88
+ }
89
+
76
90
  interface SliceBuild {
77
91
  readonly tier: RiskTier | undefined
78
92
  readonly criteria: CriteriaSlice | undefined
79
93
  readonly refactor: string
80
94
  readonly feature: string
81
- readonly more: boolean
95
+ readonly more: () => boolean
96
+ /** Absent: the slice's own base. */
97
+ readonly reviewBase?: string | undefined
98
+ /** Every criterion ID handed out so far; the slice adds those a steer adds. */
99
+ readonly handedOut: Set<string>
82
100
  }
83
101
 
84
- /** A naive pass (judged only with `flagMode` on), then implement and the quality lap. */
102
+ /** A naive pass (judged only with `flagMode` on), then implement and the quality lap; after the last slice, the review. */
85
103
  const buildSlice = async (sliceBase: string, build: SliceBuild): Promise<void> => {
86
104
  const seam = await seamBuild(sliceBase, { criteria: build.criteria, refactor: build.refactor })
105
+ const used = gather(build.handedOut)
87
106
  await slice(build.feature, async () => {
88
- await implement({
107
+ const closed = await implementing({
89
108
  reference: seam.reference,
90
109
  tier: build.tier,
91
110
  afterRefactor: seam.refactored,
@@ -95,6 +114,8 @@ const buildSlice = async (sliceBase: string, build: SliceBuild): Promise<void> =
95
114
  base: build.criteria === undefined ? undefined : sliceBase,
96
115
  more: build.more,
97
116
  })
117
+ // Closing kept the criteria exactly when there is more to build, so this reads as it did.
118
+ const more = build.more()
98
119
  // A feature slice with no code beyond the carried-over tests: the refactor carried the
99
120
  // feature, so the lap covers the whole slice.
100
121
  const carried = seam.rebuild?.acceptance?.tests ?? []
@@ -103,14 +124,19 @@ const buildSlice = async (sliceBase: string, build: SliceBuild): Promise<void> =
103
124
  !changesSince(seam.base).some((c) => isCode(c.path) && !carried.includes(c.path))
104
125
  ? sliceBase
105
126
  : seam.base
106
- // The criteria stay in the tree for the next slice; no quality fix may move them.
107
- const guard = build.more ? () => requireCriteriaFrozen(REQUIREMENTS) : undefined
108
- await tail(settleQuality, { green: true, lensBase: base, guard, guardRests: false })
127
+ if (more) {
128
+ // The criteria stay in the tree for the next slice; no quality fix may move them.
129
+ const guard = () => requireCriteriaFrozen(REQUIREMENTS)
130
+ await tail(settleQuality, { green: true, lensBase: base, guard, guardRests: false })
131
+ return
132
+ }
133
+ const reviewBase = build.reviewBase ?? base
134
+ await featureFeedback(await reviewedTail(reviewBase, base), base, { closed, used })
109
135
  })
110
136
  }
111
137
 
112
- // The lap is feature's last look: findings the fix cap left must not end the process, nor reach
113
- // the next one's lap, which appends to the file. Empty counts as resolved, as the lap reads it.
138
+ // The lap's findings the fix cap left must not reach the review, nor the next process's lap,
139
+ // which appends to the file. Empty counts as resolved, as the lap reads it.
114
140
  const settleQuality = async (): Promise<void> => {
115
141
  let rested = false
116
142
  while ((read(QUALITY) ?? "").length > 0) {
@@ -121,10 +147,44 @@ const settleQuality = async (): Promise<void> => {
121
147
  file: QUALITY,
122
148
  })
123
149
  }
124
- // The person may have fixed code by hand there; nothing else checks it before idle.
150
+ // The person may have fixed code by hand there; nothing else checks it before the review.
125
151
  if (rested) await healthy(() => fixCheck())
126
152
  }
127
153
 
154
+ /** The quality lap, then the human review since `base`. */
155
+ const reviewedTail = (base: string, lensBase: string, lap?: "feedback"): Promise<ReviewOutcome> =>
156
+ tail(
157
+ async (escalations: EscalationCount) => {
158
+ await settleQuality()
159
+ return review(base, escalations, { lap })
160
+ },
161
+ { green: true, lensBase },
162
+ )
163
+
164
+ // seam.naive / seam.judge never run on a feedback lap: its design → implement is built unjudged
165
+ // and unrefactored, held to every criterion's coverage since the process start.
166
+ const featureFeedback = async (
167
+ first: ReviewOutcome,
168
+ lensBase: string,
169
+ ids: StableIds,
170
+ ): Promise<void> => {
171
+ let outcome = first
172
+ let closed = ids.closed
173
+ const handedOut = new Set([...ids.used, ...ids.closed])
174
+ while (outcome.verdict === "feedback") {
175
+ await reUnwind(outcome)
176
+ const lap = { closed, used: [...handedOut] }
177
+ // Gathered every round, so an ID one round adds and the next drops is never reused.
178
+ await design(outcome.base, () => {
179
+ requireStableIds(REQUIREMENTS, lap)
180
+ gather(handedOut)
181
+ })
182
+ const tier = riskTierOn() ? recordRisk() : undefined
183
+ closed = await implementing({ tier, amend: true })
184
+ outcome = await reviewedTail(outcome.base, lensBase, "feedback")
185
+ }
186
+ }
187
+
128
188
  /** Design — skipped when the sketch's own criteria are judged clear — then `buildFeature`. */
129
189
  export const planAndBuild = async (firstBase: string, stated?: StatedCriteria): Promise<void> => {
130
190
  // GTD_FLAG_RISK_TIER: the tier is proposed at triage and set at the design gate, so no skip.
@@ -67,7 +67,7 @@ Feature: flag mode off lands the naive pass with no release flag, seam judge or
67
67
  """
68
68
  And step "build.quality.correctness.reviewing" changes nothing
69
69
  When the process is driven
70
- Then the process rests at "idle"
70
+ Then the process rests at "build.review.reviewing"
71
71
  And "src/calc.ts" contains "subtract"
72
72
  And "src/calc.test.ts" contains "[AC-1] subtracts"
73
73
  And the commit subjects end with:
@@ -85,5 +85,5 @@ Feature: flag mode off lands the naive pass with no release flag, seam judge or
85
85
  gtd(check): implement.health.check → implement.coverage
86
86
  gtd(judge): implement.coverage → implement.closing
87
87
  gtd(check): implement.closing → build.quality.correctness.reviewing
88
- gtd(agent): build.quality.correctness.reviewing → idle
88
+ gtd(agent): build.quality.correctness.reviewing → build.review.reviewing
89
89
  """
@@ -5,7 +5,7 @@ Feature: a naive pass the judge rules land is itself the implementation
5
5
  With GTD_FLAG_RISK_TIER off, a naive pass that
6
6
  edits an existing source file is judged land: the process skips the approval
7
7
  gate and any refactor, applies the kept pass as it is — no build turn — and
8
- runs the health loop and quality back to idle, with no review. The flag is pinned here so the per-flag test runs never
8
+ runs the health loop and quality lap to the feature review. The flag is pinned here so the per-flag test runs never
9
9
  move it off this path.
10
10
 
11
11
  Background:
@@ -62,9 +62,9 @@ Feature: a naive pass the judge rules land is itself the implementation
62
62
  """
63
63
  And step "build.quality.correctness.reviewing" changes nothing
64
64
 
65
- Scenario: a land judgment applies the naive pass and runs idle to idle with no rebuild
65
+ Scenario: a land judgment applies the naive pass and runs from idle to the review gate with no rebuild
66
66
  When the process is driven
67
- Then the process rests at "idle"
67
+ Then the process rests at "build.review.reviewing"
68
68
  And "src/greeting.ts" contains "Hello, world"
69
69
  And "src/acceptance.test.ts" contains "[AC-1] holds"
70
70
  And the commit subjects end with:
@@ -81,7 +81,7 @@ Feature: a naive pass the judge rules land is itself the implementation
81
81
  gtd(check): implement.health.check → implement.coverage
82
82
  gtd(judge): implement.coverage → implement.closing
83
83
  gtd(check): implement.closing → build.quality.correctness.reviewing
84
- gtd(agent): build.quality.correctness.reviewing → idle
84
+ gtd(agent): build.quality.correctness.reviewing → build.review.reviewing
85
85
  """
86
86
  And the commit "gtd(human): idle → unwind" has a body containing "Gtd-Var: flag_risk_tier=0"
87
87
  And the commit "gtd(agent): seam.naive → seam.check" has a body containing "Gtd-Kept:"
@@ -25,13 +25,14 @@ const noteGate = async (file: string, answer: () => Promise<void>): Promise<bool
25
25
  return true
26
26
  }
27
27
 
28
- /** Triage the sketch since `base` into requirements, until no product question is open. */
29
- export const design = (base: string): Promise<void> =>
28
+ /** Triage the sketch since `base` into requirements, until no product question is open; `guard` holds every round's criteria. */
29
+ export const design = (base: string, guard?: () => void): Promise<void> =>
30
30
  scope("design", async () => {
31
31
  do {
32
32
  await triage(base)
33
33
  requireProgress(REQUIREMENTS)
34
34
  requireReplies(REQUIREMENTS)
35
35
  requireCriteria(REQUIREMENTS)
36
+ guard?.()
36
37
  } while (await noteGate(REQUIREMENTS, answerProductQuestions))
37
38
  })
@@ -1,9 +1,9 @@
1
1
  @inmem
2
- Feature: quality findings left unresolved hold a feature process short of idle
2
+ Feature: quality findings left unresolved hold a feature process short of its review
3
3
 
4
- The quality lap is the last look `feature` takes. When the fix cap leaves
5
- `.gtd/QUALITY.md` behind, the process rests for a person instead of ending
6
- with findings nobody acted on.
4
+ When the quality lap's fix cap leaves `.gtd/QUALITY.md` behind, the process
5
+ rests for a person instead of handing findings nobody acted on to the
6
+ human review.
7
7
 
8
8
  Background:
9
9
  Given the shipped workflows, as gtd install scaffolds them
@@ -97,10 +97,10 @@ Feature: quality findings left unresolved hold a feature process short of idle
97
97
  And step "build.health.check" changes nothing
98
98
  When gtd lands
99
99
  And the process is driven
100
- Then the process rests at "idle"
100
+ Then the process rests at "build.review.reviewing"
101
101
  And the commit subjects end with:
102
102
  """
103
103
  gtd(check): build.health.check → build.quality-unresolved
104
104
  gtd(human): build.quality-unresolved → build.health.check
105
- gtd(check): build.health.check → idle
105
+ gtd(check): build.health.check → build.review.reviewing
106
106
  """
@@ -42,7 +42,7 @@ Feature: a refactor that carried the whole feature still gets a quality lap over
42
42
  """
43
43
  And step "design.gate.answer" changes nothing
44
44
 
45
- Scenario: a tests-only implement turn after a refactor that did it all reaches the quality lap and idle
45
+ Scenario: a tests-only implement turn after a refactor that did it all reaches the quality lap and the review
46
46
  Given step "seam.check" changes nothing
47
47
  And step "seam.naive" writes "src/acceptance.test.ts" with:
48
48
  """
@@ -113,7 +113,7 @@ Feature: a refactor that carried the whole feature still gets a quality lap over
113
113
 
114
114
  Given step "build.quality.correctness.reviewing" changes nothing
115
115
  When the process is driven
116
- Then the process rests at "idle"
116
+ Then the process rests at "build.review.reviewing"
117
117
  And "src/calc.ts" contains "subtract"
118
118
  And the commit subjects end with:
119
119
  """
@@ -123,7 +123,7 @@ Feature: a refactor that carried the whole feature still gets a quality lap over
123
123
  gtd(check): implement.health.check → implement.coverage
124
124
  gtd(judge): implement.coverage → implement.closing
125
125
  gtd(check): implement.closing → build.quality.correctness.reviewing
126
- gtd(agent): build.quality.correctness.reviewing → idle
126
+ gtd(agent): build.quality.correctness.reviewing → build.review.reviewing
127
127
  """
128
128
  And the output lines of gtd "slices" start with:
129
129
  """
@@ -1,10 +1,10 @@
1
1
  # One SDLC path per file, so a changed path reds exactly one fixture — do not merge.
2
2
  @inmem
3
- Feature: a refactor-first judgment walks one fixed path to idle
3
+ Feature: a refactor-first judgment walks one fixed path to the review gate
4
4
 
5
5
  With GTD_FLAG_RISK_TIER off, a naive pass touching
6
6
  an existing non-allowlisted file is ruled refactor-first, is refactored and
7
- that refactor reviewed, and only then implemented, with no feature review. The flags are
7
+ that refactor reviewed, and only then implemented and brought to the feature review. The flags are
8
8
  pinned so the per-flag test runs never move it off its path.
9
9
 
10
10
  Background:
@@ -41,7 +41,7 @@ Feature: a refactor-first judgment walks one fixed path to idle
41
41
  """
42
42
  And step "design.gate.answer" changes nothing
43
43
 
44
- Scenario: a refactor-first judgment runs refactor, its review, implement and review to idle
44
+ Scenario: a refactor-first judgment runs refactor, its review, implement and the quality lap to the feature review
45
45
  Given step "seam.check" changes nothing
46
46
  And step "seam.naive" writes "src/acceptance.test.ts" with:
47
47
  """
@@ -121,7 +121,7 @@ Feature: a refactor-first judgment walks one fixed path to idle
121
121
  [{"id":"AC-1","answer":"yes","p":0.9}]
122
122
  """
123
123
  When the process is driven
124
- Then the process rests at "idle"
124
+ Then the process rests at "build.review.reviewing"
125
125
  And "src/calc.ts" contains "op(a, b, 1)"
126
126
  And "src/calc.ts" contains "subtract"
127
127
  And the commit subjects end with:
@@ -145,7 +145,7 @@ Feature: a refactor-first judgment walks one fixed path to idle
145
145
  gtd(agent): implement.building → implement.health.check
146
146
  gtd(check): implement.health.check → implement.coverage
147
147
  gtd(judge): implement.coverage → implement.closing
148
- gtd(check): implement.closing → idle
148
+ gtd(check): implement.closing → build.review.reviewing
149
149
  """
150
150
  And the commit "gtd(human): idle → unwind" has a body containing "Gtd-Var: flag_risk_tier=0"
151
151
  And the commit "gtd(agent): seam.naive → seam.check" has a body containing "Gtd-Kept:"
@@ -242,7 +242,7 @@ Feature: refactor-first writes a refactor note, refactors on its own, then rebui
242
242
  [{"id":"AC-1","answer":"yes","p":0.9}]
243
243
  """
244
244
  When the process is driven
245
- Then the process rests at "idle"
245
+ Then the process rests at "build.review.reviewing"
246
246
  And "src/calc.ts" contains "op(a, b, -1)"
247
247
  And "src/acceptance.test.ts" contains "[AC-1]"
248
248
  And ".gtd/REFACTOR-NOTE.md" does not exist
@@ -260,7 +260,7 @@ Feature: refactor-first writes a refactor note, refactors on its own, then rebui
260
260
  gtd(agent): implement.building → implement.health.check
261
261
  gtd(check): implement.health.check → implement.coverage
262
262
  gtd(judge): implement.coverage → implement.closing
263
- gtd(check): implement.closing → idle
263
+ gtd(check): implement.closing → build.review.reviewing
264
264
  """
265
265
  And the output lines of gtd "slices" start with:
266
266
  """
@@ -4,7 +4,7 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
4
4
  After a refactor-first judgment the refactor is built, quality-lapped and
5
5
  reviewed in its own slice. Review edits re-run the refactor, never the design;
6
6
  the feature slice that follows is quality-lapped from the refactor's
7
- sign-off, with no feature review. The flags are pinned so the per-flag test runs never move it off
7
+ sign-off, then reviewed. The flags are pinned so the per-flag test runs never move it off
8
8
  its path.
9
9
 
10
10
  Background:
@@ -428,7 +428,7 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
428
428
  Then the process rests at "refactor.build.review.await-review"
429
429
  When gtd lands
430
430
  And the process is driven
431
- Then the process rests at "idle"
431
+ Then the process rests at "build.review.reviewing"
432
432
 
433
433
  Scenario: a question answered at the refactor review reaches the re-derived package as ### Answered Questions
434
434
  When the process is driven
@@ -727,7 +727,7 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
727
727
  """
728
728
  When gtd lands
729
729
  And the process is driven
730
- Then the process rests at "idle"
730
+ Then the process rests at "build.review.reviewing"
731
731
  And "src/calc.ts" contains "subtract"
732
732
 
733
733
  Scenario: a praise-only one-shot footnote signs off and the feature slice follows
@@ -769,7 +769,7 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
769
769
  """
770
770
  When gtd lands
771
771
  And the process is driven
772
- Then the process rests at "idle"
772
+ Then the process rests at "build.review.reviewing"
773
773
  And "src/calc.ts" contains "subtract"
774
774
 
775
775
  Scenario: a one-shot footnote judged edit that collecting leaves out refuses naming its anchor
@@ -1084,7 +1084,7 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
1084
1084
  """
1085
1085
  When gtd lands
1086
1086
  And the process is driven
1087
- Then the process rests at "idle"
1087
+ Then the process rests at "build.review.reviewing"
1088
1088
  And "src/calc.ts" contains "subtract"
1089
1089
 
1090
1090
  Scenario: a praise-only refactor review still signs off and the feature slice follows
@@ -1119,7 +1119,7 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
1119
1119
  """
1120
1120
  When gtd lands
1121
1121
  And the process is driven
1122
- Then the process rests at "idle"
1122
+ Then the process rests at "build.review.reviewing"
1123
1123
  And "src/calc.ts" contains "subtract"
1124
1124
 
1125
1125
  Scenario: a nit fix in the refactor slice that changes a non-stray is refused, though a glob stray widens the write access
@@ -1309,11 +1309,11 @@ Feature: the refactor slice is reviewed on its own before the feature is impleme
1309
1309
  And the resting step's content contains the hash of the commit "gtd(check): refactor.build.review.closing → implement.building"
1310
1310
  Given step "build.quality.correctness.reviewing" changes nothing
1311
1311
  When the process is driven
1312
- Then the process rests at "idle"
1312
+ Then the process rests at "build.review.reviewing"
1313
1313
  And the commit subjects end with:
1314
1314
  """
1315
1315
  gtd(check): implement.closing → build.quality.correctness.reviewing
1316
- gtd(agent): build.quality.correctness.reviewing → idle
1316
+ gtd(agent): build.quality.correctness.reviewing → build.review.reviewing
1317
1317
  """
1318
1318
 
1319
1319
  Rule: a refactor package never carries a test file, and demoted headings stop at ######