The translation layer between the spec you wrote and the code you'll generate
Lab 5 — Story Decomposition & Task Generation
The translation layer
A story without acceptance criteria is a wish. A task without a spec reference is a guess. Both produce wrong code in Lab 6.
Surface: Antigravity (Developer agent · Amelia) + editor
Artifacts: docs/stories.md + docs/tasks.md
The rule: Lab 6 quality is entirely determined by Lab 5 quality.
⚠️ How this maps to BMAD V6
The guide says "switch to the Developer persona and decompose the spec." In BMAD V6 that's the Developer agent (Amelia) running bmad-create-epics-and-stories (trigger CE), with the readiness gate (IR) before code.
| What you produce | V6 workflow | Agent | Output |
|---|---|---|---|
| Epics + stories + AC | bmad-create-epics-and-stories (CE) |
Developer (Amelia) | epic/story files |
| Readiness gate | bmad-check-implementation-readiness (IR) |
PM/Dev | PASS/CONCERNS/FAIL |
We assemble the output into
docs/stories.mdanddocs/tasks.md— the files Lab 6 works through. The content below (4 epics, the 12 tasks) is what you produce; V6 is how.
Phase 1 · Setup + INVEST recap (8 min)
Step 1 — Create the files
🪟 cd $env:USERPROFILE\stacklog-workshop ; New-Item docs\stories.md, docs\tasks.md
🍎🐧 cd ~/stacklog-workshop && touch docs/stories.md docs/tasks.md
Step 2 — Load the Developer agent
Open Antigravity's agent menu → Developer (Amelia). Run CE (bmad-create-epics-and-stories). It decomposes the signed-off spec into epics and stories with acceptance criteria.
The agent's job: every story passes INVEST, has 3–5 testable AC, and every task names a target file, signature, I/O, and spec §ref — ordered so backend comes before the frontend that calls it.
Step 3 — INVEST recap
Validate every generated story against these six:
| Means | StackLog test | |
|---|---|---|
| I Independent | builds/ships without another story first | can we demo it in isolation? |
| N Negotiable | details can still change | is this the simplest version? |
| V Valuable | value to a named persona | which persona benefits, and how? |
| E Estimable | roughly sizable | can we estimate it in hours? |
| S Small | one sprint — ideally one Lab-6 task | can one dev do it in ≤60 min? |
| T Testable | verifiable AC | can we write a failing test from each AC now? |
The most important letter for Lab 6 is S — Small. Test: "Can one agent prompt generate this story's code?" If it'd need multiple prompts, split it.
Phase 2 · Generate epics & stories (37 min)
Step 4 — The four target epics
| # | Epic | Stories | Lab 6 task sets | Spec § |
|---|---|---|---|---|
| 1 | Entry feed | display all entries newest-first; pagination | A: GET /entries · B: EntryFeed + EntryCard |
§2, §1 |
| 2 | Search & filter | full-text search; tag filter | A: GET /entries/search · B: SearchBar |
§2, §1 |
| 3 | Weekly summary | group by day for the week; one-click | A: GET /entries/week · B: WeeklySummary |
§2, §1 |
| 4 | App shell & data | Express bootstrap; SQLite + migration; React scaffold | A: server.ts + db.ts · B: App.tsx + main.tsx | §4, §3 |
Step 5 — Generate, then review against INVEST
Feed the agent the signed-off spec and generate stories in this structure (per story): a persona-framed statement, the 6-point INVEST check, 3–5 acceptance criteria, and a test stub (what to assert — not full test code).
Weak vs. strong story — the difference is everything:
- ❌ "As a developer I can search for entries so I can find what I need." — not testable ("find what I need"), not small (all search types), no AC.
- ✅ "As Alex I can type a query into the search bar so matching entries appear within 200ms." — with AC: calls
GET /entries/search?q=with trimmed query · updates within 200ms (300ms debounce) · case-insensitive across title/content/tags · empty query shows all entries.
Step 5 — example story shape (cont.)
Example story to match (paste this shape into docs/stories.md):
## Epic 2 — Search & Filter
### Story 2.1 — Full-text search
As Alex (solo developer) I can type a query into the search bar so that
matching entries appear in the feed within 200ms.
INVEST check:
- I: Yes — SearchBar and GET /entries/search are self-contained
- N: Yes — debounce timing and result count are negotiable
- V: Alex — finds any past insight without scrolling the full feed
- E: ~60 min (30 backend, 30 frontend)
- S: Yes — fits one Lab 6 session
- T: Yes — response time and match accuracy are measurable
Acceptance criteria:
1. GET /entries/search?q={query} returns Entry[] where title, content, OR tags
contain the query (case-insensitive)
2. Response time < 200ms for up to 1,000 entries
3. SearchBar input debounced 300ms before the API call
4. Empty query returns all entries (identical to GET /entries)
5. Matching is case-insensitive: "React" matches "react" and "REACT"
Test stub:
- Assert: GET /entries/search?q=typescript returns only entries where title,
content, or tags contain "typescript" (case-insensitive)
- Assert: response time < 200ms for a 1,000-entry store
✅ Story checkpoint: all 4 epics present · ≥10 stories total · every story has a named persona (Alex/Jordan) · 3–5 single-testable-statement AC each · a test stub · none would take >60 min (the S check).
Phase 3 · Generate BMAD dev tasks (23 min)
A task is the unit of code generation — one task ≈ one Lab-6 agent prompt. Vague task → vague prompt → wrong code. Precise task → right code first time.
Step 6 — The BMAD task format (every field required)
## TASK-001
Story: 2.1 (Full-text search)
Target file: backend/src/routes/entries.ts
Action: Create and export GET /entries/search route handler
Signature: searchEntries(req: Request, res: Response): void
Inputs: req.query.q — string, required. The search query.
Output: 200 + Entry[] (matching, sorted timestamp DESC)
400 + { error: "q parameter is required" } if q missing
500 + { error: "Internal server error" } on DB failure
Notes: SELECT * FROM entries WHERE LOWER(title) LIKE ?
OR LOWER(content) LIKE ? OR LOWER(tags) LIKE ?
bind: %${q.toLowerCase()}%
Spec ref: Tech Spec §2 — GET /entries/search
AC ref: Story 2.1 AC #1, #2
Step 7 — The 12 mandatory tasks
Every student must have at least these. Check generated tasks cover all 12; add any missing by hand:
| ID | Target file | What to build | Set | §ref |
|---|---|---|---|---|
| TASK-001 | backend/src/db.ts |
SQLite connection + entries table auto-migration | A | §3 |
| TASK-002 | backend/src/server.ts |
Express bootstrap, CORS, JSON middleware, port 3001 | A | §4 |
| TASK-003 | backend/src/routes/entries.ts |
GET /entries — all entries, timestamp DESC |
A | §2 |
| TASK-004 | backend/src/routes/entries.ts |
GET /entries/search — full-text across title/content/tags |
A | §2 |
| TASK-005 | backend/src/routes/entries.ts |
GET /entries/week — 7-day date-range filter |
A | §2 |
| TASK-006 | frontend/src/App.tsx |
Root — fetch entries, pass to EntryFeed + SearchBar | B | §1 |
| TASK-007 | frontend/src/components/EntryFeed.tsx |
List of EntryCards; props entries[], isLoading |
B | §1 |
| TASK-008 | frontend/src/components/EntryCard.tsx |
One entry — title, timestamp, preview, TagBadge list | B | §1 |
| TASK-009 | frontend/src/components/TagBadge.tsx |
Clickable tag chip; props tag, onClick |
B | §1 |
| TASK-010 | frontend/src/components/SearchBar.tsx |
Controlled input, 300ms debounce, calls onSearch |
B | §1 |
| TASK-011 | frontend/src/components/WeeklySummary.tsx |
Group entries by day for the week | B | §1 |
| TASK-012 | frontend/ (Vite config) |
Vite + React scaffold; proxy /api → :3001; Tailwind |
B | §4 |
Step 7 — task notes (cont.)
Each task should be completable in 10–15 min with the codegen agent in Lab 6. If one describes more than a single function/component, split it (EntryFeed and EntryCard are separate tasks).
🔗 Reconcile with the data bridge: TASK-001 (db.ts) should also handle the one-time seed from the Day-1
stacklog-entries.jsoninto SQLite, so yesterday's entries display. Add it as an explicit step in that task (or a TASK-001b).
Phase 4 · Backlog hygiene (5 min)
Step 5 — Order by dependency, flag questions
Dependency order for Lab 6:
- TASK-001 (db.ts) — first; all routes depend on it (and seeds Day-1 data).
- TASK-002 (server.ts) — second; mounts routes.
- TASK-003/004/005 (routes) — any order, all before frontend.
- TASK-012 (Vite scaffold) — before any frontend component.
- TASK-006 (App.tsx) — before child components.
- TASK-007–011 (components) — any order once App exists.
Flag unclear decisions inline (they'd be GitHub Issues in a real project):
<!-- OPEN QUESTION: WeeklySummary — client-side date grouping, or rely on
GET /entries/week? Decide before TASK-011. -->
Phase 5 · Commit (2 min)
Step 6 — Commit stories + tasks
🪟 cd $env:USERPROFILE\stacklog-workshop
🍎🐧 cd ~/stacklog-workshop
git add docs/stories.md docs/tasks.md
git commit -m "lab5: stories and tasks for all 4 epics"
git push origin main
✅ Lab 5 complete when both files are on GitHub.
Troubleshooting (top 6)
- Stories have no AC → re-load the Developer agent; instruct: "You MUST include 3–5 acceptance criteria per story, each a single testable statement. Don't proceed without them."
- Tasks too large (most common, breaks Lab 6) → if a task names more than one function/component, split it. EntryFeed ≠ EntryCard.
- <10 stories → spec too thin; prompt for edge cases: empty feed, no-results search, loading states.
- INVEST E fails (can't estimate) → ask "what would you need to know to estimate this?" — the answer is what's missing from the task.
- Path style → V6/most agents emit forward slashes (
backend/src/...); that's what Lab 6 uses cross-platform. (Original Windows guide used backslashes.) - Git auth fails → use the editor's Source Control panel → Sync; it handles browser auth.
Bridge to Lab 6
In Lab 6 you open each task, hand it to the codegen agent with the relevant spec section, and generate the code — backend before frontend.
docs/tasks.mdis your agenda.docs/spec.mdis the agent's context.
Before Lab 6: keep docs/tasks.md and docs/spec.md open; confirm your codegen agent (Antigravity / Copilot) is ready.