$npx -y skills add PolicyEngine/policyengine-claude --skill policyengine-canadaHow to compute Canadian household tax/benefit outcomes with PolicyEngine, and the hard limits. Load when a task involves Canadian taxes or benefits (CCB, GST/HST credit, CWB, OAS, provincial tax) at the household level. Key facts: pe.ca does NOT exist — Canada is not in the polic
| 1 | # PolicyEngine Canada domain knowledge |
| 2 | |
| 3 | Canada is the exception to the standard PolicyEngine stack. Read this before writing any Canadian |
| 4 | analysis — the entry points and the limits are different from the US/UK. |
| 5 | |
| 6 | Verified against `policyengine-canada` 0.99.0 (2026-07), installed standalone. Do not hardcode |
| 7 | benefit amounts; look them up live (below), because provincial and federal parameters re-index |
| 8 | every year. |
| 9 | |
| 10 | ## Two hard constraints |
| 11 | |
| 12 | **1. `pe.ca` does not exist.** The `policyengine` wrapper ships US and UK only; there is no `pe.ca`. |
| 13 | Use the `policyengine_canada` package directly. |
| 14 | |
| 15 | <!-- verify --> |
| 16 | ```python |
| 17 | import policyengine as pe |
| 18 | assert not hasattr(pe, "ca") # Canada is not in the policyengine wrapper |
| 19 | ``` |
| 20 | |
| 21 | **2. Canada is household-only — there is no representative microdata.** The package ships a small |
| 22 | synthetic template, not a survey-weighted population sample. So you **cannot** run population |
| 23 | microsimulation, and you **cannot** produce national program costs, revenue estimates, caseloads, |
| 24 | or poverty rates for Canada. If asked "what would this cost nationally" or "how many families |
| 25 | benefit," say that population estimates are not available for Canada and offer a household example. |
| 26 | What you *can* do: compute taxes/benefits for a specific family, compare baseline vs. reform for |
| 27 | that family, sweep it across an income range, and compare provinces. |
| 28 | |
| 29 | ## One household calculation |
| 30 | |
| 31 | Install the package on its own (it is not in the shared analysis venv), then build a `situation` |
| 32 | and use `policyengine_canada.Simulation`. Values below verified against 0.99.0. |
| 33 | |
| 34 | ```bash |
| 35 | uv pip install policyengine-canada |
| 36 | ``` |
| 37 | |
| 38 | ```python |
| 39 | from policyengine_canada import Simulation |
| 40 | |
| 41 | sim = Simulation(situation={ |
| 42 | "people": { |
| 43 | "parent1": {"age": {"2026": 35}, "employment_income": {"2026": 45_000}}, |
| 44 | "parent2": {"age": {"2026": 33}, "employment_income": {"2026": 20_000}}, |
| 45 | # full_custody defaults to False, which HALVES the CCB (see gotcha below): |
| 46 | "child1": {"age": {"2026": 4}, "full_custody": {"2026": True}}, |
| 47 | "child2": {"age": {"2026": 9}, "full_custody": {"2026": True}}, |
| 48 | }, |
| 49 | "households": {"household": { |
| 50 | "members": ["parent1", "parent2", "child1", "child2"], |
| 51 | "province_code": {"2026": "ONT"}, # note: "ONT" for Ontario, "QC" for Quebec |
| 52 | }}, |
| 53 | }) |
| 54 | assert round(float(sim.calculate("child_benefit", 2026)[0]), 2) == 11_030.75 # federal CCB |
| 55 | assert float(sim.calculate("adjusted_family_net_income", 2026)[0]) == 65_000 # AFNI |
| 56 | ``` |
| 57 | |
| 58 | Entities are just **`household`** and **`person`** (far simpler than the US six). `province_code` |
| 59 | is an enum on the household: `AB, BC, MB, NB, NL, NS, NT, NU, ONT, PE, QC, SK, YT` — Ontario is |
| 60 | `"ONT"`, not `"ON"`. |
| 61 | |
| 62 | Key variable names (they are not the acronyms): federal Canada Child Benefit = **`child_benefit`**, |
| 63 | GST/HST credit = **`gst_credit`**, Canada Workers Benefit = **`canada_workers_benefit`**, Old Age |
| 64 | Security = **`oas_net`**. Browse the full list with |
| 65 | `from policyengine_canada import CountryTaxBenefitSystem; CountryTaxBenefitSystem().variables`. |
| 66 | |
| 67 | ### Gotcha: `full_custody` defaults to False and halves the CCB |
| 68 | |
| 69 | Each child's `full_custody` defaults to **False**, which the model treats as 50/50 shared custody |
| 70 | and **halves** that child's benefit base. A sole-custody two-parent family gets the full amount only |
| 71 | if you set `full_custody: {"YEAR": True}` on each child — otherwise every CCB (and child-benefit- |
| 72 | derived) figure comes out at half. Verified: the family above yields **$3,658.25** with the default |
| 73 | and **$11,030.75** with `full_custody=True`. |
| 74 | |
| 75 | ### Gotcha: prefer the specific benefit variable over `household_net_income` |
| 76 | |
| 77 | In 0.99.0, computing `household_net_income` (or `gst_credit`, which depends on it) can raise a |
| 78 | `ClimateActionIncentiveCategory` enum error via the climate-incentive chain. Calculate the specific |
| 79 | variable you need (`child_benefit`, `adjusted_family_net_income`, `income_tax`) rather than the full |
| 80 | net-income roll-up. |
| 81 | |
| 82 | ## Look parameters up live — n |