DocoAlpha 4eb96ae · just now

How It Works in Detail

Doco is a versioned knowledge graph in Postgres, a vector and BM25 index in Qdrant, and a retrieval engine that answers an agent's "what applies here?" in one call, before every prompt and every file edit.

  1. SourcesGitHub, Slack, Notion, Google Drive, chats with agents
  2. IngestWebhooks plus per-minute syncs
  3. PostgresRecords, typed edges, hash-chained versions
  4. Qdrant1,024-d vectors and BM25, fed by an outbox
  5. Brief engineSeed, hop, fuse, rerank, tier, synthesize
  6. AgentHook or MCP, then a write back through policies

Every write goes back into Postgres and is in the index within a minute, so the next brief already knows it.

  1. 1

    Your knowledge is collected

    Ingestion and storage

    Connectors copy your tools into one workspace. Everything becomes typed records and edges in Postgres, with a verifiable history, and is chunked, embedded and indexed for search.

    Connectors
    GitHub App webhooks for pushes, pull requests and issues, with a 5-minute backfill sweep. Slack's Events API plus a per-minute history sync of public channels and thread replies. Notion webhooks plus a paced per-minute tick, never a full re-crawl. Google Drive, synced every minute. Chats with agents arrive through MCP.
    Data model
    One nodes table for 10 types (intent, idea, rule, decision, action, log, eval, reference, state, principal), with ids like decision_<ULID>, and typed edges: flows_to, supports, constrained_by, attributed_to, has_parent, derived_from, replaces, relates_to. 21 collection templates set the allowed types and policies.
    Lifecycle
    drafting → queued → active → retired, on nodes and edges alike. An active edge can only connect active nodes.
    History
    Every write is a changeset with a transaction id, an actor and a source (api, mcp, ui, slack, import). Each version of a node or edge stores the hash of the one before it, so any tampering breaks the chain. Nothing is lost: every earlier version stays readable.
    Embeddings
    Text is cut into chunks of about 1,600 characters, overlapping by 200, at paragraph, line or sentence boundaries. Each chunk is embedded with Voyage voyage-3.5 (1,024 dimensions) as a document; questions are embedded as queries.
    Vector index
    Embeddings go through a Postgres outbox that a cron drains into Qdrant every minute: cosine HNSW (m 32, ef_construct 200) on disk, BM25 over the chunk text, and the collection as tenant key. Postgres stays the source of truth, so Qdrant can be rebuilt from it.
    Derived indexes
    A trigger extracts every file path, URL, record id and pull request number a record mentions into a touches index. PageRank over each collection's edges is stored on every node and recomputed when its edges change.
    Example · a decision as stored
    {
      "id": "decision_01K8Z3QH…",
      "node_type": "decision",
      "lifecycle": "active",
      "prose": "Refunds go back to the card
                that paid, never as store credit.",
      "question": "How do customers get their
                   money back?",
      "alternatives": [{
        "option": "Store credit",
        "rejected_because": "Customers read it
            as us keeping their money."
      }],
      "edges": [
        { "constrained_by": "rule_01K8Y2…" },
        { "attributed_to": "principal_01K7…" }
      ],
      "version": 2,
      "prev_hash": "9f2c41…"
    }
  2. 2

    Agents query such knowledge

    Retrieval: the brief

    Before it acts, the agent sends what it's about to do and what it will touch. Four retrievers run in parallel, the graph adds neighbors, rank fusion orders the lot, and Doco returns it in tiers that fit the agent's token budget.

    The call
    doco_brief(about, touching[], budget = 4000, since = 7 days) over MCP (Streamable HTTP, JSON-RPC 2.0), or GET /api/v1/brief.json.
    Seeds
    Up to 100 candidates each, in parallel: vector search over record embeddings; Postgres full text on the ask's 10 longest words; exact matches of touching in the touches index; glossary terms the ask names.
    One hop
    The top 40 seeds add up to 5 neighbors each over live edges, 150 at most. A retired record brings in the record that replaced it.
    Fusion
    Weighted reciprocal-rank fusion with k = 60. Each seed list weighs 1, a hop 0.75, PageRank and recency 0.25 each, so authority and freshness only break ties.
    Tiers
    Must obey: the constitution, the target collection's goal and policies, active rules. Already decided: active decisions. In motion: what changed since since. Background: the rest, plus up to 10 Slack, Notion and Drive chunks per source. Gaps: what nothing matched.
    Rerank and synthesis
    In parallel. Voyage rerank-3, a cross-encoder, reorders the top 50 inside each tier, never across tiers. Claude Haiku 4.5 writes a paragraph of at most 70 words that cites the ids it draws on.
    Budget and deadline
    60% of the token budget goes to expanded items of up to 1,200 characters, the rest to one-line items. A 15-second deadline stops the brief, and any step it cut is named in a warning.
    Hooks
    Claude Code: SessionStart, UserPromptSubmit, and PreToolUse on Edit|Write|MultiEdit. Codex: SessionStart, UserPromptSubmit, PreToolUse. Gemini CLI: SessionStart, BeforeAgent. Session start injects the standing orders (constitution, rules, what changed since the last session); every other event injects a brief. At session start it also installs a prepare-commit-msg git hook in the checkout, where none is there yet, that ends each commit made within two hours of a brief with a Doco-Brief: line linking to it. The hook's token is read-only and scoped to one workspace.
    Example · a brief
    → request
    doco_brief({
      "about": "Add a refund button to the
                billing page",
      "touching": ["app/routes/billing.tsx", "#212"]
    })
    
    ← response
    Must obey
      rule_01K8Y2…  Refunds over $500 need an
      owner's approval.
      because: standing rule
    Already decided
      decision_01K8Z3…  Refunds go back to the
      card that paid, never as store credit.
      because: touches app/routes/billing.tsx
    In motion
      reference_01K9A…  #212 Move billing to
      the new layout (opened yesterday)
    Gaps
      Nothing in any collection names
      partial refunds.
  3. 3

    Agents capture more knowledge

    Writes, policies and versioning

    When a chat settles something, the agent writes it through the same API. Policies check the write, a new version joins the chain, and the record is searchable within a minute.

    Tools
    doco_capture writes one record. doco_changeset applies up to 50 operations in order (create, relate, append, activate, queue, retire, supersede), each able to reference an earlier one by alias. doco_relate adds one typed edge.
    Policies
    Run on every write. Deterministic policies (allowed types, required fields) block a write that breaks them. Probabilistic policies have an LLM judge, Claude Haiku 4.5, read the record against the policy's text, then warn or block as the policy says. The verdicts come back with the write.
    Supersede
    Creates the new record, links it to the old one with replaces, and retires the old one. The next brief that reaches the old record is handed the new one.
    After commit
    The record is embedded inline, its touches are re-extracted by trigger, the outbox reaches Qdrant within a minute, and the minute sweep refreshes PageRank when edges changed.
    Receipts
    A reply that used Doco ends with one line, linking to the brief the agent read and to every record it wrote.
    Chat log
    Each chat ends as one Log in Agents chats, linked with relates_to to the chat it continues, so long work reads as one thread.
    Example · superseding a decision
    → request
    doco_changeset({
      "collection": "acme-product-decisions",
      "operations": [{
        "op": "supersede",
        "target": "decision_01K8Z3…",
        "node_type": "decision",
        "body": {
          "prose": "Refunds, partial ones
            included, go back to the card
            that paid."
        }
      }]
    })
    
    ← the one line that ends the reply
    [🔮 Doco] briefed: 24 items, 2 held
    back · searched 70.7M tokens, sent
    3.2K (3.9s) · ✍️ Decision

Your team sees it every day

The Times
An hourly rollup counts each day's activity. Every day, a score shortlists the period's records, Claude ranks them and writes a headline and a one-sentence takeaway for each, and each member gets the top ones they may read, by email and in Slack.
Señor Doco
Doco's own agent, on Claude Sonnet 4.6, answers from the same brief engine on the web and in Slack. In channels where an owner turns him on, a thread that says "let's go with" gets one offer to record the decision; a thumbs-up records it as the person who gave it.
Agent pull requests
Each pull request is labeled with the agent that made it and whether Doco saw the work. The agent comes from what Claude Code, Codex, GitHub Copilot, Cursor, Devin, Jules and aider leave behind: co-author and session trailers on its commits, branches like claude/, codex/, copilot/, cursor/, devin/ and jules/, and the bots that open pull requests. Doco saw the work when, in the workspace or another that tracks the repository, its description or commits name a record or brief, as the Doco-Brief: line the hook ends each commit with does, its author's chat Log or record names it, or their agent read or wrote there from an hour before its first commit until it closes. The Times counts them and the Pull requests list filters them. From a workspace's second day, its owners hear within the hour when someone who isn't a member opens a pull request in its repositories, with a link to invite them, and within a day when a member's agent ships without Doco, as does the member, with the message that sets the agent up. Each alert has Disregard, just this time or always.

You decide who sees what

Roles
Owner, writer or reader, per workspace or per collection. Collections are private unless an owner makes them public.
Token ceiling
Every request acts with the lesser of its token's grant and its person's live role, checked on every call, so removing a person removes their agents.
OAuth 2.1
Discovery by RFC 8414 and RFC 9728, dynamic client registration, and the device flow for command-line agents.
Passkeys
People sign in with WebAuthn passkeys from any provider. There are no passwords.