Skip to content

Generate federal spending pages from Public Accounts data - #275

Open
xrendan wants to merge 20 commits into
mainfrom
federal-public-accounts-pipeline
Open

Generate federal spending pages from Public Accounts data#275
xrendan wants to merge 20 commits into
mainfrom
federal-public-accounts-pipeline

Conversation

@xrendan

@xrendan xrendan commented Jul 6, 2026

Copy link
Copy Markdown
Member

Summary

Replaces the hardcoded federal spending pages with statically generated pages built from JSON derived from the Public Accounts of Canada, covering all fiscal years 2014–2025 with a year toggle — matching the provincial/municipal architecture. Spec: docs/specs/federal-public-accounts-pipeline.md (plus follow-on specs in docs/specs/).

  • ETL (etl/federal/, Ruby, copied from the public_accounts repo): pb export generates data/federal/{year}/ (summary, thematic sankey, reconciliation, per-department JSON) with hard validation — zero unmapped lines, parent=children within $1M, per-year spending − revenue == published deficit identity, byte-deterministic output. Plus pb translate / pb prose pipelines. 215 tests.
  • Site: /federal/spending/[year] and [year]/[department] routes for every ministry (generateStaticParams, FY2025 default), year selector, page-wide CPI real/nominal toggle persisted across navigation, searchable transfer-payment tables with CSV export, methodology page, legacy URL redirects. The 14 hardcoded department folders, inline 800-line Sankey tree, useDepartments, and sankeyDepartmentMappings are deleted.
  • Charts & consistency: the overview is fully on the Vol I accrual basis — stat cards, thematic Sankey, and ministry list (including the statement's own actuarial/provision/Crown-corp rows) all sum to the published totals in every year. Department pages are fully on the net standard-object presentation (Personnel, Professional and special services, …, with negative revenue leaves): stat card = chart = entity list, bulk-verified across all 306 department-years, with transfer-payment objects broken out into named transfer programs. Parliamentary authorities (votes/allotments) are not shown for now — the data remains in the ETL for internal allocation only.
  • Prose: per department-year in en+fr; markdown {{section:...}} tokens script the page so narrative interleaves with stats/charts in the original production order; figures only via interpolated placeholders; gated on reviewed: true frontmatter (FY2024+FY2025 reviewed by @xrendan; 2014–2023 render stats-only pending review).
  • French: 100% label coverage all years (~85% from a 64k-term official glossary, the rest reviewed translations) and a complete UI catalog (0 missing strings).
  • RDA merge: the regional development agencies (ACOA, CED-Q, WD/PrairiesCan, PacifiCan, CanNor, FedDev-ON) grouped under one regional-economic-development portfolio wherever the source data separates them (not 2016–2018, whose editions lack the granularity).

⚠️ Headline numbers change vs the live site (deliberate)

The headline now equals the published Consolidated Statement of Operations exactly, and the spending Sankey sums to it to the dollar:

  • FY2024: total expenses $521.4B, deficit $61.9B (live site: $513.9B / implied $54.4B). The old site excluded net actuarial losses from total expenses; they are now a visible cost category under Obligations (+$7.5B in FY2024). The overview is sourced end-to-end from the Vol I statement and its segment table, so the thematic tree ties to the headline with no reconciling plug; reconciliation.json and the methodology page document the Vol II bridge.
  • Indigenous Priorities shows $63.0B vs $42.8B — the generated data includes the FY2024 $20.0B "Compensation for First Nations children" payment the curated site omitted.
  • Department pages use the net standard-object presentation (Vol II Table 3) — e.g. PSPC $8.29B — chosen so every figure on a department page matches the chart beside it; the seam to the accrual overview is labeled and explained in the methodology.
  • Every difference is reason-coded in docs/specs/federal-parity-report-2024.md (zero unexplained rows) and explained on /federal/spending/methodology.

Data-quality corrections found along the way

  • The 2020–2021 editions mislabel Shared Services Canada (~$3B) under a noise code that landed in the Governor General portfolio; an organization override routes it to PSPC, with a regression test (GG's true scale those years is ~$25M, confirmed by the standard-object table).
  • Fixed a D3 double-counting bug (parent subtotals compounding to ~$2T) with a leaf-only-amounts contract enforced in both the exporter and the chart.

Adversarial review

An adversarial review (docs/specs/adversarial-review.md, findings in docs/specs/adversarial-review-findings.md) attempted 24 falsifications of the numerical pipeline — all failed (figures re-derived from raw CSVs and checked against canada.ca). Its 8 confirmed findings (stale prose figures from superseded bases, an unescaped HTML sink, undisclosed accrual/expenditure seam magnitude, minor i18n/doc gaps) are fixed, including a permanent figure-consistency lint over reviewed prose wired into the test suite.

Testing

  • bundle exec rake in etl/federal: 215 runs, 0 failures; export byte-deterministic; per-year statement-identity validations.
  • pnpm build: passes (~14k prerendered static pages); tsc/eslint clean on touched files; French catalog 0 missing.
  • Live route checks EN+FR: year/department pages, redirects from legacy URLs, 404s for invalid years/departments, methodology, full-screen sankey, budget + provincial pages unaffected; real-dollar toggle verified in-browser including persistence across navigation.

Generated with Claude Code

xrendan added 5 commits July 6, 2026 05:22
Replace the hardcoded federal spending pages with [year]/[department]
routes driven by data/federal, mirroring the provincial pattern: year
toggle, CPI real/nominal toggle, line-item tables with CSV export,
methodology page, and server-side French label substitution. Legacy
department URLs redirect to the default year.
@build-canada-deploy-bot

build-canada-deploy-bot Bot commented Jul 6, 2026

Copy link
Copy Markdown

Dokploy Preview Deployment

Name Status Preview Updated (UTC)
nextjs ✅ Done Preview URL 2026-07-10T22:43:02.092Z

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 6, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
❌ Deployment failed
View logs
canadaspends 643249f Jul 10 2026, 10:28 PM

xrendan added 15 commits July 6, 2026 06:00
Restore list content dropped in extraction, fix figures that contradicted
the generated Vol II data (Finance transfer share, ESDC individual-transfer
share, DND entity framing), replace chart-conflicting literals with
interpolated placeholders, and bring French in line. Each file carries an
HTML-comment audit of figure decisions.
The exporter serialized subtotals on every parent node, but the shared
chart sums trees with D3 hierarchy().sum(), which adds a node's own
amount plus its descendants' — compounding to ~3.7x at the root
(~$2.0T displayed instead of $548B) and unbalancing the two sides,
since spending and revenue trees inflate by different depth-dependent
factors. Strip parent amounts at serialization (matching the provincial
contract) and harden the four sum accessors to ignore nodes with
children, which is a no-op for provincial and budget data.
Split the inflation state: the nominal/real mode lives in a single
app-level provider synced to localStorage, so the choice survives
navigation between years, departments, and full reloads; each page
provides its year's CPI multiplier. Scaling now covers the whole page —
Sankey and miniSankey trees, entity bar lists, and line-item tables
(including CSV export, with real-dollar column labels) — while
percentages stay unscaled.
…ents

Vote nodes now carry their appropriation description (Operating
expenditures, Capital expenditures, ...) instead of bare vote numbers,
with the number preserved on the node and same-named siblings
disambiguated. Votes fan out to allotment children only when the
allotments add information beyond generic shells, keeping sums exact
and amounts leaf-only. Spec: docs/specs/mini-sankey-legibility.md.
Prose markdown now scripts the department page via {{section:...}}
tokens — the 14 hand-written FY2024 pages interleave narrative with
stats/charts in their original production order, and unmarked prose
keeps the default layout. The miniSankey switches from vote/allotment
trees to the official standard-object breakdown (Personnel, services,
acquisitions, with negative revenue leaves) sourced from the bilingual
open.canada.ca dmac-meso CSVs for all 12 years, reconciled against
allotment totals with documented deltas. Vote detail remains in the
line-item tables. Also completes the French UI catalog (539/539).
Spec: docs/specs/department-page-composition.md.
The 2020-2021 editions carry SSC under a gn-dg noise code that was
mapped to the Governor General portfolio, inflating it to ~$3B; the
standard-object table confirms the true GG figure is ~$25M. An
organization override now routes SSC to Public Services and Procurement
in all years, with a regression test, corrected prose for the four
affected department-years, and full French coverage maintained.
Headline figures now equal the Consolidated Statement of Operations
exactly: total expenses include net actuarial losses (FY2024 becomes
$521.4B, deficit $61.9B — the old site's $513.9B excluded them, now
reason-coded in the parity report), the deficit field is the published
Annual operating deficit line with a Surplus stat-card branch for
surplus years, net actuarial losses become a sign-normalized cost
category under Obligations, and a single visible accounting-and-
consolidation-adjustments leaf ties the thematic spending tree to the
headline to the dollar, with per-year identity validations enforced at
export. Spec: docs/specs/consolidated-statement-alignment.md.
The overview's ministry list and thematic Sankey are now sourced from
Vol I Table 3.6 segment accruals (portfolios mapped/allocated to slugs,
sub-ministry detail prorated by Vol II shares), so every number on the
page — stat cards, chart, and list including the appended statement
rows (net actuarial losses, provisions, and pre-2017 consolidated Crown
corporations) — sums to the published total in all 12 years, and the
accounting-basis-adjustments leaf is gone. Vintage-edition figures are
scaled to the restated statement totals (residual ≤0.7%, guarded at 1%,
factors documented); department pages stay on Vol II appropriations
with the basis banner marking the seam.
Spec: docs/specs/main-page-accrual-basis.md.
Department pages move entirely to the net standard-object presentation:
stat card, entity list, federal share, and historical share all equal
the chart's figures exactly (306 department-years bulk-verified), the
votes/available/lapsed table is removed, and the line-items table is
transfers-only. Each organization's transfer-payments object fans out
into named transfer programs (scaled to the object total, top-N +
Other) where the org unambiguously holds the portfolio's transfers.
Allotment data remains internal-only (theme allocation weights and QA),
enumerated in NOTES.md. Prose, methodology, parity report, and both
French catalogs updated. Spec: docs/specs/drop-authorities-transfer-programs.md.
New pb workforce scraper pulls TBS Population of the Federal Public
Service figures (HTML 2015-2025, open-data CSV backfill for 2014, with
cross-checked overlap) into a committed reference file; the exporter
merges it with the standard-object Personnel totals into
data/federal/{year}/workforce.json for all 12 years. The overview page
gains a per-year workforce strip (headcount, personnel spending,
average personnel cost per employee, headcount trend chart) above the
existing latest-year demographics section, EN+FR.
Spec: docs/specs/workforce-headcount-salary.md.
pb workforce now also scrapes the TBS dimension tables: age bands and
employment tenure for all 12 years (summing exactly to headcount,
including the published Unknown band) and salary ranges for 2017+ (the
employment-equity population subset TBS publishes, captioned as such —
the same source the old hardcoded chart used). The demographics section
now renders the SELECTED year's data with official French labels,
degrading gracefully where a dimension isn't published; the hardcoded
FederalWorkforce/TenureChart/SalaryDistributionChart components and
417 lines of embedded data are deleted.
Spec: docs/specs/workforce-headcount-salary.md (extension).
8 confirmed findings (stale prose figures from the pre-accrual vintage,
an unescaped HTML sink in prose rendering, undisclosed magnitude of the
accrual/standard-object seam, and minor doc/i18n/lint gaps); 24 failed
falsification attempts confirming the numerical pipeline ties to source.
Adds a figure-consistency lint over reviewed prose (wired into rake)
and fixes the 80 stale literals it caught — pre-accrual totals, COVID-
era figures, drifted entity values and shares — with a justified
allowlist for source-quoted figures. Hardens renderProse (HTML escaping
+ https-only links, attack-fixture verified), discloses the accrual
seam on department pages via a new accrualSpending field and note,
flips the prose gate to opt-in, fixes the governor-general French label
collision, stale reconciliation wording, NBSP artifacts, hreflang/title
gaps, and cross-environment float-format non-determinism.
…ts-pipeline

# Conflicts:
#	src/locales/en.po
#	src/locales/fr.po
@build-canada-deploy-bot

Copy link
Copy Markdown

Dokploy Preview Deployment

Name Status Preview Updated (UTC)
nextjs (copy) 🔄 Building Preview URL 2026-07-10T22:41:59.576Z

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.

1 participant