Skip to content

Conversation

@vahid-ahmadi
Copy link
Collaborator

@vahid-ahmadi vahid-ahmadi commented Jan 10, 2026

Summary

Implements the Scottish Child Payment (SCP), a benefit provided by Social Security Scotland that provides financial support to low-income families with children under 16.

Changes

  • Adds scottish_child_payment variable with eligibility logic based on:
    • Residence in Scotland
    • Child under 16 years old (under 6 before Nov 2022)
    • Receipt of qualifying benefits (UC, Child Tax Credit, Working Tax Credit, Income Support, JSA, ESA, Pension Credit)
  • Adds is_scp_eligible_child helper variable for age eligibility
  • Adds parameters:
    • amount: Weekly payment amount (£10 from Feb 2021, £20 from Apr 2022, £25 from Nov 2022, £26.70 from Apr 2024)
    • max_age: Age threshold (6 until Nov 2022, then 16)
    • takeup_rate: Estimated at 85%
  • Includes SCP in household_benefits calculation

References

Test plan

  • Baseline tests for various scenarios (families with UC, CTC, different regions, age boundaries)
  • Run make test to verify all tests pass

🤖 Generated with Claude Code

vahid-ahmadi and others added 2 commits January 10, 2026 19:24
Implements the Scottish Child Payment, a benefit provided by Social Security Scotland for eligible children.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@vahid-ahmadi vahid-ahmadi changed the title Add Scottish Child Payment Add Scottish Child Payment benefit Jan 10, 2026
@vahid-ahmadi vahid-ahmadi self-assigned this Jan 10, 2026
@vahid-ahmadi
Copy link
Collaborator Author

@PolicyEngine review this

@policyengine
Copy link
Contributor

policyengine bot commented Jan 10, 2026

Code Review: Scottish Child Payment Implementation

I've reviewed the implementation of the Scottish Child Payment (SCP). Here's my assessment:

✅ Strengths

1. Implementation Structure

  • Follows the repository's standard patterns (e.g., similar to two_child_limit_payment.py)
  • Properly organized under gov/social_security_scotland/
  • Correctly uses BenUnit entity for the benefit calculation
  • Good separation of concerns with the is_scp_eligible_child helper variable

2. Parameters

  • amount.yaml: Correctly tracks historical payment rates from £10 (Feb 2021) to £26.70 (Apr 2024)
  • max_age.yaml: Properly captures the expansion from under-6s to under-16s in Nov 2022
  • takeup_rate.yaml: 85% is reasonable and documented with a reference
  • All have appropriate metadata (units, labels, references)

3. Eligibility Logic

  • Correctly checks for Scotland residence using country variable
  • Includes all qualifying benefits: UC, CTC, WTC, Income Support, JSA income-based, ESA income-based, and Pension Credit
  • Age-based eligibility properly uses the max_age parameter
  • Payment only available when amount > 0 (post-Feb 2021)

4. Microsimulation Handling

  • Take-up rate correctly applied only in microsimulation mode (lines 82-85)
  • Uses random(benunit) consistently with other benefits in the codebase
  • Non-microsimulation scenarios get full payment (100% take-up)

5. Tests

  • Comprehensive test coverage (10 test cases)
  • Tests historical changes (2022 age limit expansion)
  • Tests geographic restrictions (Scotland only)
  • Tests qualifying benefit requirements
  • Tests edge cases (children over 16, pre-2021, large families)
  • Appropriate error margins for rounding

6. Integration

  • Correctly added to household_benefits calculation (line 53 of household_benefits.py)
  • Changelog entry properly formatted with minor version bump

🔍 Potential Issues

1. Country Check (scottish_child_payment.py:39)
The code uses:

in_scotland = (
    benunit.household("country", period).decode_to_str() == "SCOTLAND"
)

This checks against the string "SCOTLAND", but looking at the Country enum in country.py, the enum value is Country.SCOTLAND with a value of "Scotland" (capital S, rest lowercase). This should be:

in_scotland = (
    benunit.household("country", period).decode_to_str() == "Scotland"
)

However, the tests use region: SCOTLAND which maps to the Region.SCOTLAND enum. Let me trace through: The country variable formula converts Region.SCOTLAND to Country.SCOTLAND, which has the string value "Scotland". So the check should match the enum's string value, not the enum name.

🐛 Critical Bug Found

Line 39 in scottish_child_payment.py: The comparison == "SCOTLAND" is incorrect. It should be == "Scotland" to match the Country.SCOTLAND enum value.

✨ Recommendations

  1. Fix the country comparison (critical):

    in_scotland = (
        benunit.household("country", period).decode_to_str() == "Scotland"
    )
  2. Consider adding a test for consistency with the two_child_limit_payment (they should both apply to the same Scottish households)

  3. The take-up rate of 85% seems reasonable but may want to verify against official statistics if available

📊 Overall Assessment

The implementation is well-structured and follows best practices. However, there's a critical bug in the Scotland region check that will cause all tests to fail. Once this is fixed, the implementation should work correctly.

Status: ❌ Needs Fix - The country string comparison bug must be corrected before merging.

@vahid-ahmadi
Copy link
Collaborator Author

The bot's concern is unfounded. The "SCOTLAND" pattern is used consistently across the codebase in production code:

Without this, Scottish families receiving SCP have their income
underestimated for poverty calculations, inflating child poverty
rates by ~2-4pp.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@vahid-ahmadi
Copy link
Collaborator Author

@PolicyEngine review this

@policyengine
Copy link
Contributor

policyengine bot commented Jan 10, 2026

Review Confirmed: Implementation is Correct ✅

@vahid-ahmadi is correct. The "SCOTLAND" pattern is the proper implementation and works as intended.

Key Finding: Returns Enum NAME, Not Value

OpenFisca's decode_to_str() method returns the enum member name (e.g., "SCOTLAND"), not the enum value (e.g., "Scotland").

Evidence from the codebase:

  1. Country enum definition (country.py:5-10):

    class Country(Enum):
        ENGLAND = "England"
        NORTHERN_IRELAND = "Northern Ireland"
        SCOTLAND = "Scotland"
        WALES = "Wales"
  2. Test expectations (household.yaml:7-15):

    - name: Country from region - Scotland
      input:
        region: "SCOTLAND"
      output:
        country: "SCOTLAND"  # Returns the NAME, not "Scotland"
  3. Consistent pattern across the codebase:

    • winter_fuel_allowance.py:13: == "SCOTLAND"
    • winter_fuel_allowance.py:34: == "ENGLAND" and == "WALES"
    • pawhp.py:11: == "SCOTLAND"
    • two_child_limit_payment.py:21: == "SCOTLAND" (merged in PR Add Two Child Limit Payment for Scotland #1466)
    • scottish_child_payment.py:39: == "SCOTLAND" (this PR)

Implementation Review

Eligibility Logic

  • Scotland residence check: Correct pattern (scottish_child_payment.py:39)
  • Age eligibility: Properly uses max_age parameter with historical changes
  • Qualifying benefits: Comprehensive list (UC, CTC, WTC, Income Support, JSA, ESA, Pension Credit)

Parameters

  • amount.yaml: Accurate historical rates (£10→£20→£25→£26.70)
  • max_age.yaml: Correct expansion from 6 to 16 in Nov 2022
  • takeup_rate.yaml: 85% is well-documented and reasonable

Integration

  • Added to household_benefits (household_benefits.py:53)
  • Added to hbai_household_net_income for poverty calculations (commit a571f3c)

Tests

  • 10 comprehensive test cases covering:
    • Geographic restrictions (Scotland only)
    • Qualifying benefit requirements
    • Historical age limit changes
    • Edge cases (pre-2021, over-16s, large families)

Changelog

  • Properly formatted with minor version bump
  • Clear description of the new benefit

Recommendation

Approve and merge. The implementation follows established codebase patterns and is correct. The previous bot review's concern about the string comparison was based on a misunderstanding of how OpenFisca's decode_to_str() method works.

2021-02-15: 10
2022-04-01: 20
2022-11-14: 25
2024-04-01: 26.70
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please break out into 2 separate variable files

of recipients to eligible population. SCP has relatively high take-up
due to automatic awards for some recipients and targeted outreach.
values:
0001-01-01: 0.85
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the source states 86% for families with children 6 or older and 97% for families with children 6 and under

Comment on lines 55 to 73
# Check if receiving a qualifying benefit (UC or legacy benefits)
# Families must be in receipt of UC, Tax Credits, or certain other benefits
receives_uc = benunit("universal_credit", period) > 0
receives_ctc = benunit("child_tax_credit", period) > 0
receives_wtc = benunit("working_tax_credit", period) > 0
receives_income_support = benunit("income_support", period) > 0
receives_jsa_income = benunit("jsa_income", period) > 0
receives_esa_income = benunit("esa_income", period) > 0
receives_pension_credit = benunit("pension_credit", period) > 0

receives_qualifying_benefit = (
receives_uc
| receives_ctc
| receives_wtc
| receives_income_support
| receives_jsa_income
| receives_esa_income
| receives_pension_credit
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like certain elements such as the CTC and WTC were removed from the list, source we should parameterize this to account for the composition

vahid-ahmadi and others added 3 commits January 12, 2026 09:36
Changes:
- Update SCP amount to £27.15 for April 2025
- Split variables into separate files (is_scp_eligible_child.py)
- Add age-specific takeup rates: 97% for under-6s, 86% for 6+
- Add comment about CTC/WTC phase-out in qualifying benefits

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add boolean parameters for each qualifying benefit
- CTC and WTC set to false from April 2024 (UC migration)
- Other legacy benefits remain as qualifying

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Tests verify:
- 2025 amount (£27.15/week)
- CTC no longer qualifies from 2025 (after April 2024 removal)
- CTC still qualifies in 2023 (before removal)
- WTC no longer qualifies from 2025
- Income Support still qualifies in 2025
- JSA (income-based) still qualifies in 2025
- ESA (income-related) still qualifies in 2025
- Pension Credit still qualifies in 2025

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@vahid-ahmadi
Copy link
Collaborator Author

Thanks for the review @PavelMakarchuk! I've addressed all your comments:

  1. Updated SCP amount to £27.15
  • Added 2025-04-01: 27.15 to amount.yaml
  1. Split into 2 separate variable files
  • Created is_scp_eligible_child.py for the child eligibility helper
  • Kept scottish_child_payment.py for the main benefit calculation
  1. Age-specific takeup rates per SPICe data
  • Created takeup_rate/under_6.yaml with 97% rate
  • Created takeup_rate/age_6_and_over.yaml with 86% rate
  • Updated formula to apply the appropriate rate based on whether the family has any children 6+
  1. Qualifying benefits
  • Added comment noting CTC/WTC are being phased out

Remove trailing space to pass lint check.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@vahid-ahmadi vahid-ahmadi merged commit e642ca5 into main Jan 13, 2026
2 checks passed
@vahid-ahmadi vahid-ahmadi deleted the add-scottish-child-payment branch January 13, 2026 14:23
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.

3 participants