From 75ab8f64abfc843b157f5eb868b1b838bbfff68a Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 17 Jul 2026 14:36:55 +0200 Subject: [PATCH] Finish audit UI and live validation closure --- CHANGELOG.md | 5 +++ ...st_sprint175_detection_review_hardening.py | 5 ++- ...est_sprint234_project_lifecycle_cleanup.py | 5 +++ docs/CODEX_EXECUTION_LOG.md | 23 ++++++++-- .../src/components/detection/DetectionLab.tsx | 43 ++++++++++++------- .../segmentation/SegmentationLab.tsx | 33 +++++++++++--- 6 files changed, 87 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca27817c..3c19f048 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,8 +19,13 @@ - Reduced end-user noise across Map, Quality, Detection and Segmentation by using Dutch task language and placing UUIDs, paths, checksums and runtime diagnostics behind technical disclosures. +- Replaced persisted AI run/model/class/tile internals with friendly labels in + the ordinary result tables while retaining the original provenance in + persistence and technical views. - Added widescreen layout guardrails for AI workspaces and regression coverage for project lifecycle, packaged cleanup and the new component boundaries. +- Hardened the packaged archive command for direct container execution and + added a subprocess regression for the documented operator invocation. ## Sprint 233 Operational correctness and result completion (2026-07-17) diff --git a/backend/tests/test_sprint175_detection_review_hardening.py b/backend/tests/test_sprint175_detection_review_hardening.py index 6e6d958d..8f290ce4 100644 --- a/backend/tests/test_sprint175_detection_review_hardening.py +++ b/backend/tests/test_sprint175_detection_review_hardening.py @@ -102,8 +102,9 @@ def test_detection_results_table_uses_bounded_local_pagination() -> None: assert "Paginering van gevonden objecten" in lab assert "Vorige resultatenpagina" in lab assert "Volgende resultatenpagina" in lab - assert "formatSourceTilePath(detection.source_tile_path)" in lab - assert 'title={detection.source_tile_path ?? undefined}' in lab + assert "Herkomst" in lab + assert "Luchtbeeldtegel" in lab + assert 'title={detection.source_tile_path ?? undefined}' not in lab assert "pagination-toolbar" in styles assert ".source-tile-cell" in styles assert "detectionItems.map((detection)" not in lab diff --git a/backend/tests/test_sprint234_project_lifecycle_cleanup.py b/backend/tests/test_sprint234_project_lifecycle_cleanup.py index 7a0117e5..8bab74da 100644 --- a/backend/tests/test_sprint234_project_lifecycle_cleanup.py +++ b/backend/tests/test_sprint234_project_lifecycle_cleanup.py @@ -152,12 +152,17 @@ def test_frontend_lifecycle_and_component_boundaries_are_wired() -> None: app_source = (ROOT / "frontend/src/App.tsx").read_text(encoding="utf-8") project_panel = (ROOT / "frontend/src/components/project/ProjectPanel.tsx").read_text(encoding="utf-8") detection_lab = (ROOT / "frontend/src/components/detection/DetectionLab.tsx").read_text(encoding="utf-8") + segmentation_lab = (ROOT / "frontend/src/components/segmentation/SegmentationLab.tsx").read_text(encoding="utf-8") map_workspace = (ROOT / "frontend/src/components/map/MapWorkspace.tsx").read_text(encoding="utf-8") premium_css = (ROOT / "frontend/src/styles/premium.css").read_text(encoding="utf-8") assert "OverviewWorkspace" in app_source assert "onArchiveProject={archiveProject}" in app_source assert "DetectionModelManagement" in detection_lab + assert "persistedDetectionModelLabel" in detection_lab + assert "Lokaal gebouwmodel" in detection_lab + assert "persistedSegmentationModelLabel" in segmentation_lab + assert "Testsegmentatie" in segmentation_lab assert "from './mapWorkspaceUtils'" in map_workspace assert "Werkruimte archiveren" in project_panel assert "PROTECTED_PROJECT_NAMES" in project_panel diff --git a/docs/CODEX_EXECUTION_LOG.md b/docs/CODEX_EXECUTION_LOG.md index deaee021..b791e168 100644 --- a/docs/CODEX_EXECUTION_LOG.md +++ b/docs/CODEX_EXECUTION_LOG.md @@ -9987,6 +9987,23 @@ Implemented: - Added ultrawide AI-workspace constraints so controls use the available width without creating multiple narrow nested columns. -Validation evidence is recorded after the complete compile, pytest, typecheck, -build, readiness, migration, deployment, live cleanup and browser acceptance -chain has completed. +Validation: +- The local readiness gate passed 907 backend tests, backend compilation, + documentation and API contract audits, the single Alembic head + `202607160001`, frontend TypeScript typecheck and the production build. +- The first live dry-run correctly made no database change but exposed that a + direct `/app/scripts/archive_technical_projects.py` invocation resolved the + wrong Python package. The script now bootstraps the packaged `/app` backend + root, and a subprocess regression executes the documented command shape. +- The corrected all-in-one deployment became healthy, reported PostGIS 3.6, + passed required schema/index checks and retained Alembic head + `202607160001`. +- The live cleanup dry-run matched 1,091 allowlisted technical projects and + preserved both canonical workspaces. Apply archived the same 1,091 rows. + The active-project API now reports exactly two rows (`Kempen Regional + Workbench` and `Mol Municipality Workbench`), the archived API reports + 1,091, and a repeated dry-run reports zero remaining matches. +- Live browser inspection found no console errors or document overflow at + 3440x1440. Map and Detection workspaces keep a bounded 1,680 px operational + canvas, technical disclosures are collapsed and persisted AI internals now + use friendly labels in the ordinary result tables. diff --git a/frontend/src/components/detection/DetectionLab.tsx b/frontend/src/components/detection/DetectionLab.tsx index 6f04b9c8..23938534 100644 --- a/frontend/src/components/detection/DetectionLab.tsx +++ b/frontend/src/components/detection/DetectionLab.tsx @@ -25,8 +25,29 @@ function detectionQualityInterpretation(f1: number | null | undefined): string { return 'Onvoldoende betrouwbaar voor operationeel gebruik.' } +function detectionStatusLabel(status: string): string { + if (status === 'completed') return 'afgerond' + if (status === 'success') return 'geslaagd' + if (status === 'failed') return 'mislukt' + if (status === 'running') return 'bezig' + if (status === 'queued') return 'in wachtrij' + return status.replace(/_/g, ' ') +} + +function persistedDetectionModelLabel(modelName: string | null | undefined): string { + if (!modelName) return 'Gebouwdetectie' + if (modelName === 'yolo-configured') return 'Lokaal gebouwmodel' + if (modelName === 'manual-fixture-detector') return 'Testmodel' + if (modelName === 'yolo-placeholder') return 'Niet-geconfigureerd gebouwmodel' + return modelName +} + +function detectionClassLabel(className: string): string { + if (className.toLowerCase() === 'building') return 'Gebouw' + return className +} + function formatDetectionRunLabel(run: DetectionRunRead): string { - const status = run.status === 'completed' ? 'afgerond' : run.status const timestamp = run.finished_at ?? run.created_at const dateLabel = timestamp ? new Intl.DateTimeFormat('nl-BE', { @@ -34,7 +55,7 @@ function formatDetectionRunLabel(run: DetectionRunRead): string { timeStyle: 'short', }).format(new Date(timestamp)) : 'datum onbekend' - return `${run.model_name || 'Gebouwdetectie'} · ${status} · ${dateLabel}` + return `${persistedDetectionModelLabel(run.model_name)} · ${detectionStatusLabel(run.status)} · ${dateLabel}` } interface CalibrationRow { @@ -753,18 +774,16 @@ export function DetectionLab({ Type Zekerheid Model - Beeldtegel + Herkomst {visibleDetectionItems.map((detection) => ( - {detection.class_name} + {detectionClassLabel(detection.class_name)} {detection.confidence.toFixed(2)} - {detection.model_name} - - {formatSourceTilePath(detection.source_tile_path)} - + {persistedDetectionModelLabel(detection.model_name)} + Luchtbeeldtegel ))} @@ -1119,11 +1138,3 @@ function downloadJsonFile(filename: string, payload: unknown): void { function formatNullableNumber(value: number | null, digits: number): string { return typeof value === 'number' && Number.isFinite(value) ? value.toFixed(digits) : 'n.v.t.' } - -function formatSourceTilePath(path: string | null | undefined): string { - if (!path) { - return 'n.v.t.' - } - const parts = path.replace(/\\/g, '/').split('/').filter(Boolean) - return parts[parts.length - 1] ?? path -} diff --git a/frontend/src/components/segmentation/SegmentationLab.tsx b/frontend/src/components/segmentation/SegmentationLab.tsx index 8d9d1736..1ff5cb60 100644 --- a/frontend/src/components/segmentation/SegmentationLab.tsx +++ b/frontend/src/components/segmentation/SegmentationLab.tsx @@ -53,8 +53,29 @@ function segmentationRunLabel(run: SegmentationRunRead): string { const dateLabel = timestamp ? new Intl.DateTimeFormat('nl-BE', { dateStyle: 'short', timeStyle: 'short' }).format(new Date(timestamp)) : 'datum onbekend' - const status = run.status === 'completed' ? 'afgerond' : run.status - return `${run.model_name || 'Segmentatie'} · ${status} · ${dateLabel}` + return `${persistedSegmentationModelLabel(run.model_name)} · ${analysisStatusLabel(run.status)} · ${dateLabel}` +} + +function analysisStatusLabel(status: string): string { + if (status === 'completed') return 'afgerond' + if (status === 'success') return 'geslaagd' + if (status === 'failed') return 'mislukt' + if (status === 'running') return 'bezig' + if (status === 'queued') return 'in wachtrij' + return status.replace(/_/g, ' ') +} + +function persistedSegmentationModelLabel(modelName: string | null | undefined): string { + if (!modelName) return 'Segmentatie' + if (modelName === 'fixture-segmenter') return 'Testsegmentatie' + if (modelName === 'sam-placeholder') return 'SAM-model niet geconfigureerd' + if (modelName === 'yolo-seg-placeholder') return 'YOLO-segmentatie niet geconfigureerd' + return modelName +} + +function segmentationClassLabel(className: string): string { + if (className.toLowerCase() === 'building') return 'Gebouw' + return className } export function SegmentationLab({ @@ -371,17 +392,17 @@ export function SegmentationLab({ Zekerheid Oppervlakte m² Model - Brontegel + Herkomst {segmentationItems.map((segmentation) => ( - {segmentation.class_name} + {segmentationClassLabel(segmentation.class_name)} {segmentation.confidence?.toFixed(2) ?? 'n.v.t.'} {segmentation.area_m2?.toFixed(2) ?? 'n.v.t.'} - {segmentation.model_name} - {segmentation.tile_index ?? 'n.v.t.'} + {persistedSegmentationModelLabel(segmentation.model_name)} + {segmentation.tile_index == null ? 'n.v.t.' : 'Luchtbeeldtegel'} ))}