Story-2788 · functions lane · schema foundation

Matter types can now name their participant-label options

This Story adds the durable, firm-owned link between the existing matter type and participant label catalogs. It is the database foundation for the Add-Matter picker. It does not change runtime proceeding roles.

What shipped

business.matter_types
(firm_id, id)
business.participant_labels
(firm_id, id)
business.matter_type_participant_labels
firm_id + matter_type_id + participant_label_id
is_client_side_option + ordering

The three identity columns form the primary key. Both catalog references include firm_id, so the database rejects a link that combines rows from different firms. The reverse index supports label-to-type lookups while the primary key supports type-to-label lookups.

Tenant boundary

A two-firm planted check returned only the active firm's mapping when queried as pramaan_app. A planted cross-firm link failed on fk_matter_type_participant_labels_participant_label.

Migration and metadata

Migration 0203_create_matter_type_label_mapping points to the current head a4f8c2e6d1b3. It hand-authors the table, named composite FKs, primary key, reverse index, table and column comments, FORCE RLS policies, grants, and the complete downgrade. The entity is imported by alembic/env.py, so autogenerate drift detection owns this business-schema table.

The descriptor deliberately differs from historical migration 0047_matter_type_participant_labels. That older revision changed a JSON column; it did not create this table.

Parallel migration resolved: ticket #2787 merged first with migration 0202. This Story's migration was therefore renumbered from 0202 to 0203, pointed at revision a4f8c2e6d1b3, and reverified with make openapi plus the migration gate.

Acceptance evidence

CriterionEvidence
Exact table and entity shapeFive requested columns; composite primary key verified from PostgreSQL catalogs.
Firm isolationFORCE RLS confirmed; two-firm visibility and cross-firm FK planted checks passed.
Migration reversibilityupgrade head → downgrade base → upgrade head passed.
Metadata parityalembic check first failed on the planted missing table, then reported no new operations after migration.
Index behavior at scale100,000 rolled-back mappings: primary lookup 0.121 ms; reverse lookup 0.381 ms, both using their intended indexes.
OpenAPI contractmake openapi completed with no snapshot changes, as expected for schema-only work.

Review gate

New cloud capability?None. This is an additive PostgreSQL schema change.
Who grants it?The migration grants existing pramaan_app table privileges and installs RLS policies.
What check goes red?alembic check detects the missing table or metadata drift; RLS/FK planted checks detect tenant-boundary failures.
Observed red?Yes. Before the migration existed, alembic check reported the added table and reverse index.
Legacy deleted?None. This is a new mapping and introduces no compatibility path.
Deploy smoke?Not applicable. No deploy workflow or runtime code path changes in this Story.

Deliberately deferred

Seed data belongs to Story-2789. The read endpoint and router belong to Story-2790. The representing-side enum migration belongs to Story-2787. This Story adds no seed, DTO, route, agent payload, or client behavior.

Governing issue: pramaan-spec#2788.