Skip to content

Derive hours-worked inputs from ASEC reported hours#293

Draft
daphnehanse11 wants to merge 2 commits into
mainfrom
hours-worked-source-stage
Draft

Derive hours-worked inputs from ASEC reported hours#293
daphnehanse11 wants to merge 2 commits into
mainfrom
hours-worked-source-stage

Conversation

@daphnehanse11

Copy link
Copy Markdown
Collaborator

Summary

Closes #242; unblocks the SNAP work-requirements exposure channel tracked in #248.

The published dataset either omits weekly_hours_worked_before_lsr or ships it constant at the engine's 40-hour default, so PolicyEngine-US treats everyone as working 40 hours/week — SNAP's 30-hour general and 20-hour ABAWD tests pass universally and work-requirement reforms score ~zero through the hours channel.

This adds a hours_worked source stage following the #266 (immigration) template: manifest entry + shared runtime handler + frame transform + release gate + builder wiring.

What it does

Direct mappings of measured ASEC variables — identical to the retired enhanced-CPS pipeline, so parity is by construction, nothing imputed:

  • weekly_hours_worked_before_lsrHRSWK (usual weekly hours last year; 0 for non-workers)
  • hours_worked_last_weekA_HRS1
  • weeks_workedWKSWORK clipped to [0, 52]

Ownership handoff: the org_wages manifest entry no longer claims weekly_hours_worked_before_lsr — it never produced it (the retired pipeline sourced weekly hours from ASEC HRSWK directly, not from the ORG fit) — and consumes hours as predictors instead.

Healing behavior: the frame transform is idempotent when the hours surface carries signal, and recomputes from raw columns when weekly hours are constant — the published constant-40 landmine is repaired, not trusted.

Release gate (hours_worked_signal): weighted positive-hours share must land in [0.35, 0.62] (CPS ~50% of all persons worked last year) and mean weekly hours among workers in [30, 45] (BLS ~38.5). Threaded through release gate failures, calibration diagnostics, and both manifests, mirroring the immigration gate. The L0 refit export now requires all three columns nonconstant.

Verification

  • 18 new tests (manifest declaration, derivation semantics incl. sentinel/NaN handling, frame integration incl. the constant-40 healing path, gate bands); full populace-build suite green.
  • End-to-end against the current 572,780-person base: worked share 0.512, mean weekly hours among workers 37.3, gate green — real distributions, matching the retired enhanced-CPS surface (50.4% positive share).

Documented semantics caveat

SNAP's ABAWD standard is a monthly 80-hour test; HRSWK is an annual usual-hours measure. A person who worked half the year at 40 hrs/week carries 40 here. This matches how the retired pipeline fed the engine and is stated in the stage notes — week-by-week volatility is not observable in the CPS and is out of scope. The full ORG labor-market fit (tier 2) is tracked in #291.

Merge-order note

After this merges, the weekly_hours_worked_before_lsr reviewed exclusion in #286's degenerate-input gate becomes stale and should be removed (the gate will then permanently enforce non-degenerate hours). If #286 merges first, that's a one-line follow-up here; if this merges first, I'll drop the exclusion in #286.

🤖 Generated with Claude Code

daphnehanse11 and others added 2 commits July 2, 2026 13:25
The published dataset either omits weekly_hours_worked_before_lsr or
ships it constant at the engine's 40-hour default, so PolicyEngine-US
treats every person as working 40 hours per week and SNAP's 30-hour
general and 20-hour ABAWD work requirements pass universally — reform
exposure through the hours channel is erased (populace #242, #248).

Add a hours_worked source stage that maps three measured CPS ASEC
person variables onto the PolicyEngine input names, identical to the
retired enhanced-CPS pipeline's mappings so parity is by construction:
weekly_hours_worked_before_lsr from HRSWK, hours_worked_last_week from
A_HRS1, and weeks_worked from WKSWORK clipped to [0, 52]. Nothing is
imputed. The org_wages manifest entry no longer claims weekly hours —
it never produced them; the retired pipeline sourced them from ASEC
directly — and consumes them as predictors instead.

The frame transform is idempotent when the surface carries signal and
recomputes from raw columns when weekly hours are constant, healing the
published constant-40 landmine rather than trusting it. A release gate
requires a plausible worked distribution: weighted positive-hours share
within [0.35, 0.62] (CPS ~50%) and mean weekly hours among workers
within [30, 45] (BLS ~38.5). The fiscal refresh builder runs the stage
after immigration inputs and threads the gate result through release
gate failures, calibration diagnostics, and both manifests; the L0
refit export requires all three columns nonconstant.

Verified against the current 572,780-person base: worked share 0.512,
mean weekly hours among workers 37.3, gate green.

Closes #242

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

Carry hours-worked inputs through Populace US outputs

1 participant