Skip to content

[EPAC-962]: Bill diff viewer — compare versions across readings#809

Merged
riddim-developer-bot[bot] merged 1 commit into
mainfrom
symphony/epac-962-bill-diff-viewer-compare-versions-across-reading
Jun 14, 2026
Merged

[EPAC-962]: Bill diff viewer — compare versions across readings#809
riddim-developer-bot[bot] merged 1 commit into
mainfrom
symphony/epac-962-bill-diff-viewer-compare-versions-across-reading

Conversation

@riddim-developer-bot

Copy link
Copy Markdown
Contributor

Summary

Adds the iOS "Compare versions" surface for EPAC-962. The bill detail page now exposes a button that opens a sheet showing a clause-level diff between two published versions of the bill. Users can pick which two versions to compare and toggle between an inline (before-then-after) and a side-by-side presentation. Added / removed / modified / unchanged clauses are colour-coded with the verbatim before/after clause text rendered inline. When the backend records the Hansard anchor for the speech that introduced a change, the row links out to it. When only one version has been published, the sheet renders an empty state. Verbatim text only — no LLM summaries anywhere on the surface.

The backend (versions ingestion + diff endpoint) was previously deployed to staging per the parent project plan; this PR is the iOS-only consumption side.

Architecture

Clean Architecture Shape on iOS:

  • EntitiesBillVersion, BillVersionDiff, BillClauseDiff, BillClauseChangeType (ios/epac/Domain/Entities/).
  • PortsBillVersionsRepository, BillVersionDiffRepository (ios/epac/Domain/Ports/).
  • Use casesLoadBillVersions, LoadBillVersionDiff (ios/epac/Application/).
  • AdaptersBackendBillVersionsRepository reuses the existing GET /api/v1/bills/{id} depth endpoint to pull the versions list. BackendBillVersionDiffRepository calls the new GET /api/v1/bills/{id}/diff?from=…&to=… endpoint. Both treat 204 / 404 as "absent" — the bill page hides the entry point when there are no versions; the diff viewer renders an unavailable state when the backend has no diff for the chosen pair.
  • ViewBillVersionsDiffView owns the picker + toggle state and orchestrates the diff load; BillVersionsDiffContent is the pure-rendering surface used by snapshot tests.

The clause-aware diff algorithm lives in the backend; iOS only renders the structured result. Boundary rule: no paraphrasing, no LLM summaries — only the verbatim clause text and the structured anchors the backend supplies.

API contract (new)

GET /api/v1/bills/{id}/diff?from={fromVersionID}&to={toVersionID} — added to backend/openapi/openapi.json along with the BillVersionDiff and BillClauseDiff schemas. Response shape (abridged):

{
  "from": { "id": "C-8-v1", "label": "First reading", ... },
  "to":   { "id": "C-8-v3", "label": "As passed by the House", ... },
  "clauses": [
    {
      "id": "clause-3",
      "label": "Clause 3",
      "change_type": "added",
      "from_text": "",
      "to_text": "The Minister shall publish quarterly progress reports to Parliament.",
      "hansard_anchor_url": "https://www.ourcommons.ca/.../hansard#clause-3"
    }
  ]
}

change_type is one of added | removed | modified | unchanged; the iOS adapter normalises common synonyms (inserted, deleted, replaced, context, …) so the backend can stay flexible.

Acceptance criteria (iOS scope)

  • "Compare versions" button on the bill page opens a diff viewer with version pickers and side-by-side or inline diff toggle.
  • Diff highlights additions, deletions, modifications.
  • Tapping a clause links to the chamber speech that introduced the change (when known).
  • Renders an empty state when only one version exists yet.
  • No LLM-generated summaries of changes — just the raw diff with structured anchors.

Use-case catalog

Adds LoadBillVersions and LoadBillVersionDiff to docs/architecture/use-case-catalog.md with their Clean Architecture shape and boundary rules. Expanded BillVersion, added BillVersionDiff and BillClauseDiff entity rows.

Test plan

  • xcodebuild build on iPhone 17 Pro simulator — clean (no errors, no new warnings).
  • swiftlint --strict on the changed files — clean.
  • LoadBillVersionsTests, LoadBillVersionDiffTests — use-case stubs cover the success / nil / empty / error paths.
  • BillVersionsRepositoryTests, BillVersionDiffRepositoryTests — network-mocked, asserting decode + 200 / 204 / 404 / 500.
  • BillVersionDiffRepositoryTests.changeTypeMapsKnownSynonyms — asserts every synonym the backend may emit normalises correctly.
  • SnapshotTestsBillVersionsDiff_inline, BillVersionsDiff_sideBySide, BillVersionsDiff_noChanges, BillVersionsDiff_emptyOnlyOneVersion, each in light / dark / accessibility-large variants (12 reference images committed).
  • Live verification on a real bill awaits the next staging cut and is captured in the project's Human Handoff issue, not blocking this PR.

Verification evidence

Inline diff (snapshot, light mode):

The rendered diff lists clauses in order: an unchanged context clause, an added clause (green block + Hansard link), a modified clause (red strike-through on the prior text, green replacement below), and a removed clause (red block). Side-by-side mode lays the same clauses out as a two-column "Before" / "After" comparison. The empty state renders a doc.text.magnifyingglass glyph plus the "Only one version published" message when the bill has only one version.

🤖 Generated with Claude Code

Adds an iOS "Compare versions" entry point on the bill detail page that
opens a sheet showing a clause-level diff between two published bill
versions. Pickers select the from/to version; a segmented toggle in the
toolbar switches between inline and side-by-side presentations. Added,
removed, modified, and unchanged clauses are colour-coded (green / red /
amber / neutral) with the verbatim before/after clause text rendered
inline; a Hansard anchor link surfaces the chamber speech that
introduced the change when the backend records one. When only one
version has been published the sheet shows an empty state explaining
the comparison will become available once a second version lands.

Domain: `BillVersion`, `BillVersionDiff`, `BillClauseDiff`,
`BillClauseChangeType` entities; `BillVersionsRepository`,
`BillVersionDiffRepository` ports; `LoadBillVersions`,
`LoadBillVersionDiff` use cases.

Data: `BackendBillVersionsRepository` (reuses the existing
`GET /api/v1/bills/{id}` bill-depth endpoint to read the versions list),
`BackendBillVersionDiffRepository` (calls the new
`GET /api/v1/bills/{id}/diff?from=…&to=…` endpoint). Both treat 204/404
as "absent" (hide entry point / show unavailable state); the diff
algorithm and version-text ingestion remain backend responsibilities.

Tests: use-case stubs for both loaders, network-mocked repository tests
covering decode + 204/404/error paths, and snapshot tests for the inline
mode, side-by-side mode, the "no clause-level changes" path, and the
"only one version published" empty state.

OpenAPI: documents the new `/bills/{id}/diff` endpoint and the
`BillVersionDiff`/`BillClauseDiff` schemas.

Use-case catalog: adds `LoadBillVersions` and `LoadBillVersionDiff`
entries and expands the `BillVersion` / `BillClauseDiff` entity rows.

Release-Note: Bill page — tap "Compare versions" to see exactly what
changed between two published versions of a bill, clause by clause.
@riddim-developer-bot riddim-developer-bot Bot enabled auto-merge (squash) June 14, 2026 12:01
@riddim-developer-bot riddim-developer-bot Bot merged commit 8f6c8be into main Jun 14, 2026
60 of 63 checks passed
@riddim-developer-bot riddim-developer-bot Bot deleted the symphony/epac-962-bill-diff-viewer-compare-versions-across-reading branch June 14, 2026 12:04
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