Citadel
A company brain that AI agents can query, and the Keeper: the agent that repairs it.
GitHub (opens in a new tab)October 2026
What it is
Every team asks the same questions: who knows this system, why was this decided, what breaks if we change it. The answers are already in commits, pull requests, reviews, tickets and docs, but nobody can hold all of it, and an AI assistant without that context guesses.
Citadel puts it all in one FalkorDB graph per company and answers from it, with the sources and the graph path behind every answer. When the graph does not know something, it does not guess: the Keeper finds out, checks the answer against the sources and writes it back. My entry for Graph Hacks: Context for AI Agents (FalkorDB × WeMakeDevs), on the Company Brain and Agent Memory tracks.

LLM and AI engineering
Agents that cite what they saw
Five Claude agents, one job each
- Ask answers questions. The Keeper keeps the brain right, with
pmandqaagents advising it. The Archivist fetches history older than the ingest window from GitHub or Linear and loads it into the graph. - Agents never message each other. The graph is the only channel: a handoff is a record that shows up in the next agent's queue query.
- Every job also has a deterministic offline policy, so the test suite calls no API.
Grounded answers
- Ask reads the graph only through 22 named, reviewed Cypher tools. It may cite only nodes it saw in this run's tool results; a grounding check rejects any other citation.
- The person asking picks the model per question (Haiku, Sonnet, Opus or Fable), and the page shows what each costs against Haiku.
Rules in code, not in the model
- Sources win: a claim the sources contradict is never written, and
pmopens a follow-up to fix the source instead. - A critical claim (it replaces a claim, touches a high-impact system, or has no source behind it) waits for a lead's approval.
Memory that changes
- Accepted claims become dated
Claimnodes linked to their evidence, withvalid_fromandvalid_to. A newer claim supersedes the old one, and nothing is deleted. - Every Ask turn is stored as a graph (
Thread → Turn → LLMCall → ToolCall → Ref), and every Keeper review as its steps.
Evals with deterministic graders
make evalasks Claude fixed questions on a loaded brain and grades each answer on cited uids, tools run, answer text and step count. No model grades another model.- One prompt rule, to cite each ranked row by its own uid, took Sonnet 5.5 from 88% to 100% (51/51) on the synthetic company and from 93% to 99% (68/69) on the FalkorDB/FalkorDB repo, three runs per case.

FalkorDB
How I used FalkorDB
Three graphs per company
brain_<slug>holds what the company knows: people, teams, systems, changes, PRs, issues, tickets and documents.agent_<slug>holds chat memory, andkeeper_<slug>the Keeper's ledger, in its own graph so a rebuild of the brain cannot remove it.- Each agent connects as its own database user, and a permission table says which graph it may touch.
Idempotent ingest
- Adapters fetch into a raw cache, records get stable uids, and the loader
MERGEs on uid, so a re-ingest changes nothing and makes no API calls. - Range indexes on every uid, and a shared
Nodelabel so a lookup by uid alone is an index scan.
Graph algorithms in the answers
algo.SPpathsfor how two things connect, in either direction.algo.pageRankfor the central systems. The Keeper uses it too: a system in the top quarter by PageRank counts as high impact, so claims about it need a lead.algo.betweennessoverBLOCKS,CLOSESandMENTIONSfor bottleneck tickets, and variable-lengthBLOCKSpaths for what a ticket waits on.
Search
- Full-text indexes on work items and documents.
- An HNSW cosine vector index on text chunks of work items and documents for semantic search, embedded locally with fastembed (no text leaves the machine) or with Gemini.
Guard rails
- When no tool fits, Ask may write one query. It runs as
GRAPH.RO_QUERY, so writes are refused, with a 5 s timeout and a requiredLIMIT. - Every status change is guarded in its Cypher, so of two racing approvals one wins and the other gets HTTP 409.

Stack
Built with
Python with uv, FastAPI, SvelteKit, FalkorDB in Docker, and Claude through the Anthropic API.
I built it with Claude Code. I set the architecture, the rules the agents follow and every screen; Claude wrote the code and tests on branches, and I reviewed each change before it was merged.