How it works
Two lenses, one loop.
Risk picks the conversation, Coach shapes it, and the outcome of that conversation is the label that retrains both. Split them and each half degrades into something worse. You get a queue nobody knows how to work, or advice with no idea who it is talking to.
Where the system acts
Conventional collections enters at delinquent. Ganymede enters one state earlier, and drifting → current with no contact is the transition most systems never notice they are being paid for. Select a state to see what happens there.
How the data moves
Servicing panel and contact events feed the models; the allocator turns scores into an assignment; the desk surfaces hints; every decision is logged with its arm and propensity and resolved back into training.
One row per loan per month with real calendar dates: arrears bucket, balance, origination facts.
panel.py
Trailing arrears slope and peak, balance movement, and the cross-signal terms. A timing feature from a source without calendar dates raises rather than warns.
features.py · I13
Gradient-boosted, isotonically calibrated, with SHAP-derived reason codes. L3 contactability waits for pilot contact data.
risk.py
Expected value per agent-minute under capacity. The only component allowed to produce a queue.
allocator.py · I9 · I10
Borrower state branches the strategy. Uncertain state yields the diagnostic question instead. Two tiers, one hint at a time, rate-limited.
coach/ · I5 · I7 · I11
Promise against payment. The label retrains both lenses and promotes or demotes the strategy that produced it.
outcomes.py · monitors/ · I12
Fourteen invariants, not fourteen notes
A first review found fourteen ways this system could go wrong. Written down as a list of past mistakes, that list gets read once and never again. Each is restated as a constraint with an enforcement mechanism, so it lives in the codebase.
The standing rule. A defect is not closed when it is fixed. It is closed when an invariant is written and something automated fails if that invariant breaks.
Two of those gates are new, and they exist for the same reason as the other twelve: the design system and the site's figures are places a known defect could quietly return. A contrast ratio that drops below its floor, a risk ramp that stops encoding magnitude, or a headline number that drifts from the pipeline now fails the build rather than waiting for someone to notice.
What it is built on
Freddie Mac Single-Family Loan-Level is the trajectory backbone: real dates, real delinquency transitions, used under Freddie Mac's data terms. Home Credit Default Risk supplies feature enrichment under its Kaggle terms. Neither is redistributed here. The repository references them and rebuilds from a local copy.
The panel holds — monthly rows across — loans and — vintages, of which — are delinquent account-months, which is the class the Risk Lens actually ranks within.
The conversation side has no equivalent public source. Transcripts are
generated, conditioned on real trajectories, and flagged
is_synthetic at the schema level. That flag is what lets invariant
I1 refuse a predictive-lift claim it cannot honestly support, rather than relying on whoever writes the eval to remember the distinction.