Repository navigation
Add majority-cluster outlier rejection as a third curb-calibration fallback tier - #46
Merged
Merged
Conversation
…o curb derivation - MAJORITY_THRESHOLD = 0.8 module constant (spike 009: 92% roadbed-survival at 0.8 vs 73% at 0.6; the extra yield at 0.6 is mostly uncorroborated segments) - widest_gap_majority() mirrors calibration.estimate_center_offset's widest-gap split locally (different contract: dominant cluster + share, not two lanes) - majority_threshold=None parameter on derive_segment_calibration reduces each side independently before the medians/spread gate; None is a true off switch - derive_segment_calibration_with_fallback_retries() layers tier (c) around the existing two-tier wrapper: untrimmed -> 25 ft trim -> trim+majority-at-0.8 - regenerated the vendored mirror via scripts/sync_vendored.py
…ee-tier policy - mid-block-apron fixture (detached 40 ft apron at x=120..160, 300 ft block, 50 ft width) asserted to fail untrimmed (spread_n ~15.31), to STILL fail under the 25 ft trim alone (~16.59, the trim makes it worse), and to calibrate under trim-plus-majority (share 0.854 -> c=1.0, width=34.0) - divided-geometry fixture (two full-length north curbs, share 0.50) asserted to stay rejected, so ambiguous geometry self-flags instead of being cleaned - TestWidestGapMajority pins the split, the low-cluster tie-break, degenerate input, and the harmless uniform-side no-op - TestMajorityClusterRetry pins all six tier branches, including the 0.8-vs-0.6 load-bearing check (long apron, share 0.661: refused at 0.8, cleaned at 0.6) - 18 new tests; the 20 pre-existing ones are byte-unchanged (additive diff only)
…fline index build - _derive_segment_fields now calls derive_segment_calibration_with_fallback_retries; the independent roadbed cross-check block is byte-unchanged, so a majority-rescued segment faces the same second gate as a trim-rescued one - updated all three prose references (step 2 of _derive_segment_fields, the inline comment in _build_rtree_and_metadata, the build_index docstring) to name the untrimmed -> 25 ft trim -> majority-at-0.8 chain - TestCurbCalibrationMajorityCluster proves a mid-block-apron segment reaches segments.json with calibrated: true, c ~= 1.0, width ~= 34.0, count 1; verified it lands non-calibrated (spread_n 15.31) under the old two-tier wrapper - no download/cache/CLI code touched; no citywide rebuild run (hard requirement 8)
Simplify pass on the majority-cluster fallback tier: trim doc/comment duplication between the module docstring, the MAJORITY_THRESHOLD constant, and the fallback-retry docstring in curb_calibration.py (mirrored copy included), collapse the same three-tier explanation repeated three times in build_index.py, and extract a shared _CurbBuildHarness mixin for the copy-pasted CSCL/download-patching fixtures across the three curb-calibration test classes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…test harness CodeQL flagged the no-arg lambdas in _CurbBuildHarness._patch_downloads (wrapping self._make_cscl_gdf and set with no added args) as unnecessary — pass the callables directly instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ruff format --check flagged the shortened monkeypatch.setattr call from the previous CodeQL fix as needing reformatting (fits on one line now). Co-Authored-By: Claude Sonnet 5 <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.
Summary
curb_calibration.py, after the existing 25ft endpoint-trim retry (quick-260907-c5d, merged in Fix availability false-unavailable + add curb calibration endpoint-trim retry #44).gps2asp.resolver.calibration.estimate_center_offset, repurposed from "two real lanes" to "dominant cluster vs. contamination"). If the dominant cluster holds ≥80% of that side's samples, trust it; otherwise leave the side — and the segment — untouched and non-calibrated.MAJORITY_THRESHOLD = 0.8is a fixed module constant, chosen from spike 009's sweep of 0.6/0.7/0.8 over real data: 0.8 gives 18% flip rate with 92% independent-roadbed corroboration, vs 0.6's 31%/73% — the extra yield at looser thresholds is mostly segments the independent roadbed source doesn't corroborate.scripts/build_index.pywired to the new three-tier wrapper; vendored mirror regenerated viasync_vendored.py.Real-data validation (not just synthetic fixtures)
Ran a full local citywide rebuild reusing the exact same curb/roadbed data snapshot as the prior endpoint-trim-only build, to isolate this change's effect:
calibrated: true(spreads 0.21/0.56ft, was ~17-19ft). The adjacent block (60499) the actual GPS fix lands on remains correctly non-calibrated — genuinely near-even split, safely self-flagged rather than silently miscalibrated.Test plan
tests/test_curb_calibration.py— 18 new tests (TestWidestGapMajority,TestMajorityCluster,TestMajorityClusterRetry), 20 pre-existing tests unmodifiedtests/test_build_index.py— newTestCurbCalibrationMajorityClusterend-to-end offline build regression; existingTestCurbCalibration/TestCurbCalibrationEndpointTrimunmodifiedruff check ./ruff format --check .cleanmypy src/gps2asp/andmypy custom_components/asp_parking/clean (run separately — combined invocation produces a false-positive module-identity conflict between source and vendored mirror)scripts/sync_vendored.py --dry-run— vendor-guard parity confirmedindex-rebuild.yml) + live HA index deployment — deliberately deferred, separate follow-up🤖 Generated with Claude Code