Story 1745: Firm-level RLS isolation BDD

pramaan-functions · Story-1745 · RFD-BDD-001 D-8

This story adds the first real BDD acceptance slice in pramaan-functions: a lawyer-readable feature that says Meridian can see Meridian's matter and cannot directly read Crestline's matter row.

Boundary honesty: this Tier-1 proof runs against the test Postgres database under a firm RLS session. It proves DB-level RLS row denial for matter rows. It does not prove API Gateway authorizer rejection, and it does not claim cross-matter agent coverage.

What changed

tests/features/cross_firm_isolation.feature

Business-language scenarios for firm-level RLS isolation, including a skipped Tier-2 placeholder that points to the future request-level proof.

tests/steps/test_cross_firm_isolation.py

pytest-bdd step bindings that reuse seed_test_firms, pass state through target fixtures, and query the real RLS-scoped DB session with no global mutable state.

Acceptance language

Feature: Firm-level RLS isolation

  @smoke
  Scenario: Meridian lists only Meridian matters
    When the firm lists matters through row-level security
    Then the visible matters include "Meridian Seed Matter"
    And the visible matters do not include "Crestline Seed Matter"

  @refusal
  Scenario: Meridian cannot directly read Crestline matter row
    When the firm asks directly for the matter named "Crestline Seed Matter"
    Then no matter row is returned
    And no row belonging to firm "Crestline" is returned

How the proof works

The shared seed creates two real firms: Meridian and Crestline. The step module opens a runtime DB session, applies the firm RLS context for Meridian with the shared apply_rls_context helper, then reads business.matters. Listing returns Meridian's matter title and not Crestline's. The direct lookup resolves Crestline's matter id from the seed fixture, queries that id under Meridian's RLS context, and receives no row.

Not claimed

The feature intentionally leaves API Gateway authorizer denial as a skipped @tier2 placeholder tied to pramaan-spec#1738. It also avoids wording that would imply cross-matter agent authorization; this is only firm-level matter-row isolation.

Verification

Run the feature slice with:

make test
uv run ruff check

Story-1745 walkthrough for pramaan-functions. Issue: pramaan-spec#1745.