@zigrivers/scaffold 3.32.0 → 3.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -19
- package/content/guides/knowledge-freshness/.diagrams/diagram-0.svg +1 -1
- package/content/guides/knowledge-freshness/.diagrams/manifest.json +1 -1
- package/content/guides/knowledge-freshness/index.html +9 -5
- package/content/guides/knowledge-freshness/index.md +5 -4
- package/content/guides/multi-agent/index.html +16 -15
- package/content/guides/multi-agent/index.md +16 -15
- package/content/guides/pipeline/index.html +2 -2
- package/content/guides/pipeline/index.md +2 -2
- package/content/knowledge/execution/worktree-management.md +4 -4
- package/content/knowledge/mcp-server/mcp-authentication.md +100 -0
- package/content/knowledge/mcp-server/mcp-deployment-patterns.md +119 -0
- package/content/knowledge/mcp-server/mcp-error-handling.md +131 -0
- package/content/knowledge/mcp-server/mcp-observability.md +125 -0
- package/content/knowledge/mcp-server/mcp-prompt-primitives.md +119 -0
- package/content/knowledge/mcp-server/mcp-protocol-fundamentals.md +130 -0
- package/content/knowledge/mcp-server/mcp-resource-design.md +111 -0
- package/content/knowledge/mcp-server/mcp-sdk-selection.md +136 -0
- package/content/knowledge/mcp-server/mcp-testing-strategies.md +127 -0
- package/content/knowledge/mcp-server/mcp-tool-design.md +125 -0
- package/content/knowledge/mcp-server/mcp-transport-patterns.md +122 -0
- package/content/knowledge/mcp-server/mcp-versioning.md +115 -0
- package/content/methodology/custom-defaults.yml +2 -0
- package/content/methodology/deep.yml +2 -0
- package/content/methodology/mcp-server-overlay.yml +88 -0
- package/content/methodology/mvp.yml +2 -0
- package/content/pipeline/build/multi-agent-resume.md +107 -11
- package/content/pipeline/build/multi-agent-start.md +104 -11
- package/content/pipeline/build/single-agent-resume.md +74 -8
- package/content/pipeline/build/single-agent-start.md +69 -12
- package/content/pipeline/environment/git-workflow.md +8 -2
- package/content/pipeline/finalization/materialize-plan-to-beads.md +473 -0
- package/content/pipeline/foundation/beads.md +6 -0
- package/content/pipeline/planning/implementation-plan-review.md +25 -0
- package/content/pipeline/planning/implementation-plan.md +75 -1
- package/content/pipeline/specification/mcp-tool-resource-contract.md +77 -0
- package/dist/cli/commands/adopt.d.ts.map +1 -1
- package/dist/cli/commands/adopt.js +33 -1
- package/dist/cli/commands/adopt.js.map +1 -1
- package/dist/cli/commands/init.d.ts +6 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +46 -3
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/init-flag-families.d.ts +6 -1
- package/dist/cli/init-flag-families.d.ts.map +1 -1
- package/dist/cli/init-flag-families.js +59 -2
- package/dist/cli/init-flag-families.js.map +1 -1
- package/dist/cli/init-flag-families.test.js +86 -1
- package/dist/cli/init-flag-families.test.js.map +1 -1
- package/dist/config/schema.d.ts +2310 -126
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +26 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/config/schema.test.js +75 -2
- package/dist/config/schema.test.js.map +1 -1
- package/dist/config/validators/index.d.ts.map +1 -1
- package/dist/config/validators/index.js +2 -0
- package/dist/config/validators/index.js.map +1 -1
- package/dist/config/validators/mcp-server.d.ts +4 -0
- package/dist/config/validators/mcp-server.d.ts.map +1 -0
- package/dist/config/validators/mcp-server.js +37 -0
- package/dist/config/validators/mcp-server.js.map +1 -0
- package/dist/config/validators/mcp-server.test.d.ts +2 -0
- package/dist/config/validators/mcp-server.test.d.ts.map +1 -0
- package/dist/config/validators/mcp-server.test.js +47 -0
- package/dist/config/validators/mcp-server.test.js.map +1 -0
- package/dist/core/assembly/materialize-plan-to-beads-assembly.test.d.ts +2 -0
- package/dist/core/assembly/materialize-plan-to-beads-assembly.test.d.ts.map +1 -0
- package/dist/core/assembly/materialize-plan-to-beads-assembly.test.js +75 -0
- package/dist/core/assembly/materialize-plan-to-beads-assembly.test.js.map +1 -0
- package/dist/e2e/project-type-overlays.test.js +83 -0
- package/dist/e2e/project-type-overlays.test.js.map +1 -1
- package/dist/project/adopt.d.ts.map +1 -1
- package/dist/project/adopt.js +3 -1
- package/dist/project/adopt.js.map +1 -1
- package/dist/project/detectors/coverage.test.js +1 -0
- package/dist/project/detectors/coverage.test.js.map +1 -1
- package/dist/project/detectors/disambiguate.d.ts.map +1 -1
- package/dist/project/detectors/disambiguate.js +6 -1
- package/dist/project/detectors/disambiguate.js.map +1 -1
- package/dist/project/detectors/disambiguate.test.js +18 -0
- package/dist/project/detectors/disambiguate.test.js.map +1 -1
- package/dist/project/detectors/index.d.ts.map +1 -1
- package/dist/project/detectors/index.js +2 -1
- package/dist/project/detectors/index.js.map +1 -1
- package/dist/project/detectors/mcp-server.d.ts +4 -0
- package/dist/project/detectors/mcp-server.d.ts.map +1 -0
- package/dist/project/detectors/mcp-server.js +91 -0
- package/dist/project/detectors/mcp-server.js.map +1 -0
- package/dist/project/detectors/mcp-server.test.d.ts +2 -0
- package/dist/project/detectors/mcp-server.test.d.ts.map +1 -0
- package/dist/project/detectors/mcp-server.test.js +115 -0
- package/dist/project/detectors/mcp-server.test.js.map +1 -0
- package/dist/project/detectors/types.d.ts +6 -2
- package/dist/project/detectors/types.d.ts.map +1 -1
- package/dist/project/detectors/types.js.map +1 -1
- package/dist/project/gitignore.d.ts.map +1 -1
- package/dist/project/gitignore.js +4 -0
- package/dist/project/gitignore.js.map +1 -1
- package/dist/project/gitignore.test.js +1 -0
- package/dist/project/gitignore.test.js.map +1 -1
- package/dist/types/config.d.ts +8 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/wizard/copy/core.d.ts.map +1 -1
- package/dist/wizard/copy/core.js +4 -0
- package/dist/wizard/copy/core.js.map +1 -1
- package/dist/wizard/copy/index.d.ts.map +1 -1
- package/dist/wizard/copy/index.js +2 -0
- package/dist/wizard/copy/index.js.map +1 -1
- package/dist/wizard/copy/mcp-server.d.ts +3 -0
- package/dist/wizard/copy/mcp-server.d.ts.map +1 -0
- package/dist/wizard/copy/mcp-server.js +40 -0
- package/dist/wizard/copy/mcp-server.js.map +1 -0
- package/dist/wizard/copy/types.d.ts +5 -1
- package/dist/wizard/copy/types.d.ts.map +1 -1
- package/dist/wizard/flags.d.ts +9 -1
- package/dist/wizard/flags.d.ts.map +1 -1
- package/dist/wizard/questions.d.ts +4 -2
- package/dist/wizard/questions.d.ts.map +1 -1
- package/dist/wizard/questions.js +37 -0
- package/dist/wizard/questions.js.map +1 -1
- package/dist/wizard/questions.test.js +107 -0
- package/dist/wizard/questions.test.js.map +1 -1
- package/dist/wizard/wizard.d.ts +3 -2
- package/dist/wizard/wizard.d.ts.map +1 -1
- package/dist/wizard/wizard.js +3 -1
- package/dist/wizard/wizard.js.map +1 -1
- package/package.json +1 -1
|
@@ -98,18 +98,75 @@ Before writing any code, verify the environment is ready:
|
|
|
98
98
|
|
|
99
99
|
### Beads Detection
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
**
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
-
|
|
109
|
-
|
|
110
|
-
-
|
|
111
|
-
|
|
112
|
-
|
|
101
|
+
The implementation plan is materialized into Beads issues by
|
|
102
|
+
`/scaffold:materialize-plan-to-beads` before the build phase. This block is the
|
|
103
|
+
**defensive preflight** that guarantees the tracker is populated and current
|
|
104
|
+
before any work is claimed — it never claims against an empty or stale tracker.
|
|
105
|
+
|
|
106
|
+
**Step 1 — compute `beads_usable`.** `beads_usable` is true only when **all**
|
|
107
|
+
hold: `.beads/` exists, `bd` is on `PATH`, `bd version` parses to **≥ 1.0.5**
|
|
108
|
+
(using a macOS/BSD-safe numeric compare — split major/minor/patch and compare
|
|
109
|
+
numerically, never rely on GNU `sort -V`), and `jq` is on `PATH`. Never write
|
|
110
|
+
`[ -d .beads ] && bd …` as a whole command — it returns exit 1 when `.beads/` is
|
|
111
|
+
absent and breaks callers under `set -e`; use an `if`.
|
|
112
|
+
|
|
113
|
+
**Step 2 — route on the decision table** (this is what prevents the "empty
|
|
114
|
+
tracker looks done" bug):
|
|
115
|
+
|
|
116
|
+
| Condition | Action |
|
|
117
|
+
|---|---|
|
|
118
|
+
| `.beads/` **absent** | Non-Beads project → drive the loop from the markdown playbook/plan (see "Markdown fallback" below). Do **not** call `bd`. |
|
|
119
|
+
| `.beads/` present but `beads_usable` is false (`bd`/`jq` missing or `bd` < 1.0.5) | **Fail closed.** Stop and tell the user to install/upgrade `bd` (≥ v1.0.5) and `jq`. Do **not** markdown-fall-back — Beads may already hold execution state, and a markdown re-run would re-execute completed work. |
|
|
120
|
+
| `beads_usable`, but the plan has **no** stable task IDs **and** Beads holds no plan-derived issues and no non-bootstrap claimed/closed work | Genuinely legacy plan → markdown loop, and emit "re-run planning to assign stable task IDs". Do **not** claim. |
|
|
121
|
+
| `beads_usable`, plan has no stable IDs **but** Beads already holds plausible build work (claimed/closed non-bootstrap issues) | **Fail closed** — markdown would bypass existing execution state. Require re-running planning + materialization. |
|
|
122
|
+
| `beads_usable`, contract **partially present or malformed** (some IDs present, or plan-derived issues already exist, but the contract doesn't fully parse) | **Fail closed.** Do **not** markdown-fall-back (would bypass existing plan-derived issues and diverge). Require planning to be re-run/fixed. |
|
|
123
|
+
| `beads_usable` **and a valid stable-ID contract** | **Always materialize first, then claim** (see Step 3). |
|
|
124
|
+
|
|
125
|
+
**Step 3 — `beads_usable` + valid contract → materialize, then claim:**
|
|
126
|
+
|
|
127
|
+
1. **Always invoke the canonical materializer:** `/scaffold:materialize-plan-to-beads`.
|
|
128
|
+
Run it unconditionally — do **not** gate it on a count or ID-set comparison
|
|
129
|
+
(every such gate misses content-only edits, partial imports, or stale deps).
|
|
130
|
+
The materializer is idempotent and a cheap no-op when already in sync; it is
|
|
131
|
+
the single source of the four-pass reconcile logic — this prompt **invokes**
|
|
132
|
+
it, it does not duplicate it. If the materializer returns non-zero (mid-run
|
|
133
|
+
failure), **fail closed** — stop and surface the error; do **not** claim and
|
|
134
|
+
do **not** markdown-fall-back past existing Beads state.
|
|
135
|
+
2. **Run the scoped claim loop.** Atomically claim the next ready **plan** task:
|
|
136
|
+
`TASK=$(bd ready --claim --has-metadata-key plan_task_id --json | jq -r '.id')`
|
|
137
|
+
- Scoping to `plan_task_id` keeps the loop from ever claiming the bootstrap
|
|
138
|
+
"initialize Beads" bead or a manually-created issue.
|
|
139
|
+
- This sets `assignee=$BEADS_ACTOR` (or your git user.name) and
|
|
140
|
+
`status=in_progress` in a single round-trip — no race window.
|
|
141
|
+
- If you need a specific task by ID instead, use `bd update <id> --claim`.
|
|
142
|
+
3. Implement following the TDD workflow below.
|
|
143
|
+
4. After the PR is merged, run `bd close <id>`.
|
|
144
|
+
5. Repeat the scoped claim (`bd ready --claim --has-metadata-key plan_task_id --json`)
|
|
145
|
+
until it returns no ready task, then run the **completion check**.
|
|
146
|
+
|
|
147
|
+
**Completion check (empty `bd ready` ≠ done).** An empty scoped-ready result does
|
|
148
|
+
**not** mean the build is finished — every remaining task could be blocked. On an
|
|
149
|
+
empty result, fetch all plan-derived tasks
|
|
150
|
+
(`bd list --all --limit 0 --has-metadata-key plan_task_id --json`) and classify
|
|
151
|
+
the remaining non-`closed` tasks:
|
|
152
|
+
|
|
153
|
+
- **All plan tasks `closed`** → genuinely **done**; exit gracefully.
|
|
154
|
+
- Otherwise classify **each** remaining non-`closed` task independently — do
|
|
155
|
+
**not** short-circuit on "any task is `in_progress`". Resolve blocker statuses
|
|
156
|
+
from an **unfiltered** `bd list --all --limit 0 --json` (manual blockers carry
|
|
157
|
+
no `plan_task_id`):
|
|
158
|
+
- **advancing** — the task is itself `in_progress`, **or** at least one of its
|
|
159
|
+
**transitive** blockers (walk the chain; bound the walk and reuse
|
|
160
|
+
`bd dep cycles` as a guard) is `in_progress`.
|
|
161
|
+
- **stalled** — not `in_progress` and **no** transitive blocker is
|
|
162
|
+
`in_progress` (open-but-unready behind inactive blockers, manually `blocked`,
|
|
163
|
+
or `deferred`).
|
|
164
|
+
- **All remaining tasks advancing** → exit gracefully.
|
|
165
|
+
- **Any task stalled** → **stop and report the stalled subset**, grouped by why
|
|
166
|
+
(open dependency, manual `blocked`, `deferred`), so the user can unblock them.
|
|
167
|
+
|
|
168
|
+
**Markdown fallback** (only when `.beads/` is **absent**, or for a genuinely
|
|
169
|
+
legacy plan per the table — never past existing Beads state):
|
|
113
170
|
1. Read `docs/implementation-playbook.md` as the primary task execution reference.
|
|
114
171
|
Fall back to `docs/implementation-plan.md` when no playbook is present.
|
|
115
172
|
2. Pick the first uncompleted task that has no unfinished dependencies.
|
|
@@ -26,7 +26,11 @@ parallel agents, CI pipeline, branch protection, and conflict prevention rules.
|
|
|
26
26
|
- docs/git-workflow.md — branching strategy, commit standards, rebase strategy,
|
|
27
27
|
PR workflow (8 sub-steps), task closure, agent crash recovery, branch protection,
|
|
28
28
|
conflict prevention, and worktree documentation
|
|
29
|
-
- scripts/setup-agent-worktree.sh — permanent worktree creation script
|
|
29
|
+
- scripts/setup-agent-worktree.sh — permanent worktree creation script. Create
|
|
30
|
+
worktrees project-local at `<repo>/.worktrees/<agent-slug>` (a single,
|
|
31
|
+
consistent location — never as repo siblings like `../<repo>-<agent>`). The
|
|
32
|
+
script must ensure `.worktrees/` is gitignored before creating the worktree so
|
|
33
|
+
the worktree's checkout is never accidentally committed.
|
|
30
34
|
- .github/workflows/ci.yml — CI workflow with lint and test jobs
|
|
31
35
|
- .github/pull_request_template.md — PR template with task ID format
|
|
32
36
|
- CLAUDE.md updated with Committing/PR Workflow, Task Closure, Parallel Sessions,
|
|
@@ -37,7 +41,9 @@ parallel agents, CI pipeline, branch protection, and conflict prevention rules.
|
|
|
37
41
|
- (mvp) Commit format is consistent (Beads: [bd-<id>] type(scope): desc. Non-Beads: type(scope): desc)
|
|
38
42
|
- (deep) PR workflow includes all 8 sub-steps (commit, AI review, rebase, push, create,
|
|
39
43
|
auto-merge with --delete-branch, watch CI, confirm merge)
|
|
40
|
-
- (deep) Worktree script creates permanent worktrees with workspace branches
|
|
44
|
+
- (deep) Worktree script creates permanent worktrees with workspace branches at
|
|
45
|
+
the project-local path `<repo>/.worktrees/<agent-slug>` and ensures
|
|
46
|
+
`.worktrees/` is gitignored
|
|
41
47
|
- (deep) If Beads: BEADS_ACTOR environment variable documented for agent identity
|
|
42
48
|
- (deep) CI workflow job name matches branch protection context
|
|
43
49
|
- (mvp) Branch cleanup documented for both single-agent and worktree-agent variants
|
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: materialize-plan-to-beads
|
|
3
|
+
description: Materialize the implementation plan into Beads issues before the build phase
|
|
4
|
+
summary: "When Beads is enabled, converts docs/implementation-plan.md into Beads issues — creating, updating, and reconciling tasks/stories/epics and their dependencies idempotently — so the build phase has a populated tracker to claim from."
|
|
5
|
+
phase: "finalization"
|
|
6
|
+
order: 1440
|
|
7
|
+
dependencies: [implementation-playbook]
|
|
8
|
+
outputs: []
|
|
9
|
+
conditional: "if-needed"
|
|
10
|
+
stateless: true
|
|
11
|
+
category: pipeline
|
|
12
|
+
knowledge-base: [task-tracking]
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Purpose
|
|
16
|
+
Materialize the frozen implementation plan into Beads (`bd`) issues so the build
|
|
17
|
+
phase has a populated tracker to claim work from. Reads
|
|
18
|
+
`docs/implementation-plan.md` and creates, updates, and reconciles the
|
|
19
|
+
corresponding epics, stories, tasks, and dependency edges in Beads.
|
|
20
|
+
|
|
21
|
+
## Inputs
|
|
22
|
+
- **`docs/implementation-plan.md`** (required) — the frozen plan emitting the
|
|
23
|
+
Plan Output Contract: stable task IDs (`T-001`…), stable container IDs
|
|
24
|
+
(`S-001`/`E-001`, deep only), and per-task / per-container metadata blocks
|
|
25
|
+
(`id`, `title`, `priority`, `wave`, `risk`, `story`/`epic` parent refs,
|
|
26
|
+
`depends_on`, `acceptance_criteria`). This is the design source of truth for
|
|
27
|
+
*what* the tasks are.
|
|
28
|
+
- **`docs/implementation-playbook.md`** (optional but expected) — wave ordering
|
|
29
|
+
and per-task context used to enrich issue descriptions and to compute the
|
|
30
|
+
wave-biased default priority.
|
|
31
|
+
- **`.beads/`** (required for materialization) — the Beads database initialized
|
|
32
|
+
by the foundation `beads.md` step. Its presence is the signal that this
|
|
33
|
+
project tracks work in Beads; its absence routes to the markdown fallback (see
|
|
34
|
+
Instructions → degradation split).
|
|
35
|
+
- **Scaffold state** (`.scaffold/config.yml` / methodology preset) — supplies
|
|
36
|
+
the methodology/depth that governs whether containers, waves, and risk
|
|
37
|
+
metadata are materialized.
|
|
38
|
+
|
|
39
|
+
## Expected Outputs
|
|
40
|
+
- Beads issues for every plan task (and, deep only, every story/epic container),
|
|
41
|
+
joined to the plan by metadata keys `plan_task_id` / `plan_story_id` /
|
|
42
|
+
`plan_epic_id`, with `--external-ref "plan:<id>"` stamped for human
|
|
43
|
+
traceability.
|
|
44
|
+
- All plan dependency edges materialized via `bd dep add`, with the
|
|
45
|
+
materializer-owned blocker set recorded per issue in `plan_deps`.
|
|
46
|
+
- A reconciled tracker: not-started issues updated to match the plan, started
|
|
47
|
+
(`in_progress`/`closed`) issues preserved, removed-from-plan not-started issues
|
|
48
|
+
retired, and stale duplicates collapsed to one canonical issue per join key.
|
|
49
|
+
- A deterministic one-line **summary report** (`materialize: …`).
|
|
50
|
+
- On success, a **run-stamped materialization-complete signal** that build
|
|
51
|
+
workers wait on before claiming.
|
|
52
|
+
- This step writes **no markdown documents** (`outputs: []`); its product is the
|
|
53
|
+
state of the Beads database plus the printed summary.
|
|
54
|
+
|
|
55
|
+
## Quality Criteria
|
|
56
|
+
- (mvp) Every plan task carrying a stable ID maps to exactly one not-started
|
|
57
|
+
Beads issue after a run (joined on `plan_task_id`).
|
|
58
|
+
- (mvp) The reconcile is idempotent — a second run over an unchanged plan
|
|
59
|
+
creates nothing and updates nothing (`materialize: 0 created, 0 updated, …`).
|
|
60
|
+
- (mvp) Only plan-derived issues are claimable: the build claim loop is scoped by
|
|
61
|
+
`--has-metadata-key plan_task_id`, so the bootstrap bead and manual issues are
|
|
62
|
+
never claimed.
|
|
63
|
+
- (mvp) Started issues (`in_progress`/`closed`) are never mutated except the two
|
|
64
|
+
narrow metadata-only exceptions (join-key cleanup, `ac_warn_hash`).
|
|
65
|
+
- (mvp) `bd dep cycles` reports no cycle after a run.
|
|
66
|
+
- (deep) Story/epic containers are created top-down (epics, then stories) with
|
|
67
|
+
correct `--parent` links; a depth-4 story with no epic parent is valid.
|
|
68
|
+
- (deep) Dependency edges reconcile to the plan: missing plan edges added, stale
|
|
69
|
+
`plan_deps`-owned edges removed, manual/external edges preserved.
|
|
70
|
+
- (deep) Tasks removed from the plan are retired when not-started; completed
|
|
71
|
+
(`closed`) issues stay linked for revert-safety.
|
|
72
|
+
|
|
73
|
+
## Methodology Scaling
|
|
74
|
+
Structure is a function of **methodology/depth only** — read it from scaffold
|
|
75
|
+
state, never probe `bd` for type availability. Both `-t story` and `-t epic` are
|
|
76
|
+
usable directly on the supported `bd` (≥ v1.0.5); mvp stays flat as a deliberate
|
|
77
|
+
*simplicity* choice, not because the type is unavailable. **Dependencies are
|
|
78
|
+
materialized at every depth** (the plan is a DAG even at mvp); depth scales only
|
|
79
|
+
the container hierarchy and `wave`/`risk` metadata.
|
|
80
|
+
|
|
81
|
+
(Note: the **`mvp` methodology preset disables this step entirely** — it doesn't
|
|
82
|
+
use Beads. The "mvp" rules below therefore apply to a **custom** low-depth build
|
|
83
|
+
that has Beads enabled, not to the mvp preset.)
|
|
84
|
+
|
|
85
|
+
- **mvp** → flat `-t task` issues **plus dependencies**. No story/epic
|
|
86
|
+
containers, no `--parent`; skip Pass 0b. Priority defaults to `-p 2`.
|
|
87
|
+
- **deep** → full container hierarchy + `wave`/`risk` metadata + wave-biased
|
|
88
|
+
priority. At depth 4, tasks are parented to **stories** (`-t story`) and
|
|
89
|
+
stories have no epic parent; at depth 5, the full **epic → story → task**
|
|
90
|
+
hierarchy with stories carrying an epic `--parent`.
|
|
91
|
+
- **custom:depth(1-5)** → dials between the two:
|
|
92
|
+
- Depth 1: flat `-t task` issues + dependencies; skip Pass 0b. Priority `-p 2`.
|
|
93
|
+
- Depth 2: flat `-t task` issues + dependencies; skip Pass 0b (sizing detail
|
|
94
|
+
differs upstream only). Priority `-p 2`.
|
|
95
|
+
- Depth 3: flat `-t task` issues + dependencies; skip Pass 0b. Wave-biased
|
|
96
|
+
priority if the plan assigns waves.
|
|
97
|
+
- Depth 4: tasks under `-t story` parents + `wave` metadata; a missing epic ref
|
|
98
|
+
on a story is valid, not a dangling ref. Pass 0b creates stories.
|
|
99
|
+
- Depth 5: full epic → story → task hierarchy + `risk` metadata + full
|
|
100
|
+
traceability; stories carry an epic `--parent`.
|
|
101
|
+
|
|
102
|
+
**Priority is wave-biased** so `bd ready` surfaces work in playbook order:
|
|
103
|
+
|
|
104
|
+
1. **Explicit plan priority wins**: `P0`→`-p 0`, `P1`→`-p 1`, `P2`→`-p 2`,
|
|
105
|
+
`P3`→`-p 3`.
|
|
106
|
+
2. **Otherwise bias by wave**: Wave 1 → `-p 1`, Wave 2 → `-p 2`, Wave 3+ →
|
|
107
|
+
`-p 3` (clamp at 3). Dependencies still gate readiness; the bias only orders
|
|
108
|
+
*among* ready tasks.
|
|
109
|
+
3. **No priority and no waves** (mvp) → default `-p 2`, rely on dependencies.
|
|
110
|
+
|
|
111
|
+
**Dependencies are always materialized**, at **every** depth (including mvp). The
|
|
112
|
+
plan is a DAG at every depth, and without the `bd dep add` edges the scoped
|
|
113
|
+
`bd ready --claim` would expose dependent tasks out of order. Depth governs only
|
|
114
|
+
**hierarchy** (story/epic parents), **wave**, and **risk** — never *whether*
|
|
115
|
+
dependencies exist.
|
|
116
|
+
|
|
117
|
+
## Mode Detection
|
|
118
|
+
This step is **idempotent and re-runnable**: running it again reconciles the
|
|
119
|
+
current plan against existing Beads issues rather than duplicating them. There is
|
|
120
|
+
no separate "fresh" vs. "update" code path — the same four passes
|
|
121
|
+
(duplicate-guard → container upsert → task upsert → dependency reconcile → stale
|
|
122
|
+
reconcile) cover both. Detect which case you are in by the join-key fetch:
|
|
123
|
+
|
|
124
|
+
- **No plan-derived issues exist yet** (`bd list --all --limit 0
|
|
125
|
+
--has-metadata-key plan_task_id --json` returns `[]`) → effectively a fresh
|
|
126
|
+
import: every pass falls through to "create".
|
|
127
|
+
- **Plan-derived issues already exist** → reconciliation: each pass looks up by
|
|
128
|
+
join key and creates / updates / retires as needed.
|
|
129
|
+
|
|
130
|
+
Because the build preflight invokes this step **unconditionally** before every
|
|
131
|
+
claim loop, design every pass to be a cheap **lookup-and-reconcile no-op when
|
|
132
|
+
already in sync** (a second run over an unchanged plan reports `0 created,
|
|
133
|
+
0 updated`).
|
|
134
|
+
|
|
135
|
+
## Update Mode Specifics
|
|
136
|
+
Reconcile plan changes into Beads **one-way (plan → Beads)** without clobbering
|
|
137
|
+
started work:
|
|
138
|
+
|
|
139
|
+
- **Preserve started state.** Never mutate the content fields, execution status,
|
|
140
|
+
claims, or assignees of issues in a **started** status (`in_progress` or
|
|
141
|
+
`closed`). Freely update the content fields
|
|
142
|
+
(title/description/priority/parent/wave/risk) of **not-started**
|
|
143
|
+
(`open`/`blocked`/`deferred`) issues to match the plan.
|
|
144
|
+
- **Two narrow metadata-only exceptions** to "never touch started issues" — each
|
|
145
|
+
touches a single metadata tag, is reported, and changes no content field,
|
|
146
|
+
status, claim, or assignee:
|
|
147
|
+
1. **Join-key cleanup (Pass 0a)** — a started *non-canonical duplicate* may have
|
|
148
|
+
its own join key `--unset-metadata`'d to restore the one-key invariant.
|
|
149
|
+
2. **AC-drift bookkeeping (Pass 1)** — an `in_progress` issue whose plan AC
|
|
150
|
+
changed may get `--set-metadata ac_warn_hash=…` so the warning comment posts
|
|
151
|
+
once per distinct change, not every run.
|
|
152
|
+
- **Triggers handled:** new task IDs added (created), content-only edits
|
|
153
|
+
(title/AC/priority/parent/deps changed with no ID change → not-started issues
|
|
154
|
+
updated, edges reconciled), dependency add/remove, and tasks/containers removed
|
|
155
|
+
from the plan (retired if not-started, flagged if `in_progress`, left linked if
|
|
156
|
+
`closed`).
|
|
157
|
+
- A dependency-blocked task stays stored `open` — blockers affect *computed*
|
|
158
|
+
readiness, not stored status; a stored `blocked`/`deferred` is an explicit
|
|
159
|
+
signal that descriptive-field updates neither set nor clear.
|
|
160
|
+
|
|
161
|
+
## Instructions
|
|
162
|
+
|
|
163
|
+
Execute the steps below in order. All `bd` commands use the verified v1.0.5
|
|
164
|
+
surface — do **not** invent flags (in particular, there is **no `bd list
|
|
165
|
+
--external-ref` filter**; join by metadata keys only).
|
|
166
|
+
|
|
167
|
+
### Gate on `beads_usable` (version + tooling), then split degradation
|
|
168
|
+
|
|
169
|
+
Compute a single shared `beads_usable` check. It is true **only** when *all* of:
|
|
170
|
+
|
|
171
|
+
1. `.beads/` exists.
|
|
172
|
+
2. `bd` is on PATH (`command -v bd`).
|
|
173
|
+
3. `bd version` parses to **≥ 1.0.5**, using a **macOS/BSD-portable** numeric
|
|
174
|
+
compare (do **not** depend on GNU `sort -V`). Split major/minor/patch and
|
|
175
|
+
compare numerically.
|
|
176
|
+
4. `jq` is on PATH (`command -v jq`).
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
beads_usable() {
|
|
180
|
+
[ -d .beads ] || return 1
|
|
181
|
+
command -v bd >/dev/null 2>&1 || return 1
|
|
182
|
+
command -v jq >/dev/null 2>&1 || return 1
|
|
183
|
+
# Portable >= 1.0.5 compare — no `sort -V` (absent on macOS/BSD).
|
|
184
|
+
local ver have_major have_minor have_patch
|
|
185
|
+
ver=$(bd version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
|
|
186
|
+
[ -n "$ver" ] || return 1
|
|
187
|
+
IFS=. read -r have_major have_minor have_patch <<EOF
|
|
188
|
+
$ver
|
|
189
|
+
EOF
|
|
190
|
+
# Compare against floor 1.0.5
|
|
191
|
+
if [ "$have_major" -gt 1 ]; then return 0; fi
|
|
192
|
+
if [ "$have_major" -lt 1 ]; then return 1; fi
|
|
193
|
+
if [ "$have_minor" -gt 0 ]; then return 0; fi
|
|
194
|
+
if [ "$have_minor" -lt 0 ]; then return 1; fi
|
|
195
|
+
[ "$have_patch" -ge 5 ]
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
> **Never** write `[ -d .beads ] && bd …` as a whole command — it returns exit 1
|
|
200
|
+
> when `.beads/` is absent and breaks callers under `set -e`. Always branch on
|
|
201
|
+
> the function's return code with an explicit `if`.
|
|
202
|
+
|
|
203
|
+
**Degradation splits on whether `.beads/` exists** — markdown is safe *only* when
|
|
204
|
+
there is no Beads state to diverge from:
|
|
205
|
+
|
|
206
|
+
- **`.beads/` absent** → genuinely a non-Beads project. Stop materializing and
|
|
207
|
+
let the build drive from the markdown plan/playbook. (Nothing to do here; this
|
|
208
|
+
prompt only runs when Beads is enabled.)
|
|
209
|
+
- **`.beads/` present but `bd`/`jq` missing or too old** (`beads_usable` false)
|
|
210
|
+
→ **FAIL CLOSED.** Do **not** markdown-fallback — the tracker may already hold
|
|
211
|
+
execution state (claimed/closed tasks) and the markdown loop would re-run
|
|
212
|
+
completed work. Stop and tell the user to install/upgrade `bd` (≥ v1.0.5) and
|
|
213
|
+
`jq`.
|
|
214
|
+
|
|
215
|
+
Once `beads_usable` is true, resolve **methodology/depth** from scaffold state
|
|
216
|
+
(see Methodology Scaling) and parse the plan's task / container / dependency
|
|
217
|
+
blocks per the Plan Output Contract. Then run the passes below.
|
|
218
|
+
|
|
219
|
+
### The Retire convention
|
|
220
|
+
|
|
221
|
+
Whenever a pass removes an issue from the plan-derived set (Pass 0a duplicate
|
|
222
|
+
close, Pass 3 stale close), do it in **this exact order**, against the issue's
|
|
223
|
+
**own** join key (`plan_task_id` for a task, `plan_story_id` for a story,
|
|
224
|
+
`plan_epic_id` for an epic — resolved from the issue's type, never hardcoded):
|
|
225
|
+
|
|
226
|
+
1. `bd label add <id> <stale-label>` — `stale:duplicate` or
|
|
227
|
+
`stale:removed-from-plan`. The **label is applied first** and is what marks the
|
|
228
|
+
issue as *retired-by-the-materializer* (vs. closed as completed work).
|
|
229
|
+
2. `bd close <id> --reason "…"` — skipped if already `closed`.
|
|
230
|
+
3. **then** `bd update <id> --unset-metadata <join-key>`.
|
|
231
|
+
|
|
232
|
+
The key is unset **last** so a failure at any step is **resumable**: the issue
|
|
233
|
+
keeps its join key (still found next run) and carries the `stale:*` label
|
|
234
|
+
(recognized as retire-pending, not completed work). Each run re-applies whatever
|
|
235
|
+
steps are missing for any join-keyed issue bearing a `stale:*` label. Because the
|
|
236
|
+
`stale:*` label distinguishes a retired close from a genuine completion, two later
|
|
237
|
+
rules key off it: (a) Pass 0a canonical selection **excludes any `stale:*`-labelled
|
|
238
|
+
issue**; and (b) Pass 3 leaves a completed `closed` issue linked **only when it
|
|
239
|
+
carries no `stale:*` label**.
|
|
240
|
+
|
|
241
|
+
### Pass 0a — Duplicate guard (always)
|
|
242
|
+
|
|
243
|
+
Before any upsert, fetch the plan-derived issues once and group by each join key:
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
bd list --all --limit 0 --has-metadata-key plan_task_id --json # tasks
|
|
247
|
+
bd list --all --limit 0 --has-metadata-key plan_story_id --json # stories (deep)
|
|
248
|
+
bd list --all --limit 0 --has-metadata-key plan_epic_id --json # epics (depth 5)
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
For any join key held by **more than one** issue (failed/manual/concurrent prior
|
|
252
|
+
import), restore the **exactly-one-issue-per-key invariant**:
|
|
253
|
+
|
|
254
|
+
1. **Pick the canonical issue.** First **exclude any issue already bearing a
|
|
255
|
+
`stale:*` label** (retire-pending leftovers can never win). Among the rest,
|
|
256
|
+
the ordering is total:
|
|
257
|
+
- if **exactly one** duplicate is `in_progress` → it is canonical (active work
|
|
258
|
+
wins over any `closed` or not-started copy);
|
|
259
|
+
- else if any is `closed` → canonical is the **oldest** `closed` one;
|
|
260
|
+
- else the **oldest not-started** issue (lowest `created_at`, ties by `id`).
|
|
261
|
+
|
|
262
|
+
The **only** unorderable case is **two or more `in_progress` duplicates** for
|
|
263
|
+
the same plan ID — there is no safe way to pick which active effort to detach,
|
|
264
|
+
so **FAIL CLOSED** and report. (An `in_progress` + `closed` mix is *not* a
|
|
265
|
+
conflict: `in_progress` wins.)
|
|
266
|
+
2. **Not-started non-canonical duplicates** → retire them per the **Retire
|
|
267
|
+
convention** (`stale:duplicate` label → close → unset the issue's own
|
|
268
|
+
`<join-key>`), so they leave the plan-derived set and are never re-detected.
|
|
269
|
+
3. **Started non-canonical duplicates** → do **not** close or mutate fields; only
|
|
270
|
+
`bd update <id> --unset-metadata <join-key>` to restore key uniqueness, and
|
|
271
|
+
**report** them for human review.
|
|
272
|
+
|
|
273
|
+
After Pass 0a, exactly one canonical issue retains each `plan_*_id`, so every
|
|
274
|
+
later bulk fetch, map, dep reconcile, stale pass, and the scoped claim loop can
|
|
275
|
+
rely on the one-key invariant.
|
|
276
|
+
|
|
277
|
+
### Pass 0b — Container upsert (deep only), top-down
|
|
278
|
+
|
|
279
|
+
Skip entirely at mvp / depth 1–3. **Bulk-fetch containers once** (two queries),
|
|
280
|
+
build in-memory `plan_epic_id → id` and `plan_story_id → id` maps, then process
|
|
281
|
+
**epics first, then stories** so each child can reference its parent's resolved
|
|
282
|
+
Beads ID via `--parent`. Containers carry the **same fields as tasks** (title,
|
|
283
|
+
wave-biased `-p`, description/AC, and — for stories — `--parent`).
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
EPIC_MAP_JSON=$(bd list --all --limit 0 --has-metadata-key plan_epic_id --json \
|
|
287
|
+
| jq 'map({ (.metadata.plan_epic_id): .id }) | add // {}')
|
|
288
|
+
STORY_MAP_JSON=$(bd list --all --limit 0 --has-metadata-key plan_story_id --json \
|
|
289
|
+
| jq 'map({ (.metadata.plan_story_id): .id }) | add // {}')
|
|
290
|
+
|
|
291
|
+
# Epic E-001 (depth 5): look up in the epic map; create if absent.
|
|
292
|
+
ID=$(printf '%s' "$EPIC_MAP_JSON" | jq -r '."E-001" // empty')
|
|
293
|
+
[ -z "$ID" ] && ID=$(bd create "<epic title>" -t epic -p <prio> \
|
|
294
|
+
--description "<epic body>" \
|
|
295
|
+
--metadata '{"plan_epic_id":"E-001","wave":"<n>","risk":"<type>"}' \
|
|
296
|
+
--external-ref "plan:E-001" --json | jq -r '.id')
|
|
297
|
+
|
|
298
|
+
# Story S-001: same lookup-or-create against the story map; wire --parent only
|
|
299
|
+
# when an epic ref is present (depth 4 stories have NO epic parent).
|
|
300
|
+
ID=$(printf '%s' "$STORY_MAP_JSON" | jq -r '."S-001" // empty')
|
|
301
|
+
[ -z "$ID" ] && ID=$(bd create "<story title>" -t story -p <prio> \
|
|
302
|
+
${EPIC_PARENT_ID:+--parent "$EPIC_PARENT_ID"} \
|
|
303
|
+
--description "<story body>" \
|
|
304
|
+
--metadata '{"plan_story_id":"S-001","wave":"<n>","risk":"<type>"}' \
|
|
305
|
+
--external-ref "plan:S-001" --json | jq -r '.id')
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Container upsert obeys the **same not-started-vs-started rules as Pass 1**: a
|
|
309
|
+
not-started (`open`/`blocked`/`deferred`) epic/story is updated to match the plan
|
|
310
|
+
(`bd update <id> --title … -d … -p … --parent … --set-metadata wave=… --set-metadata
|
|
311
|
+
risk=…`); a started (`in_progress`/`closed`) container is left untouched.
|
|
312
|
+
Reparenting a not-started story uses `bd update <id> --parent <new>`.
|
|
313
|
+
|
|
314
|
+
### Pass 1 — Task upsert
|
|
315
|
+
|
|
316
|
+
**Fetch existing plan-derived task issues once**, build an in-memory
|
|
317
|
+
`plan_task_id → issue` map (one `bd` process for the whole plan, not one per
|
|
318
|
+
task), then for each plan task (parent IDs already resolved by Pass 0b):
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
TASK_MAP_JSON=$(bd list --all --limit 0 --has-metadata-key plan_task_id --json \
|
|
322
|
+
| jq 'map({ (.metadata.plan_task_id): . }) | add // {}')
|
|
323
|
+
EXISTING=$(printf '%s' "$TASK_MAP_JSON" | jq -r '."T-001" // empty')
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
1. **Create if absent:**
|
|
327
|
+
```bash
|
|
328
|
+
bd create "<title>" -t task -p <prio> \
|
|
329
|
+
${PARENT_ID:+--parent "$PARENT_ID"} \
|
|
330
|
+
--metadata '{"plan_task_id":"T-001","wave":"<n>","risk":"<type>"}' \
|
|
331
|
+
--external-ref "plan:T-001" \
|
|
332
|
+
--description "<body incl. acceptance criteria + traceability>"
|
|
333
|
+
```
|
|
334
|
+
2. **If present, branch on stored status** (not-started vs. started):
|
|
335
|
+
- **`open` / `blocked` / `deferred`** → **update fields to match the plan,
|
|
336
|
+
including wave/risk metadata** so nothing drifts:
|
|
337
|
+
```bash
|
|
338
|
+
bd update <id> --title "<title>" -d "<body>" -p <prio> \
|
|
339
|
+
${PARENT_ID:+--parent "$PARENT_ID"} \
|
|
340
|
+
--set-metadata wave=<n> --set-metadata risk=<type>
|
|
341
|
+
```
|
|
342
|
+
These are not-started states; mutating their fields never changes execution
|
|
343
|
+
status or readiness, and never unblocks/disturbs a stored `blocked`/`deferred`
|
|
344
|
+
signal.
|
|
345
|
+
- **`in_progress`** → **do not mutate fields.** If the plan's AC/description
|
|
346
|
+
changed, post a warning **idempotently**: compute a hash of the current plan
|
|
347
|
+
AC text; if it differs from the issue's `ac_warn_hash` metadata, post one
|
|
348
|
+
`bd comment <id> "Plan AC changed since this task was started: …"` then record
|
|
349
|
+
the new hash with the targeted `bd update <id> --set-metadata
|
|
350
|
+
ac_warn_hash=<hash>`. Re-runs with the same AC post nothing.
|
|
351
|
+
- **`closed`** → leave **entirely untouched** (do not recreate, do not edit).
|
|
352
|
+
|
|
353
|
+
### Pass 2 — Dependency reconcile
|
|
354
|
+
|
|
355
|
+
Run after all issues exist (forward references resolved). **Read existing edges
|
|
356
|
+
from a single bulk fetch**, not per task — `bd list --all --limit 0 --json`
|
|
357
|
+
carries an inline `dependencies` array (`depends_on_id`, `type`) on each issue.
|
|
358
|
+
Reconcile in-memory against the plan's `depends_on` lists; only the mutating
|
|
359
|
+
calls need per-edge invocations.
|
|
360
|
+
|
|
361
|
+
**Materializer-owned edges are tracked explicitly** in the `plan_deps` metadata
|
|
362
|
+
key (a sorted CSV of blocker task IDs, e.g. `"T-003,T-007"`) — *not* inferred
|
|
363
|
+
from endpoint types. "Both endpoints are plan-derived" is **not** proof of
|
|
364
|
+
ownership: an agent may add an execution blocker during the build, and deleting
|
|
365
|
+
it just because it is absent from the markdown plan would corrupt Beads-owned
|
|
366
|
+
state.
|
|
367
|
+
|
|
368
|
+
For each **`open` / `blocked` / `deferred`** dependent (never mutate
|
|
369
|
+
`in_progress`/`closed`), reconcile against the plan's `depends_on` list:
|
|
370
|
+
|
|
371
|
+
- **Add** plan edges not already present: `bd dep add <blocked-id> <blocker-id>`
|
|
372
|
+
(re-adding is a no-op).
|
|
373
|
+
- **Remove** an edge **only** when it is **in the prior `plan_deps`** (materializer
|
|
374
|
+
created it) **and absent from the current plan**: `bd dep remove <blocked-id>
|
|
375
|
+
<blocker-id>`. Edges **not** in `plan_deps` (manual/external blockers) are
|
|
376
|
+
preserved untouched; a manual edge that collides with a plan edge is kept and
|
|
377
|
+
noted in the summary, not deleted.
|
|
378
|
+
- **Rewrite `plan_deps` authoritatively** at the end of each issue's reconcile:
|
|
379
|
+
|
|
380
|
+
> `plan_deps' = (prior plan_deps ∩ current-plan deps) ∪ (edges this run added)`
|
|
381
|
+
|
|
382
|
+
Keep still-valid owned edges, add the ones just created, and **explicitly
|
|
383
|
+
exclude any pre-existing edge that was *not* already in `plan_deps`** — even if
|
|
384
|
+
it collides with a current plan dep (that edge stays manual-owned so a later
|
|
385
|
+
plan removal never deletes it). Write with `bd update <id> --set-metadata
|
|
386
|
+
plan_deps=<csv>` (or `bd update <id> --unset-metadata plan_deps` when empty).
|
|
387
|
+
|
|
388
|
+
A per-issue read, if ever needed, is `bd dep list <id> --direction down --json`
|
|
389
|
+
(verified: `down` = what `<id>` depends on, i.e. its blockers; `up` returns
|
|
390
|
+
dependents and is wrong here). No manual status reconciliation is needed after
|
|
391
|
+
add/remove — readiness is computed from open blockers.
|
|
392
|
+
|
|
393
|
+
**Detect cycles after applying:** `bd dep cycles` — surface any cycle as an error
|
|
394
|
+
(the plan DAG is validated upstream, but a manual edit could reintroduce one).
|
|
395
|
+
|
|
396
|
+
### Pass 3 — Stale reconcile (tasks/containers removed from the plan)
|
|
397
|
+
|
|
398
|
+
List every plan-derived issue (the three `--has-metadata-key` queries) and diff
|
|
399
|
+
IDs against the current plan:
|
|
400
|
+
|
|
401
|
+
- ID no longer in the plan and issue **not started** (`open`/`blocked`/
|
|
402
|
+
`deferred`) → **retire it** per the Retire convention
|
|
403
|
+
(`stale:removed-from-plan` label → `bd close <id> --reason "Removed from
|
|
404
|
+
implementation plan (was <id>)"` → unset the issue's own `<join-key>`), so it
|
|
405
|
+
drops out of future queries and can never be misread as "started/dropped".
|
|
406
|
+
- Such an issue **`in_progress`** → **do not auto-close.** Report it in the
|
|
407
|
+
summary for human attention (it may be mid-flight work the plan dropped by
|
|
408
|
+
mistake). Leave its join key intact so it stays visible until a human decides.
|
|
409
|
+
- Such an issue **`closed` as genuinely-completed work** (no `stale:*` label) →
|
|
410
|
+
**leave it entirely untouched** (do **not** unset its join key). Plan IDs are
|
|
411
|
+
never reused, so a later revert restoring the task must let Pass 1 find this
|
|
412
|
+
issue by `plan_task_id` and recognize it as already `closed` — not recreate an
|
|
413
|
+
`open` issue and force redone work. (A `closed` issue that **does** carry
|
|
414
|
+
`stale:removed-from-plan` is a retire-pending leftover — the Retire convention
|
|
415
|
+
finishes unsetting its key.)
|
|
416
|
+
|
|
417
|
+
### Summary report (deterministic)
|
|
418
|
+
|
|
419
|
+
Print exactly one line:
|
|
420
|
+
|
|
421
|
+
```
|
|
422
|
+
materialize: C created, U updated, K unchanged,
|
|
423
|
+
S skipped (in_progress/closed — started, not mutated),
|
|
424
|
+
D deps added, R deps removed,
|
|
425
|
+
X stale closed, W stale flagged for review
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
where each letter is the count accumulated across the passes. A re-run over an
|
|
429
|
+
unchanged plan prints `0 created, 0 updated` (proof of idempotency).
|
|
430
|
+
|
|
431
|
+
### Run-stamped materialization-complete signal
|
|
432
|
+
|
|
433
|
+
On **success**, set a durable **materialization-complete signal** that build
|
|
434
|
+
workers wait on before their first claim. The signal **must be run-specific** —
|
|
435
|
+
carry a `run_id` or the current plan hash (e.g. `materialized_at=<run_id>` /
|
|
436
|
+
`plan_hash=<sha>` as metadata on the project merge-slot / bootstrap bead, or a
|
|
437
|
+
workspace marker file). **Clear/overwrite any prior signal *before* acquiring the
|
|
438
|
+
merge-slot lock**, so a stale signal from a previous pipeline run (or a pre-update
|
|
439
|
+
plan) can never let workers race ahead of a fresh re-materialization.
|
|
440
|
+
|
|
441
|
+
In the sequential finalization path there is no concurrency, but still set the
|
|
442
|
+
run-stamped signal so the multi-agent build preflight (which gates workers on a
|
|
443
|
+
signal matching **this run's** id/hash) is satisfied without re-running the
|
|
444
|
+
materializer. The multi-agent orchestrator runs this whole procedure **once per
|
|
445
|
+
wave under the merge-slot lock** (acquire via a real acquisition loop, verify
|
|
446
|
+
ownership with `bd merge-slot check --json`, run the idempotent materializer, set
|
|
447
|
+
the signal, then `bd merge-slot release` via a `trap … EXIT INT TERM`); workers
|
|
448
|
+
never materialize — they only wait for the signal, then claim.
|
|
449
|
+
|
|
450
|
+
## After This Step
|
|
451
|
+
|
|
452
|
+
The implementation plan is now **materialized into Beads** — every plan task
|
|
453
|
+
(and, on deep builds, every story/epic and dependency edge) exists as a `bd`
|
|
454
|
+
issue joined to the plan by `plan_task_id` / `plan_story_id` / `plan_epic_id`.
|
|
455
|
+
Print the summary line and tell the user:
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
**Plan materialized into Beads.** The tracker is populated and ready for the
|
|
459
|
+
build phase.
|
|
460
|
+
|
|
461
|
+
**Summary:** [paste the `materialize: …` line]
|
|
462
|
+
|
|
463
|
+
**Next:**
|
|
464
|
+
- **Single agent:** run `/scaffold:single-agent-start` to begin the scoped claim
|
|
465
|
+
loop (`bd ready --claim --has-metadata-key plan_task_id`).
|
|
466
|
+
- **Multiple agents in parallel:** run `/scaffold:multi-agent-start` — the
|
|
467
|
+
orchestrator re-runs this materializer once under the merge-slot lock before
|
|
468
|
+
fan-out, then workers wait for the completion signal and claim.
|
|
469
|
+
|
|
470
|
+
**Re-running:** this step is idempotent — after any plan update, re-run
|
|
471
|
+
`/scaffold:materialize-plan-to-beads` to reconcile new/changed/removed tasks and
|
|
472
|
+
dependencies into Beads without clobbering started work.
|
|
473
|
+
---
|
|
@@ -178,6 +178,12 @@ Enable when: project uses Beads task tracking methodology (user selects Beads du
|
|
|
178
178
|
setup), or user explicitly enables structured task management. Skip when: user prefers
|
|
179
179
|
GitHub Issues, Linear, or another task tracker, or explicitly declines Beads setup.
|
|
180
180
|
|
|
181
|
+
Note: this step only initializes the tracker — it does **not** create your
|
|
182
|
+
implementation tasks. Beads stays empty of plan tasks until the finalization step
|
|
183
|
+
`/scaffold:materialize-plan-to-beads` converts `docs/implementation-plan.md` into
|
|
184
|
+
Beads issues just before the build phase. An empty `bd ready` right after this
|
|
185
|
+
step is therefore expected, not a problem.
|
|
186
|
+
|
|
181
187
|
## Mode Detection
|
|
182
188
|
Update mode if `.beads/` contains a populated database (look for `.beads/embeddeddolt/`
|
|
183
189
|
in the default embedded-Dolt layout, `.beads/dolt/` in server mode, or any `.beads/*.db`
|
|
@@ -46,8 +46,33 @@ and produce a structured coverage matrix and review summary.
|
|
|
46
46
|
- (deep) Every task has verb-first description, >= 1 input file reference, >= 1 acceptance criterion, and defined output artifact
|
|
47
47
|
- (mvp) Every task complies with agent executability rules (3-file, 150-line, single-concern, decision-free, test co-location)
|
|
48
48
|
- (mvp) Tasks exceeding limits have explicit `<!-- agent-size-exception -->` justification
|
|
49
|
+
- (mvp) Plan Output Contract holds: every task and container has an ID; IDs are unique and stable; all parent and `depends_on` refs resolve; the dependency graph is acyclic; field blocks parse
|
|
49
50
|
- (depth 4+) Independent model reviews completed and reconciled
|
|
50
51
|
|
|
52
|
+
## Plan Output Contract Validation
|
|
53
|
+
|
|
54
|
+
The plan is consumed by an automated materializer that needs stable join keys
|
|
55
|
+
and a parseable structure (see the **Plan Output Contract** section in
|
|
56
|
+
`implementation-plan.md`). This review MUST verify that contract holds, rejecting
|
|
57
|
+
any plan that violates it:
|
|
58
|
+
|
|
59
|
+
- **ID presence** — every task has a `T-NNN` ID, and (deep) every story has an
|
|
60
|
+
`S-NNN` ID and every epic an `E-NNN` ID. Flag any item missing an ID.
|
|
61
|
+
- **Uniqueness and stability** — task, story, and epic IDs are each **unique**
|
|
62
|
+
and **stable** (no two items share an ID; no ID changed for an item that
|
|
63
|
+
already existed in a prior plan revision; IDs are never reused for a different
|
|
64
|
+
task/container).
|
|
65
|
+
- **Referential integrity (no dangling refs)** — every `story` / `epic` parent
|
|
66
|
+
reference **and every `depends_on` reference** **resolves** to a declared ID in
|
|
67
|
+
the plan. Report any **dangling** ref (a parent or `depends_on` entry that does
|
|
68
|
+
not resolve to a declared ID) as a blocking finding. A story with no `epic`
|
|
69
|
+
parent is valid, not dangling.
|
|
70
|
+
- **Acyclicity** — the dependency graph formed by all `depends_on` edges is a
|
|
71
|
+
**DAG**: there are **no cycles**. Walk the graph and report any cycle.
|
|
72
|
+
- **Parseability** — every per-task and per-container fenced metadata block
|
|
73
|
+
parses cleanly under the canonical serialization (well-formed key/value block
|
|
74
|
+
with the required fields present).
|
|
75
|
+
|
|
51
76
|
## Methodology Scaling
|
|
52
77
|
- **deep**: Full multi-pass review with multi-model validation. AC coverage
|
|
53
78
|
matrix. Independent Codex/Gemini dispatches. Detailed reconciliation report.
|