Skip to main content

Context

This is a feature whose entire value is that it is not built. The CRM’s non-goals say no pipeline, no deals, no forecasting in v1: building them means competing with tools our customers already pay for, with a worse version. But “never” is not the answer either — a loyalty-driven CRM with two years of behavioural history is a good place for deals to eventually live. The cost of being wrong in each direction is asymmetric. Building deals now costs months and competes badly. Not reserving the model costs a migration against production data later, which is exactly the kind of migration DEC-H2’s original reasoning was about — and which ADR-021 reminded us is the expensive kind. So: schema, dependency-cruiser rules and generated types exist. No endpoint, no console, no documentation for a tenant. It is a seam, and seams cost nothing until they are used.

Scope (normative)

  • crm.object_definitions: a tenant-defined object type with typed fields.
  • crm.object_records: instances, related to contacts.
  • Generated TypeScript types so the tables are usable the day they are needed.
  • Dependency rules preventing accidental use.

Non-scope (normative)

  • Any endpoint. Explicitly none. Adding one is a new feature spec, and that spec is where deals get designed properly.
  • Console surface. A tenant cannot see that these tables exist.
  • Pipelines, stages, probabilities, forecasting — all of it is the F2 conversation this reserves room for.
  • Migrating existing data into it. There is none.

Behaviour (normative)

  1. The tables exist with RLS, tenant scoping and the platform’s standard columns, and no code path writes to them in F1.
  2. An object definition is per tenant with typed fields, reusing the pattern from core.attribute_definitions rather than inventing a second one.
  3. An object record relates to zero or more contacts, so a future deal can involve several people — the shape that is painful to add later.
  4. CI fails on any import of these tables from application code. A reserved seam that gets used accidentally is not reserved; it is an undocumented feature.
  5. The tables appear in the ER diagram marked reserved, so nobody rediscovers them and assumes they are dead.
  6. When deals arrive, they arrive as their own feature spec that changes this one’s status to superseded — not by quietly starting to use these tables.

Data (normative)

API (normative)

None, deliberately. Adding an endpoint here is out of scope for this spec and requires its own.

Events (normative)

None.

Acceptance criteria (normative)

  1. Migrations create both tables with RLS and tenant scoping.
  2. Generated TypeScript types exist and compile.
  3. CI fails if application code imports these tables — proven with a failing fixture.
  4. The ER diagram shows them marked reserved.
  5. No endpoint, console route or public documentation references them.
  6. Negative: no code path writes to either table in F1.

Execution

Part of the CRM foundational migration. Schema only, plus the lint rule that keeps it unused.

Open questions

None. That is the point of the feature.

Changelog

Delivery record

Not implemented yet.