diff --git a/CHANGELOG.md b/CHANGELOG.md index 70ab8423..ce361cbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Added a compact frontend status strip for project, AOI, datasets, active map layer, QA/QC and exports. - The strip is driven by existing App state and suggests the next operator action in the V1 loop. - Hardened the MapLibre component so GeoJSON sources/layers wait for the map style to finish loading before updates run. +- Hardened the offline demo workflow so duplicate historical demo projects prefer complete fixture state before repairing incomplete state. - Added regression coverage to ensure the strip remains wired without introducing new API calls. - No backend behavior, migrations, API contracts, provider downloads, AI inference or new dependencies were introduced. diff --git a/backend/app/services/demo_workflow_service.py b/backend/app/services/demo_workflow_service.py index 8242aa62..72a34354 100644 --- a/backend/app/services/demo_workflow_service.py +++ b/backend/app/services/demo_workflow_service.py @@ -51,12 +51,42 @@ class DemoWorkflowService: @staticmethod def _find_existing_project(db: Session) -> Project | None: - return ( + projects = ( db.query(Project) .filter(Project.name == DemoWorkflowService.PROJECT_NAME) .filter(Project.status != "deleted") + .order_by(Project.created_at.asc()) + .all() + ) + for project in projects: + if DemoWorkflowService._has_complete_demo_state(db, project.id): + return project + return projects[0] if projects else None + + @staticmethod + def _has_complete_demo_state(db: Session, project_id: UUID) -> bool: + area = db.query(Area).filter(Area.project_id == project_id).first() + reference = ( + db.query(Dataset) + .filter(Dataset.project_id == project_id) + .filter(Dataset.dataset_role == "reference") + .filter(Dataset.source_name == "fixture") .first() ) + candidate = ( + db.query(Dataset) + .filter(Dataset.project_id == project_id) + .filter(Dataset.dataset_role == "source") + .filter(Dataset.source_name == "fixture") + .first() + ) + quality_check = ( + db.query(QualityCheck) + .filter(QualityCheck.project_id == project_id) + .filter(QualityCheck.check_type == "demo_candidate_vs_reference") + .first() + ) + return bool(area and reference and candidate and quality_check) @staticmethod def _create_area(db: Session, project_id: UUID) -> Area: diff --git a/backend/tests/test_sprint15_demo_workflow.py b/backend/tests/test_sprint15_demo_workflow.py index d0ef7dc2..e49fd3a9 100644 --- a/backend/tests/test_sprint15_demo_workflow.py +++ b/backend/tests/test_sprint15_demo_workflow.py @@ -66,3 +66,12 @@ def test_demo_workflow_service_supports_container_fixture_mount() -> None: assert "project = existing" in service assert "if not reference:" in service assert "if not candidate:" in service + + +def test_demo_workflow_prefers_complete_existing_demo_project() -> None: + service = (DemoWorkflowService._repo_root() / "backend" / "app" / "services" / "demo_workflow_service.py").read_text(encoding="utf-8") + + assert "_has_complete_demo_state" in service + assert "order_by(Project.created_at.asc())" in service + assert "if DemoWorkflowService._has_complete_demo_state(db, project.id):" in service + assert "return projects[0] if projects else None" in service diff --git a/docs/CODEX_EXECUTION_LOG.md b/docs/CODEX_EXECUTION_LOG.md index 6ddaa61a..1d869277 100644 --- a/docs/CODEX_EXECUTION_LOG.md +++ b/docs/CODEX_EXECUTION_LOG.md @@ -3,7 +3,7 @@ Changed: - Added `frontend/src/components/WorkbenchStatusStrip.tsx` to summarize existing V1 state for project, AOI, datasets, active map layer, QA/QC and exports. - Wired the status strip into `frontend/src/App.tsx` using existing orchestration state only. -- Added compact status-strip styling and regression tests for the frontend wiring contract.`n- Hardened `frontend/src/components/GeoMap.tsx` so MapLibre source/layer updates wait for style readiness before adding sources. +- Added compact status-strip styling and regression tests for the frontend wiring contract.`n- Hardened `frontend/src/components/GeoMap.tsx` so MapLibre source/layer updates wait for style readiness before adding sources.`n- Hardened demo project lookup so duplicate historical demo projects prefer complete fixture state before repairing incomplete state. - Updated frontend README, TODO and changelog. Tested: