- Decision locked (2026-06-04): avoid one-table-per-page for SPHF.
- Keep shared canonical base (`sphf_normalized_kpi`) for common grain/filters.

- Bucket 1: Shared base only
  - `sphf-dashboard`, `sphf-executive`, `sphf-live-dss`, `sphf-financial-delays`, `sphf-branch`, `sphf-ifi-view`, `sphf-notifications`
  - `sphf-stage-drill` and `sphf-kpi-drill` endpoints stay as drill projections over existing models.

- Bucket 2: Shared base + cached summary
  - `sphf-ageing` (completed)
  - `sphf-ageing-pending`
  - Use base table + pre-aggregated cache payloads (matrix/KPI/register summaries).

- Bucket 3: Separate physical read model required
  - `sphf-stage-bottlenecks`
  - `sphf-ip-performance`
  - Reason: distinct query shape, metrics logic, and/or join pattern vs ageing base.
- Clarification (history): `sphf-stage-bottlenecks` was previously treated/named under pending flow in earlier iterations; current routes keep both endpoints (`sphf-ageing-pending` and `sphf-stage-bottlenecks`) active with different handlers and payload shapes.

- Implementation principle
  - Same grain + different filters/aggregation => shared base.
  - Different grain/refresh cadence/business thresholds => dedicated read model.
