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.html is the whole app, self-contained (QR engine inlined); supabase/migrations hold the schema/security/RPCs and supabase/functions the 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 owner synecticsdevelopment.

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)

DoMCPCLI
See what's mine to domy_worktf inbox / tf mine
Capture a requestcreate_tasktf add
Take the next taskclaim_next_tasktf claim
Assign to someoneassign_tasktf assign
Log progress / findingscommenttf comment
Change statusupdate_statustf status
Add a blockeradd_blocker / link_taskstf blocker / tf link
Define doneadd_criterion / add_standard_gatestf criterion add / tf gates
Verify a gateset_checktf check pass/fail
Request / decide approvalrequest_approval / decide_approvaltf 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):

CategoryAssignee (owner)ApproverApproval requiredAdd gates
securityauditmeyesyes
infradevops-engineermeyesyes
feature(per project)meyesyes
bug-fix(per project)reviewernoyes
datadb-analystmenono
docsdoc-agentmenono
refactor(per project)reviewernoyes
*(default)*menono

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)

FieldWhat it isHow it's filled
titleshort imperative summaryfrom the request ("Fix double-bidding in auto-bidder")
projectwhich project (gl, knockknock, …)inferred from context/keywords; ask if unclear
categoryone or more labels: bug-fix · feature · security · infra · refactor · data · docsclassify the request
prioritylow · medium · high · urgenturgent only for prod-down/blocking
descriptionthe context / problem statementthe "why" and current behaviour
acceptance_criteriathe rules the work must respect → each becomes a required criterion checkthe "what must be true" (e.g. *only bid while the window is open*, *never exceed max bid*)
reposwhich repo(s) it touches (+ branch, environment)from the project's repo list / the request
verification gatestest · build · deploy · review (add via standard gates for code)added automatically for code tasks
assigneewho does itrouted by category (see routing policy) or explicit
approverwho signs it offrouted by category (see routing policy)
reporterwho raised itthe requester (usually me)
test_planhow it's tested overallderived from the criteria
blockers / linksdependenciesanything 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)

  1. Project — determine it (keywords, current context). If ambiguous, ask.
  2. Classify — pick the category label(s). This drives routing.
  3. 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.

  1. Criteria — extract the requirements/rules the result must respect → acceptance criteria.
  2. Dev context — set repos (+ branch/env) from the project's repos and the request.
  3. Create — one task per distinct piece of work; epics get subtasks. Add standard gates for code.
  4. 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 (plus blocked, cancelled).
  • Priority: low · medium · high · urgent.
  • Assignee / reporter — an agent (a human like me, or a skill like db-analyst).
  • Labels — Jira-style colored categories (security, bug-fix, feature…). Create new ones anytime.
  • Linked issuesblocks / relates_to / duplicates. A blocks link 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…UseSetup
a humanthe dashboard (kanban) + the tf CLIopen http://localhost:7373; CLI reads .env
an AI agentthe MCP server (native tools)mcp-server/README.md
a script/workerthe REST + RPC APIAPI.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 verified

To capture a new request as tasks:

create_task {title, project, acceptance_criteria, assignee, tags}   // one per piece of work

To 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)

  1. Every agent has its own identity (TASKFLOW_AGENT) — its work is attributed to it.
  2. 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.

  1. Comment as you go. The activity log is how other agents (and you) know the state and history.
  2. Model blockers, don't stall. If you can't proceed, add_blocker/link_tasks so the board

shows *why* — the task auto-unblocks when the blocker is done.

  1. Close with proof. Move to done only when acceptance_criteria are met and verified; put the

evidence in the note.

  1. Approvals gate the risky stuff. Use request_approval for 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 comments

Gate a deploy on approval

update_status {id, "review"}  ||  request_approval {id, approver:"me", note:"ready to ship"}
decide_approval / approve     → task auto-moves to done

Scope 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:

SurfaceBest 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/json

The 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|rejected

tasks 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
  • blocks links auto-set the target to blocked until the source is done; then it auto-unblocks.
  • Approval: request_approval → task to review; approveddone; rejectedin_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_at

Creating 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.

FunctionBodyEffect
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; rejectedin_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

  1. One request → tracked task(s), always. Set acceptance_criteria.
  2. assignee / reporter / agent / by = an agents.key (me, db-analyst, …).
  3. project = a projects.key (gl, general).
  4. Comment as you work (what you did / the problem). Only done when acceptance is met and verified.
  5. Model dependencies with add_blocker / link_tasks instead 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 8080

Open

  • 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:

VarRequiredMeaning
SUPABASE_URLyour project URL
SUPABASE_KEYthe publishable key
TASKFLOW_AGENTrecommendedthis agent's identity (an agents.key, e.g. db-analyst). Defaults created/claimed work to this name.
TASKFLOW_PROJECToptionaldefault 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.mjs

For 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 + defaults
  • list_projects, list_agents, list_labels

Read

  • list_tasks {project?, status?, assignee?, limit?}
  • my_tasks {status?} — tasks assigned to THIS agent
  • board {project?} — tasks grouped by status column
  • get_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 me
  • update_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 | duplicates
  • add_blocker {id, title, priority?} — new blocking task, auto-blocks id

Approvals

  • request_approval {id, approver?, note?} — moves task to review
  • decide_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 up

Typical 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 met

TaskFlow — 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 gatesadd_standard_gates (or tf gates <id>) adds test (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 done until 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

  1. 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"

```

  1. Claim the next task atomically (safe with many agents at once):

```bash

tf claim --project gl --agent <your-skill-name> # → in_progress, assignee set

```

  1. 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"

```

  1. 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

```

  1. Close with proof — only done when acceptance is met and verified:

```bash

tf done <id> --note "verified: pod BUILD_INFO == HEAD abc1234, sync returned 200"

```

Status meaning

statusmeaning
backlogcaptured, not scheduled
todoready to pick up
in_progressan agent is actively working it (assignee set)
blockedwaiting on a linked blocker or external input — reason required
reviewdone pending verification / user sign-off
doneacceptance met and verified
cancelleddropped — 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 projects shows it).
  • assignee — the skill/human doing the task now (tf assign <id> <agent>).
  • reporter — who raised it.
  • agents registrytf agents lists 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/by use an agents.key (me, db-analyst, …).
  • project uses 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)

  1. Add a "TaskFlow" section to root CLAUDE.md pointing here.
  2. Wrap capture/claim/comment/link/close in a taskflow skill any session can call.
  3. Optional autonomous worker: a scheduled/loop agent that tf claims todo tasks

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")

LayerPathRole
Source of truthtaskflow/*.md (README, USAGE, API, AGENTS, ROADMAP, mcp-server/README)hand/agent-maintained markdown
Feature statusbuild-docs.mjsFEATURES / INTERFACES arraysthe "what we already have" list
API referenceapi/main.pyFastAPI → auto OpenAPI at /docs, /redoc, /openapi.json
Generated portaltaskflow/docs/index.htmlsingle self-contained site built from all of the above
PublishedCloudflare 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:

  1. 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).

  1. 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.).

  1. Update the feature list — if a capability was added/removed, edit FEATURES/INTERFACES in

build-docs.mjs (this is the "what we already have" section).

  1. Rebuild the portal:

```bash

cd taskflow && node build-docs.mjs

```

  1. (optional) Publish the portal + verify locally first:

```bash

npx wrangler@3 pages deploy docs --project-name taskflow-docs --branch main --commit-dirty=true

```

  1. Record it in TaskFlow — comment on the task and mark its docs check 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 docs check 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 / PostToolUse hook that runs node 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.mjs any 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.html by hand; change the source and rebuild.
  • Docs are part of done — a change isn't finished until its docs gate 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.

  1. Task leasing + heartbeat. Add lease_expires_at and a heartbeat(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]

  1. Idempotency keys on create. create_task {idempotency_key} → dedupe retries so a flaky agent

doesn't double-file the same task.

  1. Optimistic concurrency. Optional if_updated_at guard on status changes so two agents editing

the same task can't clobber each other.

  1. WIP limit per agent. claim_next_task refuses 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.

  1. 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.

  1. Auto-assignment rules. Per-project/label rules ("security → audit", "infra → devops-engineer")

applied on task creation.

  1. 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)

  1. 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.

  1. Metrics & SLAs. Views for throughput, cycle time, approval latency, tasks-per-agent, blocked-age.

Due dates + sla_breached flag + escalation. [reuses Grafana/Metabase]

  1. 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

  1. 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]

  1. @mentions + watchers. @db-analyst in 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)

  1. Sentry → tasks. New Sentry issue auto-creates a bug-fix task with a link back.

[reuses your Sentry MCP]

  1. Alerts/webhooks → tasks. Prometheus/Alertmanager or gl-webhooks events open tasks automatically.
  2. GitHub/PR linking. Link a task to a PR; auto-move to review/done on open/merge.
  3. Scheduled autonomous worker. A cron/loop agent that claim_next_tasks todo items and works

them between your sessions, commenting progress and raising blockers. [reuses scheduled-tasks + /loop]

Phase 6 — Governance & richer workflow

  1. Approval policies. "Tasks labeled security cannot reach done without an approval" — enforce

the gate in the DB, not by convention. Multi-approver (N required).

  1. Checklists & subtask rollups. Parent shows % complete; auto-close parent when children finish.
  2. Templates / recurring tasks. One-command "spin up the standard deploy checklist."
  3. Custom per-project workflows. Different statuses/columns per project.

Phase 7 — Security & scale (before it leaves your machine)

  1. 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.

  1. Archival. Auto-archive done tasks past N days into a cold table to keep the board fast.
  2. Audit export. One-click export of the full activity/approval trail for compliance.

---

Suggested first sprint (highest leverage, low effort)

  1. Leasing + reaper (Phase 1.1) — makes multi-agent actually reliable.
  2. Capabilities routing (Phase 2.5) — turns it into per-skill work queues.
  3. Slack notifications (Phase 4.11) — you feel the hub working without watching it. [reuses Slack]
  4. 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 key

Docs: 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.sqlRun. 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 board

3. 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 history

Config

.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.