Skip to content

[EPAC-1006]: Bill page — PBO costing panel#808

Merged
riddim-developer-bot[bot] merged 1 commit into
mainfrom
symphony/epac-1006-bill-page-pbo-costing-panel
Jun 14, 2026
Merged

[EPAC-1006]: Bill page — PBO costing panel#808
riddim-developer-bot[bot] merged 1 commit into
mainfrom
symphony/epac-1006-bill-page-pbo-costing-panel

Conversation

@riddim-developer-bot

Copy link
Copy Markdown
Contributor

Summary

Adds the Independent costing — Parliamentary Budget Officer panel to the canonical Bill page (BillDetailView). When the backend has a PBO costing note linked to a bill, the panel shows the verbatim headline figure (5-year cost in millions), a methodology category badge, and the publication date, with a link out to the full PBO report PDF. Tapping the panel opens an in-app reader showing the PBO summary card and a button to open the PDF in Safari. When no PBO link exists, the panel renders nothing — no fallback prose.

Implements LoadPBOCosting (Clean Architecture Shape from EPAC-1006). iOS-only change; the backend GET /pbo/by-bill/{legisinfo_id} endpoint is a shipped prerequisite. The old on-device PBO scraper (PBOService/PBOReport) is removed — all PBO parsing lives in the backend; iOS decodes typed JSON only and never scrapes PBO pages on device.

Linear: https://linear.app/riddimsoftware/issue/EPAC-1006/bill-page-pbo-costing-panel

What changed

  • DomainPBOCosting entity (headlineFigureMillions, methodologyCategory, publishedAt, reportURL, sourceURL, summaryText) + PBOCostingResult (latest note + older linked notes). PBOCostingQueryPort.
  • ApplicationLoadPBOCosting: returns nil when absent/empty; otherwise sorts notes by publication date (latest wins) and keeps older notes for the reader.
  • DataBackendPBOCostingRepository (PBOCostingQueryPort): GET /pbo/by-bill/{legisinfo_id}; 204/404nil (hide panel); decodes the typed JSON contract.
  • ViewsPBOCostingPanel (renders nothing when result == nil) + PBOCostingReaderView (in-app summary card, other-notes list, open-PDF-in-Safari). Wired into BillDetailView via injected LoadPBOCosting + autoloadPBOCosting.
  • Strings — en + fr pbo.costing.* / pbo.methodology.* / common.done. Old pbo.* cost-analysis keys removed.
  • DocsLoadPBOCosting use case, PBOCostingQueryPort port, and corrected PBOCosting entity added to docs/architecture/use-case-catalog.md.
  • Removed — on-device ios/epac/Util/PBOService.swift and ios/epac/Model/PBOReport.swift.

Backend JSON contract (boundary)

GET /pbo/by-bill/{legisinfo_id}200:

{
  "costings": [
    {
      "id": "PBO-2026-014",
      "title": "Cost Estimate — Bill C-8",
      "headline_figure_millions": "1,240",
      "methodology_category": "legislative-cost",
      "published_at": "2026-05-20",
      "report_url": "https://www.pbo-dpb.ca/en/publications/RP-2627-014-S",
      "source_url": "https://www.pbo-dpb.ca/en/publications/RP-2627-014-S",
      "summary_text": "The PBO estimates the five-year cost at $1,240 million."
    }
  ]
}

204/404 → no PBO link; the bill page hides the panel. The decoder also accepts a bare top-level array and alternate envelope keys so a backend wrapper rename does not silently blank the bill page; the canonical costings key is always tried first. Figures and summary text are rendered verbatim — no paraphrasing, no LLM summaries.

Acceptance criteria

  • New "Independent costing — Parliamentary Budget Officer" panel on the canonical Bill page
  • Renders headline figure (5-year cost, in millions, verbatim) when present
  • Renders methodology category badge
  • Shows publication date and "View full report" link to the PBO PDF
  • Renders nothing when no PBO link exists — no fallback prose
  • Tapping the panel opens an in-app reader for the summary card + "Open PDF in Safari"
  • Snapshot tests: present, absent, multiple notes (latest wins, others linked) — plus no-figure and reader variants
  • LoadPBOCosting added to the use-case catalog (DoD)

Verification

All run locally on the rebased branch (iPhone 17 Pro simulator, iOS 26.5):

  • xcodebuild build + test: app and test target compile clean; 5 snapshot tests (present, presentNoFigure, multipleNotes, absent, reader) pass with 0 failures; 13 unit tests pass (LoadPBOCostingTests 7, PBOCostingRepositoryTests 6). CI's iOS job is build-only (ENABLE_TESTABILITY=NO), so the tests are run locally; CI re-runs the build as the pr-build gate.
  • SwiftLint (project config, as CI runs it): exit 0, no violations in changed files. Fixed a real cyclomatic_complexity error (and a latent type-inference bug) in the inherited JSON decoder — simplified it to the documented contract.
  • Localization audit (scripts/localization/check_localizations.py): exit 0; en/fr PBO keys at parity (verified French translations, e.g. "Directeur parlementaire du budget").

Simulator-rendered evidence

The panel below is rendered by the production SwiftUI view in the simulator via the snapshot harness. The bill page only shows the panel when staging returns a PBO link for a specific bill (data-dependent), so these deterministic renders are the visual evidence:

  • Present — section title, bill title, "5-year cost, millions / 1,240", "Legislative cost" badge, "May 20, 2026", "View full report".
  • Multiple notes — latest note in the panel with a "2 other PBO note(s)" indicator.
  • Reader — summary card (with verbatim PBO summary text) + "Open PDF in Safari", and an "Other PBO notes" list.
  • Absent — no PBO section renders between the sentinel "Key facts" and "Committee study" sections.

Release-Note: Bill pages now show the Parliamentary Budget Officer's independent costing — the 5-year cost, methodology, publication date, and a link to the full PBO report.

Add the independent Parliamentary Budget Officer costing panel to the
canonical Bill page. When the backend links a PBO note to a bill, the
panel shows the verbatim 5-year cost (in millions), a methodology
category badge, and the publication date, with a link out to the full
PBO report PDF. Tapping the panel opens an in-app reader with the PBO
summary card and an Open-PDF-in-Safari button. When no PBO link exists
the panel renders nothing — no fallback prose.

Implements the LoadPBOCosting use case (entity, port, backend-backed
repository, SwiftUI panel + reader) and wires it into BillDetailView.
Removes the old on-device PBO scraper; all PBO parsing stays in the
backend and iOS decodes typed JSON only.

Adds use-case + repository unit tests and snapshot tests (present,
absent, multiple notes with latest-wins, no-figure, reader), and
records LoadPBOCosting in the use-case catalog.
@riddim-developer-bot
riddim-developer-bot Bot enabled auto-merge (squash) June 14, 2026 11:12
@riddim-developer-bot
riddim-developer-bot Bot merged commit 97ef7c0 into main Jun 14, 2026
35 of 36 checks passed
@riddim-developer-bot
riddim-developer-bot Bot deleted the symphony/epac-1006-bill-page-pbo-costing-panel branch June 14, 2026 11:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants