diff --git a/CHANGELOG.md b/CHANGELOG.md index a6b557b9..f33a44eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ # Changelog +## Sprint 100 raster tile Segmentation Lab handoff (2026-06-24) + +- Added Segmentation Lab tile manifest state and wired it into the existing segmentation run request `tile_manifest_path`. +- Added a raster inspector handoff action that fills the selected raster dataset and tile manifest path in Segmentation Lab. +- Mirrored the existing Detection Lab manifest input pattern without adding new backend routes, migrations, AI dependencies or model behavior. +- Added regression coverage for the segmentation handoff wiring. + ## Sprint 99 raster tile Detection Lab handoff (2026-06-23) - Surfaced the latest persisted `raster.tile` manifest path in the raster dataset inspector. diff --git a/backend/tests/test_sprint100_segmentation_manifest_handoff.py b/backend/tests/test_sprint100_segmentation_manifest_handoff.py new file mode 100644 index 00000000..9256662f --- /dev/null +++ b/backend/tests/test_sprint100_segmentation_manifest_handoff.py @@ -0,0 +1,28 @@ +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] + + +def test_raster_tile_manifest_can_handoff_to_segmentation_lab() -> None: + app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") + hook = (ROOT / "frontend" / "src" / "hooks" / "useSegmentationWorkflow.ts").read_text(encoding="utf-8") + detail_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetDetailPanel.tsx").read_text(encoding="utf-8") + raster_controls = (ROOT / "frontend" / "src" / "components" / "datasets" / "RasterControls.tsx").read_text(encoding="utf-8") + segmentation_lab = (ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx").read_text( + encoding="utf-8", + ) + + assert "segmentationTileManifestPath" in hook + assert "setSegmentationTileManifestPath" in hook + assert "tile_manifest_path: segmentationTileManifestPath.trim() || null" in hook + assert "segmentationTileManifestPath={segmentationTileManifestPath}" in app + assert "onSetTileManifestPath={setSegmentationTileManifestPath}" in app + assert "onUseTileManifestForSegmentation: useRasterTileManifestForSegmentation" in app + assert "setSegmentationTileManifestPath(manifestPath)" in app + assert "setSelectedSegmentationDatasetId(selectedDataset.id)" in app + assert "onUseTileManifestForSegmentation" in detail_panel + assert "Use in Segmentation Lab" in raster_controls + assert "disabled={!latestRasterTileManifestPath}" in raster_controls + assert "Tile manifest" in segmentation_lab + assert "Raster tile manifest path" in segmentation_lab diff --git a/docs/CODEX_EXECUTION_LOG.md b/docs/CODEX_EXECUTION_LOG.md index a5917c6d..844f8920 100644 --- a/docs/CODEX_EXECUTION_LOG.md +++ b/docs/CODEX_EXECUTION_LOG.md @@ -1,3 +1,34 @@ +## Sprint 100 raster tile Segmentation Lab handoff (2026-06-24) + +Changed: +- Added `segmentationTileManifestPath` state to `frontend/src/hooks/useSegmentationWorkflow.ts`. +- Sent `tile_manifest_path` in existing segmentation run requests when the field is populated. +- Added a `Tile manifest` input to `frontend/src/components/segmentation/SegmentationLab.tsx`. +- Added a `Use in Segmentation Lab` handoff beside the existing Detection Lab handoff in `frontend/src/components/datasets/RasterControls.tsx`. +- Wired the handoff through `frontend/src/components/datasets/DatasetDetailPanel.tsx` and `frontend/src/App.tsx`. +- Updated `frontend/README.md` and `CHANGELOG.md`. +- Added `backend/tests/test_sprint100_segmentation_manifest_handoff.py`. + +Validation: +- RED: `python -m pytest backend\tests\test_sprint100_segmentation_manifest_handoff.py -q` failed before implementation because segmentation manifest state and handoff wiring did not exist. +- `python -m pytest backend\tests\test_sprint100_segmentation_manifest_handoff.py -q` passed. +- `cd frontend && npm run typecheck` passed. +- `python -m pytest backend\tests\test_sprint100_segmentation_manifest_handoff.py backend\tests\test_sprint99_raster_ui_handoff.py backend\tests\test_sprint88_ai_lab_density.py backend\tests\test_sprint9_segmentation_foundation.py -q` passed: 17 tests. +- `cd frontend && npm run build` passed. +- `python -m compileall backend/app` passed. +- `python -m pytest -q` from `backend/` passed: 323 tests. +- `bash scripts/run_readiness_check.sh` passed. +- `cd backend && python -m alembic heads` passed: `202606120900 (head)`. +- `cd backend && python -m alembic upgrade head --sql` passed. +- `bash -n scripts/live_migration_smoke.sh` passed. + +Limitations: +- Frontend handoff only; no backend route, migration, provider fetching, AI dependency or model behavior changes. +- Segmentation remains governed by the existing model registry and explicit-run controls. + +Next recommended pass: +- Run full readiness, redeploy, then validate live bundle markers and raster demo smoke on `http://192.168.10.150:1202`. + ## Sprint 99 raster tile Detection Lab handoff (2026-06-23) Changed: diff --git a/frontend/README.md b/frontend/README.md index 6d4322b0..85f3f512 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -277,7 +277,7 @@ AI Lab run controls explicitly explain when no raster dataset is available, inst - When the Map workspace has no active result layer, it lists ready vector/GeoJSON datasets as direct quick actions so populated demo projects can jump straight from the empty state to map inspection. - The Data catalog shows a compact selected/reference/candidate/source summary and scan-friendly badges. Persisted `reference` datasets are shown as Reference, non-reference vector/GeoJSON layers are shown as QA Candidates for workbench scanning, and raster/other uploads remain Source. - Dataset cards explain the recommended next action and use compact two-line action buttons for inspect, map, export/QA and metadata refresh. Disabled actions keep a visible reason, such as `Vector/GeoJSON only`. -- Raster controls show the latest generated tile manifest path from persisted `raster.tile` jobs and can hand that path directly to Detection Lab with the selected raster dataset. +- Raster controls show the latest generated tile manifest path from persisted `raster.tile` jobs and can hand that path directly to Detection Lab or Segmentation Lab with the selected raster dataset. - The QA/QC workspace shows candidate/reference handoff cards and resolves persisted quality-check dataset IDs back to dataset names when the datasets are loaded in the current project context. - The QA/QC workspace includes a selected-check evidence drilldown with candidate/reference provenance, false-positive/negative metric evidence, map handoff context and parameters/findings JSON. diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index ccb73835..26b69ed5 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -271,6 +271,7 @@ function App(): JSX.Element { selectedSegmentationDatasetId, selectedSegmentationModelId, selectedSegmentationModel, + segmentationTileManifestPath, segmentationConfidenceThreshold, runningSegmentation, segmentationRunResult, @@ -294,6 +295,7 @@ function App(): JSX.Element { resetSegmentationForProject, setSelectedSegmentationDatasetId, setSelectedSegmentationModelId, + setSegmentationTileManifestPath, setSegmentationConfidenceThreshold, setSelectedSegmentationRunId, setSegmentationClassFilter, @@ -306,6 +308,15 @@ function App(): JSX.Element { loadProjectData, loadQualityChecks, }) + const useRasterTileManifestForSegmentation = () => { + const manifestPath = latestRasterTileManifestPath.trim() + if (!manifestPath || !selectedDataset || selectedDataset.dataset_type !== 'raster') { + return + } + setSegmentationTileManifestPath(manifestPath) + setSelectedSegmentationDatasetId(selectedDataset.id) + setActiveWorkspace('ai') + } const { exports, latestExport, @@ -859,6 +870,7 @@ function App(): JSX.Element { segmentationModelError={segmentationModelError} selectedSegmentationDatasetId={selectedSegmentationDatasetId} selectedSegmentationModelId={selectedSegmentationModelId} + segmentationTileManifestPath={segmentationTileManifestPath} segmentationConfidenceThreshold={segmentationConfidenceThreshold} runningSegmentation={runningSegmentation} segmentationRunResult={segmentationRunResult} @@ -881,6 +893,7 @@ function App(): JSX.Element { onLoadModels={loadSegmentationModels} onSelectDataset={setSelectedSegmentationDatasetId} onSelectModel={setSelectedSegmentationModelId} + onSetTileManifestPath={setSegmentationTileManifestPath} onSetConfidenceThreshold={setSegmentationConfidenceThreshold} onRunSegmentation={runSegmentation} onLoadRuns={() => loadSegmentationRuns()} @@ -999,6 +1012,7 @@ function App(): JSX.Element { onRunRasterClip: runRasterClip, onRunRasterTile: runRasterTile, onUseTileManifestForDetection: useRasterTileManifestForDetection, + onUseTileManifestForSegmentation: useRasterTileManifestForSegmentation, onRunRasterNdvi: runRasterNdvi, onRunRasterNdwi: runRasterNdwi, onRunRasterNdbi: runRasterNdbi, diff --git a/frontend/src/components/datasets/DatasetDetailPanel.tsx b/frontend/src/components/datasets/DatasetDetailPanel.tsx index ba7365f6..6f64a2b8 100644 --- a/frontend/src/components/datasets/DatasetDetailPanel.tsx +++ b/frontend/src/components/datasets/DatasetDetailPanel.tsx @@ -59,6 +59,7 @@ export interface DatasetDetailPanelProps { onRunRasterClip: () => void onRunRasterTile: () => void onUseTileManifestForDetection: () => void + onUseTileManifestForSegmentation: () => void onRunRasterNdvi: () => void onRunRasterNdwi: () => void onRunRasterNdbi: () => void @@ -142,6 +143,7 @@ export function DatasetDetailPanel({ onRunRasterClip, onRunRasterTile, onUseTileManifestForDetection, + onUseTileManifestForSegmentation, onRunRasterNdvi, onRunRasterNdwi, onRunRasterNdbi, @@ -209,6 +211,7 @@ export function DatasetDetailPanel({ onRunRasterClip={onRunRasterClip} onRunRasterTile={onRunRasterTile} onUseTileManifestForDetection={onUseTileManifestForDetection} + onUseTileManifestForSegmentation={onUseTileManifestForSegmentation} onRunRasterNdvi={onRunRasterNdvi} onRunRasterNdwi={onRunRasterNdwi} onRunRasterNdbi={onRunRasterNdbi} diff --git a/frontend/src/components/datasets/RasterControls.tsx b/frontend/src/components/datasets/RasterControls.tsx index f7d57142..8234ec93 100644 --- a/frontend/src/components/datasets/RasterControls.tsx +++ b/frontend/src/components/datasets/RasterControls.tsx @@ -40,6 +40,7 @@ interface RasterControlsProps { onRunRasterClip: () => void onRunRasterTile: () => void onUseTileManifestForDetection: () => void + onUseTileManifestForSegmentation: () => void onRunRasterNdvi: () => void onRunRasterNdwi: () => void onRunRasterNdbi: () => void @@ -93,6 +94,7 @@ export function RasterControls({ onRunRasterClip, onRunRasterTile, onUseTileManifestForDetection, + onUseTileManifestForSegmentation, onRunRasterNdvi, onRunRasterNdwi, onRunRasterNdbi, @@ -168,6 +170,9 @@ export function RasterControls({ +
diff --git a/frontend/src/components/segmentation/SegmentationLab.tsx b/frontend/src/components/segmentation/SegmentationLab.tsx index a404c488..b8bbb02d 100644 --- a/frontend/src/components/segmentation/SegmentationLab.tsx +++ b/frontend/src/components/segmentation/SegmentationLab.tsx @@ -13,6 +13,7 @@ interface SegmentationLabProps { segmentationModelError: string | null selectedSegmentationDatasetId: string selectedSegmentationModelId: string + segmentationTileManifestPath: string segmentationConfidenceThreshold: number runningSegmentation: boolean segmentationRunResult: SegmentationRunResponse | null @@ -35,6 +36,7 @@ interface SegmentationLabProps { onLoadModels: () => void onSelectDataset: (datasetId: string) => void onSelectModel: (modelId: string) => void + onSetTileManifestPath: (value: string) => void onSetConfidenceThreshold: (value: number) => void onRunSegmentation: () => void onLoadRuns: () => void @@ -52,6 +54,7 @@ export function SegmentationLab({ segmentationModelError, selectedSegmentationDatasetId, selectedSegmentationModelId, + segmentationTileManifestPath, segmentationConfidenceThreshold, runningSegmentation, segmentationRunResult, @@ -74,6 +77,7 @@ export function SegmentationLab({ onLoadModels, onSelectDataset, onSelectModel, + onSetTileManifestPath, onSetConfidenceThreshold, onRunSegmentation, onLoadRuns, @@ -183,6 +187,15 @@ export function SegmentationLab({ /> +