7.1 — Plan & sprints: step-by-step user guide

7.1 — Plan & sprints: step-by-step user guide

Sprint planning changes one thing that matters to anything driving VibeMap from outside the browser: it changes what next-ready will hand your agent. Without a plan the agent takes the oldest ready criterion. With one, it takes the first unblocked criterion in the active sprint, and nothing else.

This page is about that gating — the algorithm, the dependency semantics, and the operations that are API-only today. For the in-app walkthrough — clicking Plan Sprints, reading the Gantt, re-planning — see Sprint planning in the user docs. For object shapes and routes, see 7.2 — API reference.


Contents

  1. What planning changes for your agent
  2. Prerequisites and gating
  3. Where planning lives
  4. How the agent picks work after planning
  5. Hard vs soft dependencies
  6. Bootstrap and the synthetic Sprint 0
  7. Re-plan vs re-analyze — what survives
  8. Operations that are API-only
  9. Troubleshooting
  10. v1 limitations
  11. Where to go next

What planning changes for your agent

Say you've generated 60 acceptance criteria. Unplanned, they sit in one pile and your agent takes them in roughly creation order. Two consequences:

  1. Out-of-order work. The agent tries "User can edit profile" before "User can sign up" exists. There's no User to edit, the work fails, and the criterion ends up blocked for an artificial reason.
  2. No structural narrative. You can't tell whether you're 80% through auth or 10% through billing.

Planning fixes both. An LLM derives dependency edges between criteria, they're topologically sorted, and a second pass buckets them into 3–15 themed sprints. Then next-ready gates on: bootstrap → active sprint → all hard prerequisites passed. The agent literally cannot be handed an out-of-order criterion.

In a sentence: planning turns the agent's work queue from a pile into a route.

If you want to see the whole route at once rather than one card at a time, GET /api/mcp/kanban/projects/<id>/execution-plan returns it topo-sorted and grouped by sprint — see 6.2.


Prerequisites and gating

  • Pro plan or above. The dependency analyzer and the sprint planner are both Pro+ (sprintPlanner). On Free and Starter the planning routes refuse and your agent works in approval order — which is a perfectly valid way to run a small project, not a broken state.
  • Features with acceptance criteria. Roughly 10+ criteria across 2+ features before a plan tells you anything; below that you get one or two sprints with no structure.
  • Criteria do not need to be ready. The planner uses criteria in any status — it cares which criteria exist, not which are approved. You approve as you go.
  • (Recommended) Bootstrap first. If you want a Foundation step, run it before planning; see Bootstrap and the synthetic Sprint 0.

⚠️ These routes don't use the platform's 402 convention. A plan denial is 403 { error: "plan_required", required: "pro" }; a project that isn't yours (or doesn't exist) is 404 { error: "project_not_found" }. Branch on both the status and the error field.


Where planning lives

There is no longer a Plan / Hand off / Track tab strip — those three jobs are three separate destinations:

Prepare
  ├─ Prepare for Dev      ← blueprint enrichment (Pro+)
  └─ Sprint Planning      ← plan and re-plan sprints
Development (Hand off to IDE)   ← the live board; ⓘ Handoff Instructions for MCP details
DestinationWhat it's for
Sprint Planning (under Prepare)Plan or re-plan, read the sprint Gantt, inspect dependency badges. Its own page.
DevelopmentThe kanban board your agent works against. A single tabless page. In sprint mode it renders a focus-and-collapse layout: active sprint expanded, the rest collapsed to one line.
Development → ⓘ Handoff InstructionsThe panel (top-right of the board) with your API-key status and MCP config.

Prepare runs first if you want it, then planning, then handoff — but nothing enforces that order. You can plan sprints without ever touching Prepare.

The board's expanded-sprint state is reflected in the URL as ?sprint=<sprint_id>, so a shared link lands on the same sprint. There are no ?tab= params any more.


How the agent picks work after planning

This is the part worth memorising. When your agent calls next-ready:

1. Is there an unpassed bootstrap AC?
     → Yes: return the next bootstrap AC. Sprints don't matter yet.
     → No: continue.

2. Does this project have sprints?
     → No: return the oldest ready criterion. (Backwards compat.)
     → Yes: continue.

3. Compute the active sprint.
     → None (everything passed): return null — the project is complete.
     → Active sprint exists: continue.

4. Take all `ready` criteria in the active sprint, ordered by (position, created_at),
   restricted to those unassigned or assigned to the CALLER.

5. For each candidate in order, check that every hard prerequisite is `passed`.
     → First candidate that clears is returned.
     → None clears: return null.

Two things about step 4 that bite people:

  • Reservation is applied here too. A criterion assigned to somebody else is invisible to your agent, sprint or no sprint. And a personal access token's identity is the agent, not its owner — so a criterion you assigned to yourself is skipped by your own agent. See the footgun.
  • null is ambiguous and the response says which kind. If ready work exists but belongs to someone else, the response carries reason: "reserved" and a count. An agent loop that can't tell "no work" from "not your work" looks hung.

The active sprint is derived, never stored: it's the first sprint by position (excluding Backlog) with at least one non-passed criterion. Finish everything in it and the next one becomes active on the following call — no manual flip. It can also move backwards: add a non-passed criterion to a previously complete sprint and that sprint is active again. That's deliberate — you can't skip a sprint by editing a later one.

It's pull-based. The agent calls next-ready once per work cycle. Marking something passed mid-flight is picked up on the next call; there's no push.


Hard vs soft dependencies

Every edge in the graph is one or the other, and only one of them gates:

MeaningEffect
Hard (is_hard: true)"B genuinely cannot be built before A is passed" — needs the entity, route, column or permission A createsDrives topo-sort and next-ready gating
Soft (is_hard: false)"B is awkward without A but technically possible"Advisory. A clustering hint for the planner; not enforced by next-ready

A criterion whose hard prerequisites aren't all passed is skipped by the selector and shows a red ⚠ Blocked by N unpassed prereqs badge on the board.

Cycles. If the LLM produces a circular dependency, a resolver runs after the analyzer and drops one edge per cycle deterministically — soft edges first, then latest-created as a tiebreaker. A cycle that survives into the execution plan is reported explicitly rather than swallowed: the response carries a non-null cycle naming the members, and the ordering falls back to created-at. A non-null cycle means the order you're reading is not dependency-respecting.


Bootstrap and the synthetic Sprint 0

Bootstrap criteria (the Foundation feature, features.kind = 'bootstrap') are handled entirely outside the sprint system:

  • They're filtered out of both pipelines and never get a sprint_id.
  • They're served first by next-ready while any of them is not passed — including ones still in draft, which is intentional: Sprint 0 isn't done until you've approved and the agent has implemented every bootstrap criterion.
  • The board renders them as a synthetic Sprint 0 labelled Foundation, purely for visual consistency. No such sprint exists in the database.

So a fresh project with bootstrap always runs Foundation → Sprint 1 → Sprint 2 → …, regardless of how the planner grouped things. If you skipped bootstrap, the section simply doesn't appear.


Re-plan vs re-analyze — what survives

Two buttons, different blast radii. The click-path is in Sprint planning; what matters here is what your agent sees afterwards.

Re-planRe-analyze deps
Criterion statuses (passed, ready, …)preservedpreserved
Dependency edgespreservedregenerated
User-added edges (derivation_source = 'user_added')preservedpreserved
Sprint titles and themeslostlost
Criterion↔sprint assignmentsre-derivedre-derived
Roughly~30s, planner only~60–180s, both pipelines

Practical advice: re-plan freely — it's cheap and safe. Re-analyze only when the criterion set genuinely changed, since it costs a second LLM pass.

⚠️ In v1 the Re-analyze button runs the same flow as Re-plan (planner only). For genuinely fresh edges, call POST /api/projects/<id>/sprints/plan directly — that route runs both pipelines.

Which models run: both the dependency analyzer and the sprint planner are pinned to Gemini 3 Flash. (The analyzer was pinned to Gemini 3 Pro until that model's route started returning "no endpoints found" and took the whole planning route down with it.) Both accept a per-call model override, subject to your plan's allow-list — see 3.2.

Sprint count is formula-driven: target = max(3, min(15, ceil(N / 8))) for N feature criteria. So 8 criteria → 3 sprints, 80 → 10, 200 → 15 (the cap). There's no per-project override; change N to change the count.


Operations that are API-only

The validation logic for these is correct and shipped; no UI invokes it yet. All live under /api/projects/[projectId]/.

OperationCall
Move a criterion between sprintsPOST /sprints/<destSprintId>/move-ac with { ac_id }
Reorder sprintsPOST /sprints/reorder
Rename a sprint or change its themePATCH /sprints/<sprintId> with { title } / { theme }
Delete a sprint (must be empty)DELETE /sprints/<sprintId>
Read the whole plan with counts and blocked flagsGET /sprints

Both moves are hard-dependency validated in both directions and return 422 hard_dep_violation with a detail naming the edge that would break. The Backlog sprint is immutable — PATCH/DELETE on it return 422 backlog_immutable.

The Backlog sits at position = 99999 and always exists. It catches criteria added after the last plan and holds anything you deliberately defer. It's excluded from next-ready — a ready criterion sitting in Backlog will never be handed to your agent, which is the single most common cause of "the agent says there's no work".


Troubleshooting

The agent says "no work available" but ready criteria exist

In order of likelihood:

  1. They're in the Backlog, which next-ready excludes. Re-plan to redistribute them, or move-ac them into a sprint.
  2. They're reserved. Check the response for reason: "reserved". Remember that assigning to yourself hides work from your own agent.
  3. They're blocked by unpassed hard prerequisites. The board shows a red badge; the execution plan shows blockedOn.
  4. A bootstrap criterion is still unpassed, so Foundation work is being served ahead of everything.
  5. The active sprint isn't what you think. It's the first sprint with any non-passed criterion — a stray draft in Sprint 1 keeps Sprint 1 active.

Planning returns 403 or 404

403 { error: "plan_required", required: "pro" } means the plan is below Pro — planning is Pro+. 404 { error: "project_not_found" } means the project isn't yours (or doesn't exist); check the project id and the account you're authenticated as.

Everything landed in the Backlog

Usually the analyzer returned no edges, so topo-sort produced a flat list and the planner had nothing meaningful to chunk. Criterion titles that are too generic are the usual cause — "User can sign in" derives edges, "Authentication step 1" doesn't. Sharpen the titles, then re-plan.

Planning hangs

Both pipelines run synchronously in the route handler and take 30s–3min depending on criterion count. Past about 5 minutes, check server logs for a plan_failed response. Closing the tab doesn't cancel anything — the LLM call completes and writes its result server-side.

A sprint reorder was rejected

422 hard_dep_violation means the move would place a prerequisite after its dependent. Move the depended-on criterion into an earlier-or-equal position first.


v1 limitations

  • No drag-and-drop UI for moving criteria between sprints or reordering them — the routes work, nothing calls them.
  • No per-project sprint sizing. The count is formula-driven with no override.
  • No user-authored dependency edges. The schema supports derivation_source = 'user_added'; there's no writer or UI.
  • No manual sprint creation, and no active-sprint override. Only the planner creates sprints; active is always computed.
  • No epics. Above ~120 criteria, sprints get larger rather than more numerous.
  • No graphical DAG view. Inline dependency badges and a side sheet only.
  • No sprint dates, deadlines or capacity. Sprints are ordered, not time-bound.
  • The Re-analyze button doesn't re-analyze yet — see above.

Where to go next