Skip to content

Calibrate federal student loan balances to Federal Student Aid's portfolio (#1074) - #1357

Draft
DTrim99 wants to merge 2 commits into
scf-2025-education-debtfrom
fsa-student-loan-calibration-targets
Draft

DTrim99 wants to merge 2 commits into
scf-2025-education-debtfrom
fsa-student-loan-calibration-targets

Conversation

@DTrim99

@DTrim99 DTrim99 commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Rebased on #1246's review round (2026-10-23)

  • History. Squashed onto Impute federal student loan balances from the 2025 SCF (#1073) #1246's head 3eb32320b as two commits: the feature (4b6bfce9a) and the refreshed receipt and docs (3b2886597).
  • What Impute federal student loan balances from the 2025 SCF (#1073) #1246 changed under it. A loan for a child's education now goes to a co-resident adult child when one could have borrowed it, and to the reference person's Parent PLUS otherwise; the X8000 codes of loans 4 and 6 are swapped back; Parent PLUS is drawn first. This PR moves no spec identity value at the new base: the bundle and coverage --checks pass on Impute federal student loan balances from the 2025 SCF (#1073) #1246's re-derived values (42,368/42,368 configuration fields).
  • One fix the rebase run surfaced. The US spine-blindness test requires every us_runtime module to be classified. fsa_student_loan_targets.py is now a reviewed non-registry module, and its band sort uses a named key instead of a lambda subscript the scanner cannot resolve. Nothing else changes.
  • chronicle#347 merged on 2026-10-22. The runner now builds the FSA rows at its merge commit acbedb5 on PolicyEngine/chronicle main, not the fork head. The export is byte-identical (sha256 58774e11…), so the candidate feed (d8e3d0fd…, 40,639 rows) and the test fixture are unchanged.
  • Refit at 4b6bfce9a (Modal run 20261023T191527Z-4b6bfce9a; a run at the pre-fix commit gave the same numbers):
Before (c94516ee1) After
FSA targets within 5%: B / B + L2 92% / 76% 95% / 78%
FSA mean error: B / B + L2 1.5% / 4.7% 0.7% / 3.6%
Non-FSA capped error: A / B / A + L2 / B + L2 3.30% / 3.66% / 3.85% / 3.83% 3.30% / 3.62% / 3.85% / 3.85%
Households with federal education debt: release / B / B + L2 (SCF 18.2%) 16.4% / 33.0% / 31.5% 16.4% / 33.7% / 32.3%
Parent PLUS share: release / B / B + L2 (FSA 6.8%) 16.8% / 6.8% / 7.6% 10.7% / 6.8% / 6.9%
Borrowers: release / B / B + L2 (in-scope control 44.31M) 21.22M / 43.78M / 41.95M 21.18M / 44.26M / 42.67M
Borrowers under 25: release / B / B + L2 (FSA 6.13M) 1.06M / 3.72M / 3.43M 2.58M / 6.13M / 5.52M
Borrower records under 25 (unweighted) 561 1,246

What moved, in plain words:

  • The routing moves balances from parents to their adult children in the same household. The host gains young borrowers and its Parent PLUS share falls, but it holds the same number of borrowers (21.2M) and borrower households (16.4%) at the published weights.
  • So the doubling of borrower households does not shrink: it is 33.7% after calibration, against 33.0% before. The missing borrowers are ones the SCF never sees (independent adults living on their own or with others), and the routing does not add them.
  • With young borrowers in the support, B meets every age band (under 25 was -39%), and B + L2 is 10% short under 25 (was -44%). B + L2 now falls short at 50 to 61 (-9%) and 62 and older (-13%), the ages the moved loans left.
  • Parent PLUS needs less pull (10.7% to 6.8%, not 16.8% to 6.8%), so B + L2 now meets it too. The rest of the surface is unchanged within 0.05 points.

Part of #1074 (the student debt pilot, PolicyEngine/us-student-debt#1). Draft, stacked on #1246 (base scf-2025-education-debt). PolicyEngine/chronicle#347 merged on 2026-10-22; this waits on the next Chronicle release and the US feed re-pin; nothing here pins a fork. No release is built, published or certified.

What binds

A new runtime module, us_runtime/fsa_student_loan_targets.py, turns Federal Student Aid's portfolio facts into 132 targets on the two person columns #1246 imputes. A person's federal balance is federal_student_loan_balance + federal_parent_plus_loan_balance, and a borrower is a person whose federal balance is positive. FSA's borrower tables count a Parent PLUS borrower as the parent, which is where the stage puts those balances.

Family FSA table Model measure Targets
By state Portfolio by Location balance and borrowers in each of the 50 states and DC 102
By age Portfolio by Age balance and borrowers in 5 bands of the person's age 10
By debt size Portfolio by Debt Size balance and borrowers in 9 bands of the person's federal balance (lower edge inclusive) 18
By loan type Portfolio by Loan Type Parent PLUS balance; balance of every other loan type 2

Diagnostic only, each with its reason in the receipt:

  • Repayment plans (all six sheets). The SCF records only an income-driven yes/no, the build has no plan column, and FSA counts plan recipients loan by loan. The receipt keeps FSA's Direct Loan dollars by plan at FY2026 Q3 for context: SAVE 28.7%, IBR 18.2%.
  • Portfolio summary. Recipient totals by loan program, which the balances do not record.
  • Loan-type recipient counts. These are loan-level, so a person with two loan types counts twice.
  • Stafford (Combined). A subtotal of rows already counted.

Reference period

Every family binds at FY2026 Q2 (31 March 2026), the latest quarter all four bound tables publish:

  • the location workbook publishes only that date;
  • age and debt size publish Q1 and Q2;
  • loan type publishes Q1 to Q3.

Holding every table to one date makes every family describe one portfolio on one day. The Q1 and Q3 rows stay in the receipt as other_quarter.

The build period is the H5's, 2024. The FSA levels are stocks, applied unaged:

  • Target aging gains a declared alignment, factor policy 5: period_alignment = stock_at_source_reference_date. It is neither aged nor a period-contract violation, and only targets that declare it are affected.
  • The reasons: no income-growth series describes a loan stock, the pilot analysis reads these columns in its 2026 reference year, and the SCF draws are already 2025 survey-date dollars.
  • Specs sit at period 2024 with source_period = 2026-03.

Not Reported, Puerto Rico and Other: one control total

The location table has no total row. The state targets are scaled to the national in-scope total:

  • Not Reported is allocated to every reported location in proportion to that location's reported value. It holds $52.2B and 1.95M borrowers in FFEL and Perkins loans whose commercial lenders need not report an address.
  • Puerto Rico and Other leave the total, with their share of Not Reported. The host is the 50 states and DC that the CPS samples, and "Other" is an address outside the listed states and territories.
Balance Borrowers
50 states and DC, as published $1,627.4B 42.38M
Puerto Rico and Other $16.7B 433,400
Not Reported $52.2B 1.95M
Allocation factor 1.0317 1.0456
In-scope control $1,679.1B 44.31M

FSA's tables disagree slightly: the borrower tables total $1,696.3B and the recipient-based loan-type table $1,723.8B. So the age bands, the debt-size bands and the two loan-type rows bind as their table's share of the in-scope control, control x row / partition, the #940 pattern. All four families then sum to the same total. The age table's own "not reported" row is in no partition, so the reported bands absorb it.

Units, all from Chronicle's declared scales:

  • dollars are published in $ billions;
  • location counts are in thousands, the other tables' counts in millions;
  • location, age and debt size count borrowers, while the summary and loan type count recipients. Recipient counts never bind.

How it is wired

  • Compile. compile_us_fiscal_target_registry(..., student_loan_targets=True) binds the facts; ed_fsa facts never bind through dynamic dispatch. The release tool sets the flag only with --scf-education-debt, the only builds that carry the columns. Default compiles and releases are unchanged.
  • Materializer. fsa_stored_student_loan_balances reads the stage's stored person columns (fsa_student_loan_household_values), because the pinned policyengine-us does not define them yet.
  • Restated-filter guard. It accepts the FSA age bands, through the existing age rule with the FSA materializer added, and the debt-size bands, through a new student_loan_balance_band rule. Both are accepted only on that materializer.
  • Parity contract. The opt-in family stays off the one-surface target-parity contract (_without_opt_in_student_loan_targets), so a default build and a --scf-education-debt build cannot fail each other.
  • Receipt. us_source_coverage.json gains fsa_student_loan_target_receipt, with every FSA fact's disposition and the controls.
  • Labels. ED · Federal Student Aid and four categories (federal_student_loan_balance, federal_student_loan_borrowers, parent_plus_loan_balance, loan_balance_excluding_parent_plus) join the calibration label registries.
  • Docs. docs/us-fact-to-target.md has the rules, the measured limits and the re-pin steps.
  • Spec identity: unchanged. generate_us_bundle_from_constants.py --check and spec_engine_coverage.py --check pass at Impute federal student loan balances from the 2025 SCF (#1073) #1246's head: 42,368/42,368 configuration fields.

Fit

experiments/1074-fsa-calibration-receipts.md holds the receipt, with the JSON in experiments/receipts/. The setup:

  • Host: the published default populace-us-2024-spm-20260915, 57,240 households, with the Impute federal student loan balances from the 2025 SCF (#1073) #1246 stage run at seed 0.
  • Targets: the release's own compile, Medicaid substitutions, national_state surface, materializer (one engine batch), loss weights and cap. That is 6,102 non-FSA targets plus the 132 FSA targets, with 0 dropped.
  • Solve: calibrate with the release's 1,500 epochs, learning rate, 5x weight cap and conserved mass.
  • Arms:
    • release: the published weights;
    • A: a dense refit without the FSA family;
    • B: the same refit with it;
    • +L2: A and B again with an L2 anchor to the published weights (l2_lambda = 0.03).

It is a re-solve of the published support, not a release build.

FSA targets. Mean absolute relative error, with the share of targets within 5% in parentheses:

FSA family Targets Release B, with FSA B + L2
State balance 51 47.0% (0%) 0.2% (98%) 1.0% (96%)
State borrowers 51 49.1% (0%) 1.1% (92%) 6.2% (63%)
Age-band balance 5 47.5% (0%) 0.1% (100%) 0.3% (100%)
Age-band borrowers 5 54.5% (0%) 0.2% (100%) 6.5% (40%)
Debt-size balance 9 50.6% (0%) 0.4% (100%) 3.6% (78%)
Debt-size borrowers 9 49.0% (0%) 2.7% (89%) 5.3% (67%)
Parent PLUS balance 1 20.1% 0.0% 0.0%
Balance excluding Parent PLUS 1 51.0% 0.1% 0.5%
All 132 48.3% (0%) 0.7% (95%) 3.6% (78%)

B's largest misses, mostly on borrower counts:

  • $200K+ borrowers: +14%;
  • Delaware -13%, Maine -12%, Connecticut -10%, Ohio -5% (borrowers);
  • Louisiana's balance: -7%.

The rest of the surface. Mean absolute relative error over the 6,102 non-FSA targets, each miss capped at 100% as the loss caps it:

Release A B A + L2 B + L2
All non-FSA targets 5.3% 3.3% 3.6% 3.8% 3.9%
Share within 5% 85.2% 92.0% 90.9% 90.4% 90.1%

Adding the family costs 0.31 points without the anchor and nothing measurable with it (+0.00). The families it moves most without the anchor are TANF (+2.6 points), SSI federal recipients (+1.8), JCT tax expenditures (+1.7), BEA proprietors' income (+1.4) and SOI capital gains (+1.1). With the anchor no family worsens by more than 0.5 points, and BEA proprietors' income, SSI federal recipients and SOI capital gains improve by 1.2 to 1.6.

How calibration closes the SCF gap, and what it does to the borrower share and the Parent PLUS mix

The SCF-imputed host holds about half of FSA's portfolio at the published weights: $0.86T and 21.2M borrowers, against $1.68T and 44.3M. That is about what the SCF records ($1.0T, 27.2M) and what SCF totals usually show. The stage gives balances to reference persons, spouses and co-resident adult children, and Parent PLUS to the reference person. So the host is missing borrowers rather than balances: its mean balance per borrower, $40.5K, is already close to FSA's $37.9K.

Calibration does not change any balance. It reweights:

FSA Release B, with FSA B + L2
Federal balance $1,679.1B $858.4B $1,677.3B $1,671.2B
Borrowers 44.31M 21.18M 44.26M 42.67M
Mean balance per borrower $37,894 $40,535 $37,897 $39,162
Households with federal education debt 16.4% 33.7% 32.3%
Borrower households' mean weight ratio 2.05 1.97
Other households' mean weight ratio 0.79 0.81
Parent PLUS share of the balance 6.8% 10.7% 6.8% 6.9%
Effective sample size (release 9,174) 3,045 (A: 4,149) 4,424 (A+L2: 6,435)
  • Borrower share. Matching 44M borrowers takes about twice the borrower households the SCF implies: 33.7% of household weight against 18.2% of SCF families. Routing child loans to adult children did not shrink this (33.0% before), because it moves balances within households rather than adding borrower households. Household-level tables (share of households with debt, payments per borrowing household) inherit that.
  • Borrowers under 25 now fit. The host has 1,246 borrower records under 25 among 59,179 people that age (561 before Impute federal student loan balances from the 2025 SCF (#1073) #1246's routing). B meets FSA's 6.13M; B + L2 is 10% short (it was 39-44% short). B + L2 is now short at 50 to 61 (-9%) and 62 and older (-13%). The remaining gap (adults outside the SCF's primary economic unit) belongs in the imputation, not in the weights.
  • Parent PLUS mix. Parent PLUS falls from 10.7% of the balance (16.8% before Impute federal student loan balances from the 2025 SCF (#1073) #1246's routing) to 6.8%. FSA's Parent PLUS row counts only unconsolidated Parent PLUS loans: a consolidated one is filed under "Consolidation". The SCF's loans for a child's education include consolidated ones, so the target is a lower bound on the concept it binds.
  • Weight concentration. A dense refit of an L0-selected support without an anchor concentrates weight, A as well as B. The L2 arm shows the anchor holding the FSA fit near target (3.6% mean error) at no cost to the rest.

Racial composition of borrowers, before and after calibration

Borrower households by the reference person's race (SCF racecl4; the SCF column pools the five implicates):

SCF 2025 Release B, with FSA B + L2 B - release (pp)
White non-Hispanic 57.6% 60.8% 57.6% 59.9% -3.2
Black non-Hispanic 18.6% 17.4% 18.8% 19.2% +1.4
Hispanic 9.2% 12.9% 14.4% 12.4% +1.5
Other or multiple 14.7% 9.0% 9.2% 8.5% +0.2

Borrowers as people, by their own race: White non-Hispanic 60.2% → 57.2%, Black non-Hispanic 16.6% → 18.4%, Hispanic 13.5% → 14.7%, other 9.6% → 9.7%.

So calibration moves the composition by up to 3 points:

  • The White and Black shares move toward the SCF.
  • The Hispanic share moves away from it without the anchor.
  • "Other or multiple" stays about 5.5 points under the SCF before and after. That gap is in the host's population, not the calibration: 8.0% of host households are in the group against 13.7% of SCF families, while the share of them with debt is close (18.4% against 19.5%).

The levels move much more than the composition. The share of households with debt roughly doubles in every group, as the borrower share does: Black non-Hispanic households go from 24.9% to 48.6% (SCF 24.3%) and White non-Hispanic from 15.3% to 30.2% (SCF 17.2%). Race tables of levels inherit the borrower-share issue above. Race tables of composition are less affected.

These tables go to PolicyEngine/us-student-debt#4 before any race breakdown.

Targets that do not compile or bind

  • Materialization dropped none of the 132. A build without the stage's columns does not compile them at all, because the flag is off.
  • Facts that do not bind, from the 461-row FSA bundle:
Disposition Facts Reason
diagnostic 251 repayment plans (227) and portfolio summary (24), reasons above
other_quarter 58 Q1 or Q3 of the age, debt-size and loan-type tables
excluded 8 loan-type recipient counts (6); Stafford (Combined) (2)
allocated 4 Not Reported, by location (2) and by age (2)
out_of_scope 4 Puerto Rico and Other (2 each)

For review

  1. Parent PLUS (above). Keep it binding as Calibrate US federal student loan balances to Federal Student Aid portfolio targets and show them on the dashboard #1074 asks, make it diagnostic, or bind only the total?
  2. Borrower counts at full weight double borrower households. Options:
  3. An L2 anchor for the release that carries these targets. The receipt suggests one.

Dashboard

The dashboard (calibration-diagnostics) builds its families from a release's calibration_diagnostics.json hierarchy, with no registry to edit there. The rows carry:

  • provider ed_fsa, labelled "ED · Federal Student Aid";
  • categories ed_fsa.federal_student_loan_balance, …borrowers, …parent_plus_loan_balance and …loan_balance_excluding_parent_plus;
  • the state as geography and the age or debt band as a dimension.

So the family appears with its release fit as soon as a published release carries it, which is Max's call. Arm B's schema-8 calibration_diagnostics.json is on the Modal volume microcosm-1074-cache (results/20261023T191527Z-4b6bfce9a/diagnostics/, sha256 380c3221…). It could be staged to populace-us-staging for the staging page; I have not uploaded it.

What waits on chronicle#347

  • Release. Belgian selection: country-specific L0 from the full base (dense-informed init) once the BE target surface productionizes #347 merged on 2026-10-22 (merge commit acbedb5). After the next Chronicle release the US pin moves to a commit with the six packages/ed_fsa packages:
    • add their (record set, period) pairs to us/chronicle_feed_scope.json, build year 2026;
    • rebuild with tools/build_us_chronicle_feed.py and re-pin us/chronicle_feed.json;
    • regenerate the two parity resources with the FSA families declared as reviewed exclusions that bind only with --scf-education-debt.
  • Schema versions. The FSA rows are chronicle.consumer_fact.v4; the current pin is v3. The pin's consumer_fact_schema_versions would list both, unless the whole feed is re-cut at the newer commit.
  • Fixture. tests/fixtures/us_fsa_portfolio_chronicle_347_facts.jsonl.gz holds 244 of the draft's rows. They are byte-identical to the Linux export at Belgian selection: country-specific L0 from the full base (dense-informed init) once the BE target surface productionizes #347's merge commit acbedb5 (and at its head 81e3f98, which reads its raw files from R2). Refresh it if a later Chronicle commit restates the facts.
  • The candidate feed lives only in the container. The experiments build the pinned feed at 51aa40f (reproduced byte for byte, sha256 965a29ac…), add the FSA rows built at acbedb5, and use the spliced file (sha256 d8e3d0fd…, 40,639 rows) directly. No repository file names it.

Modal runs (experiments/modal_1074.py, app microcosm-1074-experiments, volume microcosm-1074-cache)

Run Commit What
20261023T191527Z-4b6bfce9a 4b6bfce The receipt: fit with A, B, A+L2 and B+L2 on #1246's head (13 min); the merged #347 export matched
20261023T200336Z-3b2886597 3b28865 Final head: tests, ruff, test-plan verify, spine-blindness and FSA suites, all passed
20261023T191540Z-4b6bfce9a 4b6bfce tests,spec passed; its extra probe stalled on the environment-only tests and was cancelled
20261023T181926Z-2468fba75 2468fba (pre-fix) The same fit, identical numbers
20261023T182913Z-2468fba75 2468fba (pre-fix) Lint, test-plan verify, bundle and coverage --check, and the engine-free shared and US suites; found the spine-blindness classification fixed in 4b6bfce
20261023T182014Z-2468fba75 2468fba (pre-fix) The CI engine-us job (tools.run_engine_test_categories): 448 passed, 6 skipped
20261023T181958Z-2468fba75 2468fba (pre-fix) tests,contract: passed
20261022T194252Z-c94516ee1 c94516e The previous receipt: fit with A, B, A+L2 and B+L2 (13.5 min; reform passes from the volume cache; 88 GB peak)
20261022T184056Z-fd7b279ce fd7b279 First full fit, A and B (55 min); same A and B numbers
20261022T164311Z-399e6fffe 399e6ff tests,contract,spec, all passed
20261022T170743Z-0ed9d7eef 0ed9d7e Fit preempted mid-materialization, cancelled; the runner is now non-preemptible
20261022T162443Z-9772bd15b, 20261022T162425Z-9772bd15b, 20261022T163137Z-4af1cab30 Earlier failures, each fixed: a formula-owned host column (has_marketplace_health_coverage), and the two test pins below

The runs before 2026-10-23 are on the pre-rebase history (#1246's older head), which the squash replaced. The receipt commit after 4b6bfce changes only the receipt, docs, the changelog fragment and a test docstring.

Tests

  • New: test_us_fsa_student_loan_targets.py (25 tests). They pin:
    • the reference quarter, the controls and each family's values;
    • every fact's disposition;
    • refusals of partial feeds and gapped bands;
    • the materializer on a synthetic frame;
    • the release guard, both accepting and refusing;
    • the parity filter.
  • Updated: the label tests pin ed_fsa. The compile-signature pin admits student_loan_targets, the restated-rule pin admits the balance band, and the US spine-blindness inventory classifies fsa_student_loan_targets.py.
  • Modal, after the rebase:
    • final head 3b2886597 (run 20261023T200336Z-3b2886597): the tests step (786 passed, 7 skipped, plus 51 builder tests), ruff check ., ci_test_plan.py verify, and the spine-blindness and FSA suites (523 passed);
    • 4b6bfce9a (run 20261023T191540Z-4b6bfce9a): generator and coverage --check (42,368/42,368 configuration fields; 41/41 inventory checks), plus 252 spec-engine tests;
    • 2468fba75, before the spine-blindness fix: tests,contract passed (engine-contract 37); the CI engine-us job passed (448, 6 skipped); the engine-free shared and US suites (about 14,300 tests) failed only the three spine-blindness checks now fixed and six tests that cannot run in the Modal image (Python 3.14.2's worker-recycling guard, no ps binary, running as root, subprocess timeouts under 7 workers), in files this PR does not touch. The UK and node-backed orrery tests were not run.
  • GitHub CI runs only on PRs into main, so this stacked PR gets it when it is retargeted after Impute federal student loan balances from the 2025 SCF (#1073) #1246 merges.

🤖 Generated with Claude Code

DTrim99 and others added 2 commits October 23, 2026 15:15
…d portfolio facts (#1074)

A new runtime module, fsa_student_loan_targets, binds Federal Student Aid's
portfolio tables (the six packages/ed_fsa packages of chronicle#347) to the
SCF education-debt stage's person columns, federal_student_loan_balance and
federal_parent_plus_loan_balance: the balance and borrower count of each
state and DC, of five borrower age bands and of nine debt-size bands, and
the Parent PLUS balance beside the balance of every other loan type. Every
family binds at FY2026 Q2 (31 March 2026), the latest quarter all four bound
tables publish, as a stock unaged at the 2024 build period (target aging's
new declared stock_at_source_reference_date alignment). Not Reported is
allocated to the reported locations, Puerto Rico and Other leave with their
share, and the age, debt-size and loan-type rows bind as shares of the one
in-scope control ($1,679.1B, 44.31M borrowers). Repayment plans, the
portfolio summary and loan-type recipient counts stay diagnostic, each with
its reason in the receipt.

compile_us_fiscal_target_registry binds the facts only with
student_loan_targets, which the fiscal-refresh release tool sets with
--scf-education-debt; the release materializes the rows from the stored
person columns, guards their restated age and balance bands, records every
fact's disposition in us_source_coverage.json and keeps the opt-in family
off the target-parity contract. Default compiles and releases are unchanged.
The ED Federal Student Aid provider joins the calibration label registries.

experiments/fit_1074_fsa_calibration.py refits the published default with
and without the family (plus L2-anchored arms) on Modal through
experiments/modal_1074.py, against the pinned feed plus chronicle#347's FSA
rows, now built at its merge commit on Chronicle main.

Squashes #1357's earlier history onto #1246's review-round head.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…chronicle#347 (#1074)

Modal run 20261023T191527Z-4b6bfce9a at 4b6bfce. chronicle#347's export
at its merge commit (acbedb5) is byte-identical to the PR head's (sha256
58774e11...), so the candidate feed and the test fixture are unchanged.

With loans for a child's education on co-resident adult children, the host
gains young borrowers (1,246 borrower records under 25, from 561) and its
Parent PLUS share falls from 16.8% to 10.7%, but holds the same borrowers
(21.2M) and borrower households (16.4%) at the published weights. Arm B now
meets 95% of the FSA targets within 5% (92% before) and every age band;
B + L2 78% (76%). Borrower households' share of weight after calibration is
33.7% (B) and 32.3% (B + L2), against 33.0% and 31.5% before and 18.2% of
SCF families: the routing moves balances within households, so it does not
shrink the doubling. Non-FSA capped error: B 3.62% (3.66%), B + L2 3.85%
(3.83%), A unchanged at 3.30%.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@DTrim99
DTrim99 force-pushed the fsa-student-loan-calibration-targets branch from 61803e7 to 3b28865 Compare October 23, 2026 20:22

This branch has not been deployed

No deployments
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.

1 participant