From 0c826ef73639532aabcfdb7cf9d0a63085d23919 Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 8 Jul 2026 13:23:42 +0200 Subject: [PATCH] Link calibration rows to QA evidence map --- CHANGELOG.md | 7 ++++++ ..._sprint135_calibration_evidence_handoff.py | 24 ++++++++++++++++++ docs/CODEX_EXECUTION_LOG.md | 25 +++++++++++++++++++ docs/TODO.md | 1 + frontend/src/App.tsx | 1 + .../src/components/detection/DetectionLab.tsx | 14 +++++++++++ frontend/src/styles/app.css | 6 +++++ 7 files changed, 78 insertions(+) create mode 100644 backend/tests/test_sprint135_calibration_evidence_handoff.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 64feeb0d..02c0255d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ # Changelog +## Sprint 135 Calibration evidence handoff (2026-07-08) + +- Added a guided-calibration table action that opens the persisted QA/QC evidence map for successful threshold rows. +- Reused the existing quality-check evidence API and Map workspace overlay flow; no backend route, migration, model, provider or inference behavior changed. +- Kept unsuccessful/queued calibration rows read-only by disabling evidence actions until a persisted `quality_check_id` exists. +- Added regression coverage for the Detection Lab wiring and TODO tracking. + ## Sprint 134 External remote-sensing YOLO candidate benchmark (2026-07-07) - Evaluated the Hugging Face `agademer/yolo-remote-sensing-photovoltaic` YOLOv8l detection checkpoint as an explicit operator-provided runtime model asset. diff --git a/backend/tests/test_sprint135_calibration_evidence_handoff.py b/backend/tests/test_sprint135_calibration_evidence_handoff.py new file mode 100644 index 00000000..83983536 --- /dev/null +++ b/backend/tests/test_sprint135_calibration_evidence_handoff.py @@ -0,0 +1,24 @@ +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] + + +def test_guided_calibration_rows_link_to_existing_qa_evidence_map() -> None: + lab = ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx" + app = ROOT / "frontend" / "src" / "App.tsx" + todo = ROOT / "docs" / "TODO.md" + + lab_source = lab.read_text(encoding="utf-8") + app_source = app.read_text(encoding="utf-8") + todo_source = todo.read_text(encoding="utf-8") + + assert "onOpenCalibrationEvidence" in lab_source + assert "Open evidence map" in lab_source + assert "disabled={!row.quality_check_id || row.status !== 'success'}" in lab_source + assert "onOpenCalibrationEvidence(row.quality_check_id)" in lab_source + assert "Evidence" in lab_source + assert "quality_check_id" in lab_source + + assert "onOpenCalibrationEvidence={openQualityEvidenceOnMap}" in app_source + assert "[x] Link guided calibration rows to the QA evidence map" in todo_source diff --git a/docs/CODEX_EXECUTION_LOG.md b/docs/CODEX_EXECUTION_LOG.md index 833447f4..f13b2185 100644 --- a/docs/CODEX_EXECUTION_LOG.md +++ b/docs/CODEX_EXECUTION_LOG.md @@ -1,3 +1,28 @@ +## Sprint 135 Calibration evidence handoff (2026-07-08) + +Changed: +- Added an `Open evidence map` action to successful guided detection calibration rows. +- Wired the Detection Lab action to the existing `openQualityEvidenceOnMap` flow, which loads persisted QA/QC evidence GeoJSON and opens the Map workspace overlay. +- Added compact table action styling and regression coverage in `backend/tests/test_sprint135_calibration_evidence_handoff.py`. +- Updated `CHANGELOG.md` and `docs/TODO.md`. + +Tested: +- Red step: `python -m pytest backend\tests\test_sprint135_calibration_evidence_handoff.py -q` failed while the Detection Lab evidence handoff prop was absent. +- `python -m pytest backend\tests\test_sprint135_calibration_evidence_handoff.py backend\tests\test_sprint134_guided_detection_calibration_runner.py backend\tests\test_sprint133_detection_threshold_calibration_ux.py backend\tests\test_sprint112_qa_evidence_overlay.py -q` (`7 passed`) +- `python -m compileall backend/app` +- `cd frontend && npm run typecheck` +- `cd frontend && npm run build` +- `bash scripts/run_readiness_check.sh` (`414 passed`; frontend typecheck/build passed; Alembic head `202606120900`; live smoke syntax passed) + +Open: +- Live Tower deploy validation still needs to run for this pass. + +Limitations: +- This pass adds review handoff only. It does not create new QA metrics, promote thresholds, mutate model configuration, download models, add provider fetching or change API/database contracts. + +Next recommended pass: +- Add an operator-facing calibration evidence summary/export shortcut once the map handoff has been used on real AOI runs. + ## Sprint 134 External remote-sensing YOLO candidate benchmark (2026-07-07) Changed: diff --git a/docs/TODO.md b/docs/TODO.md index 693209a7..76015612 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -421,5 +421,6 @@ This file now starts with the current implementation status. Older preparation/b - [x] Add structured raster tile manifest handoff into Detection Lab with linked preflight visibility. - [x] Add full threshold calibration comparison UX so detection runs can compare candidate thresholds before promotion. - [x] Add guided in-app detection calibration runner for explicit threshold sweeps. +- [x] Link guided calibration rows to the QA evidence map. - [ ] Add more AOIs after the tile-level baseline so the next local model attempt is not limited to Geel/Mol/Turnhout. - [ ] Add negative/background AOIs so the next tile dataset is not all positive tiles. diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 808b326a..cf5fbfde 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -993,6 +993,7 @@ function App(): JSX.Element { onRunQa={runDetectionQa} onSetCalibrationThresholdText={setCalibrationThresholdText} onRunCalibration={runDetectionCalibration} + onOpenCalibrationEvidence={openQualityEvidenceOnMap} /> void onSetCalibrationThresholdText: (value: string) => void onRunCalibration: () => void + onOpenCalibrationEvidence: (qualityCheckId: string) => void } export function DetectionLab({ @@ -133,6 +134,7 @@ export function DetectionLab({ onRunQa, onSetCalibrationThresholdText, onRunCalibration, + onOpenCalibrationEvidence, }: DetectionLabProps): JSX.Element { const selectedDetectionModel = detectionModels.find((model) => model.model_id === selectedDetectionModelId) ?? null const selectedModelAsset = modelAssets.find((asset) => asset.model_asset_id === selectedModelAssetId) ?? null @@ -583,6 +585,7 @@ export function DetectionLab({ F1 False positives False negatives + Evidence @@ -596,6 +599,17 @@ export function DetectionLab({ {formatNullableNumber(row.f1_score ?? null, 3)} {row.false_positives ?? 'n/a'} {row.false_negatives ?? 'n/a'} + + + ))} diff --git a/frontend/src/styles/app.css b/frontend/src/styles/app.css index 9901e852..d439440e 100644 --- a/frontend/src/styles/app.css +++ b/frontend/src/styles/app.css @@ -3309,6 +3309,12 @@ button.entity-card { background: #ffffff; } +.table-action { + min-height: 2rem; + padding: 0.35rem 0.6rem; + white-space: nowrap; +} + .calibration-summary-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));