Agent Skills: PolicyEngine Canada domain knowledge

|

UncategorizedID: PolicyEngine/policyengine-claude/policyengine-canada

Install this agent skill to your local

pnpm dlx add-skill https://github.com/PolicyEngine/policyengine-claude/tree/HEAD/skills/policyengine-canada

Skill Files

Browse the full folder contents for policyengine-canada.

Download Skill

Loading file tree…

skills/policyengine-canada/SKILL.md

Skill Metadata

Name
policyengine-canada
Description
|

PolicyEngine Canada domain knowledge

Canada is the exception to the standard PolicyEngine stack. Read this before writing any Canadian analysis — the entry points and the limits are different from the US/UK.

Verified against policyengine-canada 0.99.0 (2026-07), installed standalone. Do not hardcode benefit amounts; look them up live (below), because provincial and federal parameters re-index every year.

Two hard constraints

1. pe.ca does not exist. The policyengine wrapper ships US and UK only; there is no pe.ca. Use the policyengine_canada package directly.

<!-- verify -->
import policyengine as pe
assert not hasattr(pe, "ca")   # Canada is not in the policyengine wrapper

2. Canada is household-only — there is no representative microdata. The package ships a small synthetic template, not a survey-weighted population sample. So you cannot run population microsimulation, and you cannot produce national program costs, revenue estimates, caseloads, or poverty rates for Canada. If asked "what would this cost nationally" or "how many families benefit," say that population estimates are not available for Canada and offer a household example. What you can do: compute taxes/benefits for a specific family, compare baseline vs. reform for that family, sweep it across an income range, and compare provinces.

One household calculation

Install the package on its own (it is not in the shared analysis venv), then build a situation and use policyengine_canada.Simulation. Values below verified against 0.99.0.

uv pip install policyengine-canada
from policyengine_canada import Simulation

sim = Simulation(situation={
    "people": {
        "parent1": {"age": {"2026": 35}, "employment_income": {"2026": 45_000}},
        "parent2": {"age": {"2026": 33}, "employment_income": {"2026": 20_000}},
        # full_custody defaults to False, which HALVES the CCB (see gotcha below):
        "child1": {"age": {"2026": 4}, "full_custody": {"2026": True}},
        "child2": {"age": {"2026": 9}, "full_custody": {"2026": True}},
    },
    "households": {"household": {
        "members": ["parent1", "parent2", "child1", "child2"],
        "province_code": {"2026": "ONT"},   # note: "ONT" for Ontario, "QC" for Quebec
    }},
})
assert round(float(sim.calculate("child_benefit", 2026)[0]), 2) == 11_030.75   # federal CCB
assert float(sim.calculate("adjusted_family_net_income", 2026)[0]) == 65_000    # AFNI

Entities are just household and person (far simpler than the US six). province_code is an enum on the household: AB, BC, MB, NB, NL, NS, NT, NU, ONT, PE, QC, SK, YT — Ontario is "ONT", not "ON".

Key variable names (they are not the acronyms): federal Canada Child Benefit = child_benefit, GST/HST credit = gst_credit, Canada Workers Benefit = canada_workers_benefit, Old Age Security = oas_net. Browse the full list with from policyengine_canada import CountryTaxBenefitSystem; CountryTaxBenefitSystem().variables.

Gotcha: full_custody defaults to False and halves the CCB

Each child's full_custody defaults to False, which the model treats as 50/50 shared custody and halves that child's benefit base. A sole-custody two-parent family gets the full amount only if you set full_custody: {"YEAR": True} on each child — otherwise every CCB (and child-benefit- derived) figure comes out at half. Verified: the family above yields $3,658.25 with the default and $11,030.75 with full_custody=True.

Gotcha: prefer the specific benefit variable over household_net_income

In 0.99.0, computing household_net_income (or gst_credit, which depends on it) can raise a ClimateActionIncentiveCategory enum error via the climate-incentive chain. Calculate the specific variable you need (child_benefit, adjusted_family_net_income, income_tax) rather than the full net-income roll-up.

Look parameters up live — never hardcode

Resolve the parameter tree at an instant (as a formula does) and read the value. The CCB maximum is an age-scaled parameter under gov.cra.benefits.ccb:

from policyengine_canada import CountryTaxBenefitSystem
p = CountryTaxBenefitSystem().parameters("2026-01-01")
p.gov.cra.benefits.ccb.base.calc(4)   # -> 7997  : CCB max for a child under 6
p.gov.cra.benefits.ccb.base.calc(9)   # -> 6748  : CCB max for a child 6-17
p.gov.cra.benefits.ccb.base.calc(20)  # -> 0     : none over 17

The reduction sub-tree (ccb.reduction.one_child, .two_children, …) holds the AFNI phase-out schedule. Provincial parameters live under gov.provinces.<code>. Read the value at your simulation year rather than quoting a remembered figure — thresholds and maximums re-index yearly.

Two facts that change the answer

  • AFNI is combined, and the benefit year lags the tax year. adjusted_family_net_income is both partners' net income summed, not one person's. In the real program the July–June benefit year is based on the prior tax year's AFNI (e.g. July 2025–June 2026 payments use 2024 income); the model computes benefits from the same-year situation, so pick the income year to match the AFNI year you intend to represent.
  • Quebec is a different system. Quebec runs its own pension plan (QPP, not CPP), parental insurance (QPIP), sales tax (QST), and a distinct set of provincial credits and a family allowance. Set province_code to "QC" and do not assume rest-of-Canada provincial rules carry over; the federal child_benefit still applies, but Quebec layers its own programs on top.