Reference

How the loop is actually built.

This page is the reference layer — tables, topology and the tool surface. For the argument, start with the four mechanisms: spec → done, the fleet, the living graph, memory.

Graphban is a FastAPI + Postgres/pgvector backend, a React SPA, and one 57-tool MCP — all sitting on a single shared service layer. Here's the architecture, the agent-loop mechanics, how authority is enforced and audited, and the data model that makes it work.

System topology

Two entry planes, one code path

Humans enter through REST with a JWT; agents enter through the MCP endpoint with a scoped API key. Both converge on the same service layer, which is the only thing that touches the database.

Web SPA
React 19 · TanStack · JWT
Agents / MCP clients
API key · JSON-RPC 2.0
↓ REST routers        MCP server (57 tools) ↓
Shared service layer
app/services/* — items · memory · prds · assistant · clustering · collision · prioritization · code_graph · events · quotas · orgs · drive_sync
↓ SQLAlchemy models · Alembic migrations
Postgres + pgvector
dialect-aware EmbeddingType · HNSW cosine index

Why the service layer is the keystone

Routers and the MCP server are thin adapters — every tool call and every REST request runs the same function. There is exactly one path to the database, so an agent's create_item appears in the web tracker with no reconciliation, and every capability is available to both people and agents for free.

The differentiator

The agent loop, mechanic by mechanic

prd_coverage
specced but unbuilt
decompose_prd
spec → tasks
get_backlog
dep-aware, code-local
claim_next
atomic, leased
update done
→ memory
A · Claiming & leases

Collision-free assignment

claim_next reads the best ready candidate then does an optimistic UPDATE guarded on claimed_by — only one caller wins the row, so two agents never claim the same item. A claim carries a lease (claimed_at); heartbeat extends it, and a lease that goes stale past the window is reclaimable, so a crashed agent's in-progress work is picked up by another. agent_id defaults to the API key's name — one key, one agent.

B · Touchpoints & clusters

Batching by code-locality

Items declare touchpoints — files, globs, or modules they affect. Two items relate when touchpoints overlap (exact, glob, or same directory), and overlap auto-creates a code link. next_cluster claims the best ready item plus its related ready neighbours in one call, so an agent pulls a whole code-neighborhood and works it in one context instead of thrashing.

C · Prioritization

Priority from the dependency graph

Readiness is computed, not flagged: an item is blocked until every item it depends on is done. The backlog sorts ready-first, then by a composite score — status, dependency fan-out (unblocking many ranks higher), request votes rolled onto the linked item, effort, and staleness. claim_next and next_cluster consume the same ranking, so agents always take the highest-leverage ready work.

D · Grill & spec coverage

Grill the spec, then it drives the tracker

PRDs are authored and sharpened by grilling — an interactive interrogation (grill_prd / a streaming grill session) that asks relentless clarifying questions and classifies each open question low- vs high-fidelity (the latter needs a prototype: the grill → prototype → grill handoff). Every decision is preserved as a candidate memory shard. Then decompose_prd parses the spec's sections into a task per gap and prd_coverage returns the per-section rollup — status counts, percent done, and outstanding high-fidelity work.

Institutional memory you can trust

Completing an item auto-extracts its lessons into pgvector memory — but agent-written memory is telemetry, not truth. Lessons (and grill decisions) enter as candidates; a human publishes them before search_memory serves them, and recurring ones cluster into one principle. The loop compounds reviewed knowledge instead of letting a hallucination become ground truth.

Agent interface

The MCP tool surface

JSON-RPC 2.0 over POST /api/mcp, authed by a project-scoped key. Every tool ships a typed outputSchema, read-only / destructive annotations, and idempotency keys on creates; reads paginate. Arguments are validated against the schema before dispatch, and failures return a typed code — not_found, validation, conflict, unauthorized, internal — with a hint naming the fix.

A key is never shipped schemas it cannot use: a read-only key sees no write tools, and a key enrolled to one role is not shipped the other roles' tools. The manifest is a courtesy; the gate is the enforcement — a manifest can only fail to mention a tool, while the gate refuses the call.

ToolKindPurpose
Orientation
get_contextreadThe key's project, scopes, project/tool counts — call first
list_projectsreadProject ids for the project_id override
create_projectwriteSelf-host only, and only while unlinked to a cloud org — a linked instance would create in the org's tenant
setup_projectreadFirst-run bootstrap — a resumable checklist that takes a fresh project from empty to useful; re-runnable, reports done/pending
Work queue
claim_nextwriteAtomically claim the best ready item (leased)
next_clusterwriteClaim a whole code-neighborhood at once
claim_clusterwriteThe way to take work in a fleet — claims a non-colliding cluster and reserves its touch-areas in the same transaction; a refusal names who holds them and when they free
collision_clustersreadThe current partition — connected components of items that share touch-areas, largest first
heartbeatwriteExtend the lease on a claimed item, and the agent's presence on the roster
release_itemwriteReturn a claim to the queue
get_backlogreadPrioritized backlog — ready-first, with blocked_by / unblocks / votes / score
suggest_nextreadSingle best next item, dependency-aware
Items
create_itemwriteCreate a task (tags, touchpoints, fidelity, PRD link) · idempotent
update_itemwritePatch / advance status · set fidelity
search_itemsreadQuery by text, tags, status (paginated)
get_item_detailsreadItem + linked shards + requests
related_workreadThe code-neighborhood around a task
link_itemswriteTyped link (dependency / code / semantic / tag)
unlink_itemswriteRemove a typed link — the inverse of link_items · idempotent
Memory · Insight
add_memorywriteRecord a memory shard — enters as a candidate pending human publish · idempotent
search_memoryreadSemantic (pgvector cosine) search over published shards
publish_memorywriteSubmits a candidate for independent adjudication — the judge decides, not the caller; kept: false is a normal outcome
reject_memorywriteDiscard your own candidate — needs no judge, since it takes nothing out of the trusted pool
learning_loopreadArtifact recommendations, drafts, usage and retirement candidates
review_recommendationwriteApprove or reject an artifact — the human boundary; approving a shared-file artifact writes nothing
extract_lessonswriteDistill an item's lessons into candidate memory
generate_digestreadProgress digest across the project
PRDs · grill
create_prdwriteAuthor a PRD — the handoff artifact; ## sections drive decompose/coverage
update_prdwritePatch a PRD's title / status / body
grill_prdreadNext clarifying questions to sharpen a PRD before building
decompose_prdwriteSpec sections → tracked tasks (classified by fidelity)
prd_coveragereadPer-section rollup + gaps + open high-fidelity work
answer_grillwriteRelay the author's answer — recorded as agent-relayed, so a reviewer can see it wasn't the author typing
Delivery acceptance
prd_acceptancereadEleven views over one surface — completeness, drift, evidence, lineage, verdicts, baseline, audit brief. None of them ever says "complete."
submit_verdictwriteA per-section verdict with typed citations (code / intent / evidence) that must resolve, or it's malformed
request_rebaselinewriteRe-open the grill for new intent — does not approve, and cannot add sections
close_prdwriteTerminal and irreversible; gated on disposition of undelivered sections, never on delivery
Fleet coordination
register_agentwriteJoin the roster; returns the active role and the tools it will be refused
fleet_statusreadWho is alive, in which role, holding what — and which posture the project is in
propose_allocationreadA mapping of live agents to free clusters, with rationale. Writes nothing
assign_rolewriteCommit the allocation — planner only. Re-tasking releases held work in the same transaction
mint_enrolmentwriteIssue a single-use 30-minute seat carrying one role — planner only, because a planner has no authored work to launder
claim_reviewwriteLease an item you did not build and are independent of; returns the branch
sign_offwriteAccept reviewed work. Above an effort threshold, refuses without a sabotage receipt
bouncewriteReturn work with a required reason that travels with the item; pinned to its author for one lease
Code graph
describe_codewriteThe coding agent upserts the code map — nodes (module/file/symbol + summary) and typed edges; echoes the paths it touched
get_code_mapreadThe project's described nodes + edges
code_neighborsreadEdges around a path + the work items touching it
search_codereadSemantic search over code-node summaries
graph_queryreadThree structural questions — hubs (inbound degree = blast radius), components, path (walked undirected). An unknown edge type is refused, not silently narrowed
link_codewriteBridge an item/request to a code path (affects / implements / fixes / tests)
unlink_codewriteRemove an item/request ↔ code link
Upstream
report_graphban_issuewriteReport a bug/idea about Graphban itself; deduped on arrival

Every accepted write is metered and recorded to an append-only audit ledger, attributed to the calling key.

Trust boundary

Authority is enforced, and audited

Authentication proves identity; authorization decides what an identity may touch — and the two are separate contracts. Capability and permission never get conflated.

Scoped, not just labelled

A key can't out-rank its owner

Every mutation is bounded by the key's declared scopes and its owner's project memberships, checked in one place (security/authz.py) at both the MCP and REST boundaries. A project-scoped key can't reach another project's data — not even by passing a different project_id. Non-members get an existence-hiding 404; a read-only member gets an honest 403.

The ledger

Every change, on the record

One append-only events row per accepted mutation — actor (the agent key and the human principal behind it, or the user), action, target, project, timestamp — written at the boundary so attribution is never lost. The Activity view answers "what did my agents do, and who ran them" directly; auditing an authority model is what makes it a real control rather than a claim.

Human plane

An in-app assistant on the same rails

Beyond the agent loop, a person can open any item or PRD and chat with a model about it over SSE. The assistant is a provider-agnostic tool-calling layer: one internal contract that Anthropic's native tools and the OpenAI function-calling shape (OpenAI, xAI/Grok, Gemini, Ollama) both translate to, so any configured provider drives the same tools.

Propose-then-approve

The assistant can't write unattended

Tool calls that read run immediately; tool calls that write are staged as proposals gated to the caller's own authz — applied only on human approval, prior value captured for revert, and recorded in the same ledger with origin=assistant:<provider>. A prompt-injected item can, at most, propose a rejected action.

Per-thread model · metered

Bring your own key

Each conversation pins its own provider + model (Claude on one thread, Grok on another), keyed per project and encrypted at rest. Text streams token-by-token; input/output tokens are metered on the thread and counted against the org's call quota in hosted mode.

Persistence

Data model

Everything is project-scoped; items is the hub the loop turns on.

TableCarries
itemsstatus, tags, effort, blocker · touchpoints (B) · assignee / claimed_by / claimed_at (A) · prd_id / prd_section (D) · github_url, pr
linkstyped edges — dependency / code / semantic / tag (drives C's ready-set and B's clusters)
memory_shardspgvector embeddings — semantic recall; auto-written on completion
prds · prd_versionsmarkdown specs with version history; sections drive coverage (D)
requests · attachmentspublic feedback triage — votes roll onto linked items (C); image attachments by id
code_nodes · code_edges · code_refsthe agent-described code graph — modules/files/symbols + typed edges, and item/request ↔ path bridges
eventsappend-only audit ledger — actor / action / target per accepted mutation
api_keysscoped to a project + read/write scopes — enforced, one key = one agent
membershipsper-(user, project) role + access — the authorization source of truth
sync_stateper-PRD last-synced hash for Drive conflict detection
platform_configper-project AI provider, integrations, spam settings

The whole Alembic chain applies clean on a fresh database — and CI proves it on Postgres every change.

Fleet substrate

Point a subagent fleet at it

Cursor 3 — and any MCP client — now fans work across a fleet of cheap-model subagents, each running in its own isolated git worktree. Worktrees stop them clobbering each other at the byte level; they can't stop two agents from solving the same problem or grabbing the same work. That coordination gap is what Graphban fills. Connect over the same MCP endpoint — Cursor 3's Team MCP distribution then hands it to every cloud, IDE, and CLI agent on the team.

Curated context

Cheap models start oriented

A sessionStart hook injects the operating loop into an otherwise empty context window — the claimed item, the surrounding code neighborhood, the invariants that define done in this repo. A non-frontier subagent wakes up knowing what it's doing instead of guessing from the prompt.

Collision safety

Past the worktree boundary

Worktrees prevent byte-level clobber; leases prevent wasted work. claim_next and next_cluster hand each subagent a leased item — or a whole code-neighborhood — and an afterFileEdit hook flags any change straying outside it, so a fleet doesn't duplicate effort or drift onto another agent's files.

Portable agent files

One source, every client

A generator emits role-scoped subagent definitions — planner, implementer, scout, verifier — for Cursor, Claude Code, and Codex from a single source, with the repository's invariants baked into each. Regenerate on change; CI fails the build if they drift.

Reach

Integrations

Runtime

Stack & deployment

What's new

Every entry is work that merged. The full history is on GitHub.