Story 1216: Matter List Numbered Pagination

This walkthrough records the backend contract change for GET /app/matters. The endpoint now supports numbered pagination for pramaan-app while preserving the existing cursor contract during the migration window.

Contract Change

SurfaceBeforeAfter
Query cursor plus limit. page and page_size are accepted. cursor and limit still work.
Response { matters, nextCursor }. { matters, nextCursor, total, page, pageSize }.
Filtering Status, type, and opened-date parameters were part of the route contract. The same filters are applied before offset/page pagination and before total counting.

Compatibility Behavior

Existing consumers that call /app/matters?limit=50 or use nextCursor can keep doing so. When page_size is absent, the response pageSize mirrors the legacy limit. When cursor is supplied, cursor paging wins for row selection, and total still reports the full filtered result count.

First-Run State

A firm with no matters returns { matters: [], nextCursor: null, total: 0, page: 1, pageSize: 50 } for GET /app/matters?page=1&page_size=50. This keeps the app's numbered pagination control usable even before a firm creates its first matter.

OpenAPI Mirror Impact

openapi/app-api.json now exposes page, page_size, and the new response metadata. pramaan-app can sync the mirror and regenerate client schemas without hand-written contract patches.

Verification

Automated coverage was added for empty first-run metadata, numbered page slicing, filtered totals, cursor compatibility, DTO round-trips, and OpenAPI parameter/response shape assertions.

The Docker-backed local test path was run against the isolated Postgres test database with make test-db and make test.