To Issues
Convert a PRD into a dependency-ordered issue set using vertical slices. Classify AFK vs HITL. Write agent briefs that close the loop.
Vertical Slice / Tracer Bullet Methodology
A vertical slice is an issue that delivers one end-to-end observable behavior — from UI or API entry point through to storage and back. Every issue should be a vertical slice unless infrastructure must exist before any slice is possible.
A vertical slice cuts through all layers of the stack for one specific behavior. It is the opposite of horizontal slicing (all frontend first, then all backend, then all database).
Horizontal slicing (avoid)
- 1. Build all database tables
- 2. Build all API routes
- 3. Build all UI components
- 4. Wire everything together
Integration risk is unknown until step 4. Everything is blocked on everything.
Vertical slicing (preferred)
- 1. Build the happy path for one feature end-to-end
- 2. Verify it works before adding another feature
- 3. Each slice reveals integration issues early
Each slice ships something observable. Integration risk is discovered per slice.
The first issue in any PRD decomposition should be the tracer bullet: the thinnest possible end-to-end slice that proves the architecture works.
- The tracer bullet does not need to be user-facing. It can be a smoke test that hits every layer.
- It should exercise the highest-risk integration point in the system.
- It defines the skeleton that subsequent slices will fill in.
- If the tracer bullet fails, stop and fix the architecture before writing more issues.
// Tracer bullet for the order notifications PRD:
// Issue: NOTIFY-001 — Tracer: emit and receive one order status notification
// This issue proves:
// - OrderService emits events correctly
// - NotificationService receives and processes events
// - EmailService (mock) receives the send call
// - The seam between the three services works
// Acceptance: a test exists that triggers a status change and asserts
// that MockEmailService.send() was called with the correct parameters.
// This is NOT a user-facing feature. It is infrastructure validation.AFK vs HITL Classification
Every issue is classified as AFK (Away From Keyboard — agent can complete without human interaction) or HITL (Human In The Loop — requires human action at some point during execution).
| Classification | Criteria | Examples |
|---|---|---|
| AFK | The agent has all the context, tools, and permissions needed to complete the issue from start to finish without asking anything. | Write a unit test. Refactor a function. Add a field to a data model. Implement a pure algorithm. |
| HITL | The issue requires a human decision, human action, or human input that cannot be anticipated. | Create a database migration (requires review before running). Configure a production service. Get sign-off on UI copy. Obtain an API key. Run a command on a production server. |
AFK issues can be batched and run in parallel. HITL issues must be sequenced around human availability. Getting this wrong wastes time: either an agent blocks waiting for human input that was not anticipated, or a human is asked to review a change that the agent could have self-validated.
- Mark all HITL issues clearly in the issue tracker.
- Group HITL issues so the human can batch their interventions.
- Estimate how long the human block will last. Issues that depend on a HITL issue cannot start until the human completes their part.
Dependency Ordering
Issues must be ordered such that no issue starts before its dependencies are complete. This requires an explicit dependency graph, not just an intuitive sequence.
- 1List all issues from the PRD decomposition.
- 2For each issue, ask: 'Is there any other issue in this list that must be complete before this one can start?' This is a hard dependency.
- 3For each issue, ask: 'Is there any other issue in this list that would be much harder if this issue is done first?' This is a soft dependency (may suggest ordering but does not block).
- 4Draw the dependency graph. Issues with no dependencies can start immediately and in parallel.
- 5Identify the critical path: the longest chain of dependent issues determines the minimum delivery time.
| Type | Definition | Notation |
|---|---|---|
| Hard block | Issue B cannot start until Issue A is merged and deployed. | B blocked-by A |
| Soft order | Issue B can start while A is in progress, but A's output will change B's implementation. | B depends-on A (advisory) |
| Parallel safe | Issues can be worked simultaneously without conflict. | No notation needed |
| File conflict | Issues touch the same file. Parallelizing creates merge conflicts. | B file-conflicts A |
Issue Template
Every issue follows this template. No shortcuts.
## [NOTIFY-003] Trigger notification on order status change
**Parent:** NOTIFY-000 — Order Notifications Epic
**What to build:**
When an order's status changes, emit a status_changed event
containing the order ID, the new status, and the customer email.
A NotificationService listener receives this event and calls
EmailService.send() with the correct template and recipient.
No email is sent in the current iteration if the order status
change is triggered by a system retry within the same 5-minute
window (idempotency key from PRD Implementation Decisions).
**Acceptance criteria:**
- [ ] OrderService emits a status_changed event on every status update
- [ ] The event payload contains: orderId (string), newStatus (OrderStatus), customerEmail (string)
- [ ] NotificationService subscribes to status_changed events
- [ ] For each status, NotificationService calls EmailService.send() with the correct template name
- [ ] For duplicate events within 5-minute window, EmailService.send() is NOT called
- [ ] Unit tests cover all four status transitions (pending, processing, shipped, delivered)
- [ ] Unit tests cover the idempotency case (second event within 5-minute window)
- [ ] MockEmailService is used in all unit tests (no real email is sent)
**Blocked by:** NOTIFY-002 (EmailService interface and MockEmailService must exist)
**Blocks:** NOTIFY-004 (template rendering cannot be tested end-to-end until this works)- Parent: the epic or parent issue that contains this issue. Required for traceability.
- What to build: a behavioral description of what the issue produces. No file paths, no function names, no implementation instructions.
- Acceptance criteria: a checklist. Every criterion is independently verifiable. No criterion is 'and then it works.'
- Blocked by / Blocks: explicit dependency notation. Required for dependency graph construction.
Agent Brief Format
An agent brief is what you attach to an issue when an AI agent will implement it. The brief is the issue template plus additional context that makes the agent's implementation trustworthy without human review of every line.
An agent brief describes what the agent should achieve and how to verify it. It does not describe how to achieve it. Procedural briefs (step 1: open file X, step 2: add function Y) produce brittle implementations that fail when the codebase has changed since the brief was written.
File paths and line numbers make briefs brittle. Reference concepts and behaviors instead:
Brittle (avoid)
“Open src/services/order-service.ts at line 147. Add a call to this.eventEmitter.emit() inside the updateStatus() function. The event emitter is imported from lib/events.ts.”
Behavioral (preferred)
“The OrderService must emit a status_changed event whenever order status is updated. The event payload is defined in the acceptance criteria. Find the status update method and add the emission there. The event system already exists in the codebase; search for similar emit() calls to find the correct import and usage pattern.”
Acceptance criteria in an agent brief must be complete enough that the agent can self-validate without human review of every edge case. Each criterion must be:
- Independently verifiable: can be tested in isolation.
- Unambiguous: only one behavior satisfies each criterion.
- Complete for the happy path and the critical edge cases.
- Written as test specifications, not requirements prose.
Every agent brief must explicitly state what is out of scope for this issue. Agents will fill gaps in scope with reasonable assumptions, and those assumptions may not match the intended design.
## Scope boundary for NOTIFY-003
**In scope:**
- Emitting status_changed events from OrderService
- NotificationService listener and EmailService.send() call
- Idempotency key logic
- Unit tests for all the above
**Out of scope — do NOT build:**
- Email template content (that is NOTIFY-005)
- Retry logic if EmailService.send() fails (that is a future iteration)
- Notification preferences or opt-out (explicitly out of scope in PRD)
- Logging of email delivery status (that is NOTIFY-007)
- Any changes to the OrderService status transition logic itselfGood vs Bad Agent Brief Examples
## Add email notifications
Add email notifications for orders. When order status changes,
send an email. Make sure it works.- No acceptance criteria — agent cannot self-validate.
- No scope boundary — agent will implement opt-out, retries, templates, logging, and everything else.
- No dependency information — agent cannot know what already exists.
- Behavioral description is a wish, not a specification.
## [NOTIFY-003] Trigger notification on order status change
**Parent:** NOTIFY-000 — Order Notifications Epic
**AFK/HITL:** AFK
**Blocked by:** NOTIFY-002
**Context:**
The codebase has an existing event system (search for emit() calls
to find the pattern). An EmailService interface and MockEmailService
were created in NOTIFY-002. The OrderService handles status updates.
**What to build:**
When OrderService updates an order status, it must emit a
status_changed event. A NotificationService listener must receive
this event and call EmailService.send() with a template name and
the customer email. Duplicate events within a 5-minute window must
not result in duplicate calls to EmailService.send().
**Acceptance criteria:**
- [ ] A test triggers status change → asserts MockEmailService.send() called once
- [ ] A test triggers same status change twice within 5 minutes → asserts send() called once total
- [ ] A test triggers same status change 6 minutes apart → asserts send() called twice
- [ ] All four statuses (pending, processing, shipped, delivered) have passing tests
- [ ] No real emails are sent during tests
**Out of scope:**
- Email template content
- EmailService retry logic
- User notification preferences
- Any changes to order status transition rules.out-of-scope/ Knowledge Base
Create a .out-of-scope/ directory at the project root to capture the things that were explicitly not built and why. This prevents future agents from re-implementing features that were deliberately excluded.
.out-of-scope/
├── notification-preferences.md
├── sms-notifications.md
└── email-retry-logic.md# Notification Preferences
**Feature:** User opt-out and notification frequency preferences
**Why not built:**
Excluded from the order notifications PRD (see Out of Scope section).
The decision was: build the notification system first, measure
user behavior, then design preferences based on actual patterns.
Premature preference management often produces complexity that
no user actually needs.
**When to revisit:**
If support volume for "too many emails" exceeds 5% of total
support tickets, or if unsubscribe rate exceeds 15%.
**What was considered:**
- Simple on/off toggle per notification type
- Send-time preferences (morning/evening)
- Channel preferences (email/SMS)
All were deferred. If implemented, the NotificationService
interface already has the seam for injecting per-user preferences.Why .out-of-scope/ matters