habitat
model risk · fair lending

Did data-prep move the protected coupling?

A model-risk team owns model risk management under SR 26-2. The unanswered question: did our own preparation move the protected coupling, in a way an examiner will find?

the pain

Every data-handling step can perturb the protected-attribute geometry: reweighting, sampling, imputation, feature transforms, de-biasing remediation. Each is defensible on its own. But any of them can shift the coupling between a protected attribute and the outcome — and the shift is exactly what a fair-lending examiner is trained to look for. The team can’t see it happening in its own pipeline.

the process today

Validation binders, segment testing, challenger benchmarking — all in place, all sound. Then the examiner asks: prove your data-prep didn’t perturb the protected outcome. The answer is documentation. It is thorough and it is honest, but nobody can reproduce, from the artifact, that the geometry held. The binder describes the process; it doesn’t carry a floor the examiner can recompute.

the cost

Exam findings and MRAs. Remediation cycles. Re-validation on every change. And with no measure of influence, change itself becomes slow and risk-averse — the team avoids adjustments it can’t cheaply prove were harmless.

the exhibit — recompute it yourself

corpus: CFPB HMDA · Washington DC · 2024 (newest settled one-year release, 16,963 applications) · protected geometry: race / sex / ethnicity × denied. Register: detection — the reported move is the argmax over the block (denied × denied).

GREEN — reorder_applications
re-ordered the application stream · order-only, name-blind
geometry digest beforec1367c64…
geometry digest afterc1367c64…
max │Δg│0e+00

The application file changed — its order changed. The certified protected geometry did not move, to the byte.

RED — launder_denials
dropped ~50% of denied Black applicants to flatten the protected gap
geometry digest beforec1367c64…
geometry digest afterefee1e0f…
│Δg│ (registered, argmax)0.874
race_black×denied │Δg│0.737
control floor · declared1e-06

This red is a deliberate control — a laundering operation we run on purpose to prove the instrument rings, not an incident that happened.

recompute fingerprint — the value the kit’s verify step reproduces from the artifact, byte-for-byte on any engine that follows the kit’s published canonicalization · sha256 = c1367c641b6dfe9e64cb8d62b79e22c95979ff7387c9c6e6c5e3e8c5dd7f3f10
The GREEN before/after and the RED “before” above are this same clean fingerprint; the RED “after” is the identical recompute applied to the declared laundering resample.

You recompute the geometry, the byte-identical benign baseline, the declared tamper yourself — no trust asked. That those vectors are drawn only from the named reference is attested by the certificate; where the reference is public, it is checkable at the source.

the relief

Run it on your validation bench, in tandem with model changes: a certificate — recomputable from the artifact — that the protected coupling didn’t move above the declared floor, or a precise reading of how much it did. The examiner re-derives the same value themselves. Your binder stands, and gains a checkable floor: the process was validated, and here is the number the examiner recomputes for themselves.

what this does not prove

The certificate attests that the operation did not move the protected geometry within a declared frame. It does not say the model is fair, or that the frame itself is the right one. It is detection, not adjudication. The fairness judgment remains yours; this only removes the argument about whether your own hand moved the thing being judged.

Your SR 26-2 documentation stands. This is the caliper for “did our own hand move the protected coupling?” Measure it; the examiner recomputes it.

Recompute it yourself — in your tab

The digit above (c1367c64…) is the number habitat published for this dataset. Recompute it here from the data's own vectors — nothing leaves your tab — then try to fool it: reshuffle the rows and it holds; change a value and it moves.

what just happened

habitat read this data once, on our engine, and reduced it to a small set of vectors — the geometry of how its columns relate. From those your browser recomputed one number: the covariance, conditioned and inverted (math.fsum → Gauss-Jordan inverse → SHA-256), ~40 lines of public arithmetic, nothing sent. Built from the relationships, not the order — so a reshuffle leaves it identical, a real change moves it. The reading (record → geometry) is the method; it stays on our engine, the recompute is yours.

what this proves — and what it doesn't

Proves: the published number falls out of the data's own vectors — reproduced byte-for-byte, no server; and it's not a checksum (survives a benign reshuffle, moves on a real change).

Doesn't: not tamper-proof, not authenticity. Same number = same relationships, not the same bytes: a uniform shift, or a change finer than the arithmetic's floor, is invisible by design, and a dataset engineered to share those relationships would pass. Self-served (our page, our vectors); the independent version is the downloadable kit against a separately-published fingerprint. And it's this published dataset, not your data — reading your data into geometry runs on our engine, not your tab.

This corpus’s noise floor has its own readout on the site’s second instrument — pre-built for this card, or run on your own data, in your tab: trace your noise →

verify this receipt in your browser

This case's receipt records its verdict and both digests above, and carries its own SHA-256. Recompute it here — WebCrypto, in your tab, nothing sent — and it matches; alter one character of a copy and it fails. It is the smallest version of the whole move: recompute, don't trust. The geometry digest itself you can recompute above, in this tab, or on your bench from the kit.

published fingerprint ·

or download this receipt and check it on your bench: receipt-hmda.jsonshasum -a 256 prints the same number. Every receipt is on the ledger.

request the kit

The recompute kit for this case — artifact, manifest, verify.py. A person sends it: the kit and its verification steps arrive by reply, usually within a business day.

← Receipts · Docs · Ledger

Rating plate — what this digest sees, and what it cannot

Measured 2026-07-16 by two independent runs, per column, on this receipt’s pinned corpus. A material single-entry edit moved the digest on 10 of 10 columns — every column. What it cannot see:

Check it: the kit’s check-pair runs this digest and a sorted-rows hash on any table — add 0.5 to one cell and both move; relabel a constant column and only one does. Nothing on this plate says anything about any person or institution: detection, not adjudication.