Skip to main content
A project is where work happens in a Driftless workspace. It is a shared execution environment for humans and their agents: the project holds cards (the unit of work), and the same board is operable from the CLI, the REST API, and the dashboard. The product lives in the headless surface; the board is occasional visibility for the human. This is the half of Driftless that isn’t documentation. Topics capture what the team knows; projects capture what the team is doing. Because both live in the same workspace, an agent picking up a card pulls the Knowledge anchored to the code it’s about to touch, and the gotchas it learns flow back into the vault.

Projects and cards

  • A project belongs to one workspace and has a status: active, done, or archived. It can carry a goal (the definition of done for the whole project) and a playbook_slug (a Knowledge topic the agent reads to learn how to run it), set with --playbook on the CLI or playbook_slug on the REST API. The MCP driftless_project tool does not expose playbook_slug yet; when a project has one, the card next bundle carries a playbook_hint.
  • A card belongs to one project and moves through a status lane: todo · in_progress · blocked · review · done. A card can carry:
    • an owner (a workspace member),
    • an acceptance criterion: what “done” means, checked before the card is closed,
    • a validate command: a shell command that must exit zero,
    • depends_on: other cards that must be done before this one becomes actionable.

The agent loop

A project is a board an agent walks in a loop, repeating four steps until the project is done:
1

Ask for the next card

project card next <project-id> returns the next actionable card plus its context bundle in one call: { card, context_bundle, project_done }. The bundle merges the card’s own context refs with the project’s, deduplicated: the team’s recorded memory for that card’s area, so the agent doesn’t re-derive what the team already knows.
2

Work the card

The agent works with the bundle loaded. This is what sets it apart from an ordinary task queue: the card arrives anchored to the Knowledge around it, not as a bare title.
3

Validate against acceptance

If the card has a validate command, the agent runs it. A non-zero exit is stop-and-fix: do not mark the card done, repair and re-validate. acceptance is the human-readable definition of done the agent checks against.
4

Write back and close

Persist anything durable learned, mark the card done, and ask for the next card. Repeat until project_done is true.
Dep-gating orders the loop without micro-management: a todo card with unmet dependencies never appears in next. It becomes “ready” only once every card in its depends_on list is done. Among actionable cards, next picks in priority order: in_progress first, then review, then blocked, then a ready todo. Listing cards with project cards uses a different, board-oriented order (blocked, review, in_progress, todo, done). When no cards are ready and none are in flight, project_done is true. Blocked, not guessed: if the agent cannot proceed without a human decision, it sets the card to blocked rather than marking it done.
Verification is report-and-surface, not enforcement. The agent reports the result of running validate; Driftless stores and surfaces it, but never runs the command itself. A done transition without a passing result is allowed by default (the card is surfaced as “needs verification”), unless the workspace opts in to blocking via require_card_validation, a workspace-level setting (default off) an owner or admin sets with PATCH /workspaces/:slug and { settings: { require_card_validation: true } }. It is not a per-card or per-project flag.

Write-back mid-loop

A gotcha discovered while working a card shouldn’t wait for the end. Attach it to the card as a note:
driftless note add \
  --content "Webhook deliveries arrive out of order, idempotency key required" \
  --project prj_abc --card card_xyz
That note rides the card’s next context bundle (the next loop tick sees it), and when the project closes, the compact synthesizes the card’s attached notes into a draft Topic, sent to the Inbox for an owner or admin to merge into Knowledge (themselves, or via an agent they ask). Work boundaries are where memory should attach: next is the moment to inject context, card-close is the moment to capture it.
--card requires --project (a card lives inside a project). Over MCP, driftless_note_add takes the same project_id and card_id.

From the CLI

Projects and cards are first-class CLI citizens; an agent runs the same commands a human would.
CommandDescription
project add <title>Create a project
project list [--status active|done|archived]List projects
project get <id>Project detail + card index
project update <id> [--status …] [--title "…"]Update a project
project cards <project-id> [--status s] [--limit n]List cards (priority order)
project card next <project-id>Next actionable card + context bundle (the loop tick)
project card add <project-id> --title "…" [--description "…"|@file] [--acceptance "…"] [--validate "cmd"] [--dep <card-id>] [--owner <id>]Create a card
project card add <project-id> --file @cards.jsonBatch-create cards (depends_on by title or id)
project card update <project-id> <card-id> […]Update a card
project card get <project-id> <card-id>Card detail
project card status <project-id> <card-id> <status>Change a card’s status
project card move <project-id> <card-id> <to-project-id>Move a card to another project
project card rm <project-id> <card-id>Delete a card
# Spin up a project with an acceptance-checked, validated card
driftless project add "Refunds revamp"
driftless project card add prj_abc \
  --title "Idempotent webhook handler" \
  --acceptance "Duplicate webhook deliveries are a no-op" \
  --validate "npm test -- webhooks" \
  --owner mem_123

# Then walk it
driftless project card next prj_abc          # card + its context bundle
driftless project card status prj_abc card_xyz done
Cards are scanned for secrets on write, like topics; pass --allow-secrets only when you intend to store one. Add --json to any command for machine output.

From the REST API

Everything above maps to REST under the workspace:
MethodPath
POST / GET/workspaces/:slug/projects
GET / PATCH/workspaces/:slug/projects/:id
POST / GET/workspaces/:slug/projects/:projectId/cards
GET/workspaces/:slug/projects/:projectId/cards/next
GET / PATCH / DELETE/workspaces/:slug/projects/:projectId/cards/:cardId
A card PATCH accepts add_context to attach a note or topic to the card mid-loop, mirroring the project’s own add_context.

On the dashboard

The board renders the same projects and cards as a kanban: columns are the card statuses, and dragging a card calls the same PATCH the CLI does. The project header surfaces its goal and playbook; cards show verified and ready badges and their acceptance / validate in detail. Marking a project done opens the compact, where you pick the cards whose learnings become Topic drafts in your Inbox; the rest retire with the project. It’s a visibility surface, not a separate source of truth: a card created by an agent from the CLI shows up immediately.
Projects and cards live in the workspace and are visible to its members. They’re governed by workspace roles, not by the Note → Knowledge lifecycle; that lifecycle is for topics. The compact’s draft topics, however, do enter that lifecycle. See Governance.