> ## Documentation Index
> Fetch the complete documentation index at: https://internal.softcrum.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Reserved Object Model

> After this ships, deals and pipelines can be added later without a data migration — and nothing in v1 uses them.

## 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)*

| Table                    | Key invariants                                                                                                                                        |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `crm.object_definitions` | `tenant_id`; `code` unique per tenant; `label`; `fields` JSONB typed schema; `is_active`; **no writes in F1**                                         |
| `crm.object_records`     | `tenant_id`, FK definition; `values` JSONB validated against the definition; `related_contact_ids` array; standard audit columns; **no writes in F1** |

## 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

| Version | Date       | Change        | Why | Author                 |
| ------- | ---------- | ------------- | --- | ---------------------- |
| 0.1.0   | 2026-08-17 | Initial draft | —   | daniel + claude-opus-5 |

## Delivery record

*Not implemented yet.*
