The dimensional substrate
The dimensional substrate gives selected pillar-owned entities a shared contract for custom fields, CEL filtering, and generic record access. It does not move business records into a shared table: each pillar still owns its physical data and invariants.
Current physical entity types
The checked-in baseline contains seven extensible physical entity types:
| Entity type | Pillar |
|---|---|
crm_contact | Customer 360 |
crm_company | Customer 360 |
crm_activity | Customer 360 |
commerce_order | Commerce |
commerce_product | Commerce |
commerce_fulfillment | Commerce |
marketing_campaign | Marketing fixture and campaign workflows |
The runtime registry is tenant-scoped and may also contain generic user-defined tables. Physical types above are provisioned from the shared baseline.
The closed accounting set includes journal_entry, journal_line, account_balance, accounting_period, posting_period, and fiscal_period. These types reject custom fields.
Generic MCP access
The current catalog exposes two generic record tools:
inblack_query_recordsreads supported records and applies an allowlisted CEL filter.inblack_update_recordvalidates and merges a record patch.
Use the pillar-specific tools when one exists. They carry the domain’s richer authorization, simulation, approval, and response contracts. The generic substrate tools are most useful for cross-pillar reads and custom-field updates.
Schema and value lifecycle
Each tenant and entity type has a versioned field declaration. Writes validate field names, types, constraints, required values, reference targets, and formula mutability at the boundary.
Schema changes are classified before activation:
| Class | Examples | Result |
|---|---|---|
none | Semantically identical declaration | No version change |
additive | Optional field, wider options, looser bound | Activate the new version directly |
breaking | Removed or retyped field, new requirement, changed formula | Preserve the old version and activate a new one |
Breaking changes use an explicit, repeatable value backfill. See Custom fields for request shapes and supported types.
Filtering behavior
The CEL-to-SQL layer accepts a restricted expression set and parameterizes values. Native entity fields retain their typed SQL behavior. Custom-field access uses custom_fields ->> <key>, so the result is text.
This distinction matters:
custom_fields.referred_by == "Instagram" // supported text equalitycustom_fields.lifetime_visits > 10 // not a numeric comparisonFor frequently filtered fields, use a supported indexed declaration so the substrate can promote the value into a typed generated column. A schema may promote at most eight fields.
Ownership boundary
The substrate owns the declaration grammar and generic access contract. Pillars continue to own:
- Native columns and business validation.
- Authorization and tenant isolation.
- Domain-specific writes, simulations, and approvals.
- Audit events and downstream projections.
This boundary keeps shared custom data queryable without weakening accounting or commerce invariants.