Skip to content

Derive hours-worked inputs from ASEC reported hours#293

Open
daphnehanse11 wants to merge 1 commit into
mainfrom
hours-worked-source-stage
Open

Derive hours-worked inputs from ASEC reported hours#293
daphnehanse11 wants to merge 1 commit 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

Add a hours_worked source stage (manifest entry, runtime handler, frame
transform, release gate, builder wiring) following the immigration
template. Direct-maps measured ASEC variables (HRSWK, A_HRS1, WKSWORK),
heals the published constant-40 landmine, and hands weekly-hours
ownership off from org_wages. Closes #242; unblocks #248.
@daphnehanse11 daphnehanse11 force-pushed the hours-worked-source-stage branch from 237a841 to 7c797c4 Compare July 6, 2026 13:40
@daphnehanse11

Copy link
Copy Markdown
Collaborator Author

Rebased onto current main (8b1795a, was 14 behind) and squashed the two commits — the indentation-restore commit was fighting main's reformat of source_stages.json and made every rebase conflict on the whole file.

Beyond conflict resolution, one intended change: dropped the now-stale weekly_hours_worked_before_lsr reviewed exclusion from US_DEGENERATE_INPUT_REVIEWED_EXCLUSIONS — the merge-order follow-up flagged in the PR description (#286 merged first, and this PR makes the column non-degenerate, so leaving the exclusion would itself fail the cannot-rot gate).

Conflict resolutions were all additive interleaves with main's newly merged take-up (#315) and eCPS-parity (#316) wiring: both gate sets now run in sequence in main(), and hours_worked_gate threads through _release_gate_failures/_build_manifests alongside degenerate_input_gate/ecps_parity_gate. Verified the rebased net diff against the original with interdiff — no other semantic drift.

Local verification: full populace-build suite green (uv run --package populace-build --extra us pytest), ruff check + format clean on touched files.

🤖 Generated with Claude Code

@daphnehanse11 daphnehanse11 marked this pull request as ready for review July 6, 2026 13:42
daphnehanse11 added a commit that referenced this pull request Jul 6, 2026
Add a snap_take_up source stage (manifest entry, runtime handler, frame
transform, release gate, builder wiring) following the immigration and
hours templates. Reported SPM_SNAPSUB recipients always take up;
non-reporters fill via seeded stable-identity draws to the cited FNS
0.82 participation rate. Heals the published all-True landmine.
Closes #243; second half of #248 alongside #293.
@daphnehanse11 daphnehanse11 requested a review from MaxGhenis July 6, 2026 15:55
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