Skip to content

fix(clustering): raise coverage floor 0.5 -> 0.6 to gate degenerate stablecoin fits - #77

Merged
elluff merged 1 commit into
mainfrom
fix/clustering-coverage-floor-0.6
Jul 23, 2026
Merged

fix(clustering): raise coverage floor 0.5 -> 0.6 to gate degenerate stablecoin fits#77
elluff merged 1 commit into
mainfrom
fix/clustering-coverage-floor-0.6

Conversation

@elluff

@elluff elluff commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Why

Follow-up to #75 / #76. Those shipped the clusterability machinery (fit_coverage, the scheduler gate, anti-flap, the unclusterable_fit marker) with MIN_CLUSTER_COVERAGE = 0.5, calibrated on synthetic fits. Mainnet data (2026-07-23) shows the real degenerate stablecoin-validator fits land just above that floor, so the gate never engaged for them.

Data

Contract window (N) clusters coverage
Strike Finance 2000 5 0.52 (drift pinned ~1.0)
Djed v2 2000 4 0.58 (drift climbing)
Djed v2 (healthy) ~1100 2 0.97

Both degenerate fits reproduce the same majority-DBSCAN-noise model on every re-fit, so re-clustering is structurally futile: at 0.5 they still nagged "re-analyze", still burned one re-fit per anti-flap interval, and their contract_anomaly rows were never marked unclusterable_fit.

Change

MIN_CLUSTER_COVERAGE default 0.5 -> 0.6. 0.6 sits in the wide empty gap between the degenerate regime (0.52–0.58) and a healthy fit (0.97), so it gates the former and leaves the latter clusterable.

Recall safety

model_unclusterable only affects the scheduler re-fit decision, the UI nag, and the evidence-only unclusterable_fit marker. classify + IsolationForest/LOF novelty scoring run every tick regardless; no verdict, publish, or band logic changes. Raising the floor only reduces futile re-fits and adds honest UI/marking. It silences no detection.

Tests

  • test_default_floor_gates_degenerate_fits_but_not_healthy_ones: pins the observed coverages (0.52, 0.58 gate; 0.97 does not).
  • test_default_floor_stops_strike_style_tight_loop: end-to-end at Strike's numbers (drift 1.0, cov 0.522, recent fit) → classify, not the re-fit loop.
  • Existing threshold-relative scheduler/API tests unchanged and green. Full sidecar suite: 495 passed.

Mainnet

Already applied live as MIN_CLUSTER_COVERAGE=0.6 in /opt/tms/.env (sidecar recreated, verified: Strike/Djed-2000 → unclusterable=True, recluster_recommended=False). The env override becomes redundant once the app/clustering images are rebuilt on this default.

🤖 Generated with Claude Code

…tablecoin fits

MIN_CLUSTER_COVERAGE=0.5 left the real mainnet stablecoin-validator fits on
the clusterable side of the floor: Strike fits at coverage 0.52 and Djed (at a
2000-tx window) at 0.58, both with online drift pinned at ~1.0 and every re-fit
reproducing the same majority-DBSCAN-noise model. So they still nagged
"re-analyze", still burned one futile re-fit per anti-flap interval, and never
got their contract_anomaly rows marked unclusterable_fit.

A genuinely healthy fit sits far above them (Djed at a ~1100 window: 0.97).
0.6 falls in the wide empty gap between the two regimes, so it gates the
degenerate fits as un-clusterable while leaving healthy fits clusterable.

Recall-safe: model_unclusterable only gates the scheduler re-fit decision, the
UI nag, and the evidence-only unclusterable_fit marker. classify +
IsolationForest/LOF run every tick regardless; no verdict, publish, or band
logic changes. Raising the floor only reduces futile re-fits and adds honest
UI/marking; it silences no detection.

Adds a calibration test pinning the observed coverages (0.52, 0.58 gate; 0.97
does not) plus an end-to-end check that Strike's pinned-drift fit stays on
classify rather than the ~60s re-fit loop.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@elluff
elluff merged commit 35580d0 into main Jul 23, 2026
9 checks passed
@elluff
elluff deleted the fix/clustering-coverage-floor-0.6 branch July 23, 2026 12:28
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