fix(clustering): raise coverage floor 0.5 -> 0.6 to gate degenerate stablecoin fits - #77
Merged
Merged
Conversation
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Follow-up to #75 / #76. Those shipped the clusterability machinery (
fit_coverage, the scheduler gate, anti-flap, theunclusterable_fitmarker) withMIN_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
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_anomalyrows were never markedunclusterable_fit.Change
MIN_CLUSTER_COVERAGEdefault0.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_unclusterableonly affects the scheduler re-fit decision, the UI nag, and the evidence-onlyunclusterable_fitmarker.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.Mainnet
Already applied live as
MIN_CLUSTER_COVERAGE=0.6in/opt/tms/.env(sidecar recreated, verified: Strike/Djed-2000 →unclusterable=True,recluster_recommended=False). The env override becomes redundant once theapp/clusteringimages are rebuilt on this default.🤖 Generated with Claude Code