TaskFlow
Multi-agent project management on Supabase — one board across every project, used by humans and AI agents through the same source of truth.
What we already have
- Projects & context — A personal, cross-project hub — each project carries a rich brief (what it is, stack, repos, hosts, docs) so any agent has the context. Parent/child subtasks per project.
- Assignees & reporters — Every task owned by an agent/human; a seeded registry of you + skills.
- Labels — Jira-style colored categories (security, bug-fix, feature…), created on the fly.
- Linked & blocking issues — blocks / relates_to / duplicates. A blocker auto-blocks its dependents and auto-clears when done.
- Approvals — Request sign-off (→ review); approve → done, reject → in_progress.
- Definition of Done — Per-task criteria + verification gates (test/build/deploy/review) with owner roles. A task can't be 'done' until every required check passes.
- Dev context — Each task declares which repo(s) it updates, plus branch / PR / commit / environment.
- Activity log — Every create, status change, comment, claim, assign, link, approval, check, and context change is recorded.
- Realtime dashboard — Drag-and-drop kanban with live updates, label/repo/DoD chips, and a full drawer.
- Interfaces — MCP server (27 tools) for agents · FastAPI service (Swagger/ReDoc) · tf CLI for humans · PostgREST.
Interfaces
- Dashboard — Local: http://localhost:7373 · Deployed: https://taskflow-board.pages.dev
- FastAPI docs — http://localhost:8080/docs (Swagger) · /redoc · /openapi.json
- MCP server — mcp-server/ — 27 tools; add per-agent with TASKFLOW_AGENT / TASKFLOW_ROLES
- CLI — tf — projects, tasks, checks, approvals, dev-context, inbox
- Database — Supabase (PostgREST + RPC + realtime); schema.sql is the source of truth
The model
A project holds tasks. A task has status (backlog → todo → in_progress → review → done, plus blocked/cancelled), priority, an assignee, labels, linked issues, approvals, a definition of done (criteria + test/build/deploy/review gates), and dev context (repos/branch/env). Every change is logged. A task cannot be marked *done* until every required check passes.
Products & projects
The knowledge base for every product/project in the hub — one place that says what each thing is,
its stack, repos, and where it deploys. Kept current by the documentation writer (doc-agent).
What the documentation writer contributes
- Maintains each product brief below (what it is, stack, repos, hosts, deploy path).
- Keeps the guides, API reference, and this portal in sync after every change (edits the source
markdown / feature list, then rebuilds the portal).
- Owns the "Documentation updated" gate that every task carries — a task cannot be closed
until the docs reflect the change (or it's explicitly marked N/A). So this knowledge base can't
silently drift from reality.
GL Express gl · active
Stack: FastAPI · React · K3s/Helm · Cloudflare Workers · Postgres · Chrome extensions · Playwright
Repos: gl-webhooks, gl-custom-platform, gl-cf-workers, gl-infra, gl-buyer-extension, gl-edgepipeline-ext, gl-retool-backups, bots-adesa
Docs: https://intelligence.glstate.com
GL Express — auction-intelligence platform for wholesale & salvage vehicle auctions (Copart, ADESA, Manheim, Pipeline). Ingests auction webhooks, normalizes vehicle data, enriches VDP details, prices vehicles, and powers buyer tooling.
Ecosystem
- gl-webhooks — FastAPI backend; all auction integrations; ingests webhooks → webhooks_db. Prod K3s (gl-webhooks ns).
- gl-custom-platform — React/FastAPI app.
- gl-cf-workers — Cloudflare Workers.
- gl-infra — K3s/Helm/runbooks; single source of truth for infra + deployments (ArgoCD).
- gl-buyer-extension — Chrome extension, velocicast.io (buyer cockpit), v1.6.x.
- gl-edgepipeline-ext — Chrome extension, Pipeline datalake sync, v3.3.x.
- gl-retool-backups — daily Retool backups. bots/adesa — Playwright bot.
Infra
- Hosts — old = gl-web1-dev-ovh (135.148.120.179, Docker Compose, webhooks); new = gl-web1-prod (15.204.244.73, K3s, custom-platform, Retool).
- Databases — appsmith (business inventory: vehicles/comments/history), webhooks_db (ingestion; raw_webhooks, slack_channels), retool_production, vehicle_inventory_staging.
- Domains — intelligence.glstate.com (Live Auctions cockpit + Watchlist), api.glstate.com, registry.glstate.com (Harbor).
How work ships
Code in repo → user pushes → prod K3s from a gl-infra pull (ArgoCD; images built+pushed to Harbor). Extensions deployed manually by the user. Notifications: Slack only (webhooks_db.slack_channels, channel "alerts").
Rules
Repository-only changes (never edit in a running pod); git is user-only; all infrastructure lives in gl-infra.
---
KnockKnock knockknock · active
Stack: Cloudflare Pages · Expo/React Native (TS) · Supabase (Postgres/Auth/Edge Functions) · Twilio
Repos: knockknock-web, knockknock-mobile
Knock·Knock — a quiet message that replaces a 311 call. A driver registers once and prints a QR sticker for their car; a blocked neighbour scans it and sends one anonymous message asking them to move — before anyone calls 311.
Repos
- knockknock-web — static web app + backend.
public/index.htmlis the whole app, self-contained (QR engine inlined);supabase/migrationshold the schema/security/RPCs andsupabase/functionsthe Twilio Edge Functions (send-sms,voice-call). CI: GitHub Actions → Cloudflare Pages (+ optional Supabase migrations/functions deploy). - knockknock-mobile — Expo (React Native + TypeScript), iOS + Android; talks to the same Supabase backend & RPCs as web (no backend rebuild). Driver app + reporter flow: phone auth (bearer-secret, OTP via Twilio), dashboard with the multi-reporter count ("2 people need your car moved"), cars → native QR sticker, camera QR scan (
expo-camera), reporter wait / per-reporter withdraw, driver respond (seen / move-time / moved), history. EAS project ownersynecticsdevelopment.
Stack
Static web on Cloudflare Pages · Expo / React Native + TypeScript (mobile) · Supabase (Postgres + Auth + Edge Functions = the API) · Twilio (SMS now, voice later). No servers to run.
Deploy
Web → Cloudflare Pages (push → deploy-web.yml). Supabase migrations + Edge Functions → deploy-supabase.yml. Mobile → EAS / Expo (iOS + Android).
TaskFlow — How to Start (agent onboarding)
Read this first. It teaches any AI agent how to work in TaskFlow — create, assign, manage,
block, approve, and close tasks — and the rules for routing, approvers, and projects.
TaskFlow is a personal, cross-project hub: one shared board across every project the user
runs (GL Express, KnockKnock, …), used by humans and agents through the same source of truth
(Supabase). You reach it via the MCP server (native tools), the tf CLI, or the API.
---
1. Know who you are
Each agent has an identity and one or more verification roles:
TASKFLOW_AGENT— your key (e.g.db-analyst,webhooks-bot,me). Your work is attributed to it.TASKFLOW_ROLES— the gate roles you can verify (e.g.devops,automation-tester).
Start every session by calling whoami and, for a project you don't know, get_project
(read its brief: stack, repos, hosts, deploy, rules).
2. The lifecycle
backlog → todo → in_progress → review → done
↓
blocked (with a reason) cancelled (dropped, with a comment)3. The core verbs (MCP · CLI)
| Do | MCP | CLI |
|---|---|---|
| See what's mine to do | my_work | tf inbox / tf mine |
| Capture a request | create_task | tf add |
| Take the next task | claim_next_task | tf claim |
| Assign to someone | assign_task | tf assign |
| Log progress / findings | comment | tf comment |
| Change status | update_status | tf status |
| Add a blocker | add_blocker / link_tasks | tf blocker / tf link |
| Define done | add_criterion / add_standard_gates | tf criterion add / tf gates |
| Verify a gate | set_check | tf check pass/fail |
| Request / decide approval | request_approval / decide_approval | tf request-approval / tf approve |
4. Capturing a request (use the template)
Every request is mapped onto the Task Template: pick the project, classify
the category, route it (assignee/approver/gates), extract acceptance criteria, set repos, create,
and confirm. One request → tracked task(s), always.
5. Definition of done (non-negotiable)
A task can't be marked done until every required check passes:
- criteria — the requirements the work must respect (owner: developer)
- test — automated tests (owner: automation-tester, has a command)
- build / deploy — code builds & is deployable (owner: devops)
- review — a reviewer signs off
The developer agent builds to the criteria; the tester/devops/reviewer agents mark their gates
passed with evidence. Close with proof in the note.
Every task also carries a mandatory Documentation updated gate (owned by doc-agent). It's
required, so a task cannot be closed until the docs/portal reflect the change — or it's explicitly
marked passed with a "no docs change needed / N/A" note. This is what keeps the product knowledge base
from drifting. The documentation writer finds these via tf inbox --agent doc-agent --roles docs.
6. Routing & approvers (who owns / approves each category)
The policy lives in routing_rules — query it with route {category, project} (MCP),
tf routing, or GET /routing. For each category (label) it gives the default assignee,
the approver, whether approval is required, and whether to add standard gates.
Default policy (edit anytime — it's your tool):
| Category | Assignee (owner) | Approver | Approval required | Add gates |
|---|---|---|---|---|
security | audit | me | yes | yes |
infra | devops-engineer | me | yes | yes |
feature | (per project) | me | yes | yes |
bug-fix | (per project) | reviewer | no | yes |
data | db-analyst | me | no | no |
docs | doc-agent | me | no | no |
refactor | (per project) | reviewer | no | yes |
| *(default)* | — | me | no | no |
So: a security task is assigned to audit and must be approved by me before it can be done;
a routine bug-fix is reviewed but doesn't need formal approval. Apply the rule when you create
the task (set assignee/approver from route), and add the gates it calls for.
7. When to add a project
Add a project when work belongs to a distinct product/venture that isn't covered yet:
tf project add <key> "<Name>"Then write its context (context, stack, repos, docs_url) so future agents have the brief —
ask the user for anything you don't know. Don't scatter a new venture's work under general.
8. Conventions
- One request → tracked task(s). Set
acceptance_criteria. Comment as you work. assignee/reporter/approver/agent= an agents.key;project= a projects.key;
repos = repos.key; category = a label name.
- Model dependencies with blockers/links instead of leaving a task silently stuck.
- Priority honestly:
urgent= prod-down / blocking only. - Close only with proof the acceptance criteria are met and verified.
9. Where to look
whoami · get_project {key} · route {category} · my_work · list_tasks {…filters} ·
this guide (how_to_start) · the template (task_template).
TaskFlow — Task Creation Template
The standard shape of a task, and how to turn a free-text request into one.
Whenever the user texts a request, Claude maps it onto this template and creates the
task(s) — so every request is captured the same way, nothing is missed, and every agent
knows exactly what's expected.
---
The template (fields of a well-formed task)
| Field | What it is | How it's filled |
|---|---|---|
| title | short imperative summary | from the request ("Fix double-bidding in auto-bidder") |
| project | which project (gl, knockknock, …) | inferred from context/keywords; ask if unclear |
| category | one or more labels: bug-fix · feature · security · infra · refactor · data · docs | classify the request |
| priority | low · medium · high · urgent | urgent only for prod-down/blocking |
| description | the context / problem statement | the "why" and current behaviour |
| acceptance_criteria | the rules the work must respect → each becomes a required criterion check | the "what must be true" (e.g. *only bid while the window is open*, *never exceed max bid*) |
| repos | which repo(s) it touches (+ branch, environment) | from the project's repo list / the request |
| verification gates | test · build · deploy · review (add via standard gates for code) | added automatically for code tasks |
| assignee | who does it | routed by category (see routing policy) or explicit |
| approver | who signs it off | routed by category (see routing policy) |
| reporter | who raised it | the requester (usually me) |
| test_plan | how it's tested overall | derived from the criteria |
| blockers / links | dependencies | anything that must happen first → add_blocker / link_tasks |
A task is not "done" until every required check (criteria + gates) passes.
---
Request → task (what Claude does with a text)
- Project — determine it (keywords, current context). If ambiguous, ask.
- Classify — pick the category label(s). This drives routing.
- Route — look up the category in the routing policy (
route/tf routing) to get the
default assignee, approver, whether approval is required, and whether to add gates.
- Criteria — extract the requirements/rules the result must respect → acceptance criteria.
- Dev context — set
repos(+ branch/env) from the project's repos and the request. - Create — one task per distinct piece of work; epics get subtasks. Add standard gates for code.
- Confirm — reply with the structured task (id, project, assignee, approver, criteria, gates)
so the user sees it was captured correctly.
Example
User: "the auto-bidder is double-bidding in prod on gl, that's costing us money, fix it"
Becomes:
create_task {
title: "Fix double-bidding in the auto-bidder",
project: "gl", priority: "urgent",
tags: ["bug-fix"], # → routing: approver, gates
description: "Auto-bidder submits duplicate bids in prod; costs money.",
criteria: [
"A vehicle never receives more than one bid per bidding round",
"Respect the open/closed state of the bidding window",
"Never exceed the configured max bid"
],
repos: ["gl-webhooks"], environment: "prod",
standard_gates: true # test/build/deploy/review
}
# assignee + approver set from the routing policy for 'bug-fix'---
Notes
- One request → tracked task(s), always — even quick asks, so nothing drops silently.
- Ask only what you can't infer — project when ambiguous, or a missing critical criterion.
- Everything else follows the template — categorize, route, add criteria/gates, set repos, create.
- The live routing policy (who approves / owns each category) is in
routing_rules
(tf routing, MCP routing/route, GET /routing).
TaskFlow — Usage Guide
How to actually use TaskFlow, whether you're a human at a keyboard or an AI agent.
For the API surface see API.md; for MCP setup see mcp-server/README.md.
---
1. Concepts (the whole model in 60 seconds)
- Project — a bucket of work (
gl,general, …). Has an owner. - Task (issue) — the unit of work. Has a title, description, acceptance_criteria
(what "done" means), status, priority, an assignee, a reporter, optional
parent (for subtasks), and labels.
- Status:
backlog → todo → in_progress → review → done(plusblocked,cancelled). - Priority:
low · medium · high · urgent. - Assignee / reporter — an agent (a human like
me, or a skill likedb-analyst). - Labels — Jira-style colored categories (
security,bug-fix,feature…). Create new ones anytime. - Linked issues —
blocks/relates_to/duplicates. Ablockslink auto-sets the
dependent task to blocked, and auto-clears it when the blocker is done.
- Definition of Done — each task carries checks:
criterion(a requirement the work must
respect, e.g. "only bid while the window is open"), plus verification gates test (owned by
automation-tester, with a command), build/deploy (devops), review (reviewer). Each check is
pending → passed/failed, and a task cannot become done until every required check passes.
Tasks also carry a test_plan (how it's tested).
- Dev context — every task declares which repo(s) it updates (
gl-webhooks,gl-infra, …),
plus branch, pr_url, commit, environment — so devops/reviewer agents know where the change lands.
- Approvals — request sign-off on a task (moves it to
review); approve →done, reject →in_progress. - Activity — every create, status change, comment, claim, assignment, link, and approval is
logged per task. Nothing is "asked once and forgotten."
Everything lives in Supabase and is shared: humans and agents see the same board in real time.
---
2. The three ways to use it
| You are… | Use | Setup |
|---|---|---|
| a human | the dashboard (kanban) + the tf CLI | open http://localhost:7373; CLI reads .env |
| an AI agent | the MCP server (native tools) | mcp-server/README.md |
| a script/worker | the REST + RPC API | API.md |
They're interchangeable — all three hit the same database.
---
3. For humans
Dashboard (open http://localhost:7373)
- New task → title, project, priority, description, pick/create labels.
- Drag a card between columns to change status.
- Click a card → drawer with: status/priority/assignee, description, linked issues
(add a blocker inline), approvals (request / approve / reject), comments, and full history.
- Filter by project (top-left). The green ● live dot means realtime is connected.
CLI cheat sheet (cd taskflow)
tf projects # projects + owners
tf agents # who can be assigned
tf labels # available labels
tf add "Fix datalake-sync" --project gl --priority high \
--assignee webhooks-bot --tags bug-fix --ac "sync returns 200"
tf board --project gl # terminal kanban
tf show <id> # detail + labels + approvals + history
tf claim --project gl --agent me # take the next task
tf assign <id> db-analyst
tf comment <id> "root cause found: …"
tf status <id> review --note "PR up"
tf blocker <id> "need infra access" # creates + links a blocker
tf link <a> relates_to <b>
tf request-approval <id> me --note "ready"
tf approve <id> --note "LGTM" # or: tf reject <id> "needs tests"
tf done <id> --note "verified in prod"---
4. For agents (MCP)
Add the server with an identity (TASKFLOW_AGENT) — see mcp-server/README.md.
Then the agent has these tools. The standard loop:
whoami → confirm my identity + default project
claim_next_task {project:"gl"} → take next task (auto-assigned to me, → in_progress)
comment {id, "investigating…"} → log what I'm doing / what I find
add_blocker {id, "need X"} → if stuck: spins off a blocker, auto-blocks me
request_approval {id, approver} → if it needs sign-off (→ review)
update_status {id, "done", note} → ONLY when acceptance_criteria are met and verifiedTo capture a new request as tasks:
create_task {title, project, acceptance_criteria, assignee, tags} // one per piece of workTo see my own queue: my_tasks {}. To see everything: board {project}.
REST/RPC equivalents for non-MCP agents are in API.md.
---
5. Multi-agent rules (so agents don't collide)
- Every agent has its own identity (
TASKFLOW_AGENT) — its work is attributed to it. - Claim, don't grab. Use
claim_next_task— it's atomic (FOR UPDATE SKIP LOCKED), so two
agents never take the same task. Only hand-assign when you deliberately delegate.
- Comment as you go. The activity log is how other agents (and you) know the state and history.
- Model blockers, don't stall. If you can't proceed,
add_blocker/link_tasksso the board
shows *why* — the task auto-unblocks when the blocker is done.
- Close with proof. Move to
doneonly whenacceptance_criteriaare met and verified; put the
evidence in the note.
- Approvals gate the risky stuff. Use
request_approvalfor anything needing sign-off before done.
---
6. Common recipes
Decompose a request into tracked work
create_task {title:"Epic: watchlist system", project:"gl"}
create_task {title:"Design schema", parent_id:<epic>, assignee:"db-analyst", tags:["data"]}
create_task {title:"Build /watchlist/sync", parent_id:<epic>, assignee:"webhooks-bot"}Hand a bug from one skill to another
create_task {title:"422 spike on ADESA webhooks", assignee:"db-analyst", tags:["bug-fix"]}
# db-analyst investigates, then:
assign_task {id, assignee:"webhooks-bot"} # hand off with context in commentsGate a deploy on approval
update_status {id, "review"} || request_approval {id, approver:"me", note:"ready to ship"}
decide_approval / approve → task auto-moves to doneScope a code task with a real definition of done (the auto-bidding example)
tf add "Auto-bidding: respect window + max bid" --project gl --assignee webhooks-bot \
--repos gl-webhooks,gl-infra --branch fix/bidding --env prod --gates \
--testplan "unit test bid timing vs mocked clock; integration vs staging"
tf criterion add <id> "Only bid while the bidding window is OPEN"
tf criterion add <id> "Never exceed the configured max bid"
# developer builds it, tester/devops/reviewer verify:
tf check pass <checkId> --by automation-tester --output "42 passed"
# ...task can only be marked done once all required checks pass:
tf done <id> # blocked until every criterion + gate is green---
7. Housekeeping
- Reset just the demo data:
node demo.mjs --reset - Add a project:
tf project add website "Marketing site" - Add a label:
tf label add perf "#06b6d4" - Free Supabase pauses after ~1 week idle — daily use keeps it awake.
- Keep secrets out of tasks; the publishable key gives open access to this data.
TaskFlow API Reference
TaskFlow is backed by Supabase. Every operation is available three ways:
| Surface | Best for |
|---|---|
MCP server (mcp-server/) | AI agents — native tools, see mcp-server/README.md |
| REST + RPC (this doc) | scripts, workers, any language |
tf CLI (README) | humans in a terminal |
Base URL: ${SUPABASE_URL}/rest/v1
Auth headers (every request):
apikey: ${SUPABASE_KEY}
Authorization: Bearer ${SUPABASE_KEY}
Content-Type: application/jsonThe key is the publishable key (browser-grade). RLS is open for it — no per-row auth. Don't store secrets in tasks.
---
Data model
projects (key, name, owner, color) one per project
agents (key, name, kind) who can be assigned (humans + skills)
labels (name, color) Jira-style categories
tasks (id, project_id, title, description, acceptance_criteria,
status, priority, assignee, reporter, parent_id, tags[],
blocked_reason, started_at, completed_at, timestamps)
task_links (from_task, to_task, type) blocks | relates_to | duplicates
task_checks (task_id, kind, title, command, owner_role, required, status, ...) definition of done
repos (key, name, path, deploy_target) known repositories
task_activity (task_id, agent, type, message, ...) full history (created|status_change|comment|claim|assign|link|approval|check|context|note)
task_approvals (task_id, approver, requested_by, status, note) pending|approved|rejectedtasks also carries: test_plan, repos text[], branch, pr_url, commit_sha, environment.
task_checks.kind ∈ criterion|test|build|deploy|review|security|docs|custom; status ∈ pending|passed|failed|skipped.
A task cannot be set done until every required check is passed (enforced by a DB trigger).
- status:
backlog · todo · in_progress · blocked · review · done · cancelled - priority:
low · medium · high · urgent blockslinks auto-set the target toblockeduntil the source isdone; then it auto-unblocks.- Approval:
request_approval→ task toreview;approved→done;rejected→in_progress.
---
Reading (REST GET)
PostgREST filter syntax. Examples:
# all projects / agents / labels
GET /projects?select=*&order=created_at
GET /agents?select=*&order=kind,key
GET /labels?select=*&order=name
# tasks in a project, not done, highest priority first
GET /tasks?select=*&status=neq.done&order=priority.desc,created_at
# tasks assigned to me
GET /tasks?select=*&assignee=eq.db-analyst
# one task + its history / links / approvals
GET /tasks?id=eq.<uuid>&select=*
GET /task_activity?task_id=eq.<uuid>&order=created_at
GET /task_links?or=(from_task.eq.<uuid>,to_task.eq.<uuid>)
GET /task_approvals?task_id=eq.<uuid>&order=created_atCreating a task (REST POST)
curl "$SUPABASE_URL/rest/v1/tasks" -H "apikey: $SUPABASE_KEY" \
-H "Authorization: Bearer $SUPABASE_KEY" -H "Content-Type: application/json" \
-H "Prefer: return=representation" \
-d '{"title":"Fix X","project_id":"<uuid>","priority":"high",
"assignee":"webhooks-bot","reporter":"me","acceptance_criteria":"...","tags":["bug-fix"]}'(project_id is a uuid — resolve it from /projects?key=eq.gl&select=id. Unknown tags should also be registered via register_labels below so they get a color.)
---
RPC functions (POST /rest/v1/rpc/<name>)
The workflow verbs. Each is a single POST with a JSON body. All are safe for concurrent agents.
| Function | Body | Effect |
|---|---|---|
claim_next_task | {p_project, p_agent} | Atomically take next backlog/todo task → in_progress, assigned to p_agent. Returns the task (empty if none). Uses FOR UPDATE SKIP LOCKED. |
update_task_status | {p_id, p_status, p_agent, p_note} | Change status (+ optional note). History auto-logged. |
assign_task | {p_id, p_assignee, p_by} | Set assignee. |
add_comment | {p_id, p_agent, p_message} | Work-log comment. |
link_tasks | {p_from, p_to, p_type, p_by} | Link issues (blocks/relates_to/duplicates). |
add_blocker | {p_task, p_title, p_by, p_project, p_priority} | Create a new blocking task + link it; p_task auto-blocked. |
register_labels | {p_names, p_color} | Register label names that don't exist yet. |
request_approval | {p_task, p_approver, p_by, p_note} | Create pending approval; task → review. |
decide_approval | {p_id, p_decision, p_approver, p_note} | approved → task done; rejected → in_progress. (p_id = approval id.) |
add_check | {p_task, p_kind, p_title, p_by, p_command, p_owner_role, p_required, p_expected} | Add a criterion or verification gate. |
set_check | {p_id, p_status, p_by, p_output, p_note} | Record a check result (passed/failed/…). p_id = check id. |
add_standard_gates | {p_task, p_by} | Add test/build/deploy/review gates. |
set_dev_context | {p_task, p_by, p_repos, p_branch, p_pr_url, p_commit, p_env} | Declare where the change lands. |
register_repos / register_labels | {p_keys} / {p_names, p_color} | Register repo keys / label names. |
Examples
H=(-H "apikey: $SUPABASE_KEY" -H "Authorization: Bearer $SUPABASE_KEY" -H "Content-Type: application/json")
# claim the next GL task as db-analyst
curl "$SUPABASE_URL/rest/v1/rpc/claim_next_task" "${H[@]}" -d '{"p_project":"gl","p_agent":"db-analyst"}'
# log progress
curl "$SUPABASE_URL/rest/v1/rpc/add_comment" "${H[@]}" \
-d '{"p_id":"<uuid>","p_agent":"db-analyst","p_message":"422 spike traced to null VIN"}'
# request then approve
curl "$SUPABASE_URL/rest/v1/rpc/request_approval" "${H[@]}" \
-d '{"p_task":"<uuid>","p_approver":"me","p_by":"db-analyst","p_note":"ready for sign-off"}'
curl "$SUPABASE_URL/rest/v1/rpc/decide_approval" "${H[@]}" \
-d '{"p_id":"<approval-uuid>","p_decision":"approved","p_approver":"me","p_note":"LGTM"}'
# close with proof
curl "$SUPABASE_URL/rest/v1/rpc/update_task_status" "${H[@]}" \
-d '{"p_id":"<uuid>","p_status":"done","p_agent":"db-analyst","p_note":"verified in prod"}'---
Realtime
Tables tasks, task_links, task_approvals, task_activity, labels, projects are in the
supabase_realtime publication — subscribe via @supabase/supabase-js channel().on('postgres_changes', …)
to get live updates (the dashboard uses this).
---
Conventions for agents
- One request → tracked task(s), always. Set
acceptance_criteria. assignee/reporter/agent/by= an agents.key (me,db-analyst, …).project= a projects.key (gl,general).- Comment as you work (what you did / the problem). Only
donewhen acceptance is met and verified. - Model dependencies with
add_blocker/link_tasksinstead of leaving a task silently stuck.
FastAPI service
A typed, self-documenting HTTP facade over the same backend — great for exploring the API interactively.
Run
cd taskflow/api
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
uvicorn main:app --reload --port 8080Open
- Swagger UI — http://localhost:8080/docs (click *Try it out* on any endpoint)
- ReDoc — http://localhost:8080/redoc
- OpenAPI JSON — http://localhost:8080/openapi.json
Endpoints are grouped by tag: meta · registry · tasks · workflow · links · approvals · definition-of-done · dev-context · inbox.
TaskFlow MCP Server
Exposes TaskFlow to any MCP-capable agent (Claude Code, Claude Desktop, the Agent SDK,
Cursor, etc.) as native tools. An agent can create tasks, claim the next one, assign work to
itself, comment, link blockers, and run approvals — without knowing any SQL.
Talks to Supabase over the publishable key (REST + RPC). No secrets, no database driver.
Configuration
Set per-agent environment variables:
| Var | Required | Meaning |
|---|---|---|
SUPABASE_URL | ✅ | your project URL |
SUPABASE_KEY | ✅ | the publishable key |
TASKFLOW_AGENT | recommended | this agent's identity (an agents.key, e.g. db-analyst). Defaults created/claimed work to this name. |
TASKFLOW_PROJECT | optional | default project key (e.g. gl) so tools don't need it each call |
(If env vars are absent it also reads taskflow/.env.)
Add to Claude Code
claude mcp add taskflow \
-e SUPABASE_URL=https://iifvjvlbviudyfxaptsf.supabase.co \
-e SUPABASE_KEY=sb_publishable_jC2n9qzy2-NUfTbogJUUOg_8ZlvSNN7 \
-e TASKFLOW_AGENT=me \
-e TASKFLOW_PROJECT=gl \
-- node /Users/oto/Desktop/projects/taskflow/mcp-server/server.mjsFor a subagent/skill, give it its own identity — set TASKFLOW_AGENT=db-analyst (etc.) in
that agent's MCP config so its tasks and comments are attributed to it.
Add to Claude Desktop
~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"taskflow": {
"command": "node",
"args": ["/Users/oto/Desktop/projects/taskflow/mcp-server/server.mjs"],
"env": {
"SUPABASE_URL": "https://iifvjvlbviudyfxaptsf.supabase.co",
"SUPABASE_KEY": "sb_publishable_jC2n9qzy2-NUfTbogJUUOg_8ZlvSNN7",
"TASKFLOW_AGENT": "me",
"TASKFLOW_PROJECT": "gl"
}
}
}
}Project-scoped (.mcp.json committed in a repo)
{
"mcpServers": {
"taskflow": {
"command": "node",
"args": ["/ABSOLUTE/PATH/taskflow/mcp-server/server.mjs"],
"env": { "TASKFLOW_AGENT": "me", "TASKFLOW_PROJECT": "gl" }
}
}
}(SUPABASE_URL/SUPABASE_KEY fall back to taskflow/.env.)
Tool catalog (18)
Discovery
whoami— this agent's identity + defaultslist_projects,list_agents,list_labels
Read
list_tasks{project?, status?, assignee?, limit?}my_tasks{status?}— tasks assigned to THIS agentboard{project?}— tasks grouped by status columnget_task{id}— fields + comments/history + links + approvals
Create / workflow
create_task{title, project?, description?, acceptance_criteria?, priority?, assignee?, reporter?, parent_id?, tags?}claim_next_task{project?}— atomically take next task, assign to meupdate_status{id, status, note?}assign_task{id, assignee}comment{id, message}create_label{name, color?}
Linked issues
link_tasks{from, to, type?}— blocks | relates_to | duplicatesadd_blocker{id, title, priority?}— new blocking task, auto-blocksid
Approvals
request_approval{id, approver?, note?}— moves task to reviewdecide_approval{id, decision, note?}— approve→done, reject→in_progress
The server also sends instructions at initialize describing the intended workflow, so a fresh
agent knows how to behave without extra prompting.
Verify it works
cd mcp-server && npm install
set -a && . ../.env && set +a
node smoke.mjs # connects over stdio, creates+comments+closes a task, cleans upTypical agent loop
whoami → claim_next_task {project:"gl"}
→ comment {id, "investigating…"}
→ (blocked?) add_blocker {id, "need X"} // auto-blocks
→ update_status {id, "review", note} // or request_approval
→ update_status {id, "done", note:"verified"} // only when acceptance metTaskFlow — Agent Integration Contract
How every Claude agent/skill plugs into TaskFlow so work is captured, tracked,
assigned, linked, and never "asked once then forgotten." Once live, this gets
folded into the root CLAUDE.md / a taskflow skill so all agents obey it.
Base URL / key live in taskflow/.env. Agents use the tf CLI or the REST API.
The golden rule
**Every substantive request becomes tracked task(s) before work starts. Every
skill writes what it did / what the problem is as it goes. No task closes without
proof its acceptance criteria are met.**
Definition of done & dev context (required for code work)
When you scope a code task, set the definition of done and where it lands:
- Criteria — the requirements/rules the work must respect (
add_criterion/tf criterion add),
e.g. *"only bid while the bidding window is open"*, *"never exceed max bid"*. The developer agent
builds to these.
- Verification gates —
add_standard_gates(ortf gates <id>) addstest(automation-tester),
build + deploy (devops), review (reviewer). The tester/devops/reviewer agents mark each
passed/failed with set_check and attach output as proof.
- A task cannot be marked
doneuntil every required check passes — the database enforces it. - Repos — declare which repo(s) the task updates + branch/env (
set_dev_context/tf context,
or --repos/--branch/--env on create). Devops/reviewer read this to know what to build & deploy.
So: developer agent knows *what to build* (criteria), tester/devops/reviewer know *what to verify*
(gates), and everyone sees *where it lands* (repos) — all in one shared, provable record.
Standard flow
- Capture & assign — decompose the request into tasks with clear
acceptance_criteria, put them in a project, set an assignee (the skill
that should do it) and reporter:
```bash
tf add "Fix datalake-sync 'Failed to fetch'" --project gl --priority high \
--assignee webhooks-bot --reporter me \
--ac "extension v-bumped, sync 200s, VDP photos enrich after"
```
- Claim the next task atomically (safe with many agents at once):
```bash
tf claim --project gl --agent <your-skill-name> # → in_progress, assignee set
```
- Work it, leaving a trail — this is the non-negotiable part. Each skill logs
what it found and did:
```bash
tf comment <id> "root cause: Chrome connection-pool exhaustion (8 concurrent VDP calls)"
tf comment <id> "fix in v3.3.76 — moved VDP enrich out of critical path"
```
- Model dependencies as linked issues. If a task can't proceed until something
else is done, create/link the blocker — the blocked task auto-flips to blocked
and auto-unblocks when the blocker is done:
```bash
tf blocker <id> "User must push v3.3.76 to prod" # creates + links a blocker
tf link <blockerId> blocks <id> # or link two existing tasks
tf link <a> relates_to <b> # non-blocking relation
```
- Close with proof — only
donewhen acceptance is met and verified:
```bash
tf done <id> --note "verified: pod BUILD_INFO == HEAD abc1234, sync returned 200"
```
Status meaning
| status | meaning |
|---|---|
backlog | captured, not scheduled |
todo | ready to pick up |
in_progress | an agent is actively working it (assignee set) |
blocked | waiting on a linked blocker or external input — reason required |
review | done pending verification / user sign-off |
done | acceptance met and verified |
cancelled | dropped — leave a comment why |
blocked is often set automatically by a blocks link. Don't hand-set a task
back to todo if it still has an open blocker — the trigger will re-block it.
Roles
- project.owner — the lead accountable for a project (
tf projectsshows it). - assignee — the skill/human doing the task now (
tf assign <id> <agent>). - reporter — who raised it.
- agents registry —
tf agentslists everyone assignable (you + every skill).
REST equivalents (no CLI)
H=(-H "apikey: $SUPABASE_KEY" -H "Authorization: Bearer $SUPABASE_KEY" -H "Content-Type: application/json")
# claim next
curl -s "$SUPABASE_URL/rest/v1/rpc/claim_next_task" "${H[@]}" -d '{"p_project":"gl","p_agent":"db-analyst"}'
# comment / work-log
curl -s "$SUPABASE_URL/rest/v1/rpc/add_comment" "${H[@]}" -d '{"p_id":"<uuid>","p_agent":"db-analyst","p_message":"found 422 spike at 14:03"}'
# link / blocker
curl -s "$SUPABASE_URL/rest/v1/rpc/link_tasks" "${H[@]}" -d '{"p_from":"<blocker>","p_to":"<blocked>","p_type":"blocks","p_by":"me"}'
# close with proof
curl -s "$SUPABASE_URL/rest/v1/rpc/update_task_status" "${H[@]}" -d '{"p_id":"<uuid>","p_status":"done","p_agent":"me","p_note":"verified"}'Conventions
- One request → tracked tasks, always — even "quick" asks, so nothing drops silently.
assignee/reporter/byuse an agents.key (me,db-analyst, …).projectuses a projects.key (gl,general). New project:tf project add.- Priority honestly:
urgent= prod-down / blocking only.
Rollout to all agents (next phase, after schema is live)
- Add a "TaskFlow" section to root
CLAUDE.mdpointing here. - Wrap capture/claim/comment/link/close in a
taskflowskill any session can call. - Optional autonomous worker: a scheduled/loop agent that
tf claimstodotasks
and works them between sessions, commenting progress and linking blockers.
TaskFlow Documentation Agent
The agent responsible for keeping TaskFlow's documentation true after every change.
Its identity in TaskFlow is doc-agent (owns docs-kind checks).
Where docs live (the "kept somewhere")
| Layer | Path | Role |
|---|---|---|
| Source of truth | taskflow/*.md (README, USAGE, API, AGENTS, ROADMAP, mcp-server/README) | hand/agent-maintained markdown |
| Feature status | build-docs.mjs → FEATURES / INTERFACES arrays | the "what we already have" list |
| API reference | api/main.py | FastAPI → auto OpenAPI at /docs, /redoc, /openapi.json |
| Generated portal | taskflow/docs/index.html | single self-contained site built from all of the above |
| Published | Cloudflare Pages (optional: taskflow-docs.pages.dev) | shareable copy |
The portal is generated, never hand-edited — edit the markdown / feature list, then rebuild.
The agent's job
On any change to TaskFlow, update the docs so they still describe reality, then rebuild the portal.
Concretely, after a change:
- Decide what changed — schema (
schema.sql, a migration), MCP (mcp-server/server.mjs),
CLI (tf.mjs), API (api/main.py), or dashboard (dashboard/index.html).
- Update the affected markdown — the model/feature/verb/flag that changed, in the right doc
(API.md for endpoints/RPCs, USAGE.md for how-to, AGENTS.md for the agent contract, etc.).
- Update the feature list — if a capability was added/removed, edit
FEATURES/INTERFACESin
build-docs.mjs (this is the "what we already have" section).
- Rebuild the portal:
```bash
cd taskflow && node build-docs.mjs
```
- (optional) Publish the portal + verify locally first:
```bash
npx wrangler@3 pages deploy docs --project-name taskflow-docs --branch main --commit-dirty=true
```
- Record it in TaskFlow — comment on the task and mark its
docscheck passed:
```bash
tf check pass <docs-check-id> --by doc-agent --note "docs + portal updated"
```
How it gets triggered ("every time we make a change")
Pick any / all — they compose:
- As a Definition-of-Done gate (recommended). Give doc-updating tasks a
docscheck owned by
doc-agent: tf check add <id> docs "Docs updated" --role doc-agent. Because a task can't be
done until every required check passes, docs become part of "done" — the doc-agent finds its
work with tf inbox --agent doc-agent --roles docs (or MCP my_work) and closes the gate.
- As a Claude Code hook. A
Stop/PostToolUsehook that runsnode build-docs.mjs(and reminds
to update markdown) whenever files under taskflow/ change. Ask to wire this via the update-config
skill; it lives in .claude/settings.json so the harness runs it automatically.
- As a scheduled sweep. A daily agent that diffs the code against the docs and opens a TaskFlow
task for any drift it finds (ties into the roadmap's "completeness critic").
- Manually / on demand.
node build-docs.mjsany time.
Principles
- Docs describe reality, not intentions — if a feature isn't shipped, it isn't in
FEATURES. - One rebuild after any change — the portal must never be stale relative to the markdown.
- The portal is generated — never edit
docs/index.htmlby hand; change the source and rebuild. - Docs are part of done — a change isn't finished until its
docsgate is green.
Register the agent (once)
curl -s "$SUPABASE_URL/rest/v1/agents" -H "apikey: $SUPABASE_KEY" \
-H "Authorization: Bearer $SUPABASE_KEY" -H "Content-Type: application/json" \
-d '{"key":"doc-agent","name":"Documentation Agent","kind":"agent"}'Then run it with TASKFLOW_AGENT=doc-agent TASKFLOW_ROLES=docs.
TaskFlow — Roadmap to a Multi-Agent Central Hub
Where it is today, and what turns it from "a shared task board" into the **coordination
backbone** many agents (and you) run work through. Ordered by leverage. Items marked
[reuses X] build on infra you already run.
---
Where we are now ✅
Projects · tasks · subtasks · assignees/reporters · labels · linked/blocking issues
(auto block/unblock) · approvals (request→approve/reject gate) · full activity log ·
atomic multi-agent claim (SKIP LOCKED) · realtime dashboard · MCP server (18 tools) ·
REST/RPC API · CLI. Any agent can already create, claim, self-assign, comment, link, and close.
The gaps below are what separate "works with several agents" from "is the hub everything routes through."
---
Phase 1 — Reliability of coordination (do first)
The current claim is atomic, but nothing recovers a task if the agent that took it dies.
- Task leasing + heartbeat. Add
lease_expires_atand aheartbeat(task_id, agent)RPC.
claim_next_task sets a lease (e.g. 15 min); a reaper (Supabase cron / your scheduled-tasks)
returns stalled in_progress tasks to todo and logs it. *Without this, a crashed agent silently
parks work forever.* [reuses scheduled-tasks/cron]
- Idempotency keys on create.
create_task {idempotency_key}→ dedupe retries so a flaky agent
doesn't double-file the same task.
- Optimistic concurrency. Optional
if_updated_atguard on status changes so two agents editing
the same task can't clobber each other.
- WIP limit per agent.
claim_next_taskrefuses if the agent already holds N in-progress tasks —
keeps one agent from hoarding the queue.
Phase 2 — Routing: the right task to the right agent
Today claim_next_task gives the next task by priority; it doesn't match capability.
- Capabilities + routing.
agents.capabilities text[](sql,k8s,frontend…) and
tasks.required_capability. claim_next_task(agent) only returns tasks the agent can do.
Turns the board into a work queue per skill — db-analyst pulls SQL tasks, devops pulls infra.
- Auto-assignment rules. Per-project/label rules ("
security→ audit", "infra→ devops-engineer")
applied on task creation.
- Auto-pickup of unblocked work. When a blocker closes, optionally notify/claim for the dependent
task's assignee instead of just flipping it to todo.
Phase 3 — Presence & observability (the "hub" you can watch)
- Agent presence.
agent_presence(agent, status, last_seen, current_task)updated on heartbeat.
Dashboard "who's working on what right now" panel — the single-pane view of the whole fleet.
- Metrics & SLAs. Views for throughput, cycle time, approval latency, tasks-per-agent, blocked-age.
Due dates + sla_breached flag + escalation. [reuses Grafana/Metabase]
- Global activity feed. A cross-task live stream (not just per-task history) so you can watch the
whole system move.
Phase 4 — Communication & notifications
- Slack notifications. On assignment / blocked / approval-requested / done → post to Slack.
You already store webhook URLs in webhooks_db.slack_channels (channel_name→webhook_url).
A Supabase trigger or your gl-webhooks consumer fans events out. [reuses your Slack setup]
- @mentions + watchers.
@db-analystin a comment notifies it; task watchers get updates.
This is how agents hand off and ping each other through the hub instead of out-of-band.
Phase 5 — Inbound automation (work *arrives* at the hub)
- Sentry → tasks. New Sentry issue auto-creates a
bug-fixtask with a link back.
[reuses your Sentry MCP]
- Alerts/webhooks → tasks. Prometheus/Alertmanager or gl-webhooks events open tasks automatically.
- GitHub/PR linking. Link a task to a PR; auto-move to
review/doneon open/merge. - Scheduled autonomous worker. A cron/loop agent that
claim_next_taskstodoitems and works
them between your sessions, commenting progress and raising blockers. [reuses scheduled-tasks + /loop]
Phase 6 — Governance & richer workflow
- Approval policies. "Tasks labeled
securitycannot reachdonewithout an approval" — enforce
the gate in the DB, not by convention. Multi-approver (N required).
- Checklists & subtask rollups. Parent shows % complete; auto-close parent when children finish.
- Templates / recurring tasks. One-command "spin up the standard deploy checklist."
- Custom per-project workflows. Different statuses/columns per project.
Phase 7 — Security & scale (before it leaves your machine)
- Real auth. Move off open-anon: Supabase Auth + per-user/team RLS; a read-only key for the
dashboard, a scoped key for agents. Interim: Cloudflare Access email-gate on the hosted page.
- Archival. Auto-archive
donetasks past N days into a cold table to keep the board fast. - Audit export. One-click export of the full activity/approval trail for compliance.
---
Suggested first sprint (highest leverage, low effort)
- Leasing + reaper (Phase 1.1) — makes multi-agent actually reliable.
- Capabilities routing (Phase 2.5) — turns it into per-skill work queues.
- Slack notifications (Phase 4.11) — you feel the hub working without watching it. [reuses Slack]
- Agent presence panel (Phase 3.8) — the "who's doing what" view that makes it a *hub*.
Those four are what most change the feel from "a database of tasks" to "the place all the agents meet."
---
Design principles to hold
- Idempotent & atomic — every agent action safe to retry; claims/decisions via RPC, never client races.
- Everything logged — no state change without an activity row; the trail is the product.
- One source of truth — agents, dashboard, and CLI all go through the same RPCs; no side doors.
- Fail visible — a stuck task must show *why* (blocked + reason), never silently stall.
TaskFlow
Portable, multi-agent project management on Supabase. One board across all your
projects. Agents and humans read/write the same source of truth over HTTP — no
per-project database, no local file, no server to host.
taskflow/
├── schema.sql # full schema — run once in the Supabase SQL editor
├── migration-002-*.sql # labels + approvals (already folded into schema.sql)
├── tf.mjs # CLI (humans + agents)
├── serve.mjs # local static server for the dashboard
├── dashboard/index.html # live drag-and-drop kanban
├── demo.mjs # populate realistic worked scenarios
├── mcp-server/ # MCP server — native tools for ANY agent
│ ├── server.mjs
│ └── README.md # install for Claude Code / Desktop
├── api/ # FastAPI service — typed, self-documenting API
│ ├── main.py # → Swagger /docs, ReDoc /redoc, /openapi.json
│ └── requirements.txt
├── build-docs.mjs # generate the docs portal from the markdown
├── docs/index.html # generated documentation portal
├── DOC-AGENT.md # the documentation-agent spec (keeps docs current)
├── API.md # REST + RPC reference (any language)
├── USAGE.md # how to use it (humans + agents) ← start here
├── ROADMAP.md # path to a multi-agent central hub
├── AGENTS.md # the contract agents follow
└── .env # SUPABASE_URL + publishable keyDocs: USAGE.md (how to use) · API.md (REST/RPC) ·
mcp-server/README.md (agents) · ROADMAP.md (what's next).
Three ways to use it: the MCP server (agents → see mcp-server/README.md),
the REST/RPC API (API.md), and the tf CLI (below).
Setup (one time)
1. Create the schema. Open Supabase → SQL Editor → New query → paste all of
schema.sql → Run. You should see the projects table get two seed rows.
2. Verify the CLI:
cd taskflow
./tf.mjs projects # → gl, general
./tf.mjs add "First real task" --project gl --priority high
./tf.mjs board3. Open the dashboard:
node serve.mjs # → http://localhost:7373(or just open dashboard/index.html directly in a browser).
The lifecycle (what "full cycle" means)
backlog → todo → in_progress → review → done
↓
blocked (with a reason) cancelled (dropped)Every status change and comment is written to task_activity, so nothing is ever
"given once and forgotten" — each task carries its whole history.
CLI cheat sheet
tf add "<title>" --project gl --priority high --ac "what "done" means"
tf board --project gl # terminal kanban
tf claim --project gl --agent me # atomically take the next task
tf status <id> review --note "PR up" # move it, log a note
tf comment <id> "waiting on infra"
tf done <id> --note "verified in prod"
tf show <id> # detail + full historyConfig
.env holds SUPABASE_URL and the publishable key (browser-grade, safe to
commit-ish). Never put the service_role/secret key here. The dashboard reads the
same values (baked in as defaults, overridable via ?url=&key= or localStorage),
so it works from any machine.
Notes
- Free Supabase projects pause after ~1 week idle — daily use keeps it awake.
- Store task metadata only. No credentials/secrets in tasks.
- To reuse for a new project:
tf project add <key> "<Name>"— that's it.