Validate legacy GRB snapshot evidence
This commit is contained in:
@@ -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]
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user