Skip to content

Observational phase: instrument the read filters before deleting them #835

Description

@bencap

Why the gate exists

Deleting the hand-written has_permission read filters converts enforcement from partial failure to total failure. Today a forgotten filter leaks one route, which is why #822 stayed contained to collections. Once they are gone, a policy dropped by a bad migration, a staging refresh from a dump that reassigned ownership, or FORCE lost is a full read leak with no second line, and rollback stops being "drop the policies" and becomes "revert several PRs".

Scope

Keep every filter site in #811, #812, and #813 active, and record when it disagrees with RLS:

filter dropped a row that RLS returned → policy gap. Must always be zero.

That single counter is the alarm.

The converse is not measurable: if RLS drops a row, the Python loop never iterates it, so "the filter would also have dropped this" cannot be counted. Confirmation that RLS is doing the work comes from the canary in #810.

This issue owns three things:

  • The counter — emission, naming, and where it surfaces. It has to be visible where the team already looks, not only in logs.
  • The soak decision. Staging is required; it doubles as an active test of policy coverage during the pilot (Pilot: enable RLS on score_calibrations in staging #825). Production for a release cycle is the recommendation, because that window is precisely when you want dropping a policy to be a complete rollback, which is only true while these filters remain. Decide it here.
  • The exit criterion, applied once across all three deletion issues.

Acceptance criteria

Metadata

Metadata

Assignees

No one assigned

    Labels

    app: backendTask implementation touches the backend

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions