From 2d0e02e16c9efc7042e1daa6a4d392766771d606 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 16 Jul 2026 19:39:14 +0200 Subject: [PATCH] Validate legacy GRB snapshot evidence --- .../app/services/temporal_analysis_service.py | 43 ++++++++++++++++++- .../test_sprint187_temporal_map_foundation.py | 19 +++++++- docs/CODEX_EXECUTION_LOG.md | 17 ++++++-- 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/backend/app/services/temporal_analysis_service.py b/backend/app/services/temporal_analysis_service.py index 77506be0..19023613 100644 --- a/backend/app/services/temporal_analysis_service.py +++ b/backend/app/services/temporal_analysis_service.py @@ -500,7 +500,10 @@ class TemporalAnalysisService: dataset.source_name != "grb" or not str(dataset.temporal_series_key or "").startswith("grb:") or source_metadata.get("authority_level") != "authoritative" - or source_metadata.get("geometry_clipped_to_area") is not True + or not TemporalAnalysisService._has_governed_grb_area_contract( + source_metadata, + provenance, + ) or provenance.get("operator_tool") not in TemporalAnalysisService.GOVERNED_GRB_IDENTITY_OPERATORS or provenance.get("reference_truncated") is not False ): @@ -515,6 +518,44 @@ class TemporalAnalysisService: prefixes = tuple(sorted(f"{str(collection)}:{str(collection)}." for collection in collections)) return "grb_ogc_feature_id", prefixes + @staticmethod + def _has_governed_grb_area_contract( + source_metadata: dict[str, Any], + provenance: dict[str, Any], + ) -> bool: + if source_metadata.get("geometry_clipped_to_area") is True: + return True + + partition_checksums = provenance.get("partition_checksums") + artifact_checksum = str(provenance.get("artifact_sha256") or "") + has_valid_checksum = len(artifact_checksum) == 64 and all( + character in "0123456789abcdefABCDEF" for character in artifact_checksum + ) + has_valid_partition_checksums = ( + isinstance(partition_checksums, dict) + and len(partition_checksums) == 28 + and all( + len(str(checksum)) == 64 + and all(character in "0123456789abcdefABCDEF" for character in str(checksum)) + for checksum in partition_checksums.values() + ) + ) + return ( + source_metadata.get("coverage_scope") == "kempen-transport-region" + and source_metadata.get("scope_type") == "transport_region" + and source_metadata.get("member_count") == 28 + and source_metadata.get("partition_count") == 28 + and source_metadata.get("partition_strategy") + in { + "municipality_bbox_maximum_boundary_intersection", + "municipality_bbox_maximum_same_dimension_intersection", + } + and bool(provenance.get("manifest_path")) + and bool(provenance.get("source_url") or provenance.get("source_urls")) + and has_valid_checksum + and has_valid_partition_checksums + ) + @staticmethod def _rows_match_identity_contract(rows: list[VectorFeature], prefixes: tuple[str, ...]) -> bool: identities = [str(row.source_feature_id or "").strip() for row in rows] diff --git a/backend/tests/test_sprint187_temporal_map_foundation.py b/backend/tests/test_sprint187_temporal_map_foundation.py index 92cbc16d..2a4db971 100644 --- a/backend/tests/test_sprint187_temporal_map_foundation.py +++ b/backend/tests/test_sprint187_temporal_map_foundation.py @@ -142,8 +142,12 @@ def governed_grb_dataset(*, project_id, observed_day: int) -> Dataset: dataset.temporal_series_key = "grb:buildings:kempen-transport-region" dataset.source_metadata = { "authority_level": "authoritative", - "geometry_clipped_to_area": True, "collection": "GRB/GBG", + "coverage_scope": "kempen-transport-region", + "scope_type": "transport_region", + "member_count": 28, + "partition_count": 28, + "partition_strategy": "municipality_bbox_maximum_boundary_intersection", "selection_aggregation": { "method": "intersection_area", "label": "Bebouwde grondoppervlakte", @@ -153,7 +157,13 @@ def governed_grb_dataset(*, project_id, observed_day: int) -> Dataset: dataset.provenance_metadata = { "operator_tool": "provision_regional_grb_buildings.py", "reference_truncated": False, + "manifest_path": "/storage/operator/grb/manifest.json", + "source_url": "https://geo.api.vlaanderen.be/GRB/ogc/features/v1/collections/GBG/items", + "artifact_sha256": "a" * 64, + "partition_checksums": {f"{index:05d}": "b" * 64 for index in range(28)}, } + if observed_day > 14: + dataset.source_metadata["geometry_clipped_to_area"] = True return dataset @@ -414,6 +424,13 @@ def test_governed_grb_object_history_fails_closed_for_unverified_identity() -> N assert warnings == ["De geselecteerde objecten bevatten geen volledig verifieerbare stabiele bronidentiteit."] +def test_legacy_grb_identity_requires_complete_partition_evidence() -> None: + dataset = governed_grb_dataset(project_id=uuid4(), observed_day=14) + dataset.provenance_metadata["partition_checksums"] = {"13025": "b" * 64} + + assert TemporalAnalysisService._identity_contract(dataset) is None + + def test_temporal_frontend_and_official_operator_contracts_exist() -> None: workspace = (ROOT / "frontend/src/components/map/MapWorkspace.tsx").read_text(encoding="utf-8") temporal_api = (ROOT / "frontend/src/services/api/temporal.ts").read_text(encoding="utf-8") diff --git a/docs/CODEX_EXECUTION_LOG.md b/docs/CODEX_EXECUTION_LOG.md index e9f34fd0..6c49e4b7 100644 --- a/docs/CODEX_EXECUTION_LOG.md +++ b/docs/CODEX_EXECUTION_LOG.md @@ -9413,9 +9413,18 @@ Boundaries: to metric-only output whenever source identity cannot be proven. Validation so far: -- Focused GRB operator and temporal tests pass: 39 tests. -- Frontend typecheck and production build pass. +- Focused GRB operator and temporal tests pass: 39 tests in the first pass. +- The complete readiness gate passes after the legacy regression addition: 786 + backend tests, 110 documented routes, one Alembic head, frontend typecheck, + production build and packaged script checks. +- The first Tower API comparison correctly exposed that the 14 July snapshots + predated the explicit `geometry_clipped_to_area` marker. Those snapshots are + now accepted only when their actual retained legacy evidence is complete: + the exact Kempen scope, 28 members and partitions, an approved assignment + strategy, manifest/source evidence, artifact checksum and 28 valid partition + checksums. A focused negative test proves incomplete partition evidence still + fails closed. Next: -- Run the full release gate, deploy, verify a real bounded GRB edition delta - through the canonical API and inspect the daily Evolution UI on Tower. +- Redeploy the legacy-contract correction, verify a real bounded GRB edition + delta through the canonical API and inspect the daily Evolution UI on Tower.