[Story-1201] Seed participant label defaults and linkage

Story 1201Epic None7 files · +523/−3

PR #304 · pramaan-io/pramaan-functions · merged 2026-06-11

Why this matters

A firm is born with an empty participant-label catalog — and every matter invents its own vocabulary.

Firms provisioned today ship with no default roles; matters, reports, and cross-matter links all hang off that missing roster.

A roster built per-matter never agrees with itself.

The fix — A firm gets its role roster on day one, matters link to a role, and no catalog is ever empty.

How it flows

sequenceDiagram
  autonumber
  participant firm as Firm service
  participant catalog as Label catalog
  participant matter as Matter service
  participant migration as Migration 0091
  firm->>catalog: create_firm → seed_default_participant_labels
  migration->>catalog: upgrade() backfills existing firms
  matter->>catalog: honor participantLabelId → source_participant_label_id
  matter->>matter: no participantLabelId? freeform path preserved

participant_label/service.py + alembic 0091

Methodin → outwhat it does
seed_default_participant_labels(session, firm_id, created_by_user_id) → Noneopens the role roster for a new firm and stamps the default entries with unique references
upgrade() → Nonevisits every existing firm with an empty catalog and seeds the default label set
downgrade() → Nonerolls back the seeded defaults so old firms return to their pre-0091 catalog state

What changed, file by file

Backfills existing firms with the default participant-label set, allocating a unique reference per firm. Anchors the backfill claim of story 1201.

new methodsignature
upgrade() → None
downgrade() → None
New fields: revision : strdown_revision : str | Sequence[str] | Nonebranch_labels : str | Sequence[str] | Nonedepends_on : str | Sequence[str] | None

Seeds a new firm's participant-label catalog atomically with create_firm, stamping the default roles. Anchors the seed-on-provisioning claim of story 1201.

new methodsignature
seed_default_participant_labels(session, firm_id, created_by_user_id) → None

Files changed (7)

File±
alembic/versions/b6c7d8e9f012_0091_seed_default_participant_labels.py ★+110
docs/walkthroughs/story-1201-walkthrough.html+83
src/domains/firm/service.py+7
src/domains/matter/service.py ★+51 −3
src/domains/participant_label/service.py ★+76
tests/integration/test_app_matters.py+176
tests/routes/test_admin_firms.py+20