Story 1997: the Brief read DTO

pramaan-docs ADR-153 makes the boundary simple: Vega proposes facts, canonical tables own facts. This endpoint gives the Brief page one payload that keeps those authorities visible instead of merging them into one misleading blob.

The endpoint

GET /app/matters/{matter_id}/brief   (matter_id = firm-local matter number)

200 {
  "frame": current matter_frames version or null,
  "locks": field locks + whole-frame freeze,
  "canonical": parties, forum, case ref, value, outcome,
  "clock": matter status + live proceeding stages
}

404 pramaan.matter.not_found      // unknown OR cross-firm matter

What landed where

PieceReason
src/domains/matter_frame/dtos.py Defines the authority-labeled response: frame is proposed or counsel-authored, canonical is canonical truth, locks are live pin state, and clock comes from runtime tables.
src/domains/matter_frame/service.py Assembles the Brief with fixed reads: matter anchor, current frame, locks, participants, Main proceeding forum, and live proceeding stages. It reuses the cached RLS app context when present and falls back to the session GUC for worker-style sessions.
src/app_api/router_matters.py Adds GET /app/matters/{matter_id}/brief with an explicit response_model and canonical auth/not-found/error responses.
tests/integration/test_app_matter_brief.py Exercises the real ASGI route against the isolated Postgres test DB, including empty first-run state, populated assembly, DTO round-trip, cross-firm 404, and bounded query budget.
openapi/app-api.json Regenerated producer contract for the app mirror.

Authority Labels

The response does not ask the frontend to infer legal authority from position. The canonical.authority field is always canonical. Agent-produced frames are labeled proposed; counsel-authored frame versions are labeled counsel. The raw envelope.frame, envelope.resolution, and envelope.provenance compartments are passed through as stored, so the read side does not invent reconciliation state.

First Run

A matter with no business.matter_frames row is a valid first state. The endpoint returns frame: null, empty locks, and the canonical/clock facts that already exist. It does not 404 a real matter just because Vega has not produced a frame yet.

Verification

make test-db-reset && make test-db
set -a; source .env.test-db; set +a; uv run pytest tests/integration/test_app_matter_brief.py -q
uv run ruff check src/app_api/router_matters.py src/domains/matter_frame/service.py src/domains/matter_frame/dtos.py tests/integration/test_app_matter_brief.py
make openapi