01The shape
The knowledge graph is a Postgres table — knowledge_entries — with one row per durable claim about the engagement. Every row carries the tenant's company_id and a kind that says what type of claim it is.
Append-only by convention; soft-deletable per row. The full knowledge graph for a project is what the operator exports under GDPR Article 15 + 20.
02How decisions land here
Operators don't write knowledge_entries rows by hand. Decisions land here as a side effect of the approval workflow:
approvals row, attributed to that consultant by team_member_id.decide_approval(...). The approval transitions pending → approved.AFTER UPDATE trigger — tg_approvals_materialise_action — writes a row to knowledge_entries with kind=decision, the approval body as context, and a back-reference to the approval id.knowledge_entries filtered by tenant. The new decision is in the system prompt before the model produces a single token.This is what lets the team compound. Decisions aren't held in a session memory that resets between calls. They sit in a row that every consultant reads, with provenance back to the approval that produced them.
03How consultants read it
Every persona runner runs a query like this before reasoning:
- Pull the most recent
client_rule,decision, andrationaleentries for this company (default: last 30). - Format each as a single line:
- [decision] Vendor approved: ACME Antwerp / GL 60100 / 2026-04-21. - Splice into the system prompt under "Knowledge graph — recent baselines, client rules, and decisions" before the persona's role-specific instructions.
Every persona output has a knowledge_grounding field listing the entries that influenced the call. The reviewer sees this on the approval card; future consultants see it in their own grounding context. Provenance flows forward.
04Tenancy and privacy
Postgres row-level security on the table — every SELECT joins through is_company_member(company_id). A reviewer can only see knowledge for tenants they have a membership in. A consultant runner — running under service-role for the agent path — gets the same scoping, but only against the project's tenant.
Cross-tenant queries are structurally impossible: the helper functions that gate access don't accept a "wildcard tenant" parameter, and the table doesn't have a "global rules" mode. The only path that crosses tenants is the platform-admin GDPR audit (export / delete), which is explicitly logged in gdpr_operations with a tenant snapshot. See the audit trail for that story.
05What this isn't
It is not a vector store. It is not RAG over a document corpus. The knowledge graph is structured, deliberate, audit-trailed — each row arrived because a human approved a decision, or an admin captured a client rule. There's no fuzzy match producing memories that nobody can trace.
This matters for compliance and for product trust: when a consultant cites a knowledge entry in a proposal body, you can click through to the original approval, see who decided it, when, and on what evidence. No "the model says it remembered" — only "this row exists because that decision was made on that day."
Most agent platforms forget the engagement between conversations. This one remembers, and shows you what it's reading.