Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
31dbe09
Track B insured status: quarters of coverage and fully, currently and…
MaxGhenis Oct 23, 2026
3c13653
Document the Track B insured-status interface, scope and source findings
MaxGhenis Oct 23, 2026
b85c1e3
Recount test tiers after the insured-status tests
MaxGhenis Oct 23, 2026
35a2bd7
Check every row of the RS 00301.800 exhibit; mark the two source-conf…
MaxGhenis Oct 23, 2026
f865bb5
Recount test tiers after the RS 00301.800 exhibit tests
MaxGhenis Oct 23, 2026
1e20546
Merge master 9967dfd6 into the insured-status branch
MaxGhenis Oct 25, 2026
74da853
Separate medical onset from an established period of disability
MaxGhenis Oct 25, 2026
3fd1d22
Recount test tiers after insured-status fix round 2
MaxGhenis Oct 25, 2026
d21ece2
Merge master 1ec57a5d into the insured-status branch
MaxGhenis Oct 25, 2026
7a27f50
Bound insured status by period ends, FRA and pre-1951 credits (#562 r…
MaxGhenis Oct 31, 2026
f7edf0d
Merge master a747bafa into the insured-status branch
MaxGhenis Oct 31, 2026
06f6e57
Keep insured-status proofs sound on every statutory route (#562 round 4)
MaxGhenis Oct 31, 2026
2ae76db
Classify impossible insured declarations exactly (#562 round 5)
MaxGhenis Oct 31, 2026
7a99eb0
Apply insured declaration proof order and historical route limits (#5…
MaxGhenis Oct 31, 2026
15a8575
Preserve the pre-1956 alternate when 20/40 first appears at FRA (#562…
MaxGhenis Oct 31, 2026
035f7ba
Keep pre-1956 alternate refusals past the period's covered quarters (…
MaxGhenis Oct 31, 2026
7c5ed6b
Refuse by name when UNKNOWN establishment hides the pre-1956 alternat…
MaxGhenis Oct 31, 2026
281cfb4
Respect recovery quarters and prove insured establishment first (#562…
MaxGhenis Oct 31, 2026
ad653e7
Refuse foreign credits by name and prove shortfalls past flags (#562 …
MaxGhenis Oct 31, 2026
a0162b3
Stop flags from hiding an empty recent window (#562 round 12)
MaxGhenis Oct 31, 2026
aee9d4a
Require one feasible insured-period completion across credits and sta…
MaxGhenis Oct 31, 2026
5ad3172
State why a flag can't delay the first insured quarter (#562 round 13)
MaxGhenis Oct 31, 2026
36c5c20
Keep lawful insured rows and refuse starts a filling can't settle (#5…
MaxGhenis Nov 1, 2026
c28bb1d
Refuse a decisive aggregate and tie each status to its own start (#562)
MaxGhenis Nov 1, 2026
e9f4140
Track B insured status: hold exact diagnostics to the reported start
MaxGhenis Nov 1, 2026
ab1692b
Track B insured status: refuse where the long period's readings differ
MaxGhenis Nov 1, 2026
88a0e7b
Merge master 18082859 into the insured status branch
MaxGhenis Nov 1, 2026
129d44b
Hold UNKNOWN-prior results to a filling with no period in force
MaxGhenis Nov 2, 2026
bbed6be
Hold insured results to every placement a period's start allows
MaxGhenis Nov 2, 2026
168bb3a
Merge master 89d064b5 into the insured status branch
MaxGhenis Nov 2, 2026
f928751
Keep years after a period's start out of its filling (#562)
MaxGhenis Nov 2, 2026
7d8cecb
Kill round 19's four surviving mutations (#562)
MaxGhenis Nov 2, 2026
38a573a
Record which round 20 tests fail against 168bb3a8 (#562)
MaxGhenis Nov 2, 2026
6eb708a
Record round 20's 41 mutation kills from the cloud run (#562)
MaxGhenis Nov 2, 2026
e250780
Hold a listed prior on each basis its record allows (#562)
MaxGhenis Nov 2, 2026
e2b9390
Test each basis a listed prior can rest on (#562)
MaxGhenis Nov 2, 2026
8ea25a8
Record round 21's wide search and first mutation pass (#562)
MaxGhenis Nov 2, 2026
98fb6c7
Merge master into #562 and take its tier counts plus this branch's tests
MaxGhenis Nov 2, 2026
d1af036
Pin the frozen exit on a prior's basis (#562)
MaxGhenis Nov 2, 2026
fbfb08b
Keep the moving-start test on its own refusal (#562)
MaxGhenis Nov 2, 2026
f37f7f7
Cap the subsequent generator and keep split seeds on their refusal (#…
MaxGhenis Nov 3, 2026
aa53bfd
Record round 21's 62 cloud mutation kills (#562)
MaxGhenis Nov 3, 2026
7b5b126
Keep 404.130(c) to flagged listed priors (#562 round 22)
MaxGhenis Nov 3, 2026
9d2db19
Record round 22's 70 cloud mutation kills (#562)
MaxGhenis Nov 3, 2026
8522452
Flag 404.130(c)-only current periods; share quarters (#562 round 23)
MaxGhenis Nov 3, 2026
c25a32c
Limit round 23's note check to the insured modules (#562 round 23)
MaxGhenis Nov 3, 2026
2f5ee7a
Merge master 97003ac2 into #562 and take its tier counts plus this br…
MaxGhenis Nov 3, 2026
aeef5c7
Align #562's issue draft with round 23's contracts (#562 round 23)
MaxGhenis Nov 3, 2026
fc689ec
Raise on a prior that ends past FRA and pin three branches (#562 roun…
MaxGhenis Nov 3, 2026
f32d881
Scan down from the query for the stress oracle's date last insured (#…
MaxGhenis Nov 3, 2026
0c39ea4
Refuse by name where a completion leaves a listed prior unsupported (…
MaxGhenis Nov 3, 2026
d30e442
Record round 23's 107 cloud mutation kills (#562)
MaxGhenis Nov 4, 2026
c81036b
Record round 24's 135 cloud mutation kills (#562)
MaxGhenis Nov 6, 2026
ecbee31
Merge master 4f99edfb into #562 and take its tier counts plus this br…
MaxGhenis Nov 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,966 changes: 3,966 additions & 0 deletions docs/design/track_b_insured_status.md

Large diffs are not rendered by default.

16,690 changes: 16,690 additions & 0 deletions docs/design/track_b_insured_status_mutations.json

Large diffs are not rendered by default.

185 changes: 185 additions & 0 deletions docs/design/track_b_insured_status_rulespec_issue.md

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions scripts/first_estimates_birth_evidence.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,9 @@
# nothing in the historical projection imports it.
Path("src/populace_dynamics/track_b/gross_benefits.py"),
Path("src/populace_dynamics/track_b/_provenance.py"),
# Insured status and QC accounting are opt-in statutory references.
Path("src/populace_dynamics/track_b/insured_status.py"),
Path("src/populace_dynamics/track_b/_insured_status_qc.py"),
# R2 statutory payment mechanics also stay outside birth evidence.
Path("src/populace_dynamics/track_b/ret_grace.py"),
Path("src/populace_dynamics/track_b/ret_ledger.py"),
Expand Down
304 changes: 304 additions & 0 deletions src/populace_dynamics/track_b/_insured_status_qc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,304 @@
"""Private quarter-of-coverage arithmetic for the statutory reference.

Verification class: statutory conformance on invented and published cases.
These helpers admit no population, behavioral, or forecast claim. The
public insured-status boundary validates and owns its inputs once; helpers
consume those normalized primitives and do not establish public provenance.

Authority: Act 213, 20 CFR 404.140-.146, and POMS RS 00301.200-.350.
Integer quarter identifiers are ``4 * year + quarter - 1``. Flexible
credits are assigned separately for each statutory window, as RS 00301.230
requires, rather than silently fixing all annual credits to year-end.
"""

from datetime import date
from fractions import Fraction
from itertools import combinations
from types import MappingProxyType

__all__ = []

# POMS RS 00301.250 (1978-2026) and captured OACT QC history (2027).
QC_AMOUNTS = MappingProxyType(
dict(
zip(
range(1978, 2028),
(
250,
260,
290,
310,
340,
370,
390,
410,
440,
460,
470,
500,
520,
540,
570,
590,
620,
630,
640,
670,
700,
740,
780,
830,
870,
890,
900,
920,
970,
1000,
1050,
1090,
1120,
1120,
1130,
1160,
1200,
1220,
1260,
1300,
1320,
1360,
1410,
1470,
1510,
1640,
1730,
1810,
1890,
1970,
),
strict=True,
)
)
)


class _UnsupportedQC(Exception):
"""Private indication translated to the public layer's own refusal."""

def __init__(self, reason: str):
self.reason = reason
super().__init__(reason)


def _quarter_id(year: int, quarter: int) -> int:
return year * 4 + quarter - 1


def _year_quarters(year: int) -> tuple[int, ...]:
return tuple(range(year * 4, year * 4 + 4))


def _date_quarter(value: date) -> int:
return _quarter_id(value.year, (value.month - 1) // 3 + 1)


def _qc_amount_from_nawi(nawi: Fraction, prior_amount: int) -> int:
"""Act 213(d)(2): exact index, nearest $10 with $5 ties upward."""
indexed = Fraction(250) * nawi / Fraction("9226.48")
rounded = ((indexed + 5) // 10) * 10
return max(prior_amount, rounded)


def _post1977_qc_count(
year: int, wages: Fraction, self_employment: Fraction
) -> int:
"""Act 213(a)(2)(A)(ii); earnings are already calendar-year credited."""
return min(4, (wages + self_employment) // QC_AMOUNTS[year])


def _annual_wage_limit(year: int) -> int:
"""POMS RS 00301.345B.1's pre-1978 maximum taxable wage chart."""
for last, amount in (
(1950, 3000),
(1954, 3600),
(1958, 4200),
(1965, 4800),
(1967, 6600),
(1971, 7800),
(1972, 9000),
(1973, 10800),
(1974, 13200),
(1975, 14100),
(1976, 15300),
(1977, 16500),
):
if year <= last:
return amount
raise _UnsupportedQC("pre1978_wage_limit_year_unsupported")


def _pre1978_qc_credits(
year: int,
quarterly_wages: tuple[Fraction, ...],
self_employment: Fraction,
agricultural_wages: Fraction,
) -> tuple[tuple[int, ...], int]:
"""Fixed wage/SE quarters and additional flexible agricultural QCs.

Quarterly wages are covered nonagricultural wages (also covered
agricultural wages in 1951-54). Calendar-year SEI >= $400 covers four
quarters. Separate annual agricultural wages are supported for 1955-77;
their floor-$100 QCs occupy quarters not otherwise credited. Maximum
taxable wages, or combined wages and SEI, give four gift QCs. Exclusions
are applied afterwards and cannot turn an excluded fixed QC flexible.
"""
if year == 1937 and (
any(quarterly_wages) or self_employment or agricultural_wages
):
# Act 213(b) half-year allocation has an age-65 exception. The
# annual interface can consume explicitly credited quarter flags,
# but cannot infer this historical allocation from wages alone.
raise _UnsupportedQC("1937_earnings_allocation_unsupported")
if year < 1955 and agricultural_wages:
raise _UnsupportedQC("pre1955_agricultural_allocation_unsupported")
if year < 1951 and self_employment:
raise _UnsupportedQC("pre1951_self_employment_unsupported")
quarters = _year_quarters(year)
wages = sum(quarterly_wages) + agricultural_wages
if (
wages + self_employment >= _annual_wage_limit(year)
or self_employment >= 400
):
return quarters, 0
fixed = tuple(
quarter
for quarter, wages in zip(quarters, quarterly_wages, strict=True)
if wages >= 50
)
flexible = min(4 - len(fixed), agricultural_wages // 100)
return fixed, flexible


def _allowed_quarters(
year: int,
*,
through_quarter: int,
death_quarter: int | None = None,
disability_periods: tuple[tuple[int, int], ...] = (),
) -> tuple[int, ...]:
"""404.146: begun, not after death, outside disability interiors.

Disability endpoints themselves remain eligible. A caller that elects
not to take a period into consideration under 404.146(d)(2) omits it.
A coverage exit bars only later years, which the history leaves out, so
it bars no quarter of its own year here.
"""
last = min(
value
for value in (through_quarter, death_quarter)
if value is not None
)
# Source conflict us:oasdi-insured-disability-period-endpoint-quarters
return tuple(
quarter
for quarter in _year_quarters(year)
if quarter <= last
and not any(start < quarter < end for start, end in disability_periods)
)


def _assignment_candidates(
fixed: tuple[int, ...],
flexible_count: int,
allowed: tuple[int, ...],
) -> tuple[tuple[int, ...], ...]:
"""All feasible assignments, after exclusions and without duplicates."""
permitted = frozenset(allowed)
retained = frozenset(fixed) & permitted
vacant = tuple(sorted(permitted - retained))
count = min(flexible_count, len(vacant))
return tuple(
tuple(sorted((*retained, *choice)))
for choice in combinations(vacant, count)
)


def _assign_qcs(
fixed: tuple[int, ...],
flexible_count: int,
allowed: tuple[int, ...],
window: tuple[int, ...] = (),
) -> tuple[int, ...]:
"""Maximize this window; otherwise keep ordinary agricultural order.

Act 213(a)(2)(B)(iv) assigns pre-1978 agricultural credits to the last
uncredited quarters, with reassignment to meet the stated tests. The
post-1977 rule has no ordinary latest-quarter preference, so its ties
use the earliest assignment deterministically.
"""
target = frozenset(window)
latest = bool(allowed) and allowed[0] // 4 < 1978
return min(
_assignment_candidates(fixed, flexible_count, allowed),
key=lambda candidate: (
-len(target.intersection(candidate)),
tuple(-q for q in reversed(candidate)) if latest else candidate,
),
)


def _allocate_sei_pre1978(
start: date, end: date, self_employment: Fraction
) -> tuple[tuple[int, Fraction], ...]:
"""404.142 / RS 00301.340: equal shares in last at most four quarters.

This is allocation arithmetic for already declared covered SEI, not a
determination of taxable income. The public annual interface requires
calendar-year declarations and does not infer a fiscal taxable year.
"""
last = _date_quarter(end)
first = max(_date_quarter(start), last - 3)
amount = self_employment / (last - first + 1)
return tuple((quarter, amount) for quarter in range(first, last + 1))


def _allocate_sei_post1977(
start: date, end: date, self_employment: Fraction
) -> tuple[tuple[int, Fraction], ...]:
"""404.144: full months, including the month in which the year ends."""
if start.year == end.year:
return ((start.year, self_employment),)
months_first = 12 - start.month + (start.day == 1)
months_last = end.month
total = months_first + months_last
return (
(start.year, self_employment * months_first / total),
(end.year, self_employment * months_last / total),
)


def _pre1978_taxable_qcs(
start: date,
end: date,
self_employment: Fraction,
wages: Fraction = Fraction(0),
) -> tuple[int, ...]:
"""404.141(d)(2), .142: fiscal-year SE/gift-credit worked arithmetic.

At the maximum, every partly included quarter is credited, including a
fifth calendar quarter; otherwise SEI is allocated to at most four.
Wage QCs themselves are supplied separately by their actual quarters.
"""
# RS 00301.345 B.2 sets $7,800 for a taxable year ending before 1972 and
# $9,000 for one beginning in 1972, so a fiscal year from 1971 into 1972
# fits neither, and this helper takes 1972's $9,000 there. Only tests
# call it: public calls refuse fiscal taxable years.
limit_year = start.year if start.year >= 1972 else end.year
if self_employment + wages >= _annual_wage_limit(limit_year):
return tuple(range(_date_quarter(start), _date_quarter(end) + 1))
return tuple(
quarter
for quarter, amount in _allocate_sei_pre1978(
start, end, self_employment
)
if amount >= 100
)
7 changes: 5 additions & 2 deletions src/populace_dynamics/track_b/_provenance.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
caller-declared attribution of an earlier genuine result or refusal.
The contract excludes code accessing private names, monkeypatching modules,
or mutating an input from another thread during a call.
One private capability seals every declared G, R2 and R3 result, refusal,
One private capability seals every declared G, R2, R3 and insured-status
result, refusal,
outcome and denominator. Public construction and copying are input errors;
serialization is rejected. Persist declared inputs and recompute instead.
"""
Expand Down Expand Up @@ -984,7 +985,9 @@ def _external_declarations():

def _schema(annotation, module, *, name="", owner=None):
origin, args = get_origin(annotation), get_args(annotation)
exact = module.endswith((".arf", ".recompute", ".ret_ledger"))
exact = module.endswith(
(".arf", ".recompute", ".ret_ledger", ".insured_status")
)
if annotation is object or annotation is Fraction:
return lambda value, label: _numeric_schema(
value,
Expand Down
Loading
Loading