How We Evaluate

The methodology behind ThumbGate's enforcement quality — published with the numbers that don't flatter us left in. Every figure on this page is reproducible from the open-source repo with one command.

The headline we retired

ThumbGate's learned risk model used to report one quality figure: trainingAccuracy: 0.820 — measured on the same rows it trained on, against a corpus where answering "risky" unconditionally scores 71.1%. That is an 11-point in-sample lift presented as an 82-point achievement, and it said nothing about generalization at all.

We replaced it with held-out evaluation under two different splits, and we publish both results — including the one that inverts.

The result, honestly

+12 0 −12 +10.9 +9.9 −10.5 in-sample (old headline) held-out, familiar kinds held-out, unseen kinds

Accuracy lift over the majority-class baseline, in points. 1,791 production rows, 12 independent group-aware stratified splits, mean across resamples. IID: +9.9 ± 1.7, AUC 0.887, 12/12 folds beat baseline. Novel-context: −10.5 ± 9.0, 1/12 folds, calibration error 0.31 vs 0.06.

On familiar traffic the model genuinely helps. On action types it has never seen, it is worse than a constant classifier — and unfamiliar actions are precisely the case a firewall exists for. That asymmetry is why deterministic rules enforce and the model only triages. We consider publishing that inversion more valuable than hiding it: it is the reason ThumbGate's blocking behaviour does not degrade with the model.

The methodology

PracticeWhat it prevents
Lift vs the majority baseline, reported beside every accuracy71% base-rate corpora making any model look strong
MCC + ROC-AUC + Brier + calibration erroraccuracy hiding degenerate or miscalibrated behaviour
Dual holdouts: IID and whole-category (distribution shift)quoting only the flattering generalization estimate
Group-aware splits keyed on the feature vectorduplicate inputs straddling folds and inflating scores
12 resamples, mean ± sd, paired t on shared foldssingle lucky splits shipping as "improvements"
Per-fold vocabulary rebuildheld-out rows choosing the model's features (transductive fit)

The evaluation suites

The gates that block a release

Error analysis: the five bugs we found in our own evaluator

The uncomfortable finding of this work: the evaluator was wrong more often than the model, and its errors all flattered us.

BugEffect before the fix
Tied-hash rows straddling foldstest fold contained memorized rows
Cross-class duplicate leakage under label noisesame input in both folds with different labels
Group quota overshoot (one 655-row category)novel-context result read +0.016; truth is −0.105
Vocabulary built before the splitheld-out rows chose the model's features
Raw text in the split keyidentical model inputs split across folds

Three of the five were found by adversarial review after we had published numbers from the leaky evaluator. The corrected conclusion was stronger, not weaker — and the correction is documented in the repo, not overwritten.

The rule all of this enforces: no accuracy figure ships without the baseline it beats and the split it was measured on.

Reproduce it

Honest scoping: the measured corpus is private operator feedback telemetry and does not ship in the public repo. The exact machine-readable report behind the figures on this page is checked in with provenance at evals/risk-model-report.json. A clean clone gives you that report, the methodology write-up, the CI quality gate (synthetic fixtures / no private data), and the same harness to run against your feedback log. The figures on this page are first-party operator measurements documented in docs/ML-EVALUATION.md — not a committed private corpus dump.

git clone https://github.com/IgorGanapolsky/ThumbGate
cd ThumbGate && npm ci
npm run test:risk-quality              # CI quality gate — no private data needed
# Optional: re-run the harness on your own feedback log
node scripts/eval-risk-model.js --corpus <your-own-feedback-log> --resamples 12 --json

Full methodology: docs/ML-EVALUATION.md · case studies: /case-studies · gate scorecard: /eval-scorecard