Story 1740: seed_test_firms for BDD and integration tests

Why this exists. Cross-firm tests need a small, realistic two-tenant world. If every scenario hand-seeds its own firms, roles, clients, and matters, the next migration turns those scenarios into stale SQL. This Story creates one reusable seed coupled to the current factories and schema, so later BDD slices can test product behavior instead of rebuilding tenant setup.

Scope. Test infrastructure only. No API route, production provisioning path, OpenAPI snapshot, or migration changes. The seed is opt-in, so existing empty first-run firm tests keep proving the zero-catalog state.

Seed Shape

seed_test_firms
├── firm_a: Meridian
│   ├── owner user + active firm_admin membership
│   ├── default firm roles: Partner / Counsel / Member
│   ├── one active client
│   └── one active matter attached to that client
└── firm_b: Crestline
    ├── owner user + active firm_admin membership
    ├── default firm roles: Partner / Counsel / Member
    ├── one active client
    └── one active matter attached to that client

Migration Coupling

The fixture builds rows through tests/factories.py and the domain seed service for firm roles. Client and matter numbers come from business.allocate_reference(...), the same DB function used by application code, so the seed follows current constraints instead of relying on a static SQL dump.

RLS Proof

The smoke test uses the runtime session factory, applies a real firm-admin RLS context with bound set_config calls, and checks that Meridian can see only Meridian's client and matter while Crestline can see only Crestline's rows.

Meridian runtime context  -> Meridian client + matter only
Crestline runtime context -> Crestline client + matter only

Verification

make test-db
bash scripts/run-with-test-db-env.sh uv run pytest tests/integration/test_seed_test_firms.py -v --tb=short
uv run ruff check tests/factories.py tests/integration/conftest.py tests/integration/test_seed_test_firms.py tests/steps/conftest.py
make test-db-alembic-roundtrip

One-sentence recall: seed_test_firms gives BDD tests a realistic two-firm stage, while the runtime-role smoke test proves the stage still has a wall between tenants.