Add AI lab run readiness panels
This commit is contained in:
@@ -1000,3 +1000,11 @@ Added:
|
||||
- Added responsive styling and regression coverage for the Map quick-action grid.
|
||||
- Verified locally against the live demo state that the empty map state exposes two dataset actions and opens `demo_predicted_buildings.geojson` as a 2-feature map layer.
|
||||
- No API contracts, migrations, product capabilities, live provider fetching or AI/model dependency changes were introduced.
|
||||
|
||||
## Sprint 103 AI Lab run readiness (2026-06-24)
|
||||
|
||||
- Added compact run-readiness panels to Detection Lab and Segmentation Lab.
|
||||
- Detection readiness now shows raster dataset, model availability and the configured-YOLO tile manifest requirement before submitting a run.
|
||||
- Segmentation readiness now shows raster dataset, model availability and tile manifest provenance state before submitting a run.
|
||||
- Added regression coverage for the AI Lab readiness UI contract and styling.
|
||||
- No API contracts, migrations, product capabilities, live provider fetching or AI/model dependency changes were introduced.
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def test_detection_lab_exposes_run_readiness_contract() -> None:
|
||||
lab = (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
|
||||
assert "selectedDetectionModel = detectionModels.find" in lab
|
||||
assert "detectionRequiresTileManifest = selectedDetectionModelId === 'yolo-configured'" in lab
|
||||
assert "detectionTileManifestPath.trim().length > 0" in lab
|
||||
assert "detectionRunReady" in lab
|
||||
assert 'aria-label="Detection run readiness"' in lab
|
||||
assert "Run readiness" in lab
|
||||
assert "Raster dataset" in lab
|
||||
assert "Model availability" in lab
|
||||
assert "Tile manifest" in lab
|
||||
assert "Ready to submit" in lab
|
||||
|
||||
|
||||
def test_segmentation_lab_exposes_run_readiness_contract() -> None:
|
||||
lab = (ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
|
||||
assert "segmentationHasDataset" in lab
|
||||
assert "segmentationHasTileManifest = segmentationTileManifestPath.trim().length > 0" in lab
|
||||
assert "segmentationRunReady" in lab
|
||||
assert "selectedSegmentationModelConfigured" in lab
|
||||
assert "selectedSegmentationModelLimitation" in lab
|
||||
assert 'aria-label="Segmentation run readiness"' in lab
|
||||
assert "Run readiness" in lab
|
||||
assert "Raster dataset" in lab
|
||||
assert "Model availability" in lab
|
||||
assert "Tile manifest" in lab
|
||||
assert "Ready to submit" in lab
|
||||
|
||||
|
||||
def test_ai_lab_run_readiness_css_contract() -> None:
|
||||
css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8")
|
||||
|
||||
assert ".lab-readiness-panel" in css
|
||||
assert ".lab-readiness-panel-ready" in css
|
||||
assert ".lab-readiness-grid" in css
|
||||
assert ".lab-readiness-item" in css
|
||||
assert ".lab-readiness-item-ready" in css
|
||||
@@ -3722,3 +3722,28 @@ Limitations:
|
||||
|
||||
Next recommended pass:
|
||||
- After live validation, continue with the next V1 usability gap from the workbench flow rather than adding new model/provider scope.
|
||||
|
||||
## Sprint 103 AI Lab run readiness (2026-06-24)
|
||||
|
||||
Changed:
|
||||
- Added compact run-readiness panels to Detection Lab and Segmentation Lab.
|
||||
- Detection readiness now checks selected raster dataset, selected model availability and the configured-YOLO tile manifest requirement before a run is submitted.
|
||||
- Segmentation readiness now checks selected raster dataset, configured segmentation model state and whether a tile manifest is present for provenance.
|
||||
- Added shared AI Lab readiness styling and regression coverage in `backend/tests/test_sprint103_ai_lab_run_readiness.py`.
|
||||
- Updated `CHANGELOG.md` and `docs/TODO.md`.
|
||||
|
||||
Tested:
|
||||
- Red step: `python -m pytest backend\tests\test_sprint103_ai_lab_run_readiness.py -q` failed while the readiness panels and CSS contracts were absent.
|
||||
- `python -m pytest backend\tests\test_sprint103_ai_lab_run_readiness.py -q` (`3 passed`)
|
||||
- `python -m compileall backend/app`
|
||||
- `cd frontend && npm run typecheck`
|
||||
- `cd frontend && npm run build`
|
||||
|
||||
Open:
|
||||
- Full repository validation, commit, deploy and live browser verification are still pending for this pass.
|
||||
|
||||
Limitations:
|
||||
- Frontend readiness guidance only; no backend API, persistence, migration, provider fetching, AI dependency or model execution behavior changed.
|
||||
|
||||
Next recommended pass:
|
||||
- After deploy validation, continue with V1 usability work that reduces operator confusion without expanding frozen product scope.
|
||||
|
||||
@@ -369,3 +369,4 @@ This file now starts with the current implementation status. Older preparation/b
|
||||
- [x] Add raster pipeline readiness and guardrail surfaces for metadata, CRS, preview, tile manifest and clip-AOI handoff.
|
||||
- [x] Add useful default dataset context so Data, Map and Exports are immediately usable after project/demo load.
|
||||
- [x] Make raster tile handoff to Detection Lab auto-select the configured YOLO run form.
|
||||
- [x] Add AI Lab run-readiness checks for Detection and Segmentation before job submission.
|
||||
|
||||
@@ -84,6 +84,16 @@ export function DetectionLab({
|
||||
onSelectReferenceDataset,
|
||||
onRunQa,
|
||||
}: DetectionLabProps): JSX.Element {
|
||||
const selectedDetectionModel = detectionModels.find((model) => model.model_id === selectedDetectionModelId) ?? null
|
||||
const detectionRequiresTileManifest = selectedDetectionModelId === 'yolo-configured'
|
||||
const detectionHasDataset = selectedDetectionDatasetId.length > 0
|
||||
const detectionHasModel = selectedDetectionModel !== null
|
||||
const detectionModelReady = Boolean(selectedDetectionModel?.configured)
|
||||
const detectionHasTileManifest =
|
||||
!detectionRequiresTileManifest || detectionTileManifestPath.trim().length > 0
|
||||
const detectionRunReady =
|
||||
Boolean(selectedProjectId) && detectionHasDataset && detectionHasModel && detectionModelReady && detectionHasTileManifest
|
||||
|
||||
return (
|
||||
<section className="workspace-panel ai-lab-shell detection-lab-shell">
|
||||
<div className="panel-title-row">
|
||||
@@ -143,6 +153,44 @@ export function DetectionLab({
|
||||
<div className="lab-block">
|
||||
<div className="ai-lab-run-surface" aria-label="Detection run controls">
|
||||
<h3>Run detection</h3>
|
||||
<div
|
||||
className={detectionRunReady ? 'lab-readiness-panel lab-readiness-panel-ready' : 'lab-readiness-panel'}
|
||||
aria-label="Detection run readiness"
|
||||
>
|
||||
<div className="ai-lab-section-header">
|
||||
<div>
|
||||
<h3>Run readiness</h3>
|
||||
<p>Checks the selected dataset, model and tile manifest before submitting a detection job.</p>
|
||||
</div>
|
||||
<span className={detectionRunReady ? 'status-badge status-badge-ready' : 'status-badge'}>
|
||||
{detectionRunReady ? 'Ready to submit' : 'Blocked'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="lab-readiness-grid">
|
||||
<div className={detectionHasDataset ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
|
||||
<span>Raster dataset</span>
|
||||
<strong>{detectionHasDataset ? 'Selected' : 'Select a raster dataset'}</strong>
|
||||
</div>
|
||||
<div className={detectionModelReady ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
|
||||
<span>Model availability</span>
|
||||
<strong>
|
||||
{detectionModelReady
|
||||
? `${selectedDetectionModel?.display_name ?? selectedDetectionModelId} is configured`
|
||||
: selectedDetectionModel?.limitation_message ?? 'Select a configured model'}
|
||||
</strong>
|
||||
</div>
|
||||
<div className={detectionHasTileManifest ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
|
||||
<span>Tile manifest</span>
|
||||
<strong>
|
||||
{detectionRequiresTileManifest
|
||||
? detectionHasTileManifest
|
||||
? 'Provided for configured YOLO'
|
||||
: 'Required for configured YOLO'
|
||||
: 'Not required for this model'}
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{rasterDatasets.length === 0 ? (
|
||||
<div className="result-state result-state-empty">
|
||||
<strong>No raster datasets available for detection.</strong>
|
||||
|
||||
@@ -88,6 +88,11 @@ export function SegmentationLab({
|
||||
onSelectReferenceDataset,
|
||||
onRunQa,
|
||||
}: SegmentationLabProps): JSX.Element {
|
||||
const segmentationHasDataset = selectedSegmentationDatasetId.length > 0
|
||||
const segmentationHasTileManifest = segmentationTileManifestPath.trim().length > 0
|
||||
const segmentationRunReady =
|
||||
Boolean(selectedProjectId) && segmentationHasDataset && selectedSegmentationModelConfigured
|
||||
|
||||
return (
|
||||
<section className="workspace-panel ai-lab-shell segmentation-lab-shell">
|
||||
<div className="panel-title-row">
|
||||
@@ -147,6 +152,38 @@ export function SegmentationLab({
|
||||
<div className="lab-block">
|
||||
<div className="ai-lab-run-surface" aria-label="Segmentation run controls">
|
||||
<h3>Run segmentation</h3>
|
||||
<div
|
||||
className={segmentationRunReady ? 'lab-readiness-panel lab-readiness-panel-ready' : 'lab-readiness-panel'}
|
||||
aria-label="Segmentation run readiness"
|
||||
>
|
||||
<div className="ai-lab-section-header">
|
||||
<div>
|
||||
<h3>Run readiness</h3>
|
||||
<p>Checks the selected raster and segmenter state before submitting a segmentation job.</p>
|
||||
</div>
|
||||
<span className={segmentationRunReady ? 'status-badge status-badge-ready' : 'status-badge'}>
|
||||
{segmentationRunReady ? 'Ready to submit' : 'Blocked'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="lab-readiness-grid">
|
||||
<div className={segmentationHasDataset ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
|
||||
<span>Raster dataset</span>
|
||||
<strong>{segmentationHasDataset ? 'Selected' : 'Select a raster dataset'}</strong>
|
||||
</div>
|
||||
<div className={selectedSegmentationModelConfigured ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
|
||||
<span>Model availability</span>
|
||||
<strong>
|
||||
{selectedSegmentationModelConfigured
|
||||
? 'Selected model is configured'
|
||||
: selectedSegmentationModelLimitation ?? 'Select a configured segmentation model'}
|
||||
</strong>
|
||||
</div>
|
||||
<div className={segmentationHasTileManifest ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
|
||||
<span>Tile manifest</span>
|
||||
<strong>{segmentationHasTileManifest ? 'Provided for provenance' : 'Optional for the fixture segmenter'}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{rasterDatasets.length === 0 ? (
|
||||
<div className="result-state result-state-empty">
|
||||
<strong>No raster datasets available for segmentation.</strong>
|
||||
|
||||
@@ -2667,6 +2667,58 @@ button.entity-card {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.lab-readiness-panel {
|
||||
display: grid;
|
||||
gap: 0.6rem;
|
||||
min-width: 0;
|
||||
border-left: 4px solid #c9d8d1;
|
||||
border-radius: 8px;
|
||||
padding: 0.64rem 0.68rem;
|
||||
background: #f8fbf9;
|
||||
}
|
||||
|
||||
.lab-readiness-panel-ready {
|
||||
border-left-color: #2f7d56;
|
||||
background: #f5fbf6;
|
||||
}
|
||||
|
||||
.lab-readiness-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
|
||||
gap: 0.5rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.lab-readiness-item {
|
||||
display: grid;
|
||||
gap: 0.18rem;
|
||||
min-width: 0;
|
||||
border: 1px solid #d8e3de;
|
||||
border-radius: 7px;
|
||||
padding: 0.52rem 0.58rem;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.lab-readiness-item-ready {
|
||||
border-color: #b8dcc9;
|
||||
background: #fbfffc;
|
||||
}
|
||||
|
||||
.lab-readiness-item span {
|
||||
color: var(--muted);
|
||||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.lab-readiness-item strong {
|
||||
min-width: 0;
|
||||
color: var(--text);
|
||||
font-size: 0.88rem;
|
||||
line-height: 1.28;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.ai-lab-shell .model-list {
|
||||
grid-template-columns: repeat(auto-fit, minmax(12.5rem, 1fr));
|
||||
gap: 0.55rem;
|
||||
|
||||
Reference in New Issue
Block a user