semantics — Module Spec
1. Purpose
semantics is the shared business ontology layer of the agents-v2/v3 build — the vocabulary future agent decisions read to know what a business fact (e.g. "SKU margin") actually MEANS and how it should be computed, and what a tenant's own priorities (e.g. "protect cash over margin") are. It is Phase 3 of a 6-phase agents-v2/v3 build authorization (Phase 1 reopened platform for the module registry + AI-capacity layer, PROJECT_DECISIONS #62; Phase 2 reopened ai for the model-registry/deployment/prompt/routing layer + memory-provenance, PROJECT_DECISIONS #63; this is Phase 3). Unlike every other module built so far in this codebase, semantics has zero v1 antecedent — confirmed: v1 had no semantic-layer concept of any kind, not even a stale placeholder to reconcile against (unlike tax's own zero-precedent build, this isn't even a domain v1 fully outsourced elsewhere — the concept simply didn't exist).
It exists to close a specific, real gap: today an agent_task.goal_description (or any agent's understanding of "margin," "at-risk customer," or "protect cash") is free text or hard-coded logic, invented independently by whichever module needs it. semantics gives every future agent capability a single, versioned, DB-enforced place to look up: what a metric means and how it's computed (with re-verified provenance, not just a name), what business entities and their aliases across schemas are, what dimensions a metric can be sliced by, and what goals/constraints a tenant has actually bound as authoritative — all before any agent acts on any of it.
See PROJECT_DECISIONS #64 for the full build record, including the Section 4 self-audit fixes and the live-reproduction guard proofs.
2. Ownership
Owns — 14 tables, 114 columns:
| Table | Cols | Tenant-scoped? | Role |
|---|---|---|---|
approved_function_registry |
11 | No (global) | I2's supply-chain-hole-closing allowlist — a metric/attribution-model version's "expression" is a POINTER to a reviewed function, never raw SQL in a data row |
metric_definition |
6 | No (global) | The stable, versioned concept — "what does SKU margin mean" |
metric_version |
8 | No (global) | A revision of a metric definition — how it's actually computed, resolved_function_reference a real FK into the registry |
tenant_metric_binding |
12 | Yes | Which metric version is AUTHORITATIVE for a tenant, for a time range |
metric_dependency |
4 | No (global) | Which metrics depend on which others — real cycle detection via trigger |
entity_definition |
6 | No (global) | Names a business entity concept and its canonical table |
entity_alias |
5 | No (global) | An alternate table/view representing the same or related entity |
dimension_definition |
5 | No (global) | A dimension a metric can be sliced/grouped by |
goal_definition |
8 | No (global) | A named tenant priority a goal binding can point at (e.g. cash-protection) |
constraint_definition |
7 | No (global) | A hard/soft limit, optionally tied to a goal |
tenant_goal_binding |
15 | Yes | Which goal a tenant has bound, at what target/priority, for a time range |
tenant_constraint_binding |
15 | Yes | Which constraint a tenant has bound, same shape as the goal binding |
attribution_model_definition |
4 | No (global) | A methodological definition — "how do we interpret a business fact" |
attribution_model_version |
8 | No (global) | A revision of an attribution model, same registry-pointer pattern as metric_version |
| Total | 114 | 3 of 14 tenant-scoped |
Does NOT own:
- The
agentsmodule's own runtime (Phase 5, not yet built) —semanticssupplies the vocabulary an agent decision reads; it does not itself decide, act, or execute anything. - The
signalsmodule's own outcome-observation/experiment layer (Phase 4, not yet built) —signals.outcome_observationis expected to FK intosemantics.attribution_model_version, butsemanticsowns no observation or experiment data itself. - The reviewed function bodies themselves —
approved_function_registryis a pointer/allowlist, not a code store. The actualCREATE FUNCTIONstatements live in ordinary, human-reviewed migration files elsewhere in the codebase, the same discipline every other SQL statement in this repo already goes through. - Module registry / entitlement / AI-capacity policy — those are
platform's (module_catalog,tier_module_entitlement,tenant_module_activation,ai_capacity_policy, built Phase 1, PROJECT_DECISIONS #62).semantics.goal_definition.applies_to_domainreadsplatform.module_catalogas a scoping FK; it does not duplicate or extend the registry itself. - Agent identity/authority —
identity.agent_identity/agent_duty_grant(already built and locked).semanticsintroduces no new authority mechanism of its own (see §8).
3. Layer & Dependencies
Foundation layer (per SCHEMA_CONVENTIONS.md's foundation/business/consumer taxonomy) — infrastructure every future agent capability reads, not a nursery-vertical product surface, structurally the same positioning as ai and approvals.
Depends on:
platform—tenant.id(the FK target for the 3 tenant-scoped binding tables'tenant_idcolumn) andmodule_catalog.id(the FK target forgoal_definition.applies_to_domain— the first real consumer ofmodule_catalogas an FK target outsideplatformitself, built Phase 1, PROJECT_DECISIONS #62).identity—actor.id(every*_actor_idcolumn:approved_function_registry.approved_by_actor_id,tenant_metric_binding.owner_actor_id,tenant_goal_binding.approved_by_actor_id,tenant_constraint_binding.approved_by_actor_id).
Depends on NO domain/nursery-vertical module — the same reverse-dependency discipline approvals established for itself: semantics never references crm, inventory, pos, or any other product module directly. Where a metric or entity concept needs to point at a domain table, it does so via a plain TEXT seam (canonical_table_ref/alias_table_ref, e.g. 'crm.customer'), never a real FK — a structural guarantee that semantics can describe any table in the system without ever being blocked by, or blocking, that table's own schema evolution.
Depended on by: the not-yet-built agents (Phase 5) and signals (Phase 4) modules — this is the entire reason semantics exists as its own phase, built before either of them. agents is expected to resolve a tenant's authoritative goals/constraints via tenant_goal_binding/tenant_constraint_binding before acting, and to reference metric_definition/metric_version when a decision needs to know what a business metric means. signals.outcome_observation (not yet built) is expected to FK into attribution_model_version, replacing v2's original free-text attribution_method column. Neither dependency is live today — semantics has zero real callers at this phase, by design (see §7).
4. Tables — grouped
Function Registry (1): approved_function_registry — the I2 allowlist every metric/attribution-model version's computation pointer resolves through.
Metric family (4): metric_definition → metric_version (standard A2b definition/version pattern) → tenant_metric_binding (which version is authoritative for a tenant, EXCLUDE USING gist overlap-prevented) → metric_dependency (which metrics depend on which others, real cycle-detection trigger).
Entity family (2): entity_definition → entity_alias (names an already-existing cross-schema ambiguity — e.g. crm.customer.consumer_id's still-deferred FK to consumer.consumer — as a queryable fact, without retrofitting the FK itself).
Dimension (1): dimension_definition — what a metric can be sliced/grouped by.
Goal & Constraint family (4): goal_definition / constraint_definition (global catalogs) → tenant_goal_binding / tenant_constraint_binding (tenant-scoped, same EXCLUDE USING gist overlap-prevention pattern as tenant_metric_binding).
Attribution family (2): attribution_model_definition → attribution_model_version (same registry-pointer pattern as the metric family, methodological rather than definitional — "how do we interpret a business fact" vs. "what does it mean").
Full column-level detail (types, nullability, defaults, CHECK constraints, indexes, triggers) lives in docs/database/schema_docs/semantics.md — not restated here.
5. Capabilities
semantics defines no user-facing capability and no agent-authored write path of its own in this phase — it is a pure vocabulary/ontology layer, populated by platform/service_role and (for the 3 tenant-scoped binding tables) by tenant admin action, not by any agent decision.
- I2 approved-function-registry re-verification —
semantics.verify_function_still_matches_approval(p_registry_id uuid)re-resolves a registered function fresh from stored TEXT columns on every call and compares its live definition hash against what was approved;trg_tenant_metric_binding_verify_functioncalls this on everytenant_metric_bindingwrite, rejecting a binding whose referenced function has since been silently redefined. - Overlap-safe tenant bindings —
tenant_metric_binding/tenant_goal_binding/tenant_constraint_bindingeach useEXCLUDE USING gistto hard-reject an overlapping effective date range for the same(tenant, definition)pair — no service-layer read-then-write race is possible; the database itself is the single source of truth for "what's authoritative right now." - Cycle-safe metric dependencies —
metric_dependency's cycle-detection trigger walks the dependency graph via a recursive CTE and rejects any write that would close a cycle (A→B→C→A), with a direct self-reference separately rejected by a CHECK.
No AI write path is implemented this phase — schema-only build (Drizzle + migration + tests), no SemanticsService. See §6 and §9.
6. Service Contract — SemanticsService
No SemanticsService exists yet — schema-only this phase, see PROJECT_DECISIONS #64. This matches every phase of the agents-v2/v3 build so far (Phase 1's platform reopen and Phase 2's ai reopen were both schema-only too) — every phase of this build stays schema-only until Phase 5 lands the agents module itself. Downstream code (once it exists) would query semantics.* directly via Drizzle until a service layer is built. The 2 DB-enforced mechanisms that matter most for correctness — the I2 re-verification trigger and the 3 EXCLUDE USING gist overlap guards — are NOT waiting on that service layer to be correct: they are already live, atomic, and enforced directly by the schema regardless of what future application code does or doesn't do.
7. Data-Flow / Population Model
semantics is unusual among this project's modules in the same way ai's agent-runtime cluster was at its own first pass: it has no first-party UI and is not populated by a user filling out a form in this module directly.
- The 11 global catalog tables (
approved_function_registry,metric_definition,metric_version,metric_dependency,entity_definition,entity_alias,dimension_definition,goal_definition,constraint_definition,attribution_model_definition,attribution_model_version) are populated by platform/service_role—authenticatedgetsSELECTonly on all 11, with an explicitREVOKE INSERT, UPDATE, DELETE. A tenant reads this vocabulary; only Vrida (or a future admin/curation tool) writes it. - The 3 tenant-scoped binding tables (
tenant_metric_binding,tenant_goal_binding,tenant_constraint_binding) are where a tenant's own choices land — which metric version is authoritative, which goals/constraints are bound, at what priority, for what time range.authenticatedhas full CRUD here, RLS-scoped to the tenant. This is expected to be driven by a tenant-admin console screen once one exists, not built this phase. agents/signals(both deferred) are the expected FIRST REAL READERS of this entire schema. Until either is built, every table insemanticscan be populated (by platform, in a future seed/migration pass) and queried, but nothing in the live system actually consumes it yet — a deliberate "build the vocabulary before the speaker" sequencing, matching this build's own stated 6-phase order (Phase 3 semantics before Phase 4 signals before Phase 5 agents).
This means semantics, like ai's own runtime cluster, has essentially no first-party UI of its own at this phase — it is infrastructure other, not-yet-built modules will read from and write authoritative tenant choices into.
8. Agent Authority Mapping
semantics introduces no new authority mechanism. It is not itself a consumer of identity.agent_duty_grant in this phase either — unlike crm/inventory/every other product module, semantics defines no agent-authored write path at all (see §5): the 11 global tables are platform-write-only, and the 3 tenant-scoped binding tables have no automation_source/review_status autonomy pack of their own (a deliberate scope choice — v3's own design does not model an agent proposing a goal/metric binding in this phase; a tenant admin does, via a not-yet-built console). Once agents (Phase 5) is built, agent actions that READ this ontology will be logged the same way every other module's agent actions are — via ai.agent_execution — but that is ai's mechanism, not a new one semantics invents.
9. Cross-Module Seams
Cataloged in docs/modules/CROSS_MODULE_CONTRACTS.md (referenced, not restated here). Key relationships:
| Direction | Contract |
|---|---|
| Semantics → Platform | tenant_metric_binding.tenant_id/tenant_goal_binding.tenant_id/tenant_constraint_binding.tenant_id → platform.tenant.id (enforced FK, the RLS scope on all 3). goal_definition.applies_to_domain → platform.module_catalog.id (enforced FK, nullable) — the first real consumer of module_catalog as an FK target outside platform itself. |
| Semantics → Identity | every *_actor_id → identity.actor.id (enforced FK) — actor attribution for who approved a function / owns or approved a binding. |
| Semantics (internal, deliberately bare) | tenant_goal_binding.module_id/.site_id and tenant_constraint_binding.module_id/.site_id are plain uuid, NO FK — a deliberate judgment call disclosed in docs/open-items/OPEN_ITEMS.md (semantics | FK), not a forward-ref debt of the ordinary kind. platform.module_catalog already exists (unlike ai.routing_policy.workload_class_id's genuine forward-ref to the not-yet-built agents.workload_class), but the design of record does not specify wiring module_id to it, and no multi_loc.site composite-FK prerequisite check was run for site_id either — left exactly as specified, not over-interpreted. |
| agents (deferred, Phase 5) → Semantics | (forward, not yet built) expected to read tenant_goal_binding/tenant_constraint_binding to resolve a tenant's authoritative priorities before acting, and metric_definition/metric_version/tenant_metric_binding when a decision needs to know what a business metric means. |
| signals (deferred, Phase 4) → Semantics | (forward, not yet built) signals.outcome_observation is expected to FK into attribution_model_version, replacing v2's original free-text attribution_method column. |
10. Design Rationale
- DR-1 — the I2 re-verification pattern is the load-bearing mechanism of this phase. A metric/attribution-model version's computation is a pointer (
resolved_function_reference/computation_reference) intoapproved_function_registry, never raw SQL in a data row — but a pointer alone only proves a function was ONCE approved, not that it still matches.verify_function_still_matches_approval()closes that gap by re-resolving and re-hashing on every call, andtrg_tenant_metric_binding_verify_functionmakes that check load-bearing at write time, not merely available to whoever remembers to call it. Seedocs/database/schema_docs/semantics.md's own "Design Patterns Summary" for the full live-reproduction record. - DR-2 —
EXCLUDE USING gist, HARD-REJECT, deliberately departing fromplatform.accounting_period's own flag-not-reject pairing. All 3 tenant-scoped binding tables reuseaccounting_period'sbtree_gist-based overlap-prevention shape, but reject outright rather than flag-and-continue — no offline-sync reason exists for a tenant setting an authoritative metric/goal/constraint binding (an online, synchronous admin action), unlike POS's offline-first sale posting, which is exactly whyaccounting_perioditself needs the gentler flag path. - DR-3 — the
module_id/site_idbare-FK disclosure is a judgment call, not an oversight. See §9 above anddocs/open-items/OPEN_ITEMS.mdfor the full framing — logged explicitly so a future reopen has a clear, correctly-scoped trigger to act on, rather than silently wiring an FK the design of record never asked for. - DR-4 — Section 4 self-audit found and fixed 2 real gaps before lock, both same-pass, both disclosed in full in
docs/database/schema_docs/semantics.md: 3lifecycle_statuscolumns missing their CHECK entirely (fixed), andtenant_goal_binding/tenant_constraint_bindingmissingcreated_at/updated_at+ the maintaining trigger their siblingtenant_metric_bindingalready had (fixed — this is why the schema is 114 cols, not the original 110). - DR-5 — a schema-usage GRANT gap was found only by the regression suite, not the read-only Section 4 pass. The migration's first draft omitted
GRANT USAGE ON SCHEMA semantics TO authenticated— every prior new-schema migration in this codebase does this explicitly, and this one silently missed it. The RLS cross-tenant-isolation test caught it (permission denied for schema semantics); fixed live and in the migration file.
11. Deferred / Future Items
All items tracked in docs/open-items/OPEN_ITEMS.md, attributed to semantics (PROJECT_DECISIONS #64). Summary for context:
| Item | Status | Trigger |
|---|---|---|
tenant_goal_binding.module_id/.site_id and tenant_constraint_binding.module_id/.site_id — deliberately bare, no FK |
open, disclosed judgment call | If a future reopen decides these SHOULD be real FKs — module_id → platform.module_catalog(id) is immediately wireable today; site_id → multi_loc.site(id, tenant_id) needs the same composite-FK treatment already applied to identity.user_site_assignment.site_id (PROJECT_DECISIONS #54). |
SemanticsService |
open | No service layer exists yet for any of the 14 tables — schema-only this phase, matching every phase of this build. Trigger: when a service layer is built, most likely alongside or just before Phase 5's agents module, since agents is this schema's first real consumer. |
agents/signals real consumption |
open | semantics has zero real callers today — a deliberate build-the-vocabulary-first sequencing. Trigger: Phase 4 (signals) and Phase 5 (agents) of the same build authorization. |