@boopkit/cap-workflows 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,404 @@
1
+ ---
2
+ name: boop-feature-dev-workflow
3
+ description: "When you are tasked with building a feature end-to-end, follow this guidance."
4
+ metadata:
5
+ category: workflow
6
+ ---
7
+ # Workflow for building features.
8
+ When you are tasked with building a feature end-to-end, follow this guidance.
9
+
10
+ > **If the job is a refactor, use [boop-refactor-dev-workflow](../boop-refactor-dev-workflow/SKILL.md) instead.**
11
+ > Changing a contract, cardinality, or shape that existing code already depends on is a different
12
+ > kind of work: it ships no user-visible behaviour, so it has no demonstrable slices, and its
13
+ > completeness has to come from the compiler rather than from a Done-when. Running it through this
14
+ > workflow produces a plan built around keeping the build green, which is the specific way that job
15
+ > fails. Refactors done to make a feature buildable run first, as their own unit; the feature then
16
+ > follows on this workflow.
17
+
18
+ ## Relvant skills
19
+
20
+ ### Step zero — before claiming any task
21
+ - [When you pick up a non-trivial task, sweep the available skills first](../../../cap-skills/skills/boop-skills-discovery/SKILL.md) — 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.
22
+
23
+ ### Project Management
24
+ - [When you need to plan work with beads](../../../cap-project-mgmt/skills/boop-project-mgmt-beads-planning/SKILL.md) — from project-mgmt
25
+ - [When you need to plan engineering work](../../../cap-project-mgmt/skills/boop-project-mgmt-engineering-planning/SKILL.md) — from project-mgmt
26
+
27
+ ### Design
28
+ - [When you need to design a feature or system](../../../cap-product-design/skills/boop-product-design-engineering-design/SKILL.md) — from product-design
29
+ - [When you need to design REST APIs](../../../cap-api/skills/boop-api-rest-api-design/SKILL.md) — from api
30
+ - [When you need to design services](../../../cap-services/skills/boop-services-services/SKILL.md) — from services
31
+ - [When you need to design controllers](../../../cap-services/skills/boop-services-controllers/SKILL.md) — from services
32
+ - [When you need to design data models](../../../cap-database/skills/boop-database-data-model/SKILL.md) — from database
33
+ - [When you need to design web UIs](../../../cap-web/skills/boop-web-web-ui-design/SKILL.md) — from web
34
+ - [When the feature includes new or substantially modified UI surface — purpose lens](../../../cap-ux/skills/boop-ux-purpose-judgement/SKILL.md) — 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.
35
+ - [When the feature includes new or substantially modified UI surface — visual lens](../../../cap-ux/skills/boop-ux-visual-judgement/SKILL.md) — taste lens (eye-path, register, anatomy, scan-line). Run during Review against a screenshot of the built page; optional during Plan against a nearby existing surface for register calibration only.
36
+
37
+
38
+ ### Coding
39
+ - [When you need to start working on a claimed issue](../../../cap-coding/skills/boop-coding-beads-flow/SKILL.md) — from coding
40
+ - [When you need to develop or modify code](../../../cap-coding/skills/boop-coding-development/SKILL.md) — from coding
41
+ - [When you need to commit code changes](../../../cap-coding/skills/boop-coding-commit-changes/SKILL.md) — from coding
42
+ - [When you need to review uncommitted code changes](../../../cap-coding/skills/boop-coding-review-changes/SKILL.md) — from coding
43
+ - [When you need to design or migrate the database schema](../../../cap-database/skills/boop-database-prisma/SKILL.md) — from database
44
+
45
+ ### Testing
46
+
47
+ Read all three for any code-touching task. They cover different layers, and the easy default of "I'll write a unit test" misses contract bugs at component boundaries.
48
+
49
+ - [**General testing principles**](../../../cap-testing/skills/boop-testing-testing/SKILL.md) — the foundational skill: pairwise producer/consumer contracts, integration vs. unit decisions, mock boundaries, data-driven config coverage. Read this **before** picking a test shape; it tells you which shape the change actually needs.
50
+ - [**Light TDD cycle**](../../../cap-testing/skills/boop-testing-tdd-light/SKILL.md) — the Red / Green / Verify loop applied to features and bug fixes.
51
+ - [**Express web tests**](../../../cap-testing/skills/boop-testing-testing-express-web/SKILL.md) — the domain-specific skill for services, controllers, middleware, and route tests in the Express stack.
52
+
53
+ ### Documentation
54
+ - [When you need to write feature documentation](../../../cap-product-design/skills/boop-product-design-feature-documentation/SKILL.md) — from product-design
55
+ - [When you need to integrate knowledge into reference docs](../../../cap-product-design/skills/boop-product-design-reference-integration/SKILL.md) — from product-design
56
+
57
+
58
+ ## Project structure
59
+ Documents are organized into
60
+ - `reference/` — The current state of the system.
61
+ - `specs`/ — The future state of the system, in the form of specifications for features, components, and capabilities. These are the source of truth for what we are building.
62
+ - `skills/` — Instructions for how to do specific tasks related to building and maintaining the system. Each skill is a folder containing a `SKILL.md` file. Additional skills reside in `packages/cap-*/skills/`.
63
+ - `tools/` - a few cmd line tools used to automate the workflow to do the work defined in issues. Do not run these until instructed to do so.
64
+
65
+ ## Operating constraints for autonomous execution
66
+
67
+ The loop runs without a live operator. The patterns below follow from that and apply to every task in the workflow.
68
+
69
+ ### Respect safety mechanisms; do not override them
70
+
71
+ 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.
72
+
73
+ Setting the consent value yourself, passing the flag yourself, or treating "work autonomously" as authority to bypass these mechanisms is not the correct action. "Work autonomously" means making the routine judgment calls that a competent engineer would make without checking in for every small decision; it does not extend to overriding the checks that exist because particular decisions need human judgment. When you encounter one of these mechanisms during autonomous work, the right action is to raise a **human gate** naming the confirmation needed (see "Waiting protocol" below) and wait for the operator, rather than to find a way around the check.
74
+
75
+ ### Treat `AskUserQuestion` as a request to block, not as a fallback
76
+
77
+ In a spec-driven autonomous loop there is no live operator to answer prompts. `AskUserQuestion` does not pause for an answer in this context; the tool returns and the agent continues past it without input. When your reasoning reaches a point that genuinely needs operator input, the correct next action is to raise a **human gate** (see "Waiting protocol" below) — this is a human choice, so it must never auto-release:
78
+
79
+ ```
80
+ bd gate create --type=human --blocks <current-issue-id> \
81
+ --reason="<the decision the operator must make, and what you completed up to this point>"
82
+ ```
83
+
84
+ Then stop work on the issue. Do not resolve the question by inferring an answer, because the question reached that state precisely because it required input that was not available.
85
+
86
+ This rule catches a common failure pattern in which the agent recognizes that confirmation is needed, discovers that the confirmation path is unavailable, and proceeds anyway. The third step is the part that should change. Raising a gate is a legitimate and expected outcome for autonomous-loop work; it preserves the progress already made and surfaces the decision to the operator without discarding the run. **Do not set `status=blocked` instead** — that is terminal, and the issue will never come back on its own.
87
+
88
+ ### Run long verification steps BLOCKING, in one turn — never detach and poll
89
+
90
+ A build task is not done until its verification passes, and some verifications are legitimately slow — a full end-to-end run can take several minutes. **Run a slow verification as a single blocking foreground call with a generous tool timeout, and wait for its exit code in the same turn** — and the deterministic test suite the same way. Do **not** launch it detached (`nohup … &`, `run_in_background`, a backgrounded server you then poll) and end your turn to "wait" for the result on a later turn.
91
+
92
+ The reason is structural, not stylistic. The loop has no notion of "making progress": it inspects only whether the issue is closed at the end of each turn, and every un-closed turn counts as a failed attempt against `--resume-limit` (default 3). An agent that detaches a healthy, still-running verification and ends its turn to poll therefore looks identical to a stalled task — after a few "still waiting" turns the loop **abandons the issue while the verification is still passing normally**. Keeping the whole verification inside one blocking call makes it complete-or-fail within a single turn, so a slow-but-healthy check can never be mistaken for a stall or burn the resume budget. If a verification is genuinely too long for one foreground call, reduce its *scope* (run fewer cases) rather than detaching it; do not offload a required check to a cross-turn poll.
93
+
94
+ ### Waiting protocol — never hand-set `status=blocked`; represent the cause
95
+
96
+ **Never set `status=blocked` yourself.** A status is not a relation. Nothing in bd recomputes it, satisfying the real dependency does not clear it, and bd never sets it itself — every `blocked` issue in the tracker was typed by a human or an agent. A condition hand-set as `blocked` stays frozen even after the real cause clears on its own, so the spec never gets archived and the reference docs go stale.
97
+
98
+ **Every real block has a cause. Represent the cause, and the block manages itself.**
99
+
100
+ **Waiting on work that will finish on its own** — a container whose children are still open, a Closeout whose punch-list children have not all closed. Add a blocking edge for each thing you are waiting on and **stay `open`**:
101
+
102
+ ```
103
+ bd dep add <current-issue-id> <upstream-id> -t blocks
104
+ ```
105
+
106
+ You leave `bd ready` because of the edge, and you return to it automatically when the upstream closes. Nobody has to notice, and no tool has to run — the edge *is* the machine-checkable condition, and bd already checks it.
107
+
108
+ Two constraints, both easy to get wrong:
109
+
110
+ - **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.)
111
+ - **Use `blocks`, not `waits-for`.** In bd 1.1.2 `waits-for` is not even a valid `bd dep add -t` type (valid types are `blocks|tracks|related|parent-child|discovered-from|until|caused-by|validates|relates-to|supersedes`); and where `waits-for` does exist it releases when the *first* child of its target closes, not all of them — unsafe against any container with more than one child. Use `blocks`, one edge per thing waited on.
112
+
113
+ **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`:
114
+
115
+ ```
116
+ bd gate create --type=human --blocks <current-issue-id> \
117
+ --reason="<the specific decision the operator must make, and what you completed>"
118
+ ```
119
+
120
+ The gate holds you out of `bd ready`, leaves your status reading `open`, stays listed in `bd gate list` as the operator's queue of pending decisions, and releases you **automatically** when the operator runs `bd gate resolve <gate-id>`. It never auto-clears, which is exactly the property an operator-decision needs.
121
+
122
+ Then stop work on the issue. Do not resolve the question by inferring an answer — it reached that state precisely because it needed input that was not available.
123
+
124
+ Three practical notes:
125
+
126
+ - **`bd gate list` shows only IDs and types.** Read the decision text with `bd gate show <gate-id>`, so write a `--reason` that stands alone.
127
+ - **Use only `--type=human` for now.** `timer` and `gh:*` gates do not resolve themselves — an expired timer gate holds its waiter indefinitely until something runs `bd gate check`. Until something reliably runs it, those types reproduce the exact stranding this protocol exists to prevent.
128
+ - **`bd gate create` is not atomic.** If the blocking edge is rejected, the gate issue is still created and left orphaned in `bd gate list`. Check the command succeeded; clean up an orphan with `bd gate resolve <gate-id>`. A gate cannot attach to an **epic**-typed issue at all (*"epics can only block other epics, not tasks"*) — gate the task that needs the decision, not the epic above it.
129
+
130
+ **When in doubt between the two, choose the human gate.** A wait that is really a decision, expressed as an edge, auto-releases into a task nobody can finish; a decision that is really a wait just sits in the operator's gate queue, which is safe and visible.
131
+
132
+ ## Workflow
133
+ Prime directive : **Use beads issues for everything**: Beads has hierarchical issues (epics, features, tasks, sub-tasks, bugs). All steps of this feature development workflow should be an issue, with appropriate dependencies and reference material (breadcrumbs, etc). We will use feature -> task -> sub-task for most work. If you find yourself doing work that is not in an issue, stop and create an issue for it.
134
+
135
+ **Flow overview.** The feature ships as a chain of issues, each one a separate task whose output is written somewhere the next task can find it:
136
+
137
+ > epic (root) → understand → plan → decompose → implement → review → punch-list → closeout → reconcile-and-finalize
138
+
139
+ The first issue is the epic root; working it produces the next issue(s), and so on. `bd-loop` picks up each newly-ready issue and runs it until the chain is complete.
140
+
141
+ **The last step (Reconcile-and-finalize) closes the tree.** bd never auto-closes a parent when its children close, so the flow needs one explicit task that — after a final sweep confirms every descendant is done — runs any un-run Closeout activity, closes the do-the-work container, and closes the root. It is deliberately the one lifecycle task that does not gate on Closeout closing before it can finish the tree.
142
+
143
+ **The loop ends with the feature branch pushed for human review.** Merging, main-branch coordination, and branch deletion are out of scope for the loop. See step 6 (Closeout) and step 7 (Reconcile and finalize).
144
+
145
+ ### First step:
146
+ 1. **Create the root issue — as an `epic`**: `bd create "<feature>" -t epic`. This is the parent of every task in the flow. Write a clear title and description outlining the feature and its requirements, and add references to specs, designs, and any other documentation (these are the 'breadcrumbs').
147
+
148
+ **Use `-t epic`, not `-t feature`.** boop's claim filter drops `epic` unconditionally, so the root can never be picked up as work; a `feature` root is protected only by a secondary "has children" guard, and `--features` exists precisely to make feature-typed issues claimable for light efforts. `boop loop --work/--flow` accepts either type as a root, so nothing downstream changes. The one consequence to know is the closing order in step 7: bd refuses to close an epic that still has an open child. (Existing trees with a `feature` root are fine — they work; the epic is the more robust default for new ones.)
149
+
150
+ ### Task construction:
151
+ Create tasks as child issues of the feature issue. The descriptions below are the content of the created issue - so don't *do* the work described in the issue description, this is just the content of the issue.
152
+
153
+ 1. **Understand the feature**: First task issue goal (don't *do* this work, this is the content of the task issue): Read the spec and any related documentation and skills to fully understand the feature requirements, user stories, and acceptance criteria. For each success criterion, identify the outcome it must actually prove and the governing principle behind it, and flag any criterion a partial or intermediate implementation could satisfy without delivering that outcome. If anything is unclear, ask for clarification. eg. for developing capabilities, use the [boop-capability-development](../../../../skills/boop-capability-development/SKILL.md) skill
154
+
155
+ **Findings handoff (critical):** The next task runs in a fresh session with zero context — anything not written to a discoverable file is lost. Write findings to `specs/{feature-name}-plan-findings.md` covering: spec understanding, prior art patterns, identified gaps, open questions, and decisions/recommendations. Reference this file as a breadcrumb in the "plan" task's issue description.
156
+
157
+ A `bd close --reason` one-liner or short `--append-notes` summary is fine as a supplement, but the findings file is the primary handoff artifact — issue fields have length limits and aren't diff-friendly.
158
+
159
+ 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.
160
+
161
+ **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:
162
+ - **Brand-new surface (the page does not exist yet):** run [`boop-ux-purpose-judgement`](../../../cap-ux/skills/boop-ux-purpose-judgement/SKILL.md) against the spec, reference docs, and any existing nearby templates that anchor the register. **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`](../../../cap-ux/skills/boop-ux-visual-judgement/SKILL.md) against a nearby existing surface for **register 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.
163
+ - **Modified existing surface (the page exists and is being changed):** capture a screenshot of the current surface with the capture script cap-ux ships (`scripts/ux-eval-capture.ts`), then run both lenses in order — purpose first, visual second.
164
+ 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.
165
+
166
+ 3. **Decompose the plan**: Next task (don't *do* this work, this is the content of the task issue): **Start by reading the engineering plan** at `specs/{feature-name}-plan.md` (confirm path from this task's breadcrumbs). Break it down into a "do the work" **container** — a `feature`-typed organiser, never a task — holding smaller, manageable `task` children, following the [beads-planning skill](../../../cap-project-mgmt/skills/boop-project-mgmt-beads-planning/SKILL.md) for the issue mechanics (hierarchy, dependencies, breadcrumb links). Create the feature branch `work/<feature-issue-id>` (e.g. `work/boop-arc.14`) — branch names MUST use the `work/` prefix + issue ID, never feature names like `email-capability`. All sub-tasks work within this branch.
167
+
168
+ **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:
169
+ - 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.
170
+ - **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.
171
+
172
+ **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.
173
+
174
+ **One rule underneath all of it: only `task` issues are claimed. `epic` and `feature` are for organising and for hanging dependencies off — they are never work.**
175
+
176
+ ```
177
+ epic ROOT organising; never claimable
178
+ ├─ feature Do the work organising; never claimable
179
+ │ ├─ task Slice 1 ← the work
180
+ │ └─ task Slice 2 ← the work
181
+ ├─ task Review blocked-by Slice 1, Slice 2
182
+ ├─ task Punch-list blocked-by Review
183
+ ├─ task Closeout blocked-by Punch-list
184
+ └─ task Reconcile and finalize blocked-by Closeout
185
+ ```
186
+
187
+ **For a small effort, drop the top two levels** — a `feature` root with `task` children, chained the same way. Same rule, fewer boxes:
188
+
189
+ ```
190
+ feature ROOT organising; never claimable
191
+ ├─ task Do the work
192
+ ├─ task Review blocked-by Do the work
193
+ └─ task Closeout blocked-by Review
194
+ ```
195
+
196
+ **Why those container types are safe.** boop applies two filters to `bd ready` (`src/commands/loop.ts`): `typeFilter` drops `epic` **always** and `feature` unless `--features`; then `isClaimableReadyIssue` drops any `feature` **that has children**, regardless of the flag. So an `epic` is never claimed, and a `feature` **with children** is never claimed either — which is exactly what both roots and the do-the-work container are. (`--features` exists to let light efforts claim *childless* feature-typed issues; nothing in either shape is one.) `boop loop --work/--flow` accepts a `feature` or `epic` root, so neither shape needs a flag.
197
+
198
+ Copy-paste and replace the IDs:
199
+
200
+ ```
201
+ ROOT=<root-issue-id> # bd create "<feature>" -t epic
202
+
203
+ # 1. Do-the-work container — a FEATURE child of the root (a plain organiser)
204
+ bd create "Do the work: <feature>" -t feature --parent $ROOT # → <container-id>
205
+
206
+ # 2. Slices are TASK children of the container (repeat per slice)
207
+ bd create "Slice 1: <title>" -t task --parent <container-id> # → <slice-1-id>
208
+ bd create "Slice 2: <title>" -t task --parent <container-id> # → <slice-2-id>
209
+
210
+ # 3. Review blocks on EACH slice; then chain the rest. All plain `blocks`.
211
+ bd dep add <review-id> <slice-1-id>
212
+ bd dep add <review-id> <slice-2-id>
213
+ bd dep add <punchlist-id> <review-id>
214
+ bd dep add <closeout-id> <punchlist-id>
215
+ bd dep add <reconcile-id> <closeout-id>
216
+ ```
217
+
218
+ **Every edge is plain `blocks`. Never `waits-for`** — in bd 1.1.2 `waits-for` is not a valid `bd dep add -t` type, and where it does exist it releases when the *first* child of its target closes, not all of them, contradicting its own documentation. See the Waiting protocol.
219
+
220
+ > **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.
221
+
222
+ Two rules that keep the shape correct as it grows:
223
+
224
+ - **Anything that waits must be a SIBLING of what it waits on, never an ancestor.** An unresolved edge on a parent holds *every* descendant at every depth, so blocking the container on its own slices would deadlock them. Slices live under the container; every lifecycle task lives under the root. That is why Review is a child of the root, not of the container.
225
+ - **A slice that decomposes itself at implementation time needs no rewiring.** Its children become claimable, the slice itself stays open until its own agent closes it, and Review is blocked on the slice — so Review waits for the whole subtree for free. (A slice added *after* Decompose does need its own `bd dep add <review-id> <new-slice>`.)
226
+
227
+ Verify the wiring before moving on — all three must hold:
228
+ ```
229
+ bd ready -n 30 --json | jq -r '.[] | select(.issue_type == "task") | .id + " " + .title'
230
+ ```
231
+ - only the **slices** come back (no root, no container, no lifecycle task);
232
+ - `bd dep list <review-id>` names every slice, each `via blocks`;
233
+ - `bd dep list <reconcile-id>` names Closeout `via blocks`.
234
+
235
+ **Re-wire the Review dependency (critical — gate on slice completion, not container status):** If the "review" task issue (step 4) already exists and was sequenced to depend on *this Decompose task*, that dep is wrong — Decompose only plans the work, it does not perform it. First remove the stale Review→Decompose dependency:
236
+ ```
237
+ bd dep remove <review-task-id> <this-decompose-task-id>
238
+ ```
239
+ Then wire Review to fire **only when every slice has closed**, with one plain `blocks` edge per slice. `blocks` resolves only when *that* issue closes, so any slice still open holds Review:
240
+ ```
241
+ # for each slice sub-task created under the do-the-work container:
242
+ bd dep add <review-task-id> <slice-id> # slice blocks Review (plain dep)
243
+ ```
244
+ **Never wire Review `waits-for` the do-the-work container** — `waits-for` releases the moment the *first* slice closes, so Review would run against a partial branch and the largest slice could ship with no review pass. If the Review task does not yet exist when Decompose runs, create it now (or let a later step do so) — but wire it to slice completion as above. The step 4 precondition check is the defensive net that also catches slices further decomposed at implementation time.
245
+
246
+ **Create the Reconcile-and-finalize task (step 7):** the last task in the chain, a childless leaf child of the ROOT, blocked-by Closeout:
247
+ ```
248
+ bd create "Reconcile and finalize <feature-name>" -t task --parent $ROOT # → <reconcile-task-id>
249
+ bd dep add <reconcile-task-id> <closeout-id>
250
+ ```
251
+ Two things make this the exit rather than one more thing that strands:
252
+ - **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.
253
+ - **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.
254
+
255
+ 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). Run `bd children <feature-issue-id>` to see what sub-tasks were created and closed, for context on what was implemented.
256
+
257
+ **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, run both checks:
258
+
259
+ **Check 1 — slices:** `bd children <feature-issue-id>`, and `bd show <id>` on each slice (and on any sub-slice a slice was further decomposed into during implementation). Confirm **every** slice/sub-task is `closed`. Any slice that is `open`, `in_progress`, or itself `blocked` means the branch is partial.
260
+
261
+ **Check 2 — container:** `bd show <do-the-work-container-id>`. If it is `blocked`, the work stopped at a partial state.
262
+
263
+ **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):
264
+ ```
265
+ bd dep add <this-review-task-id> <every-open-slice-id> -t blocks
266
+ 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."
267
+ ```
268
+ 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.
269
+
270
+ This task will:
271
+ - 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).
272
+ - **Verify each closed slice against its Done-when, by name.** For every closed sub-task (`bd children <feature-issue-id>`, 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.
273
+ - write the review to `labs/claude-runs/{feature-issue-id}-review-summary.md`.
274
+ - append a punch-list of issues that must be addressed before merge.
275
+
276
+ **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:
277
+
278
+ 1. Pick a port and confirm it is free before you start anything: `lsof -ti tcp:<port>` 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.
279
+ 2. Capture each changed surface with `scripts/ux-eval-capture.ts`.
280
+ 3. Run [`boop-ux-purpose-judgement`](../../../cap-ux/skills/boop-ux-purpose-judgement/SKILL.md) first against the screenshot plus the rendered HTML, template, controller, and route. Run [`boop-ux-visual-judgement`](../../../cap-ux/skills/boop-ux-visual-judgement/SKILL.md) second against the screenshot.
281
+ 4. 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.
282
+ 5. **Stop the dev server before this task closes**, unless you reused one you found already running. Stop it by port: `lsof -ti tcp:<port> | xargs kill`. Never pattern-kill (`pkill -f nodemon`, `pkill -f tsx`, and the like) — a pattern matches every other worktree's server and the operator's own, and this project routinely has several alive at once. Never stop a server you did not start, including any port a concurrently running loop owns.
283
+
284
+ **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.
285
+
286
+ 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.
287
+
288
+ **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.
289
+
290
+ **Verify before closing the punch-list task:** Run `bd children <punch-list-task-id>` and confirm every item appears in the tree. Fix any missing item with `bd update <id> --parent <punch-list-task-id>` (remove conflicting deps first if needed). Only close once the tree is correct.
291
+
292
+ **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.
293
+
294
+ **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 (and in bd 1.1.2 `waits-for` is not even a valid `bd dep add -t` type). `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.
295
+
296
+ **Critical order — do these steps IN ORDER, do not close Punch-list until step 4 has been verified:**
297
+
298
+ 1. Create the punch-list fix children with `--parent <punch-list-task-id>`.
299
+ 2. **Verify children landed in the parent tree:** `bd children <punch-list-task-id>` must list every fix child. If any are missing, fix with `bd update <id> --parent <punch-list-task-id>` before continuing.
300
+ 3. **Add a blocking edge from Closeout onto EACH fix child** (keep the existing edge on the Punch-list task itself):
301
+ ```
302
+ bd dep add <closeout-task-id> <each-fix-child-id> -t blocks
303
+ ```
304
+ 4. **Verify every edge landed:** `bd dep list <closeout-task-id>` must name the Punch-list task **and every fix child**, each `via blocks`. Count them against `bd children <punch-list-task-id>`; a missing edge is an unguarded child.
305
+ 5. **Only after steps 1–4 have all completed and verified, close the Punch-list task itself.**
306
+
307
+ Closeout is a **sibling** of the punch-list, not the parent of these children, so the edges gate Closeout without holding the fix work — see the Waiting protocol on why a gate must never be an ancestor of what it waits on.
308
+
309
+ **Why the order matters (recurs):** if Punch-list closes before the child edges are added, the original `blocks` dep releases on Punch-list close — even though children are still open — and Closeout becomes ready against pre-fix state. The fix is the explicit verify-then-close sequence above; the Closeout step 6 precondition check (below) is the defensive net.
310
+
311
+ **If no punch-list fix children are filed** (Review came back clean), leave the single `blocks` dep on the Punch-list task — it correctly releases when Punch-list itself closes. Skip steps 3–4 and close Punch-list normally.
312
+
313
+ One boundary worth keeping: **fix children must be parented directly under the punch-list task, never nested deeper**, so that `bd children` enumerates every issue needing an edge in one call. And `bd show` does not display dep types — verify wiring with `bd dep list <closeout-task-id>`.
314
+
315
+ 6. **Close out the feature**: After the punch-list is resolved and the feature is complete, create a closeout task as a child of the feature issue. Wire its dependency on the punch-list task per the critical callout at the end of step 5 (a plain `blocks` edge on the punch-list task, plus one `blocks` edge per fix child once they exist).
316
+
317
+ **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:
318
+
319
+ **Check 1 — implementation:** `bd show <do-the-work-container-id>` and confirm status is `closed`.
320
+
321
+ **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.)
322
+
323
+ **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 implementation container 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):
324
+ ```
325
+ bd dep add <this-closeout-task-id> <do-the-work-container-id-if-open> -t blocks
326
+ bd dep add <this-closeout-task-id> <every-open-punch-list-child-id> -t blocks
327
+ 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."
328
+ ```
329
+ Closeout is a **sibling** of the implementation container and of the punch-list, 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.
330
+
331
+ When (and only when) the checks pass, the closeout task performs two activities:
332
+
333
+ **a. Archive the spec AND its planning artifacts:**
334
+ - Move the completed spec from `specs/` to `specs/history/` (create the directory if it doesn't exist).
335
+ - **Also archive the planning artifacts this feature produced in `specs/`** — the Understand-phase findings (`specs/{feature-name}-plan-findings.md`, step 1) and the engineering plan (`specs/{feature-name}-plan.md`, step 2). These belong in `specs/history/` alongside the spec once the work ships; leaving them at the `specs/` root is a recurring miss. Move every `-plan.md` / `-plan-findings.md` sibling of the spec you just archived. Their durable content lands in `reference/` via activity (b); the files themselves are historical record.
336
+ - If any aspects of the spec were NOT implemented (deferred, descoped, or punted), extract those into a new revised spec in `specs/` that captures just the remaining work. Reference the archived spec for full context.
337
+ - The archived spec and planning artifacts should be the full originals — do not edit or trim them.
338
+
339
+ **b. Integrate into reference:**
340
+ - Follow the [reference integration skill](../../../cap-product-design/skills/boop-product-design-reference-integration/SKILL.md) to blend the new feature's knowledge into existing `reference/` pages.
341
+ - This is NOT "create a new page for the feature" — it's merging knowledge into the existing concept pages (and creating new concept pages only when a genuinely new concept has no home).
342
+ - Update cross-links between affected reference pages.
343
+ - Remove or update any reference content that is now outdated.
344
+
345
+ **c. Hand off — the loop ends at the feature branch:**
346
+
347
+ Terminal state after Closeout: closeout commits pushed on `work/<feature-issue-id>`, beads synced, Closeout task closed. The root stays `open` for the Reconcile-and-finalize task (step 7) to close after its final sweep; a human then reviews and (if approved) merges.
348
+
349
+ **Scope boundary:** Authority ends at the feature branch. The branch (local and remote) stays intact after closeout so the reviewer can inspect it and so it remains a revert anchor. Merging, pushing to `main`, and branch deletion belong to the human reviewer — partly because concurrent worktrees may have other `work/<id>` branches in flight that need out-of-band coordination.
350
+
351
+ **Final actions of the closeout task (in order):**
352
+ 1. Commit the closeout edits (spec archive + reference updates) on the feature branch.
353
+ 2. `git push` the feature branch to its own remote ref.
354
+ 3. `bd dolt push` so beads state is synced.
355
+ 4. `bd close <closeout-task-id>`.
356
+ 5. **Do NOT close the feature/root issue here.** The Reconcile-and-finalize task (step 7) is the single chokepoint that closes the do-the-work container and the root, after a final sweep confirms every descendant is genuinely done. Closing the root here would strand Reconcile as an open child and skip that sweep. Leave the root `open`; Reconcile closes it.
357
+ 6. Stop. The loop picks up Reconcile-and-finalize next (it `blocks` on Closeout, which is closed by now).
358
+
359
+ 7. **Reconcile and finalize (the EXIT)**: This is the final lifecycle task, created and wired at Decompose time (step 3) as a childless leaf child of the ROOT, with a plain `blocks` edge onto Closeout. Its job is to confirm the feature is genuinely done, run any Closeout activity that never ran, and close the container + root — the last of which matters because bd never auto-closes a parent when its children close.
360
+
361
+ This task is **idempotent and safe to fire early.** Its first action is a sweep of the tree that tells it whether to finalize, wait, or hold — so running it against an unfinished tree costs nothing but a re-run.
362
+
363
+ **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:
364
+
365
+ - **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:
366
+ ```
367
+ bd gate create --type=human --blocks <reconcile-task-id> \
368
+ --reason="Cannot finalize; descendants <held issue IDs> need an operator decision. <summary>."
369
+ ```
370
+
371
+ - **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:
372
+ ```
373
+ bd dep add <reconcile-task-id> <each-in-flight-id> -t blocks
374
+ ```
375
+ 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.
376
+
377
+ - **Every descendant is closed.** Proceed to Step 2.
378
+
379
+ **Step 2 — finalize (only when every descendant is closed).** In order:
380
+
381
+ 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 [reference integration skill](../../../cap-product-design/skills/boop-product-design-reference-integration/SKILL.md), then `bd close <closeout-task-id>` if it is still open.
382
+
383
+ 2. **Commit + push + sync** any edits Step 2.1 produced:
384
+ ```
385
+ git add -A && git commit -m "chore(closeout): reconcile & finalize <feature>" # only if Step 2.1 produced edits
386
+ git push # feature branch — NOT main
387
+ bd dolt push
388
+ ```
389
+
390
+ 3. **Close the do-the-work container, then yourself, then the root — in that order.** This is the one ordering in the flow that is not interchangeable:
391
+ ```
392
+ bd close <container-id> # the do-the-work container: a feature, closes freely
393
+ bd close <reconcile-task-id> # close yourself — you are the last open child of the root
394
+ bd epic close-eligible # now every root child is closed, so the epic root closes
395
+ ```
396
+ 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`.
397
+
398
+ **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 running `bd epic close-eligible` afterwards is fine — and it is better than `bd close <root> --force`, which closes the root while leaving Reconcile open.
399
+
400
+ **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.
401
+
402
+ Then **verify**: `bd list --status=open` over the tree must return nothing.
403
+
404
+ **STOP HERE — do NOT merge to main.** Reconcile closes the *work-tracking* loop on the `work/<feature-issue-id>` branch. It never merges to main, pushes to main, or deletes the branch — the same hand-off rule as Closeout (step 6c), and for the same reasons (the human owns the merge; concurrent worktrees; the branch is the easy-revert anchor). The loop goes idle on "No ready issues"; the human reviews the branch and merges if approved.