ChatMaestro — Data Model: Overview

For ease of interpretation, the tables of the schema (a database’s complete set of table definitions) can be viewed as organized into four modules. A module is a reading aid only; it has no counterpart in the SQL (Structured Query Language) schema, where every table lives in one flat namespace (all table names in a single shared list, with no grouping). The map below shows the four modules and their interconnections. Each line between two modules stands for one or more relationships between individual tables of those two modules — the modules themselves are not connected, since they are not real database objects. A solid line marks real relationships, realized as foreign keys (a foreign key is a column whose value must match a row’s identity in another table); a dashed line marks virtual relationships, which the search sidecar (a self-contained companion service that runs alongside the main app) forms by embedding another module’s rows with no foreign key. An arrow points to the table, and so the module, being referenced; a double-headed line means each side references the other. One per-module ERD (entity-relationship diagram) backs each box.

Module Map

search Tables: document embedding embedding_job embedding_ingest_registry nl_query core-identity Tables: profile cohort cohort_member audit_log email_template core-experiment Tables: experiment experiment_run nudge run_enrollment experiment_context_file run_result message message_recipient llm-chat Tables: model_catalog model_config model_config_model chat_round chat_round_candidate authors · enrollees · cohorts · messaging model config + judge ⇄ rounds enrollees · authors context files uploaders · query owners embeds model configs · virtual link How to read thisa module (its tables listed)a relationship, as one or more foreign keys (double-headed = bidirectional)a virtual relationship, no foreign key (the search sidecar embeds those rows)

Entity-Relationship Diagram (ERD) Notation

The per-module ERDs share the following notation, loosely based on the crow’s-foot, or Information Engineering, notation system (a widely used convention for drawing how database tables relate). Each box is a table, the badges mark its key columns, and the lines mark how tables relate. These diagrams — this module map and the per-module ERDs — are a visual aid for understanding the entities and their relationships; they are not a functionally complete definition of the schema, which lives in schema.dbml.

example_table id PK owner_id FK name UK member_id PK · FK notes Column keys PKprimary key — the row’s identity FKforeign key — points to a row in another table UKunique — no two rows share this value PK · FKboth at once — part of the key and a foreign key Relationship lines exactly one many zero or one virtual link — no foreign key lines cross, not joined

Narrative

The Four Modules

How the Modules’ Tables Connect

Real relationships, the solid arrows, are foreign keys between tables in the two modules.

Virtual relationships, the dashed arrows, are not foreign keys. They are opaque, generic row references: the search sidecar records, with each embedding row, a plain (source_schema, source_table, source_id) triple that names a target table and row by value, with no foreign-key constraint declared — which is what lets the target live in another schema or a remote system. The registered sources are individual tables, and the references run table to table: embedding to cohort and email_template; to experiment, experiment_run, and nudge; to model_config and model_catalog; and to document and nl_query. The map draws the search-to-llm-chat link explicitly, because embeddingmodel_config / model_catalog is the only relationship between any table in those two modules; the references from embedding into core-experiment and core-identity tables run alongside the existing foreign-key edges there. One more opaque row reference is internal to reporting: audit_log.target_type / target_id names a row in any table.

Indexing and Access Paths

Beyond the indexes that come with every primary key, unique constraint, and foreign key, the schema carries secondary indexes chosen from the documented query paths. They are declared next to each table, in the indexes blocks and table notes of schema.dbml. The families in use:

The highest-volume tables — chat_round, chat_round_candidate, and audit_log — are ordinary tables with simple id primary keys. Deep paging uses keyset pagination (paging by a cursor value rather than a numeric offset) — ORDER BY (created_at, id) with a WHERE (created_at, id) < (:cursor) cursor, backed by the (created_at, id) indexes on those tables — which stays fast at any depth, so partitioning is not needed for paging; counts use count(*) OVER(). Range partitioning (splitting one table into sub-tables by a key range, for instant DROP-PARTITION retention and smaller per-partition indexes) is a scale option to revisit only if these tables reach the tens of millions of rows, a known migration deliberately deferred to keep the primary keys and foreign keys simple now. Postgres has no maintained clustered index (rows kept physically ordered by a key), no persistent bitmap index (the planner builds bitmap scans from ordinary B-trees at query time), and hash indexes are avoided in favor of B-tree. The exhaustive audit of the remaining secondary indexes is deferred to a later performance-tuning pass: once the app runs against real data, they are added by reading query plans, not guessed at design time.

A Note on the Search Module

The search module is generic, and nothing in it is specific to ChatMaestro. It can embed the rows of any table, in this database, another schema, or even a remote system, and query them by similarity, through that virtual source pointer with no foreign key, which is exactly why its cross-module links are dashed. It is designed to be extracted into its own standalone service that ChatMaestro, and other applications, connect to. On this map it is shown as one of ChatMaestro’s modules, but think of it as a self-contained building block plugged in here.