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 agents module's own runtime (Phase 5, not yet built) — semantics supplies the vocabulary an agent decision reads; it does not itself decide, act, or execute anything.
  • The signals module's own outcome-observation/experiment layer (Phase 4, not yet built) — signals.outcome_observation is expected to FK into semantics.attribution_model_version, but semantics owns no observation or experiment data itself.
  • The reviewed function bodies themselvesapproved_function_registry is a pointer/allowlist, not a code store. The actual CREATE FUNCTION statements 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_domain reads platform.module_catalog as a scoping FK; it does not duplicate or extend the registry itself.
  • Agent identity/authorityidentity.agent_identity/agent_duty_grant (already built and locked). semantics introduces 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:

  • platformtenant.id (the FK target for the 3 tenant-scoped binding tables' tenant_id column) and module_catalog.id (the FK target for goal_definition.applies_to_domain — the first real consumer of module_catalog as an FK target outside platform itself, built Phase 1, PROJECT_DECISIONS #62).
  • identityactor.id (every *_actor_id column: 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_definitionmetric_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_definitionentity_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_definitionattribution_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-verificationsemantics.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_function calls this on every tenant_metric_binding write, rejecting a binding whose referenced function has since been silently redefined.
  • Overlap-safe tenant bindingstenant_metric_binding/tenant_goal_binding/tenant_constraint_binding each use EXCLUDE USING gist to 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 dependenciesmetric_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_roleauthenticated gets SELECT only on all 11, with an explicit REVOKE 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. authenticated has 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 in semantics can 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) into approved_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, and trg_tenant_metric_binding_verify_function makes that check load-bearing at write time, not merely available to whoever remembers to call it. See docs/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 from platform.accounting_period's own flag-not-reject pairing. All 3 tenant-scoped binding tables reuse accounting_period's btree_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 why accounting_period itself needs the gentler flag path.
  • DR-3 — the module_id/site_id bare-FK disclosure is a judgment call, not an oversight. See §9 above and docs/open-items/OPEN_ITEMS.md for 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: 3 lifecycle_status columns missing their CHECK entirely (fixed), and tenant_goal_binding/tenant_constraint_binding missing created_at/updated_at + the maintaining trigger their sibling tenant_metric_binding already 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.
Last modified: Jul 12, 2026, 3:03 AM PT
On this page
Esc