Data, memory & assistant (four layers)
The product promise is a per-client operations brain: collect each client's data, accrete their memory from day one, and later offer an in-business chat over that data — not a generic CRUD admin. The architecture is four layers, decided in ADR-007 (see Decisions).
One headline: a per-client operations brain in four layers — structured SoR, event-driven ingestion, per-tenant memory (activity log now + pgvector next), and an assistant that is retrieval over SoR + memory through the existing engine.
The four layers
- System of Record — the structured "what is": per-client domain data (person/identity now;
offering, enrollment, payment, certificate next), modeled in
@tedos/domain. Nouns and state. See Product SoR. - Ingestion — connectors + tenant inbound webhooks → upsert the SoR and append the
activity log, event-driven:
external black box → webhook → SoR upsert → enqueue objective. No new state-machine engine — state lives in the SoR, transitions are short engine objectives. - Memory — two stores per tenant:
- a per-tenant activity log — an append-only event table, written on every SoR write. Built from day one, so memory accretes from the first write.
- pgvector semantic memory — embeddings of the client's content/events, in the same product Postgres (no separate vector DB). Built in a later effort.
- Assistant — a per-client chat over SoR + memory, implemented as retrieval + the existing orchestrator/evals/human-gate (no new engine). Staff-facing first (the client's back-office); end-user chat later.
Locked decisions (ADR-007)
| # | Decision | What it sets |
|---|---|---|
| D1 | Schema-per-client isolation — one cluster, a schema per project, a control/registry schema for the project list | A tenant-scoped DB accessor selects the schema; the person/identity tables become the per-client template |
| D2 | pgvector in the product Postgres — no separate vector DB | Memory embeddings live beside the SoR; one operational surface, one backup, one export |
| D3 | Activity log from day one | The assistant is later just retrieval, not a backfill project |
| D4 | Chat = retrieval + the existing engine | Reuses the orchestrator + evals + the human gate; adds a retrieval step, nothing more |
| D5 | Staff chat before end-user chat | The client's back-office is the safer first consumer |
Why schema-per-client
Schema-per-client is the deliberate middle option in the isolation spectrum:
| Model | Isolation | Export | Verdict |
|---|---|---|---|
Shared DB, tenant_id column | weakest | hard (filtered dumps) | rejected — fails the clean-export promise |
| Schema-per-client (one cluster) | strong (schema boundary) | clean (per-schema dump) | chosen |
| Database-per-client | strongest | cleanest | escalate later (data-residency / scale ceiling) |
The tenant-scoped accessor (D1) is the seam that makes escalating to database-per-client a mechanical move, not a rewrite.
Hard rules
- Tenant/client data never enters the dev-workflow graph — the product SoR + memory are a separate database from the Plan Engine.
- Per-client isolation is enforced by the schema boundary, not query discipline — all SoR/memory access goes through the tenant-scoped accessor.
- Secrets/PII never leak across tenants and never enter the dev corpus; fiscal PII stays masked.
Sequencing
- Layers 1–2 first: schema-per-client SoR + ingestion contract + activity log.
- pgvector semantic memory next.
- Assistant later (staff chat first).
- Real connectors (Moodle, store, events, payments/bank, CFDI, WhatsApp) land in later efforts; the ingestion contract ships a connector seam now. See Engine.