AI Development Methodology

04 — Backlog items

Purpose: define the format, fields, states, and lifecycle of an individual work unit — the BL-### item. Items are the smallest atom of tracked work in this methodology. Every concrete change to the product corresponds to one or more items.

Items live inside epics (see 03_epics.md). Each item has a unique project-wide ID, a frontmatter table of structured fields, and a body of acceptance criteria and notes. They progress through a defined lifecycle, gated at the end by the Definition of Done.


What a backlog item is

A backlog item is:


ID assignment

Item IDs are monotonic and project-wide.

Why project-wide and not per-epic

Why monotonic and not random

Zero-padding

Use three to four digits with zero padding (BL-0001, BL-0042, BL-1247). Keeps lexical sort matching numerical sort up to a sensible project size. Once a project exceeds 9999 items, widen the padding.


FUTURE.md numbering: two valid schemes

Items in FUTURE.md (deferred to a later phase) can use one of two ID schemes. Pick one per project and stay consistent.

Scheme A — monotonic, same as BACKLOG.md

Items in FUTURE.md use the same project-wide monotonic numbering as active items. Example: BL-0428 lives in FUTURE.md until it’s promoted to BACKLOG.md, at which point it keeps the same ID.

Scheme B — epic-scoped, F-prefixed

Items in FUTURE.md use scoped IDs like BL-E02-F01, BL-E02-F04. When promoted to BACKLOG.md, the item is renumbered to a real BL-### from the monotonic counter.

Either is fine. The project picks one in its instruction file or backlog README and stays consistent. Record the choice explicitly — a one-line entry in CLAUDE.md / AGENTS.md / the backlog README.md saves every future contributor the cost of grepping FUTURE.md and guessing from the IDs they see. Mixing schemes inside the same project produces confusion.


Item heading format

The item begins with a level-3 heading:

### BL-### — <Short imperative title>

Rules

Why imperative

Imperative titles read like commits and PR titles. The reader sees what the change does. A noun-phrase title (“CSV export”) leaves the reader unclear whether it is a feature ask, a bug, a refactor, or a doc edit.


Required frontmatter table

Immediately below the heading, every item carries a frontmatter table with the following fields:

| Field    | Value                                  |
|----------|----------------------------------------|
| Epic     | E<NN>-<slug>                           |
| Pillar   | P<#>                                   |
| Priority | P0 \| P1 \| P2 \| P3                   |
| Effort   | XS \| S \| M \| L \| XL                |
| Status   | backlog \| ready \| in-progress \| under-review \| to-be-tested \| done \| blocked \| rejected |
| Test     | not-tested \| pending \| manual-verified \| partial \| pass \| fail: <detail> \| regression-needed \| n/a |
| Deps     | BL-### or —                            |
| Lock     | — or <holder>@<ISO-8601-timestamp>     |

The table comes before any body text. It is the at-a-glance summary of the item’s state and is parsed by automation and humans alike.

Field rules


Assurance and accountable owner

The assurance profile is selected before implementation. A0/A1 items may inherit it and their human owner from the project/epic without extra rows. A2/A3 items require both rows, even when inherited:

| Assurance | A2 — material |
| Accountable owner | <named human or human-held role with current holder recorded in the repo> |

In the body, record required reviewers/evidence and the human acceptance decision or durable reference, including date and accepted residual risks. The profile rules govern readiness, escalation, and completion; an absent row never downgrades risk. These fields add no Status or Test values.

Priority enum

P0 | P1 | P2 | P3
Value Meaning
P0 Ship-blocker or outage. Drop everything; fix this first. Used sparingly.
P1 Required for the current phase. The phase cannot complete without this item.
P2 Planned for the current phase but not blocking. Most items live here.
P3 Nice-to-have. Future. Typically moved to FUTURE.md rather than worked.

Discipline

When to re-prioritize

Re-prioritization is a deliberate event, not a passive drift.


Effort enum

XS | S | M | L | XL
Value Rough time bound Typical shape
XS Under 2 hours A tiny fix, a config change, a one-line correction with a single test.
S Half a day A small feature or fix touching 1–3 files.
M 1–2 days Most items. A focused feature, a meaningful fix, a small refactor.
L 3–5 days A larger feature spanning multiple modules; a non-trivial refactor.
XL 1–2 weeks The upper bound. Anything beyond is a candidate for splitting into multiple items.

When effort grows beyond XL

An item that genuinely takes more than two weeks of focused work should be split. The split units are usually:

Splitting before starting is cheaper than splitting mid-flight.

Effort vs. priority

Effort and priority are independent. A P1 item can be XS; a P3 item can be XL. The two fields answer different questions:

Together they help prioritize the picking queue: P1-XS items are usually picked first because they unblock big phase outcomes cheaply; P2-L items are scheduled as capacity allows.


Prioritization — the ROI heuristic

The default rule for picking the next item to work on: highest impact per unit of effort. With Priority: (P0–P3) and Effort: (XS–XL) both set on every active item, the picking order falls out naturally.

The default rule

Priority Effort What to do
P0 any Drop everything. Work this.
P1 XS, S Pick first among non-P0 work. Cheap items that unblock big outcomes.
P1 M, L, XL Schedule deliberately. Don’t grab opportunistically.
P2 XS, S Good for sprint slack and gap-filling.
P2 M+ Defer unless explicitly scheduled.
P3 any Likely belongs in FUTURE.md, not in active BACKLOG.md.

This is the default heuristic, not a contract. The user (or epic owner) can override when product reasons require it — but the override is explicit, not silent.

Why ROI is the default

Without a rule, contributors (especially AI agents in autonomous mode) tend to pick whichever item caught their attention. The results: many partially-touched items, no compounding wins, cheap-and-impactful items perpetually skipped for whatever felt interesting.

The ROI heuristic biases toward shipping the highest-value work per unit of time invested. Over many items, the small high-value wins land daily, while larger work gets scheduled with full awareness of cost.

When to deviate

Recording the deviation

When a contributor (human or AI) picks a non-ROI-optimal item, the commit message or the item body should say why. This prevents future-you (or another contributor) from re-deriving the reasoning, and it’s the kind of decision that often becomes a memory entry (see 08_lessons_and_memory.md).

Applying ROI in autonomous runs

The autonomous-loop prompt template explicitly uses this heuristic. The loop picks the highest-impact-per-effort ready item, executes through the DoD, archives, and picks again. The heuristic is what keeps the loop converging on milestone-level outcomes rather than meandering through interesting-but-low-impact work.


Status enum

backlog | ready | in-progress | under-review | to-be-tested | done | blocked | rejected
Status Meaning
backlog Filed but not yet ready for work. Missing detail, blocked on a dependency, awaiting refinement.
ready All prerequisites met — no unresolved Deps:, no open Needs clarification marker. The item can be picked up immediately by any contributor with the right context.
in-progress A contributor is actively working this item. Lock: is set.
under-review Code complete. Awaiting review, CI, or merge.
to-be-tested Review passed, merged or ready to merge, awaiting UI verification per Definition of Done.
done All gates passed. The item is closed. Requires Test: pass.
blocked Cannot proceed. A Blocker: body line names what is in the way.
rejected A decision was made not to do this. The item stays as a record.

Acceptable project-specific aliases

The canonical names above are the methodology’s defaults. Projects sometimes use shorthand variants that are recognized as equivalent:

Canonical Common alias Notes
backlog todo Some teams prefer todo; treat as equivalent to backlog. Pick one per project and stay consistent.
(no canonical) future Used specifically for items living in FUTURE.md. Not a transition state — once promoted, the item flips to backlog or ready.
(no canonical) parked An item set aside indefinitely (priority shifted, not enough info). Project may use this when blocked doesn’t fit (nothing concrete blocks it; just deprioritized). Document the choice.

Adding aliases is fine; inventing new lifecycle states is not. Stick to the eight canonical states’ semantics; if a project needs a new state, that’s a methodology-change candidate (T2/T3 per the tier matrix in templates/AUTONOMOUS_LOOP.md).

Status transitions

backlog ──> ready ──> in-progress ──> under-review ──> to-be-tested ──> done

backlog | ready | in-progress | under-review | to-be-tested  ──> blocked ──> ready (when unblocked)
backlog | ready                                               ──> rejected

Discipline

When to flip back

Items occasionally move backward — a review uncovers a bigger problem than expected, a test fails, the user reports the change is wrong. Move the status back to the appropriate stage (in-progress if more work is needed, under-review if a re-review is needed) and continue. Backward transitions are normal; covering up an issue to keep the status forward is not.


Test enum

not-tested | pending | manual-verified | partial | pass | fail: <detail> | regression-needed | n/a
Value Meaning
not-tested No verification has been attempted yet. Default for new items.
pending Tests exist (written or scaffolded) but not yet executed against this item. Often the intermediate state right before pass or fail:.
manual-verified Verified by a human (UI walkthrough, manual reproduction of the bug fix) but no automated test exists. Acceptable for Status: done when automation is impractical (e.g., third-party API quirks); flag for follow-up regression-test item.
partial Some test surface exists and passes; some is missing or pending. Example: “happy path tested; error-path test not yet written.” Cannot transition to Status: done; explicit limitation.
pass All gates of Definition of Done passed. Required for Status: done.
fail: <detail> A gate failed. The <detail> names what failed and how.
regression-needed A regression test is required (typically for a bug fix) and has not yet been added.
n/a The item has no testable behavior (e.g., a pure docs change, a folder-creation chore). Acceptable for Status: done when genuinely applicable; using n/a to dodge testing is the anti-pattern.

Free-form artifact references

The Test field may also carry artifact references — a file path, PR number, or short note pointing to where the verification evidence lives. These pair with an enum-style value when more traceability helps:

Test: pass — apps/api/src/test/admin.test.ts (19 tests)
Test: pass — manual UI walkthrough; PR #160 review notes
Test: partial — Google branch unit-tested; e2e pending real client ID
Test: pending — apps/web/src/hooks/__tests__/useEntitlements.test.ts (scaffolded)

The enum value at the start makes the field greppable + machine-readable; the free-form tail makes it humanly useful. Prefer this two-part form for non-trivial items where the verification evidence isn’t obvious from the item body.

Why this field exists separately from Status

Status is about workflow position (where in the flow this item is). Test is about verification truth (does this work). They are not the same thing.

A common failure pattern is to flip Status: done while Test: still reads not-tested. The separate Test field makes this impossible to do accidentally — the hard rule below makes it explicit.

Failure details

When a gate fails, capture what specifically failed in the value:

Vague fail values do not help the next contributor. Specifics do.

Regression-needed

Used specifically when a bug fix has been written but the test that proves the bug is fixed (and won’t return) has not been added yet. This is a meaningful in-between state — the fix works, but the safety net is missing. Move to pass once the regression test exists and passes.

When manual-verified and n/a are acceptable for Status: done

The hard rule below requires Test: pass for Status: done. The two extensions:

For everything else, the canonical path remains not-tested → pending → pass → done.


The hard rule

Status: done requires Test: pass, manual-verified (with a regression-needed follow-up), or n/a (with a body-documented reason).

There is no other path. Not from not-tested. Not from pending. Not from partial. Not from fail:. Not from regression-needed (without a backing pass).

This rule is the single most important enforcement point in the methodology. See 07_definition_of_done.md for the gates that produce Test: pass.

If the gates cannot be passed for some reason, the item stays at to-be-tested, under-review, or blocked. Not done.

The two narrow extensions (manual-verified, n/a) exist to keep the methodology honest about real-world cases where automated testing is genuinely impractical or doesn’t apply — without becoming an escape hatch. Both require explicit body documentation; both are auditable in the next eval cycle.


Deps field

Deps: BL-### or —

Discipline


Lock field

Lock: — or <holder>@<ISO-8601-timestamp>

Documented in full in 05_locks_and_parallel_work.md. Short summary:

Acquiring an item to work on it requires setting this field. Releasing it requires clearing the field back to —.


Coupled fields: Lock + Status + Test

The three state-bearing fields are not independent — they move together. Never edit one without checking the others. The table below shows valid combinations for typical outcomes:

Outcome Lock Status Test
Item just picked up; work starting <id>@<now+TTL> in-progress not-tested
Code complete; PR open; awaiting review <id>@<refreshed TTL> under-review not-tested
Reviewer requested changes; back to work <id>@<refreshed TTL> in-progress (unchanged)
Code merged; automated tests green; no UI to verify — done pass
Code merged; manual UI verification pending — to-be-tested not-tested
Code merged; UI verified per DoD — done pass
Tests revealed a bug; fix coming in same PR <id>@<refreshed TTL> in-progress fail: <one-line>
Bug-fix shipped; regression test not yet written <id>@<refreshed TTL> in-progress regression-needed
External blocker discovered — (release the lock) blocked (unchanged)
Abandoning the work entirely — backlog or rejected (unchanged)

Why the coupling matters

When changing any one of these three fields, ask: which of the other two should change with it?


Body sections

Below the frontmatter table, the item body has these sections, in this order:

**Why / Description:** <one paragraph — problem, approach, what "done" looks like>

**Approach:** (optional, multi-step)
1. <Step one>
2. <Step two>
3. ...

**Done means:**
- [ ] <Acceptance criterion 1>
- [ ] <Acceptance criterion 2>
- [ ] <Acceptance criterion 3>

**Files (probable):** (at Effort M+ this becomes a **Code Map** — each path
annotated with its role, the utilities to reuse, the non-obvious constraints)
- `<path/to/likely/file/1>`
- `<path/to/likely/file/2>`

**Blocker:** (only if Status: blocked)
<One-line reason. Reference to the blocker — another item, an external dependency, a decision pending.>

**Notes:** (optional)
<Any additional context, design decisions, or links worth preserving.>

Section-by-section rules

Why / Description. One paragraph. Three to seven sentences. Must answer: What is the problem? What is the approach? What does done look like? The reader should be able to decide if this item is worth picking up from the description alone.

Approach. Optional. For items with non-obvious sequence — a refactor, a multi-step rollout, a coordinated change. Numbered list. Each step is actionable.

Done means. Required. Acceptance criteria as checkboxes. These are the item-level exit criteria. They are not the Definition of Done gates (those are universal). They are the specific conditions for this item.

Examples:

Files (probable). A best-guess list of files the change will likely touch. Not authoritative — the real diff may differ — but useful as a starting point for the contributor and as a way to surface conflicts (“two items will probably touch the same file, mind the merge”). At Effort M and above, this section is upgraded to a Code Map — see The Code Map below.

Blocker. Required if and only if Status: blocked. A one-line statement of what is blocking and what needs to happen to unblock. “Blocked on API spec from team X — expected by Y.” Without this, a blocked item is a black hole.

Notes. Optional. For context that does not fit elsewhere: design rationale, links to discussions, partial findings from research, alternatives considered.

Writing testable acceptance criteria (EARS)

Vague criteria (“works correctly,” “handles errors gracefully”) are the most common reason an item’s done gets disputed after the fact. EARS (Easy Approach to Requirements Syntax) is a lightweight writing convention — no tooling, just five sentence shapes — that forces each criterion to name a trigger and an observable response, which is exactly what makes it testable:

Pattern Shape Example
Ubiquitous THE SYSTEM SHALL <response> The export SHALL be UTF-8 encoded.
Event-driven WHEN <trigger>, THE SYSTEM SHALL <response> WHEN the user clicks Download, the system SHALL stream a CSV of the filtered rows.
State-driven WHILE <state>, THE SYSTEM SHALL <response> WHILE a filter is active, the export SHALL contain only matching rows.
Unwanted behavior IF <condition>, THEN THE SYSTEM SHALL <response> IF the result set is empty, THEN the system SHALL download a header-only file, not an error.
Optional feature WHERE <feature present>, THE SYSTEM SHALL <response> WHERE SSO is enabled, the system SHALL record the exporter’s identity.

A Done means: checkbox written this way doubles as its own test plan: the trigger tells the verifier what to do, the response tells them what to observe — and it maps cleanly onto the Test: field (each criterion resolves to pass or fail: <which clause broke>).

Apply it where ambiguity is expensive — anything touching money, authentication, data integrity, or an external contract. For a one-line doc fix, a plain checkbox is fine; don’t ceremonialize trivial work.

The Code Map — writing M+ items for cold handoff

For XS and S items, **Files (probable):** is enough. At Effort M and above, upgrade it to a Code Map: the same list, annotated — each path carries its role in this change, the existing functions or utilities to reuse, and the constraints that aren’t obvious from the code:

**Code Map:** (drained from planning — not re-derived at implementation time)
- `apps/api/src/routes/reports.ts` — add the CSV endpoint here; reuse
  `buildActivityQuery()` (already filter-aware).
- `apps/web/src/pages/Reports/ActivityReport.tsx` — toolbar gets the
  button; follow the existing `ExportPdfButton` pattern.
- Constraint: report queries must stream — the 50k-row case times out
  buffered (see memory entry `streaming-reports`).

The rule that makes it work: whoever plans the item drains their investigation into the Code Map instead of carrying it in their session. Codebase knowledge gathered while planning — which files, which helpers already exist, which approach was rejected and why — dies with the planning session unless it lands in the item body.

The cold-handoff test: could a fresh session, holding this item and its repository links, start implementing without repeating the planner’s investigation? For an M+ item the answer must be yes. Rotation readiness adds purpose, constraints, human ownership, verification commands/procedures, and relevant decisions/rejected alternatives to that check. A new human or AI must be able to plan or verify from those artifacts without private chats or personal prompts. Fix missing context before handoff; record an actual cold walkthrough only when one occurred.

The dispatch rule: when the item is handed off — to a subagent, to tomorrow’s session, to another contributor — the instruction is “work BL-####,” pointing at the item. Don’t paste a summary of the item into the handoff prompt: the summary drifts from the item, and now two versions of the truth exist. If the item body isn’t good enough to work from, fix the item body. (A subagent handed a slice of the item — research this, test that — is still told what its slice is; the rule is that the item’s own goal and constraints are read from the item rather than restated around it. See 05 — Subagent delegation.)

Verify the Code Map before implementing, not after. A Code Map makes factual claims about the codebase — these files exist, that helper does this, this constraint holds. If any of them is wrong, everything derived from it is wrong, and the review that catches it comes after the code is written. For M+ items, have a fresh session check those claims against the actual code first: see spec-verification, the cheapest point at which a wrong plan can be caught.

The Code Map is durable item knowledge, distinct from the volatile session state in the active-context file: the Code Map says “reuse buildActivityQuery()”; active context says “halfway through step 3.” One survives the item’s whole life; the other is wiped at handoff.

Frozen intent — approved goals are human-owned

Once a human approves an item’s Why / Description: (the goal) and its Done means: criteria — or an epic charter’s Outcome and Exit criteria — those sections become frozen intent: human-owned, agent-immutable. Every other section (Approach, Code Map / Files (probable), Blocker, Notes) stays editable as the work teaches; the definition of success does not.

The two frozen sections are not adjacent — Approach sits between them — so the marker goes once, directly below the frontmatter table, and names what it covers rather than fencing a contiguous block:

> **Frozen intent** — `Why / Description:` and `Done means:` approved by
> <who> on <YYYY-MM-DD>. Agents do not edit them; halt and renegotiate instead.

One line, greppable (rg "Frozen intent" backlog/), and unambiguous about scope without needing the frozen text to be contiguous.

Why this exists. Without the boundary, the drift is invisible: an agent that rewords an acceptance criterion to match what it actually built produces a diff that looks like editing, not scope change — and the item then “passes” a spec-compliance review against criteria the implementation wrote for itself. Freezing the intent leaves exactly one path: halt and renegotiate — surface what’s wrong with the approved goal, get explicit re-approval from the human who approved it (or higher authority), and land the change as a visible edit with a one-line reason next to the badge. The executing agent never thaws its own item.

This is the third member of a family: protected regions bound the code an agent may touch, the tier matrix bounds authoritative docs, and frozen intent bounds the approved definition of the work itself.

Needs clarification — an unanswered question blocks ready

An item can be filed with a question its author could not answer; without somewhere to put it, the question gets guessed at implementation time. Epic charters already carry open questions and a closing gate (03); items get the same in one line, reusing the frozen-intent marker shape — below the frontmatter table, one per question:

> **Needs clarification** — <the specific unanswered question>

Greppable like the other (rg "Needs clarification" backlog/). Three rules keep it a gate rather than a note:

Distinct from Principle 1: that governs an implementer stating assumptions before coding an item they can work; this governs an author who could not finish specifying one. One happens after pickup; the other prevents it.

Size budgets — context artifacts must earn their length

Every artifact an agent loads routinely costs context even when it’s correct. 08 already budgets the two it owns — the instruction file and memory entries. Generalized across the artifacts a contributor loads to do an item: each has a target size, and “too big” is a signal with a defined response, not a style complaint.

Artifact Target Too big means
Item body ~1–2 pages It’s more than one item. Split it — the beyond-XL splitting shapes apply below XL too; mid-task, use scope-creep recovery.
Epic charter ~2–3 pages It’s absorbing design content. Move that to docs/planning/ or a pillar (03); the charter keeps decisions and links.
Instruction file see 08 Demote entries to memory (08 demotion loop).
Memory entry see 08 It’s a doc, not a memory. Promote to a pillar or design doc; the entry keeps the pointer.

The bottom two rows carry links, not numbers, on purpose: 08 owns those figures, and a second copy here would drift from it.

Defaults, not laws — a project re-tunes them in its instruction file, the same pattern as the per-project DoD overlay. The rationale travels with the numbers: a context artifact is a liability that must earn its length. An 800-line item body doesn’t make the work clearer; it makes every session that loads it duller on what matters.


Resolution notes for rejected and non-obvious-done items

When an item is moved to rejected — or to done for a reason that isn’t obvious from the diff — add a **Resolution:** block to the body explaining why.

Template

**Resolution:** <one or two sentences explaining what happened and why.>

When to use it

Why this matters

Example

### BL-0517 — Add per-place language preference override

| Field    | Value     |
|----------|-----------|
| Status   | rejected  |
| ...      | ...       |

**Why / Description:** [Original problem description...]

**Resolution:** Covered by the broader Phase 3 i18n epic; per-place
override is now derived from the user's account-level language
preference set during onboarding. No standalone feature needed.
See E11 — Internationalization.

The next person who notices “we should let users pick per-place languages” can rg BL-0517 and find the resolution before re-filing.


Lifecycle of an item

stateDiagram-v2
  [*] --> backlog : filed
  backlog --> ready : refined
  ready --> in_progress : picked up + locked
  in_progress --> under_review : code complete
  under_review --> to_be_tested : review passed
  to_be_tested --> done : DoD passed; Test: pass
  in_progress --> blocked : blocker discovered
  blocked --> ready : unblocked
  ready --> rejected : not pursuing
  backlog --> rejected : not pursuing
  done --> [*] : moved to ARCHIVE.md
  rejected --> [*] : moved to ARCHIVE.md

Diagram-vs-enum note: Mermaid state names cannot contain hyphens, so the diagram uses in_progress, under_review, to_be_tested with underscores. The corresponding Status: enum values in actual items use hyphens (in-progress, under-review, to-be-tested). Same states, different rendering — match the enum form, not the diagram form, when filling in a Status: field.

Stage-by-stage

  1. Filed. A contributor identifies work that needs doing. Assigns the next monotonic ID. Files the item in the right epic’s BACKLOG.md with Status: backlog, Test: not-tested, Lock: —.
  2. Refined. Description filled out, acceptance criteria written, effort estimated, dependencies named, open questions resolved (no Needs clarification marker left standing). Move to Status: ready.
  3. Picked up. A contributor takes the item. Sets Lock: <holder>@<now + TTL>. Sets Status: in-progress. Both changes in the same commit.
  4. Code complete. The change is implemented locally and committed (typically on a feature branch). Open the PR. Move to Status: under-review.
  5. Reviewed. Reviewer approves; CI passes. Move to Status: to-be-tested.
  6. Tested. Apply the Definition of Done gates. UI-verify, document, etc. When all gates pass, set Test: pass and Status: done. Release the lock (Lock: —).
  7. Archived. Move the item entry from BACKLOG.md to ARCHIVE.md (preserving everything). Update the epic’s rollup counts in EPICS.md.

Blocking and recovery

If a blocker is discovered at any stage, move to Status: blocked and add a Blocker: body line. When the blocker resolves, move back to whatever status is appropriate (usually ready if no code has been written yet, or back to in-progress to continue).

Rejection

If the item is decided against, set Status: rejected, add a one-line note explaining why, move to ARCHIVE.md. Do not delete. The rejection record prevents the same proposal from being relitigated from scratch.


When things go sideways

The happy path is backlog → ready → in-progress → under-review → to-be-tested → done. Real items deviate. Unified recovery guide:

Tests fail (automated or UI verification)

External blocker discovered

You hit something you can’t unblock yourself: missing credential, pending decision, upstream PR, legal review.

Scope creep mid-task

You started implementing and realized the item is bigger than Effort implied (or touches things you didn’t expect).

Lost or expired your own lock

You held longer than the TTL and forgot to refresh, or you crashed and resumed later.

A done item turns out to not actually be done

Post-merge, someone discovers the item didn’t really work.

General principle

In all these recovery flows, the three coupled fields (Lock, Status, Test) move together. Never change one in isolation without considering the others. The “Coupled fields” table above shows the valid joint states.


Where new items go

Always into a specific epic’s BACKLOG.md. Never into:

What if no epic fits?

Two cases:

  1. The work belongs in an existing epic but does not quite match the charter. Re-read the charter. If the work is genuinely in scope, file it. If it’s truly orthogonal, the right answer is usually a new item belongs in a different existing epic.
  2. The work doesn’t fit any existing epic. Create a new epic first. Charter it. Then file the item. Items without a parent epic are orphans; orphans get neglected.

The friction of “must create an epic first” is intentional. It prevents random items from accumulating without a home, and it forces the team to think about scope before working.


HUMAN_NEEDED.md — work blocked on human agency

Some items can’t be completed by an AI agent alone:

These items shouldn’t sit Status: in-progress consuming a lock — the lock TTL would expire while the agent waits indefinitely, and the unfinished work would block other contributors. They also shouldn’t be deferred to FUTURE.md — the work is real and current; it’s just gated on a human.

The methodology uses a dedicated backlog/HUMAN_NEEDED.md file at the backlog root (sibling of EPICS.md) listing every item currently blocked on human agency.

The protocol

When an AI agent (or human contributor) encounters human-blocked work:

  1. Update the originating BL item:
    • Status: blocked
    • Lock: — (released)
    • Add a **Blocker:** line in the body naming what the human needs to do and what unblocks it.
  2. Add a one-line entry to HUMAN_NEEDED.md linking back to the BL item.
  3. Pick the next ready item per the ROI heuristic and continue work.

The human, when they have time, scans HUMAN_NEEDED.md, completes the actions, and updates the BL items (Status: ready or Status: in-progress) so an agent can pick them up. The corresponding line in HUMAN_NEEDED.md moves from the “Active” section to “Recently unblocked” (then gets pruned after ~30 days — the BL item’s ARCHIVE.md entry remains the permanent record).

Why a dedicated file

Skeleton

# HUMAN_NEEDED.md

Tasks blocked on human action. Each entry links to the originating
BL item. Add an entry when you set `Status: blocked` and the blocker
is human-only (not waiting on another agent, not waiting on a
dependency that an agent can resolve).

## Active

- [BL-0123](epics/E02-payments/BACKLOG.md#bl-0123) — set up Stripe
  production webhook; need account owner access. Added 2026-05-12.
- [BL-0211](epics/E03-launch/BACKLOG.md#bl-0211) — review legal
  disclaimer wording with counsel. Added 2026-05-18.
- [BL-0289](epics/E04-billing/BACKLOG.md#bl-0289) — physical card
  needed for test charge in production. Added 2026-05-21.

## Recently unblocked (last 30 days)

- ~~[BL-0099]~~ — sign contractor NDA. Done 2026-05-09. Item resumed.
- ~~[BL-0142]~~ — capture screenshots from on-site demo. Done 2026-05-15.

_(Older unblocked items live in their epic's `ARCHIVE.md`.)_

What does NOT belong in HUMAN_NEEDED.md

How this interacts with the rest of the methodology

The file is a passive registry, not an actively-managed surface — items land in it via the blocked-item protocol above, and humans scan it when they check in. The autonomous loop does not interact with HUMAN_NEEDED.md directly; blocked items simply leave the ready set via the lock release, so the loop stops touching them.


BACKLOG.md structure (the per-epic active-items file)

Each epic’s BACKLOG.md opens with a summary table — one line per item — followed by the detailed item blocks. The summary is a navigation index that lets a reader see “what’s open in this epic” without scrolling.

Skeleton

# E<NN> — <Epic name> — Active Backlog

_Items currently in scope for this epic. See [charter](README.md) for exit criteria._

## Summary

| ID      | Title                                                    | Priority | Effort | Status       |
|---------|----------------------------------------------------------|----------|--------|--------------|
| BL-0428 | Add CSV export to the activity report                    | P2       | M      | done/pass    |
| BL-0517 | Fix off-by-one in date parser                            | P1       | S      | in-progress  |
| BL-0518 | Document the export feature in the user guide            | P3       | XS     | backlog      |
| BL-0519 | Stale-data audit on the activity report                  | P2       | M      | rejected (covered by BL-0428) |

---

### BL-0428 — Add CSV export to the activity report

<full item block — frontmatter table + body sections>

### BL-0517 — Fix off-by-one in date parser

<full item block>

### BL-0518 — Document the export feature in the user guide

<full item block>

### BL-0519 — Stale-data audit on the activity report

<full item block, including the `**Resolution:**` note>

Notes on the summary

Why this matters

A BACKLOG.md with 30 items and no summary table requires scrolling and Ctrl+F. With the summary at the top, the at-a-glance scan is one screen. For epics that accumulate items quickly, the summary is the difference between a usable backlog and an opaque scroll.


Item heading + frontmatter table skeleton

Paste this to start a new item:

### BL-### — <Short imperative title>

| Field    | Value     |
|----------|-----------|
| Epic     | E<NN>-<slug> |
| Pillar   | P<#>      |
| Priority | P2        |
| Effort   | M         |
| Status   | backlog   |
| Test     | not-tested |
| Deps     | —         |
| Lock     | —         |

<!-- Once a human approves the goal + Done means, add the frozen-intent marker here:
> **Frozen intent** — `Why / Description:` and `Done means:` approved by <who>
> on <YYYY-MM-DD>. Agents do not edit them; halt and renegotiate instead. -->

**Why / Description:** <One paragraph: problem, approach, done.>

**Approach:** (optional)
1. <Step>
2. <Step>

**Done means:**
- [ ] <Criterion>
- [ ] <Criterion>

**Code Map:** (Effort M+; at XS/S a plain `**Files (probable):**` list is enough)
- `<path>` — <role in this change>; reuse `<existing helper>`
- Constraint: <what isn't obvious from reading the code>

**Notes:** (optional)
<Context, links, design rationale.>

A worked (abstract) example

### BL-0428 — Add CSV export to the activity report

| Field    | Value                              |
|----------|------------------------------------|
| Epic     | E04-reporting                      |
| Pillar   | P9                                 |
| Priority | P2                                 |
| Effort   | M                                  |
| Status   | in-progress                        |
| Test     | not-tested                         |
| Deps     | —                                  |
| Lock     | alice-laptop@2026-04-18T16:30Z     |

**Why / Description:** Operators currently view the activity report only
in the web UI and re-key totals into a spreadsheet for monthly billing.
Add a "Download CSV" button that exports the same data the table displays,
applying the user's current filter selection. Done when the button
appears, the CSV downloads on click, and the contents match the on-screen
table including filters.

**Approach:**
1. Add a GET `/reports/activity.csv` endpoint that re-uses the existing
   activity-report query, accepting the same filter parameters.
2. Stream the result as CSV rather than buffering — the report can be
   large.
3. Add a "Download CSV" button to the report toolbar that calls the
   endpoint with the current filter state.
4. Tests: a backend test that asserts the endpoint returns the right
   columns for a known dataset, plus a UI test that asserts the button
   triggers a download.

**Done means:**
- [ ] `GET /reports/activity.csv` returns CSV matching the table query.
- [ ] CSV streams (does not buffer) and works for large result sets.
- [ ] Filter parameters in the URL match the UI's current filter state.
- [ ] Backend test covers the success and empty-data cases.
- [ ] UI test asserts the button triggers a download.
- [ ] Verified manually in actual UI: button appears, downloads work,
      filters are respected.

**Code Map:** (Effort M — annotated per "The Code Map" above)
- `apps/api/src/routes/reports.ts` — add `GET /reports/activity.csv`;
  reuse `buildActivityQuery()`, already filter-aware and already shared
  with the email-summary job.
- `apps/api/src/test/reports.test.ts` — endpoint tests (success + empty).
- `apps/web/src/pages/Reports/ActivityReport.tsx` — toolbar gets the
  button; reuse the page's existing filter state, don't re-derive it.
- `apps/web/src/pages/Reports/ActivityReport.test.tsx` — download test.
- Constraint: the response must stream. Tested locally at 50k rows —
  buffered hit the default response timeout.

**Notes:** Reusing the shared query keeps the table view, the email-
summary job, and the export on one code path.

Every field is filled. Every section is concrete. A contributor who has never seen this item can pick it up cold and know what to do — which is the cold-handoff test passing.


Items spanning multiple files (a note on grouping)

One item may produce multiple commits and one PR — that is normal. One PR may close multiple items — also normal, when the items are tightly coupled and the work is naturally bundled. What is not normal:

A healthy ratio is roughly one item to one PR, plus or minus a small constant.


Greppable metadata — specific query patterns

The frontmatter table is designed to grep cleanly. Common queries:

# All items advancing a given pillar (cross-epic)
rg "^\| Pillar +\| P7"  backlog/

# All P0 (ship-blocking) items
rg "^\| Priority +\| P0" backlog/

# All currently free (unlocked) items
rg "^\| Lock +\| —"      backlog/

# All currently in-progress items
rg "^\| Status +\| in-progress" backlog/

# All items held by a specific agent (find stale locks)
rg "^\| Lock +\| claude-session" backlog/

# All blocked items, with 12 lines of context (to read the Blocker line)
rg "^\| Status +\| blocked" -A 12 backlog/

# All items rejected with a specific resolution pattern
rg "^\*\*Resolution:\*\*.*covered by" backlog/

# Find the next BL-ID to use (largest current + 1)
rg -oN "^### BL-\d+" backlog/ | grep -oE "[0-9]+" | sort -n | tail -1

# All items in a specific effort bucket
rg "^\| Effort +\| L" backlog/

When designing a new field or value, design it to grep cleanly: predictable position in the frontmatter, predictable value format, simple regex anchors. Avoid free-form values where grep would need to parse English.

ID collision under concurrency

If two contributors file new items in parallel and both compute the same “next” BL-ID, the second to merge bumps to the next number in their PR (single-file edit, cheap to fix). The first contributor to push wins the ID; the second renumbers. Never reuse a previously-rejected ID — even if the rejected item’s ID becomes “gappy” in the sequence, the gap stays. Rejected IDs remain in the audit trail.


How items connect to the rest of the methodology


Common mistakes when filing or running items

Mistake Fix
Title is a noun (“CSV export”). Rewrite imperative: “Add CSV export to the activity report.”
Description is one sentence with no acceptance criteria. Flesh out: problem, approach, what done looks like. Add the Done means: checklist.
Status is done but Test: reads not-tested. Roll back to to-be-tested. Run the gates. Set Test: pass only when truthful.
Effort is XL and never started. Split. Two M items finish; one XL item rots.
Deps: references an item that does not exist. Either typo (fix) or stale (clear). Broken refs erode trust.
Item is in-progress but Lock: is —. Inconsistent state. Either someone is silently working it (acquire the lock retroactively, in their name) or no one is (drop back to ready).
Item is blocked with no Blocker: line. Add the blocker line, or drop back to ready if it is no longer blocked.
Item lingers in backlog for many months with no refinement. Either refine it to ready, or move it to FUTURE.md, or reject it. Limbo helps no one.
Item is in two epics’ BACKLOG.md. An item lives in exactly one epic. Pick one; delete the duplicate (preserving the ID in the chosen one).
Item IDs are not monotonic (someone reused BL-0042). Find the duplicate. Renumber the newer one. Reuse breaks every grep-based query going forward.
Acceptance criteria are vague (“works correctly”). Replace with specifics, or use an EARS trigger→response shape (“WHEN the result set is empty, the system SHALL download a header-only file”).
M+ item handed off, and the receiver re-investigated the codebase from scratch. The planning session didn’t drain its findings. Add the Code Map; apply the cold-handoff test before dispatch.
Acceptance criteria quietly reworded mid-task to match what was built. Frozen-intent violation. Restore the approved text, then renegotiate visibly — or accept the finding that the work missed the goal.

TL;DR for a fresh contributor (human or AI)

Open the epic’s BACKLOG.md. Scan the summary table at the top. Find an item with Status: backlog or ready AND Lock: — (or an expired-timestamp lock). Acquire the lock in one commit: set Lock: <your-id>@<now+TTL> and Status: in-progress together. Read the item body for the goal. Use plan mode for anything non-trivial (see 06 — Plan before executing). Do the work. Pass every gate in the Definition of Done. Release the lock in the merging PR: set Lock: —, Status: done, Test: pass together. Move the item block from BACKLOG.md to ARCHIVE.md. Bump the epic’s Open/Done count in EPICS.md. If blocked: Status: blocked, Lock: —, add a one-line **Blocker:** to the body. If scope creeps mid-task: stop, split, file follow-up items.


Authority

Items are bound by their epic charter. An item whose work would clearly fall outside the epic’s exit criteria does not belong in that epic. Move it (rare, requires careful migration) or close it as rejected with a pointer to where the work actually goes.

Items are bound by the Definition of Done. No shortcuts to done. No done without Test: pass.

Items are bound by the Working Principles. The principles apply at item-execution time: the contributor working an item must think before coding, keep it simple, stay surgical, and execute toward a verifiable goal.


Next: 05 — Locks and parallel work