@boopkit/cap-workflows 0.2.3 → 0.2.4
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/package.json +1 -1
- package/skills/boop-architecture-audit-workflow/SKILL.md +5 -5
- package/skills/boop-feature-dev-workflow/SKILL.md +80 -32
- package/skills/boop-light-dev-workflow/SKILL.md +18 -2
- package/skills/boop-refactor-dev-workflow/SKILL.md +98 -19
- package/skills/boop-test-audit-workflow/SKILL.md +17 -2
package/package.json
CHANGED
|
@@ -137,16 +137,16 @@ For the work that is genuinely new:
|
|
|
137
137
|
- **One child per structural change, not one per call site.** Enumerate-and-patch is the failure that [`boop-refactor-dev-workflow`](../boop-refactor-dev-workflow/SKILL.md) exists to prevent, and it starts at decomposition time.
|
|
138
138
|
- **Carry the method on every child.** Each child must state the go-red discipline explicitly: delete the old shape first, commit the red build, follow the compiler. An agent picking up "split this interface" without that instruction will keep the build green by leaving a fallback, which is the exact failure this is meant to prevent.
|
|
139
139
|
- **Carry the named change too**, so the executor can tell a finished refactor from a rearranged one.
|
|
140
|
-
- **Name the skills on every child, and name only their names.** Step 8 says each child runs under [`boop-refactor-dev-workflow`](../boop-refactor-dev-workflow/SKILL.md) — but the executing session never reads this workflow, only the issue body, so that pointer reaches it exactly when the child says so and never otherwise. Put `boop-refactor-dev-workflow` on every child, plus the skill for the layer the structural change touches (services, controllers, data model, view) and `boop-testing-testing` for the real-path proof. Names, not extracts: the go-red method in the bullet above is copied deliberately, because a fallback that survives into the branch cannot be corrected afterwards, and that is the one exception. Everything else stays in its skill so there is a single copy to keep current.
|
|
140
|
+
- **Name the skills on every child, and name only their names.** Step 8 says each child runs under [`boop-refactor-dev-workflow`](../boop-refactor-dev-workflow/SKILL.md) — but the executing session never reads this workflow, only the issue body, so that pointer reaches it exactly when the child says so and never otherwise. Put `boop-refactor-dev-workflow` on every child, plus the skill for the layer the structural change touches (services, controllers, data model, view) and `boop-testing-testing` for the real-path proof. Write them as the skills list that `boop-project-mgmt-beads-planning` sets out in its section "Breadcrumb the skills that carry the discipline": a `## Skills` heading, the line "Before making changes, read each of these in full.", and a path to each skill's `SKILL.md`, because a session opens a skill named only in a bare list of names about one time in seven. Names, not extracts: the go-red method in the bullet above is copied deliberately, because a fallback that survives into the branch cannot be corrected afterwards, and that is the one exception. Everything else stays in its skill so there is a single copy to keep current.
|
|
141
141
|
- **Settle open decisions before the work starts, not during.** A choice like which validation library, or where a declaration lives, belongs in the issue body marked as decided, with the reasoning. An executor who has to pick one mid-implementation will pick silently.
|
|
142
142
|
- **Include a review child** that checks each change against its named change, not merely that the issue closed.
|
|
143
143
|
- **Put the test run scope on every child.**
|
|
144
|
-
|
|
145
|
-
Left unstated, a child that has just changed a widely called function runs the whole suite on its own reasoning
|
|
146
|
-
The rule and the wording to
|
|
144
|
+
Each child runs the focused tests for what it touches, plus the project's whole-program static check; the full suite runs once per branch, on the review child above.
|
|
145
|
+
Left unstated, a child that has just changed a widely called function runs the whole suite on its own reasoning.
|
|
146
|
+
The rule and the wording to put on each are in the section "Test run scope: what each task runs, and what runs once" in `boop-project-mgmt-beads-planning`.
|
|
147
147
|
- **Do not create the feature branch.** The loop creates the worktree and its `work/<epic-id>` branch when it takes the first child.
|
|
148
148
|
|
|
149
|
-
- **Audit the issue bodies before the session ends, then revise them.** Writing an issue and auditing it are separate passes, and doing both at once produces bodies that read well to their author and cost their reader time. Once every child
|
|
149
|
+
- **Audit the issue bodies before the session ends, then revise them.** Writing an issue and auditing it are separate passes, and doing both at once produces bodies that read well to their author and cost their reader time. Once every child exists, re-read them all side by side as the agent who will claim each one cold, following the section "Write, audit and revise in three separate turns" in `boop-project-mgmt-beads-planning`, then rewrite whatever the audit finds. Some faults show only side by side: a child that says "a sibling issue handles this" without naming it, or a term the container defines and a child then uses bare.
|
|
150
150
|
|
|
151
151
|
### 8. Execute, verify, close out
|
|
152
152
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: boop-feature-dev-workflow
|
|
3
|
-
description: "When you are tasked with building a feature end-to-end, follow this
|
|
3
|
+
description: "When you are tasked with building a feature end-to-end, follow this beads-issue-per-step workflow: epic root → understand → plan → decompose → implement → review → punch-list → closeout → reconcile-and-finalize."
|
|
4
4
|
metadata:
|
|
5
5
|
category: workflow
|
|
6
6
|
---
|
|
@@ -20,7 +20,7 @@ When you are tasked with building a feature end-to-end, follow this guidance.
|
|
|
20
20
|
> fails. Refactors done to make a feature buildable run first, as their own unit; the feature then
|
|
21
21
|
> follows on this workflow.
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## Relevant skills
|
|
24
24
|
|
|
25
25
|
### Step zero — before claiming any task
|
|
26
26
|
- `boop-skills-discovery` — when you pick up a non-trivial task, before claiming it: the discovery backstop. An issue's breadcrumbs are the primary way the right skills reach you, but an issue can miss one. Before claiming any task in this workflow, run the discovery sweep so a forgotten breadcrumb does not cost you the discipline that skill carries.
|
|
@@ -37,7 +37,7 @@ When you are tasked with building a feature end-to-end, follow this guidance.
|
|
|
37
37
|
- `boop-database-data-model` — when you need to design data models.
|
|
38
38
|
- `boop-web-web-ui-design` — when you need to design web UIs.
|
|
39
39
|
- `boop-ux-purpose-judgement` — when the feature includes new or substantially modified UI surface: the structural lens. Does each affordance belong, in the right shape, for the right job? Run during Plan against the spec; run during Review against a screenshot of the built page.
|
|
40
|
-
- `boop-ux-visual-judgement` — when the feature includes new or substantially modified UI surface: the taste lens (eye-path,
|
|
40
|
+
- `boop-ux-visual-judgement` — when the feature includes new or substantially modified UI surface: the taste lens (eye-path, loudness, anatomy, scan-line). Run during Review against a screenshot of the built page; optional during Plan against a nearby existing surface for loudness calibration only.
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
### Coding
|
|
@@ -71,6 +71,15 @@ Documents are organized into
|
|
|
71
71
|
|
|
72
72
|
The loop runs without a live operator. The patterns below follow from that and apply to every task in the workflow.
|
|
73
73
|
|
|
74
|
+
### Keep working until the Done-when is true
|
|
75
|
+
|
|
76
|
+
A task is finished when every one of its Done-when conditions holds, checked against the artifact the condition names: the call site in the code, the archived spec, the rendered page.
|
|
77
|
+
"I performed the listed steps" and "the tests are green" are the two weaker stopping points an agent drifts to, and neither is the bar.
|
|
78
|
+
If a condition cannot be met, register the wait as the "Waiting protocol" below says, with a blocking edge or a human gate, rather than closing on partial work.
|
|
79
|
+
|
|
80
|
+
This is the same discipline Review applies afterwards, when it verifies each closed slice against its Done-when by name (step 4).
|
|
81
|
+
Review is the second net, not the only one.
|
|
82
|
+
|
|
74
83
|
### Respect safety mechanisms; do not override them
|
|
75
84
|
|
|
76
85
|
Tools sometimes expose safety mechanisms for destructive operations. Examples include consent environment variables like `PRISMA_USER_CONSENT_FOR_DANGEROUS_AI_ACTION`, a required `--force` flag, an interactive "are you sure?" prompt, or a warning that the command emits before it runs. These mechanisms exist because the action behind them was deemed to require explicit human approval before it could be considered safe to run.
|
|
@@ -113,7 +122,7 @@ You leave `bd ready` because of the edge, and you return to it automatically whe
|
|
|
113
122
|
Two constraints, both easy to get wrong:
|
|
114
123
|
|
|
115
124
|
- **The edge must point at a sibling or an unrelated issue — never at your own ancestor or your own descendant.** An unresolved edge on a parent holds *every* descendant out of `bd ready` at every depth, so gating a container on its own children deadlocks those children. (bd rejects ancestor/descendant gating edges outright.)
|
|
116
|
-
- **Use `blocks`, not `waits-for`.** In bd 1.1.2 `waits-for`
|
|
125
|
+
- **Use `blocks`, not `waits-for`.** In bd 1.1.2 a `waits-for` edge added with `bd dep add -t waits-for` releases when the *first* child of its target closes, not all of them — unsafe against any container with more than one child. (`bd dep add --help` does not list `waits-for`, but bd accepts it, as it accepts any string after `-t`; see `boop-project-mgmt-beads-planning` for the measured types.) Use `blocks`, one edge per thing waited on.
|
|
117
126
|
|
|
118
127
|
**Waiting on a human decision** — a build-or-defer call, a safety mechanism that needs operator consent, a spec ambiguity only the operator can resolve. Create a **human gate** and stay `open`:
|
|
119
128
|
|
|
@@ -164,7 +173,7 @@ Create tasks as child issues of the feature issue. The descriptions below are th
|
|
|
164
173
|
2. **Plan the work**: Next task (don't *do* this work, this is the content of the task issue): **Start by reading `specs/{feature-name}-plan-findings.md`** from the "understand" task (path is in this task's breadcrumbs; also check `bd show <predecessor-id>` notes). The findings contain gap analysis and open questions that must inform the plan. Then find the feature implementation plan document, or create one in `specs/`. Follow the engineering planning skill. Ensure the plan covers the spec; create follow-up tasks for any gaps.
|
|
165
174
|
|
|
166
175
|
**UX paired-lens pass (when the feature includes new or substantially modified UI surface):** If the spec touches the app's UI surface, run a paired-lens pass during planning so the plan and downstream punch-list carry explicit UI work and acceptance criteria. The two cases are not symmetric:
|
|
167
|
-
- **Brand-new surface (the page does not exist yet):** run `boop-ux-purpose-judgement` against the spec, reference docs, and any existing nearby templates that anchor the
|
|
176
|
+
- **Brand-new surface (the page does not exist yet):** run `boop-ux-purpose-judgement` against the spec, reference docs, and any existing nearby templates that anchor how loud the surface should be. **Do not try to screenshot a not-yet-built surface** — the purpose lens has an explicit fallback for missing inputs; use it and label each finding by which inputs supported it. The output validates the spec's interaction-shapes table: each affordance traced to a job, mechanism matched against frequency × consequence, anything underspecified flagged as a plan or punch-list candidate. Optionally run `boop-ux-visual-judgement` against a nearby existing surface for **loudness calibration only** (output is "the calm/loud target the new surface should match," not findings about the unbuilt page); skip it if no nearby surface is a useful anchor.
|
|
168
177
|
- **Modified existing surface (the page exists and is being changed):** capture a screenshot of the current surface with `npm run ux:capture -- <path> <label>`, then run both lenses in order — purpose first, visual second.
|
|
169
178
|
Write the lens findings into the same `specs/{feature-name}-plan-findings.md`, labelling each with the lens that produced it (`[purpose]` or `[visual]`) and noting which inputs were loaded.
|
|
170
179
|
|
|
@@ -173,11 +182,24 @@ Create tasks as child issues of the feature issue. The descriptions below are th
|
|
|
173
182
|
**Every code-touching sub-task carries a `Tests` section and testing breadcrumbs.** This is how the workflow produces tasks that get tested correctly — author it when you write the sub-task, not later. Each sub-task that creates or changes code must include in its description:
|
|
174
183
|
- A **`Tests`** section naming: (a) the contracts the task creates or modifies — if it writes a function other code calls, or is itself a new caller of an existing function, name that producer/consumer boundary explicitly (e.g. *"this task calls `buildXyz`; exercise the caller's actual argument shape pairwise, not just the function's contract in isolation"*); (b) the test shapes required at this scope — unit for pure helpers, pairwise component where two pieces of code share a boundary, integration where data crosses a real boundary (DB, HTTP, serialization), route/render for surfaces; (c) any test scope deliberately deferred, with why. A `Tests` section that only says "tests pass" gives the implementing agent no signal about which shape the work needs, and the easy default (a unit test on whatever was written) misses contract bugs.
|
|
175
184
|
- **Breadcrumbs to the design/layer skill** for the code being written (controller, service, data-access, view) **and to all three testing skills** (general principles, Light TDD, the domain testing skill — see the Testing section above). The general-principles skill is the one most often dropped because a task "feels like just unit-testing" — which is exactly when pairwise contract tests get missed. List all three by default; drop one only if it is genuinely irrelevant.
|
|
185
|
+
A child's session starts cold and sees only its issue; it does not read this workflow.
|
|
186
|
+
Write them as the skills list that `boop-project-mgmt-beads-planning` sets out in its section "Breadcrumb the skills that carry the discipline": a `## Skills` heading, the line "Before making changes, read each of these in full.", and a path to each skill's `SKILL.md`.
|
|
187
|
+
Names, not extracts: a command or checklist copied out of a skill into an issue is a second copy of the method, and it drifts out of step with the skill.
|
|
176
188
|
- **The test run scope.**
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
189
|
+
Each sub-task runs the focused tests for what it touches, plus the project's whole-program static check; the full suite runs once per branch, on the Review task in step 4, so give Review that scope when you create it below.
|
|
190
|
+
Left unstated, a sub-task that has just changed a widely called function runs the whole suite on its own reasoning.
|
|
191
|
+
The rule and the wording to put on each are in the section "Test run scope: what each task runs, and what runs once" in `boop-project-mgmt-beads-planning`.
|
|
192
|
+
- **The live checks, scoped the way the tests are.**
|
|
193
|
+
A live check starts a running system and watches its real behaviour, such as an end-to-end run or a browser pass over a changed page, and each run costs minutes.
|
|
194
|
+
Beside its run scope, each slice names the live checks that exercise what it changed, and runs only those.
|
|
195
|
+
The complete set runs once, at Review, against the complete branch.
|
|
196
|
+
A spec that asks for each stage to be verified before the next starts is satisfied by each slice's own checks plus that one complete run.
|
|
197
|
+
Rerun the whole set at a stage boundary only when the spec gives a reason that stage needs it, such as a stage that ships on its own.
|
|
198
|
+
|
|
199
|
+
**Audit the child issue bodies, then revise them, before this task closes.**
|
|
200
|
+
Writing an issue and auditing it are separate passes, and doing both at once produces bodies that read well to their author and cost their reader time.
|
|
201
|
+
Once every child exists, re-read them all side by side as the agent who will claim each one cold, following the section "Write, audit and revise in three separate turns" in `boop-project-mgmt-beads-planning`, then rewrite whatever the audit finds.
|
|
202
|
+
Some faults show only side by side: a child that says "a sibling issue handles this" without naming it, or a term the container defines and a child then uses bare.
|
|
181
203
|
|
|
182
204
|
**THE TREE RECIPE — verified on bd 1.1.2.** Wire it exactly this way. The whole lifecycle was driven through a simulated loop against this shape, including a slice that decomposed itself mid-flight, and at every step the loop claimed exactly one correct issue: no leak, no deadlock, no stall.
|
|
183
205
|
|
|
@@ -225,7 +247,7 @@ Create tasks as child issues of the feature issue. The descriptions below are th
|
|
|
225
247
|
bd dep add <reconcile-id> <closeout-id>
|
|
226
248
|
```
|
|
227
249
|
|
|
228
|
-
**Every edge is plain `blocks`. Never `waits-for`** — in bd 1.1.2 `waits-for`
|
|
250
|
+
**Every edge is plain `blocks`. Never `waits-for`** — in bd 1.1.2 a `waits-for` edge added with `bd dep add` releases when the *first* child of its target closes, not all of them. See the Waiting protocol.
|
|
229
251
|
|
|
230
252
|
> **There is no rollup-gate leaf any more, and adding one back is a regression.** Earlier versions of this recipe created a childless "all slices complete" marker task for Review to depend on. It existed only because bd refuses gating edges to or from an **epic**, so an epic container could not be depended on directly. That restriction applies to **epics alone** — `task blocks-on feature` and `feature blocks-on task` are both **accepted**. Review can simply block on the slices. The gate was an extra node, an extra agent turn, and an extra thing to forget to close.
|
|
231
253
|
|
|
@@ -262,15 +284,15 @@ Create tasks as child issues of the feature issue. The descriptions below are th
|
|
|
262
284
|
- **It is the last link in one chain, so it cannot fire early.** Every lifecycle task is blocked-by the previous one, and Review is blocked-by every slice, so nothing downstream can run against partial work.
|
|
263
285
|
- **It is a sibling of the work, so it can wait on it without holding it.** Reconcile is a childless leaf child of the *root*, never a parent of any slice — an unresolved edge on a parent holds every descendant, so a gate placed above the work would deadlock it.
|
|
264
286
|
|
|
265
|
-
4. **Review the work done**: This task issue must be gated on **slice completion**, not on the Decompose task — Decompose only plans. Step 3 wires this: it removes any stale Review→Decompose dep and makes each slice block Review with a plain `blocks` edge (so Review fires only when *every* slice closes), and deliberately does **not** wire Review `waits-for` the do-the-work container, because `waits-for` releases on the first slice closing and hands Review a partial branch. (If this Review issue was pre-created with a dep on Decompose, step 3 is responsible for rewiring it to slice completion.) This task reviews all changes **in the feature branch** `work/<feature-issue-id>` (confirm with `git branch` or breadcrumbs).
|
|
287
|
+
4. **Review the work done**: This task issue must be gated on **slice completion**, not on the Decompose task — Decompose only plans. Step 3 wires this: it removes any stale Review→Decompose dep and makes each slice block Review with a plain `blocks` edge (so Review fires only when *every* slice closes), and deliberately does **not** wire Review `waits-for` the do-the-work container, because `waits-for` releases on the first slice closing and hands Review a partial branch. (If this Review issue was pre-created with a dep on Decompose, step 3 is responsible for rewiring it to slice completion.) This task reviews all changes **in the feature branch** `work/<feature-issue-id>` (confirm with `git branch` or breadcrumbs).
|
|
288
|
+
The slices are the children of the do-the-work container, so list them with `bd children <do-the-work-container-id>`; in the small shape, which has no container, they are the root's children.
|
|
289
|
+
In the epic shape, `bd children <root-id>` returns the container and the lifecycle tasks, not the slices.
|
|
266
290
|
|
|
267
|
-
**Precondition check before reviewing anything (forbid reviewing a partial branch, recurs):** Review must cover the *whole* feature, not whatever happens to be built. The slice-completion wiring in step 3 is meant to hold Review until every slice closes, but a slice further decomposed at implementation time — or a mis-wired dep — can still let Review go ready against a partial branch. Before reading any code,
|
|
291
|
+
**Precondition check before reviewing anything (forbid reviewing a partial branch, recurs):** Review must cover the *whole* feature, not whatever happens to be built. The slice-completion wiring in step 3 is meant to hold Review until every slice closes, but a slice further decomposed at implementation time — or a mis-wired dep — can still let Review go ready against a partial branch. Before reading any code, check the slices: list them as above, and `bd show <id>` on each, including any sub-slice a slice decomposed into during implementation (`bd children <slice-id>` shows them). Confirm **every** slice and sub-slice is `closed`. Any that is `open`, `in_progress`, or itself `blocked` means the branch is partial.
|
|
268
292
|
|
|
269
|
-
**
|
|
293
|
+
**Do not check the do-the-work container's own status.** It is an organising issue that nobody claims, and only Reconcile (step 7) closes it, long after Review has run. So on a finished branch the container is still `open`, and that is the healthy state; requiring it to be closed, or adding an edge onto it, would hold Review forever.
|
|
270
294
|
|
|
271
|
-
**
|
|
272
|
-
|
|
273
|
-
**If either check fails, stop and add a blocking edge for each still-open slice — do NOT scope the review down to the built slices and close, and do NOT set `status=blocked`.** Scoping down is precisely the failure this guard exists to prevent: it lets the largest slice ship with zero review pass. Review is waiting on the remaining slices to close, which happens on its own with no human input, so an edge is the right primitive — it holds Review out of `bd ready` and returns it automatically when the last slice closes (see "Waiting protocol" above). One edge per still-open slice ID from the failing check(s):
|
|
295
|
+
**If any slice is still open, stop and add a blocking edge for each one — do NOT scope the review down to the built slices and close, and do NOT set `status=blocked`.** Scoping down is precisely the failure this guard exists to prevent: it lets the largest slice ship with zero review pass. Review is waiting on the remaining slices to close, which happens on its own with no human input, so an edge is the right primitive — it holds Review out of `bd ready` and returns it automatically when the last slice closes (see "Waiting protocol" above). One edge per still-open slice ID:
|
|
274
296
|
```
|
|
275
297
|
bd dep add <this-review-task-id> <every-open-slice-id> -t blocks
|
|
276
298
|
bd update <this-review-task-id> --notes "Review cannot run against a partial branch; <which slices are still open, with specific IDs and statuses>. Blocking edges added; this returns to bd ready on its own once they close."
|
|
@@ -278,22 +300,39 @@ Create tasks as child issues of the feature issue. The descriptions below are th
|
|
|
278
300
|
Review is a **sibling** of the slices, never their parent, so these edges gate Review without holding the slices themselves. Do not review, do not write the review summary, do not file a punch-list, do not close this task. Only once every slice is closed does Review return to `bd ready` and the review below run — against the complete branch.
|
|
279
301
|
|
|
280
302
|
This task will:
|
|
303
|
+
- **Run the full test suite first, before reviewing anything.**
|
|
304
|
+
Every slice ran only its focused tests, so this is the first run that sees the complete branch.
|
|
305
|
+
Run it as one blocking call, as "Run long verification steps BLOCKING" above says, and read the summary rather than the exit code.
|
|
306
|
+
A red suite stops the review and becomes the first punch-list item; do not review code whose tests fail.
|
|
307
|
+
For a test that passes alone and fails in the full run, follow the `boop-testing-testing` skill's section "A suite that passes alone and fails in a full run is a suspect, not a verdict".
|
|
308
|
+
- **Then run each live check the spec's success criteria name, once.** This is the one place they all run together. A failing check becomes a punch-list item.
|
|
281
309
|
- perform a code review and test review of all changes in the feature branch (use available skills for code review, test review, and any other relevant review skills).
|
|
282
|
-
- **Verify each closed slice against its Done-when, by name.** For every closed sub-
|
|
310
|
+
- **Verify each closed slice against its Done-when, by name.** For every closed slice and sub-slice (listed as in the precondition check, then `bd show <id>` on each), read its Done-when / acceptance conditions and confirm **each condition is actually met on the branch** — not merely that the task is closed. A closed task only means an agent decided it was done; it does not mean the goal was reached. Check the real artifact each condition names: if a condition says a function is wired into a code path, confirm the call site exists in `src/` — not just that a test exercises the function. List any unmet Done-when condition as a punch-list item, citing the slice ID and the specific condition.
|
|
283
311
|
- write the review to `labs/claude-runs/{feature-issue-id}-review-summary.md`.
|
|
284
312
|
- append a punch-list of issues that must be addressed before merge.
|
|
285
313
|
|
|
286
314
|
**UX paired-lens pass (when the change touched UI files):** if `git diff main...HEAD -- views/ styles/ src/controllers/` shows changes, the surface needs a post-build UX review. The page now exists, so both lenses run normally against it:
|
|
287
315
|
|
|
288
|
-
1. Pick a port and confirm it is free before you start anything: `lsof -ti tcp:<port
|
|
316
|
+
1. Pick a port and confirm it is free before you start anything: `lsof -ti tcp:<port> -sTCP:LISTEN` prints nothing when the port is free. Then start the dev server in the feature worktree: `PORT=<port> npm --prefix <absolute-worktree-path> run dev` — the `--prefix` form is required; `cd <worktree> && npm run dev` silently falls back to the project root and screenshots the wrong build. If the port is already held, do not kill what is there and do not assume it serves your code — several worktrees and loops can run at once. Identify the holder with `lsof -a -d cwd -p <pid> -Fn` and reuse the server only when that directory is this feature's worktree; otherwise move to the next free port.
|
|
289
317
|
2. Capture each changed surface with `npm run ux:capture -- <path> <label>`, passing `BASE_URL=http://localhost:<port>`.
|
|
290
318
|
3. Run `boop-ux-purpose-judgement` first against the screenshot plus the rendered HTML, template, controller, and route. Run `boop-ux-visual-judgement` second against the screenshot.
|
|
291
|
-
4.
|
|
292
|
-
5.
|
|
319
|
+
4. Drive each changed surface against its slices' Done-when, as the section "Verifying a change someone will look at" in `boop-web-testing` describes when the project has the web capability: the lenses judge how the page reads, and that pass confirms a person gets what each issue promised.
|
|
320
|
+
5. Label findings by lens (`[purpose]`, `[visual]`) in `labs/claude-runs/{feature-issue-id}-review-summary.md`, alongside the code-review findings, and feed them into the punch-list the same way code-review findings do.
|
|
321
|
+
6. **Stop the dev server before this task closes**, unless you reused one you found already running.
|
|
322
|
+
Stop all of it, not only the process on the port.
|
|
323
|
+
A dev script that watches files runs a supervisor above the server, and only the server holds the port: boop's web-app profile runs `nodemon --exec tsx src/server.ts`, so killing the port holder leaves `nodemon` running while the port reads free, and `nodemon` starts the server again on the next file change.
|
|
324
|
+
Find the supervisor by the worktree's absolute path in its command line, `ps -eo pid=,args= | grep "<absolute-worktree-path>/node_modules/.bin/[n]odemon"`, with the dev script's own watcher in place of `nodemon` if it runs another, and kill that pid; the server under it stops too.
|
|
325
|
+
Then run that search again and `lsof -ti tcp:<port> -sTCP:LISTEN`, and confirm both print nothing, because a free port does not show that the supervisor is gone.
|
|
326
|
+
If the dev script runs no supervisor, the process on the port is the whole server, and `lsof -ti tcp:<port> -sTCP:LISTEN | xargs kill` stops it.
|
|
327
|
+
Never pattern-kill (`pkill -f nodemon`, `pkill -f tsx`, and the like): a pattern reaches every other worktree's server and the operator's own.
|
|
328
|
+
Never stop a server you did not start, or one a running loop owns.
|
|
293
329
|
|
|
294
330
|
**Why the stop belongs in this step, not at Closeout.** This pass runs once per surface-touching task, so a server left behind is not one stray process but one per surface. Deferring teardown to a distant Closeout is how orphaned watchers accumulate — each holding memory, none listening, none visible unless someone goes looking. The phase that starts a process is the phase that has to stop it.
|
|
295
331
|
|
|
296
332
|
5. **Add a punch-list work task**: Create a 'punch list' task issue as a child of the feature issue. Its goal: turn each item in the punch-list from the review document into a sub-task issue under this punch-list task.
|
|
333
|
+
**Those sub-tasks are built in the same run, before Closeout.**
|
|
334
|
+
Filing them is this task's job; the loop then picks each one up as ready work, and the edges below hold Closeout until every one has closed.
|
|
335
|
+
A run that files a punch list and stops has not finished the feature: it leaves the review's findings unaddressed on the branch the operator is asked to merge.
|
|
297
336
|
|
|
298
337
|
**Hierarchy rule:** Create punch-list items with `--parent <punch-list-task-id>` so they are true hierarchical children: `bd create "..." --parent <punch-list-task-id> ...`. `bd dep add` creates sequencing only — it is not a substitute for `--parent`. Sequencing deps between siblings are fine to add after creation.
|
|
299
338
|
|
|
@@ -301,7 +340,7 @@ Create tasks as child issues of the feature issue. The descriptions below are th
|
|
|
301
340
|
|
|
302
341
|
**Wire Closeout's dependency carefully (critical, recurs):** Closeout (step 6) must not run until the punch-list task *and all its children* are closed. The default `blocks` dep releases as soon as the punch-list task itself closes — which can happen while the just-filed punch-list fix children are still open, so Closeout would archive the spec and integrate reference docs against the *pre-fix* state.
|
|
303
342
|
|
|
304
|
-
**Use one plain `blocks` edge per thing you are waiting on. Never `waits-for`.** `waits-for` releases as soon as the *first* child of its target closes, not all of them — so with several fix children open, the first one closing would release Closeout against pre-fix state, the exact failure this step exists to prevent (
|
|
343
|
+
**Use one plain `blocks` edge per thing you are waiting on. Never `waits-for`.** `waits-for` releases as soon as the *first* child of its target closes, not all of them — so with several fix children open, the first one closing would release Closeout against pre-fix state, the exact failure this step exists to prevent (that is how a `waits-for` edge added with `bd dep add` behaves in bd 1.1.2). `blocks` is unambiguous — it resolves only when *that* issue closes — so gating on N issues means N edges. There is no cascade to rely on and none is needed.
|
|
305
344
|
|
|
306
345
|
**Critical order — do these steps IN ORDER, do not close Punch-list until step 4 has been verified:**
|
|
307
346
|
|
|
@@ -326,17 +365,18 @@ Create tasks as child issues of the feature issue. The descriptions below are th
|
|
|
326
365
|
|
|
327
366
|
**Precondition check before any closeout activity (defensive layer, recurs):** the activities below — archive the spec, integrate reference docs, close the issues — are procedural, and an agent will walk through them cleanly even when the actual work is not done. The dep-wiring race (see the callout at the end of step 5) can let this task become ready before implementation has run, or before the punch-list fix children have closed. Before taking any closeout activity, run both checks:
|
|
328
367
|
|
|
329
|
-
**Check 1 — implementation:**
|
|
368
|
+
**Check 1 — implementation:** list the slices as in step 4's precondition check, and confirm every slice and sub-slice is `closed`.
|
|
369
|
+
Check the slices, not the container: only Reconcile closes the container, and Reconcile is blocked by Closeout, so requiring the container to be closed here, or adding an edge onto it, leaves the two tasks waiting on each other.
|
|
330
370
|
|
|
331
371
|
**Check 2 — punch-list children:** `bd children <punch-list-task-id>` and confirm every child is `closed`. (Even if the punch-list task itself is closed, individual children can still be open — that means the Closeout dep wiring in step 5 was skipped or arrived too late, and the original `blocks` dep released prematurely.)
|
|
332
372
|
|
|
333
|
-
**If either check fails, stop and add a blocking edge for each still-open issue — do NOT set `status=blocked`.** Closeout is almost always waiting on the
|
|
373
|
+
**If either check fails, stop and add a blocking edge for each still-open issue — do NOT set `status=blocked`.** Closeout is almost always waiting on the remaining slices and/or the punch-list children to close, which happens on its own with no human input, so an edge holds it and returns it automatically (see "Waiting protocol" above). Setting the status instead freezes the task even after the cause clears. One edge per still-open issue ID from the failing check(s), pointed at the open slices and fix children themselves, never at the do-the-work container:
|
|
334
374
|
```
|
|
335
|
-
bd dep add <this-closeout-task-id> <
|
|
375
|
+
bd dep add <this-closeout-task-id> <every-open-slice-id> -t blocks
|
|
336
376
|
bd dep add <this-closeout-task-id> <every-open-punch-list-child-id> -t blocks
|
|
337
377
|
bd update <this-closeout-task-id> --notes "Closeout cannot run; <check failure summary, including specific issue IDs and statuses>. Blocking edges added; this returns to bd ready on its own once they close."
|
|
338
378
|
```
|
|
339
|
-
|
|
379
|
+
The slices sit under the container and the fix children under the punch-list, both siblings of Closeout, so these edges gate Closeout without holding any of that work. Never point such an edge at your own ancestor or descendant. (If the wait is instead a genuine operator decision — e.g. the feature cannot proceed without a human call — raise a **human gate** on this Closeout task instead, per the Waiting protocol; it will not auto-release.) Do not archive the spec, do not integrate reference docs, do not commit, do not close bd issues. Closeout returns to `bd ready` on its own once the edges resolve, or when the operator resolves the gate. Do not interpret a subsequent boop resume prompt as authorization to override this — the wait stands until its edges resolve or the operator confirms.
|
|
340
380
|
|
|
341
381
|
When (and only when) the checks pass, the closeout task performs two activities:
|
|
342
382
|
|
|
@@ -347,10 +387,14 @@ Create tasks as child issues of the feature issue. The descriptions below are th
|
|
|
347
387
|
- The archived spec and planning artifacts should be the full originals — do not edit or trim them.
|
|
348
388
|
|
|
349
389
|
**b. Integrate into reference:**
|
|
350
|
-
- Follow the `boop-product-design-reference-integration` skill
|
|
351
|
-
|
|
390
|
+
- Follow the `boop-product-design-reference-integration` skill. Its goal is that a reader with a question about one subject finds one page whose title names that subject; name the subjects from what actually shipped, and let the skill decide where each one goes.
|
|
391
|
+
Pages named in this task's description are where to start, not the limit.
|
|
392
|
+
- **Write this task's description as what was learned, not as pages to edit.** When you create the Closeout task, list the knowledge the feature produced: what now exists, what changed, and what was decided. Do not list the reference pages to update. A page list written before the work can only name pages that already existed, and the session that runs Closeout tends to treat it as the whole job, which is how a subject the feature created ends up buried in a page about something else.
|
|
352
393
|
- Update cross-links between affected reference pages.
|
|
353
394
|
- Remove or update any reference content that is now outdated.
|
|
395
|
+
- **Then decide, explicitly, whether the feature changed what a customer could truthfully be told.**
|
|
396
|
+
If the project keeps a capability catalog and the answer is yes, update it against the rule in the reference-integration skill's section "A capability catalog, if the project keeps one".
|
|
397
|
+
Otherwise record "no product-facing change" in the closing comment.
|
|
354
398
|
|
|
355
399
|
**c. Hand off — the loop ends at the feature branch:**
|
|
356
400
|
|
|
@@ -372,21 +416,23 @@ Create tasks as child issues of the feature issue. The descriptions below are th
|
|
|
372
416
|
|
|
373
417
|
**Step 1 — sweep the tree and branch.** Enumerate every descendant of the root (`bd children <root-id>`, recursing into containers) and read each one's status. Branch on what you find:
|
|
374
418
|
|
|
419
|
+
**An open container is not work in flight.** An open `epic` or `feature` that has children is an organising container, and only Step 2 below closes it, so judge it by its children, never by its own status. An unfinished slice still means a wait, pointed at the slice; an edge onto the container would wait for this task to close it, which never happens.
|
|
420
|
+
|
|
375
421
|
- **A descendant genuinely needs a human** (an operator decision, a safety mechanism awaiting consent). The feature is **not** done. Raise a **human gate** on *this* Reconcile task naming the held IDs, and stop:
|
|
376
422
|
```
|
|
377
423
|
bd gate create --type=human --blocks <reconcile-task-id> \
|
|
378
424
|
--reason="Cannot finalize; descendants <held issue IDs> need an operator decision. <summary>."
|
|
379
425
|
```
|
|
380
426
|
|
|
381
|
-
- **Real work is still in flight** (any descendant `open` or `in_progress`). Add a blocking edge onto each in-flight ID, and stop. Reconcile returns to `bd ready` on its own once they close — no monitoring pass required:
|
|
427
|
+
- **Real work is still in flight** (any descendant other than an organising container is `open` or `in_progress`). Add a blocking edge onto each in-flight ID, and stop. Reconcile returns to `bd ready` on its own once they close — no monitoring pass required:
|
|
382
428
|
```
|
|
383
429
|
bd dep add <reconcile-task-id> <each-in-flight-id> -t blocks
|
|
384
430
|
```
|
|
385
431
|
Reconcile is a childless leaf **child of the root**, and the in-flight work sits under the do-the-work container, so these edges point at issues that are neither its ancestors nor its descendants — which is what keeps them from deadlocking the work they wait on.
|
|
386
432
|
|
|
387
|
-
- **Every descendant is closed.** Proceed to Step 2.
|
|
433
|
+
- **Every descendant is closed, apart from the organising containers.** Proceed to Step 2, which closes them.
|
|
388
434
|
|
|
389
|
-
**Step 2 — finalize (only when every descendant is closed).** In order:
|
|
435
|
+
**Step 2 — finalize (only when every descendant but the containers is closed).** In order:
|
|
390
436
|
|
|
391
437
|
1. **Run any un-run Closeout activity.** Confirm Closeout's two knowledge-integration activities actually landed — the spec (and its `-plan.md` / `-plan-findings.md` siblings) moved to `specs/history/`, and the feature's knowledge folded into `reference/`. If Closeout never ran or left either undone, do that work here now per step 6a/6b and the `boop-product-design-reference-integration` skill, then `bd close <closeout-task-id>` if it is still open.
|
|
392
438
|
|
|
@@ -401,11 +447,13 @@ Create tasks as child issues of the feature issue. The descriptions below are th
|
|
|
401
447
|
```
|
|
402
448
|
bd close <container-id> # the do-the-work container: a feature, closes freely
|
|
403
449
|
bd close <reconcile-task-id> # close yourself — you are the last open child of the root
|
|
404
|
-
bd
|
|
450
|
+
bd close <root-id> # now every root child is closed, so the epic root closes
|
|
405
451
|
```
|
|
406
452
|
For the **small** shape there is no container and the root is a `feature`, so it is just `bd close <reconcile-or-last-task-id>` then `bd close $ROOT`.
|
|
407
453
|
|
|
408
|
-
**Why this order.** bd **refuses to close an epic that still has an open child** — Reconcile is that open child, so closing the root before yourself simply fails, and if the failure is swallowed the root is left open with every other issue closed. (A `feature`-typed root does allow it, which is why the order looks optional until the root is an epic.) Closing your own bd issue does not end your turn, so
|
|
454
|
+
**Why this order.** bd **refuses to close an epic that still has an open child** — Reconcile is that open child, so closing the root before yourself simply fails, and if the failure is swallowed the root is left open with every other issue closed. (A `feature`-typed root does allow it, which is why the order looks optional until the root is an epic.) Closing your own bd issue does not end your turn, so closing the root afterwards is fine — and it is better than `bd close <root> --force`, which closes the root while leaving Reconcile open.
|
|
455
|
+
|
|
456
|
+
**Close the root by its id, not with `bd epic close-eligible`.** That command takes no issue id: it closes every epic in the database whose children are all closed, including unrelated ones another effort left eligible.
|
|
409
457
|
|
|
410
458
|
**Do this even though boop has a safety net, because the net does not cover every run.** `closeCompletedFeatureRoot` in `src/commands/loop.ts` closes a root whose children have all closed, and it accepts a `feature` *or* `epic` root — but its call site is guarded by `work && resolvedFlow && prefix`, so it fires **only in spec-driven runs (`--work --flow`)**. An issue-driven `--prefix` run never reaches it. In spec-driven mode your explicit close just makes the net a no-op, which costs nothing.
|
|
411
459
|
|
|
@@ -56,21 +56,37 @@ If the design fails, redesign now — it is far cheaper here than after an epic'
|
|
|
56
56
|
Now swap the "implement" tail of a one-pass flow for a decomposition. In the **same session**, while grounded, emit **one bd epic** (an epic container is claim-safe) with child issues scoped to the actual wiring:
|
|
57
57
|
|
|
58
58
|
- **Ground each child in the real code** — name the files, call sites, conditions, and events from *investigate*. A child issue written by the agent that read the real system is a far less lossy input to its execution session than a spec would be.
|
|
59
|
+
- **Name the skills each child needs, and name only their names.**
|
|
60
|
+
A child issue is the whole briefing its session gets: that session never reads this workflow, and it will not read every skill hunting for the few that matter.
|
|
61
|
+
Put on each child the skills its kind of work uses: the skill for the layer it touches, and all three testing skills — `boop-testing-testing` for which shape of test the change needs, `boop-testing-tdd-light` for the cycle, and the stack's testing skill.
|
|
62
|
+
Write them as the skills list that `boop-project-mgmt-beads-planning` sets out in its section "Breadcrumb the skills that carry the discipline": a `## Skills` heading, the line "Before making changes, read each of these in full.", and a path to each skill's `SKILL.md`.
|
|
63
|
+
Names, not extracts: a command or checklist copied out of a skill into an issue is a second copy of the method, and it drifts out of step with the skill.
|
|
64
|
+
- **Put the test run scope on every child.**
|
|
65
|
+
Each child runs the focused tests for what it touches, plus the project's whole-program static check; the full suite runs once per branch, on the review child that completes it.
|
|
66
|
+
Left unstated, a child that has just changed a widely called function runs the whole suite on its own reasoning.
|
|
67
|
+
The rule and the wording to put on each are in the section "Test run scope: what each task runs, and what runs once" in `boop-project-mgmt-beads-planning`.
|
|
59
68
|
- **Keep it proportional.** The epic is the real work plus its safety tasks — not a regrown four-session front half or a reflexive 13-issue tree. But it does need to *be* an epic: this workflow's floor is a change that genuinely decomposes into several grounded children. If the work is a one-line edit that needs no decomposition, you are in the wrong workflow — file a plain bd issue or just make the change; do not spin up this flow for it.
|
|
60
69
|
- **Size children to a session, not to a step.** A child issue is the unit a session picks up, and every session boundary is a cold start: the next agent re-reads the surface, re-derives the pattern, and re-runs the gates before it changes a character. So never split work that one session could carry. Group related small changes — a set of copy fixes on one template, several style corrections in one stylesheet — into a single child, and keep grouping until that child would take a session past roughly **50%** context. A one-line text change must never be its own child issue; that is a full session spent on a cold start to earn one line of diff.
|
|
61
70
|
- **Include a review task and a punch-list** as children — the relocated safety net. For engine/back-end extensions especially, the review must verify the new wiring is in the **live path** (not tests-only) and handles **every** existing case it touches.
|
|
62
71
|
- Use `boop-project-mgmt-beads-planning` and `boop-project-mgmt-engineering-planning` to shape the epic.
|
|
72
|
+
- **Audit the child issue bodies before the session ends, then revise them.**
|
|
73
|
+
Writing an issue and auditing it are separate passes, and doing both at once produces bodies that read well to their author and cost their reader time.
|
|
74
|
+
Once every child exists, re-read them all side by side as the agent who will claim each one cold, following the section "Write, audit and revise in three separate turns" in `boop-project-mgmt-beads-planning`, then rewrite whatever the audit finds.
|
|
75
|
+
Some faults show only side by side: a child that says "a sibling issue handles this" without naming it, or a term the epic defines and a child then uses bare.
|
|
63
76
|
- **Do not create the feature branch yourself.** This decompose step runs in the *launch directory* (the planning phase runs before any worktree exists), so a `git checkout -b work/<epic-id>` here parks the launch dir on the feature branch — which then collides with the executor's `git worktree add` ("already checked out") and, worse, strands a concurrent session's next commit on your feature branch. The executor owns the branch: `boop loop` creates the worktree and its `work/<epic-id>` branch when it picks up the first child. Just create the bd tree and stop.
|
|
64
77
|
|
|
65
|
-
The loop then picks up each child as its own execution session. That boundary is the context-budget seam, and it is fine *when the child is large enough to need one* — the children are grounded, so a fresh session can execute them. It is pure waste when the child is smaller than the cold start that precedes it, which is what the sizing rule above exists to prevent.
|
|
78
|
+
The loop then picks up each child as its own execution session. That boundary is the context-budget seam, and it is fine *when the child is large enough to need one* — the children are grounded, so a fresh session can execute them. It is pure waste when the child is smaller than the cold start that precedes it, which is what the sizing rule above exists to prevent. The loop creates the worktree and feature branch itself; the decompose session above must not.
|
|
66
79
|
|
|
67
80
|
### 6. Run the epic — implement, review, punch-list
|
|
68
81
|
The loop implements each child (`boop-coding-development`, `boop-testing-tdd-light`), then the review and punch-list run against the whole branch (`boop-coding-review-changes`). Do not treat these as leftover process — they are where the independent perspective the single front session lacked actually happens. Verify each child against its goal, not merely that it closed.
|
|
69
82
|
|
|
70
83
|
### 7. Verify and close out
|
|
71
84
|
- **Run the real path, not just the suite.** For UI, capture the render and re-critique against it. For back-end, drive the real end-to-end flow against a throwaway environment — a green suite proves structure compiles, not that the goal is served.
|
|
72
|
-
-
|
|
85
|
+
- The full suite runs once, on the review child that completes the branch; every other child ran its focused tests and the whole-program static check.
|
|
73
86
|
- Integrate knowledge into `reference/` (`boop-product-design-reference-integration`) and archive anything the change completed.
|
|
87
|
+
- **Decide whether the change altered what a customer could truthfully be told, and say so in the handoff.**
|
|
88
|
+
If the project keeps a capability catalog and the answer is yes, update it against the catalog rule in `boop-product-design-reference-integration`.
|
|
89
|
+
Otherwise record "no product-facing change", which is the expected answer for most changes rather than a skipped step.
|
|
74
90
|
- Leave the branch for review. **Do not merge to main** — the human owns the merge.
|
|
75
91
|
|
|
76
92
|
## Front-end, back-end, full-stack: what "investigate" means
|
|
@@ -24,10 +24,14 @@ This is the opposite instinct from feature work, and the difference is worth sta
|
|
|
24
24
|
| The work is | capability that does not exist yet | every existing caller of a contract you are changing |
|
|
25
25
|
| Progress looks like | vertical slices, each demonstrable | concept groups, none user-visible |
|
|
26
26
|
| A green build means | the slice works | **possibly that you have not deleted enough** |
|
|
27
|
-
| Completeness comes from | the Done-when and a demo | the compiler, a
|
|
27
|
+
| Completeness comes from | the Done-when and a demo | the compiler, a search for the deleted name, and the suite |
|
|
28
28
|
|
|
29
29
|
The central failure mode: **keeping the build green throughout a refactor is only possible by leaving a fallback** — a scalar, an optional-handle-with-default, a first-of-set pick — that silently absorbs the callers you have not converted yet. Every "additive, expand-contract, drop the old thing last" plan re-commits that sin, which is why it keeps producing refactors that look finished and leave an undetected tail.
|
|
30
30
|
|
|
31
|
+
A scope boundary that leaves the old path callable is the same fallback under another name.
|
|
32
|
+
"Runtime only; the pages keep showing the first item" and "defer the deletion to a later phase" both keep it alive.
|
|
33
|
+
So whoever briefs, reviews or reports on a refactor checks that it began by deleting the old path and committed the red build, and calls a surviving optional parameter with a default unfinished.
|
|
34
|
+
|
|
31
35
|
## When this workflow applies
|
|
32
36
|
|
|
33
37
|
- Changing a relationship's cardinality (one-to-one becoming one-to-many).
|
|
@@ -37,6 +41,9 @@ The central failure mode: **keeping the build green throughout a refactor is onl
|
|
|
37
41
|
|
|
38
42
|
**When it does not apply:** adding new capability (use the feature workflow), or a change contained to one file with a handful of callers you can see at once. This workflow's overhead only pays when the call sites are too many to hold in your head — which is exactly when diligence-based approaches fail.
|
|
39
43
|
|
|
44
|
+
It is also the wrong workflow for a change to a database that already holds rows, when the readers of those rows cannot all be updated at the same moment.
|
|
45
|
+
Old and new code then have to run against the same data for a while, and that is the one case where expand-contract — add the new shape, move every reader, drop the old shape last — is the right method.
|
|
46
|
+
|
|
40
47
|
## The workflow
|
|
41
48
|
|
|
42
49
|
### 1. Establish the target vocabulary first, grounded in the code
|
|
@@ -54,6 +61,30 @@ Ground the vocabulary in the domain, not in the code's current names. If the cod
|
|
|
54
61
|
### 2. Decompose by concept group, not by call site
|
|
55
62
|
|
|
56
63
|
One task per concept, ordered so that later concepts build on earlier ones. Enumerate-and-patch — a task per call site, each threading a handle through — always leaves a tail, because the list of sites is exactly the thing you cannot trust yourself to have enumerated.
|
|
64
|
+
Add one more task beside the concept groups, for the verify stage in step 7, blocked by all of them.
|
|
65
|
+
|
|
66
|
+
**Split a wide concept group at the compiler's checkpoints.**
|
|
67
|
+
A concept group is as wide as its callers, and the widest can be more than one session's work.
|
|
68
|
+
A session should not be run up to the model's context limit, because it slows down there and its judgement drops.
|
|
69
|
+
How wide a group will turn out is hard to predict, but where to cut it is not, because the work falls into three stages and each ends on a state a command can check:
|
|
70
|
+
|
|
71
|
+
1. Delete the old names and convert until the product code compiles, checked by compiling the product code alone, without the tests.
|
|
72
|
+
2. Convert the tests until they type-check, checked by the whole-program static check over the product code and the tests together.
|
|
73
|
+
3. Walk the test suites, as step 6 describes, until they pass.
|
|
74
|
+
|
|
75
|
+
Make each stage its own issue, blocked by the one before it, so that each starts in a fresh session.
|
|
76
|
+
Each stage issue carries the method like any other task, and its run scope says which check must be clean when it closes.
|
|
77
|
+
The red the first two stages leave behind is intended: green is the end state of the group, not of each stage.
|
|
78
|
+
Decide which groups are wide from the concept map — a deleted name that appears across dozens of files, or fixtures that reach most of the test tree — and leave a narrow group as one issue, since splitting it only adds cold starts.
|
|
79
|
+
|
|
80
|
+
**Audit the issue bodies, then revise them, before the work starts.**
|
|
81
|
+
Writing an issue and auditing it are separate passes, and doing both at once produces bodies that read well to their author and cost their reader time.
|
|
82
|
+
Once every issue exists, re-read them all side by side as the agent who will claim each one cold, following the section "Write, audit and revise in three separate turns" in `boop-project-mgmt-beads-planning`, then rewrite whatever the audit finds.
|
|
83
|
+
The go-red instruction in step 3 is the one most often lost to compressed wording, and an executor who misreads it keeps the build green with a fallback.
|
|
84
|
+
|
|
85
|
+
**Put the test run scope on every issue.**
|
|
86
|
+
Each issue runs the focused tests for what its group touches, plus the project's whole-program static check; the full suite runs once per branch, in the verify stage that completes it.
|
|
87
|
+
The rule and the wording to put on each are in the section "Test run scope: what each task runs, and what runs once" in `boop-project-mgmt-beads-planning`.
|
|
57
88
|
|
|
58
89
|
### 3. Delete the escape hatch first, and commit the red build
|
|
59
90
|
|
|
@@ -64,30 +95,65 @@ If the build is still green after the deletion, a surviving hatch is absorbing t
|
|
|
64
95
|
### 4. Follow the compiler to green
|
|
65
96
|
|
|
66
97
|
Fix each site by giving it the handle it needs, reading the set, or — only at a genuine display boundary — an explicit **commented reduction** (`set[0]` with a comment saying it is a render choice). Never inside a reusable resolver, where a write path can reach it.
|
|
98
|
+
More generally, a site that keeps part of the old shape is legitimate only when a comment on that line says why; without one it is an unconverted call site.
|
|
67
99
|
|
|
68
100
|
**A fallback is never correct.** Not a defaulted handle, not `?? oldThing`, not a first-of-set inside a resolver. If a call site genuinely cannot supply the handle, that is a **finding to surface**, not a fallback to invent. Green is the *end state*, never a state maintained throughout.
|
|
69
101
|
|
|
70
|
-
### 5.
|
|
102
|
+
### 5. Loop on the compiler and a search, not on the test suite
|
|
103
|
+
|
|
104
|
+
The compiler cannot see code that reaches the changed thing without a type.
|
|
105
|
+
A read behind an inline cast (`as { field?: ... }`) re-declares the deleted field locally and compiles fine.
|
|
106
|
+
So do a raw query naming a dropped column, a value read out of an `any`-typed blob, and a field name inside a template or a data fixture.
|
|
107
|
+
The only tool that finds these is a search for the deleted name — the column, the field, the identifier — and for the inline-cast pattern, across the code, the templates and the data fixtures.
|
|
71
108
|
|
|
72
|
-
|
|
109
|
+
**The conversion loop is: delete, let the compiler list the callers, fix them, search for the deleted name, fix what the search finds, and repeat until the static checks are clean and the search finds nothing.**
|
|
110
|
+
Before the first deletion, confirm that both checks are clean on the unchanged tree, so every error they report afterwards is the refactor's own.
|
|
111
|
+
If the project's static check does not cover the tests, widen it to cover them, because a fixture that still builds the old shape is a caller too.
|
|
73
112
|
|
|
74
|
-
|
|
113
|
+
The test suite is not part of this loop, and the reason is how directly each tool names the cause.
|
|
114
|
+
The compiler names a file and a line, and the search names every occurrence; both are complete over what they cover.
|
|
115
|
+
The suite reports one broken query as a cascade of failures in tests that never mention it, so it names symptoms where the compiler names the cause.
|
|
116
|
+
A green static check is still a checkpoint, not the finish line: the compiler cannot see a writer that fails to fill the new field, or a value that now lands where no reader looks, and only running code finds those.
|
|
117
|
+
That is the next step's job.
|
|
75
118
|
|
|
76
|
-
|
|
119
|
+
### 6. Walk the suites one at a time
|
|
77
120
|
|
|
78
|
-
|
|
79
|
-
|
|
121
|
+
Take the list of every suite from the test runner — all of them, not a list of failures — and walk it.
|
|
122
|
+
Run one suite on its own; if it fails, read the failure, fix the cause, run it again, and move on.
|
|
80
123
|
|
|
81
|
-
|
|
124
|
+
**Do not collect the failures first.**
|
|
125
|
+
A full run up front looks like it produces the work-list, and it actually manufactures one.
|
|
126
|
+
Failures share causes: one missing method or one query naming a dropped column fails many suites at once, so a full run records one cause as many separate work items.
|
|
127
|
+
A walk meets each cause once, in the first suite that reaches it, and by the time you arrive at the rest they pass.
|
|
82
128
|
|
|
83
|
-
|
|
129
|
+
Nothing is filed during the walk; the suites you have not reached yet are the work-list.
|
|
130
|
+
The only thing that becomes an issue is what cannot be resolved in the walk: a call site that genuinely cannot supply what the new contract requires, or a design question a person has to answer.
|
|
84
131
|
|
|
85
|
-
|
|
132
|
+
### 7. Verify — a stage, and an issue, of its own, ending in a punch list
|
|
86
133
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
- The
|
|
90
|
-
-
|
|
134
|
+
Finish with evidence a reader can check without repeating the work:
|
|
135
|
+
|
|
136
|
+
- **The search for every deleted name finds nothing**, except occurrences whose line comment says why they are deliberate.
|
|
137
|
+
- **One test drives the real path end to end** — not a unit test of the new primitive, but the whole path, because concurrency and wiring defects only appear there. Where the change made something plural, drive it with three items and assert on the middle one.
|
|
138
|
+
- **Every suite passes.** This is the one place in the workflow that runs them all.
|
|
139
|
+
|
|
140
|
+
These checks are their own issue, a sibling of the concept groups and blocked by all of them, and a session that did not do the converting works it.
|
|
141
|
+
The agent that converted the call sites is the worst-placed reader of its own work: it knows what it meant the code to do, so it reads that intention into the code.
|
|
142
|
+
|
|
143
|
+
**Anything unresolved becomes an issue** under the same container, blocking the hand-off.
|
|
144
|
+
A check that was never run counts as failed, since both leave the claim unproved.
|
|
145
|
+
File one issue per cause, not per symptom.
|
|
146
|
+
**A long punch list means the walk in step 6 was skipped.**
|
|
147
|
+
Go back to the walk rather than filing every failure.
|
|
148
|
+
|
|
149
|
+
**How to run and report the tests here.**
|
|
150
|
+
Go through the project's test script, as "Go through the project's test script" in `boop-testing-testing` says.
|
|
151
|
+
Write a run's output to a file and read the file, rather than piping it into a filter, so a wrong pattern does not force another run.
|
|
152
|
+
A claim that the work is verified names the command, the counts and the commit, so a reviewer can check it without running the suite again.
|
|
153
|
+
|
|
154
|
+
### 8. Hand off
|
|
155
|
+
|
|
156
|
+
Fold what changed into the reference docs, and leave the merge decision to the operator.
|
|
91
157
|
|
|
92
158
|
## Principles that carry across the whole workflow
|
|
93
159
|
|
|
@@ -95,13 +161,25 @@ Finish with evidence that does not depend on anyone's care:
|
|
|
95
161
|
|
|
96
162
|
**Verify the guarantee mechanism itself works.** "The compiler enforces this" is a claim worth testing. A type-check config that does not actually compile the files you think it does has been silently enforcing nothing.
|
|
97
163
|
|
|
98
|
-
**
|
|
164
|
+
**When the change made something plural, prefer three over two when proving it.** Two is the weakest multi case: a first-of-set bug survives it whenever the arbitrary pick happens to match what the assertion reads. Use three, and assert against the middle one.
|
|
99
165
|
|
|
100
|
-
**
|
|
166
|
+
**When asserting over a collection, assert by name and by count.** Naming catches something missing; counting catches something doubled. A set-equality assertion reads identically whether an item was written once or twice.
|
|
167
|
+
|
|
168
|
+
**When the new model gives a different answer from the shipped code, that is a design question, not a regression.**
|
|
169
|
+
Do not patch a consumer back to the old output: that hides the question the refactor existed to raise.
|
|
170
|
+
Record what the old code says, what the new model says, and which surface shows the difference, and leave the choice to whoever owns the design; an unattended session hands it back rather than choosing.
|
|
171
|
+
If the new answer is right, update the copy, the tests and every surface that read the old behaviour; if the old one is right, narrow the new model.
|
|
101
172
|
|
|
102
173
|
## Running this unattended
|
|
103
174
|
|
|
104
|
-
If the work is executed by autonomous agents, put **the method itself into each task's
|
|
175
|
+
If the work is executed by autonomous agents, put **the method itself into each task's description** — not just the task's scope.
|
|
176
|
+
The description is the copy that counts, because `boop loop` sends a session the issue's description and nothing else; the notes can repeat it.
|
|
177
|
+
An agent picking up "make this field required" without the go-red discipline will keep the build green, which is the failure this workflow exists to prevent. Carrying the method on every task is what makes an unattended run trustworthy, and it is cheap: the same block on each issue, with a task-specific section.
|
|
178
|
+
|
|
179
|
+
**Name the skills each task needs, too:** `boop-testing-testing` on every task, plus the skill for the layer the concept group changes.
|
|
180
|
+
Write them as the skills list that `boop-project-mgmt-beads-planning` sets out in its section "Breadcrumb the skills that carry the discipline": a `## Skills` heading, the line "Before making changes, read each of these in full.", and a path to each skill's `SKILL.md`.
|
|
181
|
+
The method is the one thing worth copying into an issue, because a fallback left in place cannot be corrected afterwards.
|
|
182
|
+
Copy nothing else out of a skill: the copy in the issue is the one that goes stale without looking stale.
|
|
105
183
|
|
|
106
184
|
## Relationship to the feature workflow
|
|
107
185
|
|
|
@@ -110,6 +188,7 @@ A refactor of this kind usually exists to **make a feature buildable**. It ships
|
|
|
110
188
|
## Related
|
|
111
189
|
|
|
112
190
|
- [boop-feature-dev-workflow](../boop-feature-dev-workflow/SKILL.md) — the peer workflow, for building capability.
|
|
191
|
+
- [boop-architecture-audit-workflow](../boop-architecture-audit-workflow/SKILL.md) — when the structural change has not been chosen yet; it audits an area, grades its findings, and hands the survivors to this workflow.
|
|
113
192
|
- `boop-product-design-engineering-design` — the concept map in step 1 is a design artifact; the current-state audit guidance applies.
|
|
114
|
-
- `boop-testing-testing` — for the real-path proof in step 7.
|
|
115
|
-
- `boop-coding-review-changes` — the
|
|
193
|
+
- `boop-testing-testing` — for the real-path proof in step 7, and how to run the suites.
|
|
194
|
+
- `boop-coding-review-changes` — the search for the deleted name in step 5 belongs in review too.
|
|
@@ -110,7 +110,21 @@ In the same session, while the classification is fresh, create one bd epic (an e
|
|
|
110
110
|
|
|
111
111
|
**Keep the child count low, and group coarsely — at the suite level or higher.** Every child issue is a session for whoever runs it, and a fresh session reloads its context from scratch. Many small children multiply that cost and lose context between them. Prefer a few coarse issues, split by area or by suite rather than by file or by kind of change. Keep a removal together with the rewrite that covers its invariant, so one session cannot delete an old test before its replacement exists. Add one issue that confirms coverage and one that reviews the result. Ground each issue in the real test files it touches, and use `boop-project-mgmt-beads-planning` to shape the tree.
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
**Name the testing skills on every child.**
|
|
114
|
+
A child's session sees the issue body and little else: not this workflow, and not the trim list unless the body points at it.
|
|
115
|
+
Every child that rewrites or promotes a test carries `boop-testing-testing`, because the decision about a test's level (unit, pairwise, integration, seam) lives there, and the level is exactly what a rewrite gets wrong.
|
|
116
|
+
Add the testing skill that matches the suite: the stack's (`boop-typescript-testing` in a TypeScript project) and, for routes, controllers and middleware, `boop-web-testing`.
|
|
117
|
+
The children are all of one kind, so one named set covers the tree; write it once and put it on each child.
|
|
118
|
+
Write them as the skills list that `boop-project-mgmt-beads-planning` sets out in its section "Breadcrumb the skills that carry the discipline": a `## Skills` heading, the line "Before making changes, read each of these in full.", and a path to each skill's `SKILL.md`.
|
|
119
|
+
Names, not extracts: copy no method or checklist out of a skill into an issue, so that there is a single copy to keep current.
|
|
120
|
+
The run-scope line is not an extract, and each child still carries it with the project's actual commands, as that skill asks.
|
|
121
|
+
|
|
122
|
+
**Audit the issue bodies before the session ends, then revise them.**
|
|
123
|
+
Writing an issue and auditing it are separate passes, and doing both at once produces bodies that read well to their author and cost their reader time.
|
|
124
|
+
Once every child exists, re-read them all side by side as the agent who will claim each one cold, following the section "Write, audit and revise in three separate turns" in `boop-project-mgmt-beads-planning`, then rewrite whatever the audit finds.
|
|
125
|
+
Some faults show only side by side: a child that says "a sibling issue handles this" without naming it, or a term the epic defines and a child then uses bare.
|
|
126
|
+
|
|
127
|
+
Once the audited tree is in place, stop. Do not run the work yourself, and do not create the feature branch: the loop creates the worktree and its `work/<epic-id>` branch when it takes the first child. Creating the branch here collides with that and can strand commits.
|
|
114
128
|
|
|
115
129
|
### 6. How the children are executed (the loop runs them; these are the rules each one carries)
|
|
116
130
|
Hand the finished tree to `boop loop --prefix <epic-id> --run`. Prefer `--reuse-session` for this workflow: every child has to re-read the invariant list and the trim list to make a correct keep-or-cut call, and carrying one session across the cluster is exactly the context a fresh-session-per-issue run would reload each time.
|
|
@@ -118,7 +132,7 @@ Hand the finished tree to `boop loop --prefix <epic-id> --run`. Prefer `--reuse-
|
|
|
118
132
|
Write these rules into the child issues so each executing session follows them:
|
|
119
133
|
|
|
120
134
|
- **The suite passes before and after.** This is a change to the tests, not to behaviour, so the tests in scope stay green throughout, and the only tests that stop existing are the ones the trim list marked for removal.
|
|
121
|
-
- **Run tests per file or per small group, not the whole suite on each pass.**
|
|
135
|
+
- **Run tests per file or per small group, not the whole suite on each pass.** The file you just rewrote is the test that can tell you whether the rewrite holds, and a whole-suite run answers that question far less directly; this workflow makes hundreds of such passes, so the difference compounds.
|
|
122
136
|
- **Change only tests.** Fixtures, factories, and test harness code count as tests and may change, which is what makes a promotion to a real-collaborator test possible. Production behaviour does not. If a rewrite uncovers a real bug — and a promotion often will, since it exercises a seam nothing exercised before — write a separate issue for it rather than fixing it in place.
|
|
123
137
|
|
|
124
138
|
The review child then reads the whole branch and checks each change against the invariant it was meant to protect, not merely that the issue closed.
|
|
@@ -139,6 +153,7 @@ This is the work of the coverage and review children, and the loop's own closeou
|
|
|
139
153
|
| Extend an existing system toward a goal, wiring new behaviour from parts that already exist | [boop-light-dev-workflow](../boop-light-dev-workflow/SKILL.md) |
|
|
140
154
|
| Build a genuinely new subsystem, data model, or API from a spec | [boop-feature-dev-workflow](../boop-feature-dev-workflow/SKILL.md) |
|
|
141
155
|
| Clear a noisy backlog of small lingering items in a themed sweep | `boop-project-mgmt-issue-sweep-workflow` |
|
|
156
|
+
| Audit a surface's production code for structural debt rather than its tests | [boop-architecture-audit-workflow](../boop-architecture-audit-workflow/SKILL.md), which depends on this workflow's verdict that the suite can catch a behaviour change |
|
|
142
157
|
|
|
143
158
|
If the set of tests is small enough that the audit is a handful of edits with no real decomposition, skip the epic and do the rewrite under a plain bd issue. This workflow is for a surface big enough that classifying first, then executing, earns its keep.
|
|
144
159
|
|