From ad288df76115ceeacb9bea176fb70dab14b51514 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 18 Jun 2026 23:38:06 +0200 Subject: [PATCH] Polish export handoff workflow --- CHANGELOG.md | 8 ++ .../test_sprint64_export_handoff_polish.py | 33 +++++ docs/CODEX_EXECUTION_LOG.md | 27 ++++ docs/TODO.md | 1 + frontend/README.md | 1 + .../src/components/exports/ExportCenter.tsx | 134 +++++++++++++----- frontend/src/styles/app.css | 101 +++++++++++++ 7 files changed, 273 insertions(+), 32 deletions(-) create mode 100644 backend/tests/test_sprint64_export_handoff_polish.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 93051b9b..994c3377 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -695,3 +695,11 @@ Added: - Added regression coverage for AppError, HTTPException and validation-error envelopes. - Added static frontend parser coverage so API client error parsing does not drift silently. - No migrations, product capabilities, live provider fetching or AI/model dependency changes were introduced. + +## Sprint 64 export/report handoff polish (2026-06-18) + +- Added a handoff readiness summary to the Export Center for selected dataset, detection run, segmentation run and latest artifact context. +- Grouped existing export actions into scan-friendly artifact cards for project report, metadata, vector GeoJSON, detection GeoJSON and segmentation GeoJSON. +- Added clearer export history provenance with formatted export-type badges, analysis-run ids and created timestamps when available. +- Added regression coverage for the Export Center handoff structure and responsive styling. +- No API contracts, migrations, product capabilities, live provider fetching or AI/model dependency changes were introduced. diff --git a/backend/tests/test_sprint64_export_handoff_polish.py b/backend/tests/test_sprint64_export_handoff_polish.py new file mode 100644 index 00000000..0cd1e25d --- /dev/null +++ b/backend/tests/test_sprint64_export_handoff_polish.py @@ -0,0 +1,33 @@ +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] + + +def test_export_center_surfaces_handoff_readiness_context() -> None: + export_center = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportCenter.tsx").read_text( + encoding="utf-8" + ) + + assert 'className="handoff-summary-card"' in export_center + assert 'className="handoff-readiness-grid"' in export_center + assert 'className="handoff-action-grid"' in export_center + assert 'className="export-type-badge"' in export_center + assert "Latest handoff artifact" in export_center + assert "Project report" in export_center + assert "Detection run" in export_center + assert "Segmentation run" in export_center + assert "selectedDetectionRunId || 'none'" in export_center + assert "selectedSegmentationRunId || 'none'" in export_center + + +def test_export_handoff_polish_has_responsive_styles() -> None: + css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8") + + assert ".handoff-summary-card" in css + assert ".handoff-readiness-grid" in css + assert ".handoff-action-grid" in css + assert ".export-type-badge" in css + assert ".handoff-action-card" in css + assert "grid-template-columns: repeat(4, minmax(0, 1fr));" in css + assert ".handoff-action-grid" in css and "@media (max-width: 980px)" in css diff --git a/docs/CODEX_EXECUTION_LOG.md b/docs/CODEX_EXECUTION_LOG.md index ef870748..a6541532 100644 --- a/docs/CODEX_EXECUTION_LOG.md +++ b/docs/CODEX_EXECUTION_LOG.md @@ -2460,3 +2460,30 @@ Limitations: Next recommended pass: - Continue with export/report handoff polish or add a live browser smoke that explicitly visits every workspace after deployment. + +## Sprint 64 Export/report handoff polish (2026-06-18) + +Changed: +- Added a handoff readiness card to the Export Center using existing project, selected dataset, selected detection run, selected segmentation run and latest export state. +- Reworked existing export actions into artifact cards for refresh, project report, project metadata, selected vector GeoJSON, detection GeoJSON and segmentation GeoJSON. +- Added formatted export-type badges and extra export-card provenance for analysis-run ids and created timestamps when those fields are available. +- Added responsive styling for handoff summary/action cards. +- Added `backend/tests/test_sprint64_export_handoff_polish.py`. +- Updated `frontend/README.md`, `docs/TODO.md` and `CHANGELOG.md`. + +Tested: +- Red step: `python -m pytest backend/tests/test_sprint64_export_handoff_polish.py -q` failed on missing handoff summary/action structure and styles. +- `python -m pytest backend/tests/test_sprint64_export_handoff_polish.py backend/tests/test_sprint51_quality_export_polish.py -q` (`4 passed`) +- `cd frontend && npm run typecheck` +- `cd frontend && npm run build` +- Local Chrome/Playwright check against `http://127.0.0.1:5175` with live API proxy: Exports workspace opened, handoff summary/actions present, no console warnings/errors, no horizontal page overflow on desktop or mobile. +- `bash scripts/run_readiness_check.sh` (`233 passed`) + +Open: +- Deploy Tower and run live browser verification after this pass. + +Limitations: +- This is a frontend handoff usability pass only. It does not add export endpoints, change export persistence, introduce provider fetching or add AI/model behavior. + +Next recommended pass: +- Run a live browser smoke across Exports and Overview after deployment, then continue with report artifact readability if the exported HTML itself needs visual polish. diff --git a/docs/TODO.md b/docs/TODO.md index 2213403c..cee7a964 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -336,3 +336,4 @@ This file now starts with the current implementation status. Older preparation/b - [x] Expand golden datasets beyond the original single building QA fixture pair. - [x] Add workbench visual polish pass for command bar, panel surfaces, empty states and mobile nav density. - [x] Add map/result overlay ergonomics for active layer provenance and feature property summaries. +- [x] Add export/report handoff polish for artifact readiness, action grouping and export provenance. diff --git a/frontend/README.md b/frontend/README.md index 3c6dd942..55814d69 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -220,6 +220,7 @@ The workbench now uses a task-based shell instead of a single long panel stack. - Offline demo workflow orchestration lives in `src/hooks/useDemoWorkflow.ts`, because it coordinates project, dataset, map, QA/QC, detection, segmentation and export state after the backend fixture seed. - Workbench bootstrap and reload effects live in `src/hooks/useWorkbenchBootstrap.ts`, keeping `App.tsx` focused on composing hooks into panels. - The workbench shell includes a compact command bar, consistent raised/sunken surfaces, structured empty states and scroll-safe AI result tables to keep the V1 workflow usable across desktop and mobile widths. +- The Export Center includes a handoff readiness summary, grouped artifact actions and provenance-rich export cards so report/GeoJSON handoff stays understandable in long-running demo projects. ## Workbench shell refactor diff --git a/frontend/src/components/exports/ExportCenter.tsx b/frontend/src/components/exports/ExportCenter.tsx index 7e60aefb..d9fa3bdb 100644 --- a/frontend/src/components/exports/ExportCenter.tsx +++ b/frontend/src/components/exports/ExportCenter.tsx @@ -43,6 +43,14 @@ function compactPath(value: string): string { return parts.slice(-2).join('/') } +function formatExportType(value: string): string { + return value + .split('_') + .filter(Boolean) + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join(' ') +} + function exportMatchesSearch(item: ExportRead, query: string): boolean { const normalizedQuery = query.trim().toLowerCase() if (!normalizedQuery) { @@ -79,6 +87,7 @@ export function ExportCenter({ const canExportDataset = Boolean(selectedDataset && isVectorDatasetType(selectedDataset.dataset_type)) const geojsonExports = exports.filter((item) => item.export_type.includes('geojson')).length const reportExports = exports.filter((item) => item.export_type === 'project_report_html').length + const handoffStatus = selectedProjectId ? 'project selected' : 'select project' const exportTypes = useMemo(() => Array.from(new Set(exports.map((item) => item.export_type))).sort(), [exports]) const exportStatuses = useMemo(() => Array.from(new Set(exports.map((item) => item.status))).sort(), [exports]) const filteredExports = useMemo( @@ -117,43 +126,102 @@ export function ExportCenter({ {selectedDataset?.name ?? 'none'} +
+
+
+

Handoff readiness

+

Pick the artifact that matches the current review state before sharing data outside the workbench.

+
+ {handoffStatus} +
+
+
+ Vector dataset + {canExportDataset ? selectedDataset?.name : 'none ready'} +
+
+ Detection run + {selectedDetectionRunId || 'none'} +
+
+ Segmentation run + {selectedSegmentationRunId || 'none'} +
+
+ Latest handoff artifact + {latestExport ? formatExportType(latestExport.export_type) : 'none'} +
+
+
- - - - - - +
+
+ Refresh + Export registry +

Reload the persisted artifact list for the selected project.

+ +
+
+ Project report + HTML summary +

Generate the human-readable V1 handoff report from persisted project state.

+ +
+
+ Metadata + Project JSON +

Export project, dataset, QA and artifact metadata for machine-readable review.

+ +
+
+ Vector dataset + Selected GeoJSON +

Export the active vector/reference dataset when it is selected and ready.

+ +
+
+ Detection run + Detections GeoJSON +

Export persisted detection geometries for the selected analysis run.

+ +
+
+ Segmentation run + Segmentations GeoJSON +

Export persisted segmentation polygons for the selected analysis run.

+ +
+
{exportError ?

{exportError}

: null} {latestExport ? (
Latest export - {latestExport.export_type} + {formatExportType(latestExport.export_type)}

{latestExport.path}

) : null} @@ -233,9 +301,11 @@ export function ExportCenter({
  • - {item.export_type} + {formatExportType(item.export_type)}
    export id: {item.id} + {item.analysis_run_id ? analysis run: {item.analysis_run_id} : null} + {item.created_at ? created: {item.created_at} : null}
    {item.status} diff --git a/frontend/src/styles/app.css b/frontend/src/styles/app.css index a94314ee..2237c9c2 100644 --- a/frontend/src/styles/app.css +++ b/frontend/src/styles/app.css @@ -1202,6 +1202,105 @@ button.entity-card { text-align: left; } +.handoff-summary-card { + display: grid; + gap: 0.72rem; + margin-top: 0.8rem; + border: 1px solid rgba(15, 118, 110, 0.24); + border-radius: 8px; + padding: 0.85rem; + background: linear-gradient(180deg, #f5fbf8, #ffffff); +} + +.handoff-summary-card .panel-title-row { + margin-bottom: 0; +} + +.handoff-readiness-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 0.55rem; +} + +.handoff-readiness-grid > div { + min-width: 0; + border: 1px solid var(--line); + border-radius: 7px; + padding: 0.62rem; + background: #ffffff; +} + +.handoff-readiness-grid span, +.handoff-action-card span { + display: block; + color: var(--muted); + font-size: 0.68rem; + font-weight: 800; + letter-spacing: 0.05em; + text-transform: uppercase; +} + +.handoff-readiness-grid strong, +.handoff-action-card strong { + display: block; + margin-top: 0.22rem; + overflow: hidden; + font-size: 0.88rem; + line-height: 1.25; + text-overflow: ellipsis; + white-space: nowrap; +} + +.handoff-action-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 0.6rem; + grid-column: 1 / -1; + width: 100%; +} + +.handoff-action-card { + display: grid; + min-width: 0; + gap: 0.35rem; + border: 1px solid var(--line); + border-radius: 8px; + padding: 0.72rem; + background: #ffffff; +} + +.handoff-action-card-primary { + border-color: rgba(15, 118, 110, 0.28); + background: var(--accent-soft); +} + +.handoff-action-card p { + min-height: 2.55rem; + margin: 0; + color: var(--muted); + font-size: 0.84rem; + line-height: 1.36; +} + +.handoff-action-card button { + width: 100%; + margin-top: 0; +} + +.export-type-badge { + display: inline-flex; + max-width: 100%; + align-items: center; + border: 1px solid rgba(15, 118, 110, 0.22); + border-radius: 999px; + padding: 0.18rem 0.54rem; + background: var(--accent-soft); + color: var(--accent-strong); + font-size: 0.74rem; + font-weight: 850; + line-height: 1.2; +} + .latest-export-card { display: grid; gap: 0.2rem; @@ -1575,8 +1674,10 @@ button.entity-card { .layer-provenance-rail, .lab-form-grid, .quality-summary-grid, + .handoff-readiness-grid, .quality-score-row, .export-action-grid, + .handoff-action-grid, .export-history-controls, .inspector-action-bar, .list-limit-banner {