Story 1745: Firm-level RLS isolation BDD
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.
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.