From 6446c78035b3b6e38897f2fe734c145657556f6b Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 19 Jun 2026 05:09:11 +0200 Subject: [PATCH] Polish QA metric evidence cards --- CHANGELOG.md | 8 +++ .../test_sprint71_quality_metric_polish.py | 43 ++++++++++++++ docs/CODEX_EXECUTION_LOG.md | 28 +++++++++ docs/TODO.md | 1 + frontend/README.md | 2 + .../quality/QualityResultsPanel.tsx | 54 ++++++++++++++++- frontend/src/styles/app.css | 58 +++++++++++++++++++ 7 files changed, 191 insertions(+), 3 deletions(-) create mode 100644 backend/tests/test_sprint71_quality_metric_polish.py diff --git a/CHANGELOG.md b/CHANGELOG.md index d9543532..f766f9cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ # Changelog +## Sprint 71 QA/QC metric card polish (2026-06-19) + +- Added core metric evidence cards for precision, recall, F1, mean IoU and false positive/negative counts. +- Kept the raw persisted metric list available below the promoted metric evidence. +- Added number formatting for compact metric display while preserving persisted metric values. +- Added static regression coverage for metric promotion and responsive metric-card styles. +- No API contracts, migrations, backend behavior, provider fetching or AI model behavior changed. + ## Sprint 70 QA/QC handoff polish (2026-06-19) - Added candidate/reference handoff cards to the QA/QC workspace. diff --git a/backend/tests/test_sprint71_quality_metric_polish.py b/backend/tests/test_sprint71_quality_metric_polish.py new file mode 100644 index 00000000..aa209334 --- /dev/null +++ b/backend/tests/test_sprint71_quality_metric_polish.py @@ -0,0 +1,43 @@ +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] + + +def test_quality_panel_promotes_core_metrics_before_raw_metric_list() -> None: + quality_panel = ( + ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx" + ).read_text(encoding="utf-8") + + assert "CORE_METRIC_ORDER" in quality_panel + assert "precision" in quality_panel + assert "recall" in quality_panel + assert "f1" in quality_panel + assert "mean_iou" in quality_panel + assert "false_positive_count" in quality_panel + assert "false_negative_count" in quality_panel + assert "qualityMetricValue" in quality_panel + assert "qualityMetricLabel" in quality_panel + + +def test_quality_panel_renders_metric_evidence_cards_and_raw_metrics() -> None: + quality_panel = ( + ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx" + ).read_text(encoding="utf-8") + + assert "quality-metric-grid" in quality_panel + assert "quality-metric-card" in quality_panel + assert "quality-metric-card-critical" in quality_panel + assert "Metric evidence" in quality_panel + assert "Raw metrics" in quality_panel + assert "check.metrics.map" in quality_panel + + +def test_quality_metric_styles_are_dense_and_responsive() -> None: + css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8") + + assert ".quality-metric-grid" in css + assert "repeat(auto-fit, minmax(7.5rem, 1fr))" in css + assert ".quality-metric-card" in css + assert ".quality-metric-card-critical" in css + assert ".quality-metric-card strong" in css diff --git a/docs/CODEX_EXECUTION_LOG.md b/docs/CODEX_EXECUTION_LOG.md index 0e3cfce8..c75051cb 100644 --- a/docs/CODEX_EXECUTION_LOG.md +++ b/docs/CODEX_EXECUTION_LOG.md @@ -2666,3 +2666,31 @@ Limitations: Next recommended pass: - Continue with QA/QC result card polish, especially making metric groups easier to scan in long-lived demo projects. + +## Sprint 71 QA/QC metric card polish (2026-06-19) + +Changed: +- Added core metric evidence cards to QA/QC result cards for precision, recall, F1, mean IoU, false positives and false negatives. +- Kept the raw persisted metric list visible below the promoted evidence cards. +- Added compact metric label/value formatting in `QualityResultsPanel`. +- Added responsive `.quality-metric-grid`, `.quality-metric-card` and `.quality-metric-card-critical` CSS. +- Added `backend/tests/test_sprint71_quality_metric_polish.py`. +- Updated `frontend/README.md`, `docs/TODO.md` and `CHANGELOG.md`. + +Tested: +- Red step: `python -m pytest backend/tests/test_sprint71_quality_metric_polish.py -q` failed on missing metric promotion helpers, markup and styles. +- `python -m pytest backend/tests/test_sprint71_quality_metric_polish.py backend/tests/test_sprint70_quality_handoff_polish.py backend/tests/test_sprint51_quality_export_polish.py backend/tests/test_sprint30_workbench_components.py -q` (`13 passed`) +- `cd frontend && npm run typecheck` +- `cd frontend && npm run build` +- Local browser check against `http://127.0.0.1:5175` with live API proxy: QA/QC workspace rendered 12 promoted metric cards across 2 persisted quality checks, retained 2 raw metric sections, no console warnings/errors and no horizontal overflow on desktop or mobile. +- Screenshots captured under `artifacts/sprint71-quality-metric-polish/`. +- `bash scripts/run_readiness_check.sh` (`253 passed`) + +Open: +- Redeploy Tower and verify live QA/QC metric cards after deployment. + +Limitations: +- Frontend readability pass only; no QA calculation, persistence, API, migrations, provider or AI/model changes. + +Next recommended pass: +- Continue with QA/QC metric/result filtering or result-card density for long-lived demo projects. diff --git a/docs/TODO.md b/docs/TODO.md index 9379e827..55067570 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -342,3 +342,4 @@ This file now starts with the current implementation status. Older preparation/b - [x] Add Data catalog role-density polish for reference/candidate/source scanning. - [x] Add Data catalog action polish for map, metadata, export and QA affordances. - [x] Add QA/QC handoff polish for candidate/reference context and persisted results. +- [x] Add QA/QC metric card polish for precision, recall, F1, IoU and error counts. diff --git a/frontend/README.md b/frontend/README.md index e28c199e..bedb98a6 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -283,6 +283,8 @@ When using the repository Docker Compose stack, the frontend is published on hos The frontend API client uses same-origin requests by default. In Docker Compose, nginx serves the built frontend and reverse proxies `/api` and `/health` to the backend service, so browser clients on LAN hosts do not call their own `localhost:8000`. +The QA/QC workspace promotes core metrics into evidence cards before the raw metric list, keeping precision, recall, F1, mean IoU and false positive/negative counts scan-friendly while preserving the persisted raw metrics. + ## Useful repository scripts - `bash scripts/frontend_install.sh` diff --git a/frontend/src/components/quality/QualityResultsPanel.tsx b/frontend/src/components/quality/QualityResultsPanel.tsx index 98b6c349..b51214f3 100644 --- a/frontend/src/components/quality/QualityResultsPanel.tsx +++ b/frontend/src/components/quality/QualityResultsPanel.tsx @@ -1,5 +1,14 @@ import { useMemo } from 'react' -import type { DatasetCreateResponse, QualityCheckRead } from '../../types' +import type { DatasetCreateResponse, MetricRead, QualityCheckRead } from '../../types' + +const CORE_METRIC_ORDER = [ + 'precision', + 'recall', + 'f1', + 'mean_iou', + 'false_positive_count', + 'false_negative_count', +] interface QualityResultsPanelProps { selectedProjectId: string | null @@ -10,6 +19,29 @@ interface QualityResultsPanelProps { onRefresh: () => void } +function qualityMetricLabel(metricKey: string): string { + const labels: Record = { + precision: 'Precision', + recall: 'Recall', + f1: 'F1', + mean_iou: 'Mean IoU', + false_positive_count: 'False positives', + false_negative_count: 'False negatives', + } + return labels[metricKey] ?? metricKey.replaceAll('_', ' ') +} + +function qualityMetricValue(metric: MetricRead | undefined): string { + if (!metric || metric.metric_value === null || metric.metric_value === undefined) { + return 'n/a' + } + const value = Number(metric.metric_value) + if (!Number.isFinite(value)) { + return String(metric.metric_value) + } + return Number.isInteger(value) ? String(value) : value.toFixed(3) +} + export function QualityResultsPanel({ selectedProjectId, qualityChecks, @@ -123,11 +155,27 @@ export function QualityResultsPanel({ {check.id} +
+ Metric evidence +
+ {CORE_METRIC_ORDER.map((metricKey) => { + const metric = check.metrics.find((item) => item.metric_key === metricKey) + const isCriticalCount = metricKey === 'false_positive_count' || metricKey === 'false_negative_count' + return ( +
+ {qualityMetricLabel(metricKey)} + {qualityMetricValue(metric)} +
+ ) + })} +
+
+ Raw metrics diff --git a/frontend/src/styles/app.css b/frontend/src/styles/app.css index 7d1e907d..01b6e61f 100644 --- a/frontend/src/styles/app.css +++ b/frontend/src/styles/app.css @@ -1304,6 +1304,64 @@ button.entity-card { white-space: nowrap; } +.quality-metric-section { + margin-top: 0.75rem; +} + +.metric-list-label, +.quality-metric-section > span { + display: block; + color: var(--muted); + font-size: 0.72rem; + font-weight: 800; + letter-spacing: 0.05em; + text-transform: uppercase; +} + +.quality-metric-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr)); + gap: 0.5rem; + margin-top: 0.45rem; +} + +.quality-metric-card { + min-width: 0; + border: 1px solid rgba(15, 118, 110, 0.18); + border-radius: 8px; + padding: 0.58rem; + background: #f8fbf9; +} + +.quality-metric-card-critical { + border-color: rgba(185, 28, 28, 0.18); + background: #fff7ed; +} + +.quality-metric-card span { + display: block; + color: var(--muted); + font-size: 0.7rem; + font-weight: 800; + letter-spacing: 0.04em; + text-transform: uppercase; +} + +.quality-metric-card strong { + display: block; + margin-top: 0.24rem; + overflow: hidden; + color: var(--ink); + font-size: 1.08rem; + line-height: 1.15; + text-overflow: ellipsis; + white-space: nowrap; +} + +.metric-list-label { + margin-top: 0.82rem; +} + .metric-list { grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr)); margin-top: 0.75rem;