Fix stable Statbel release identity
This commit is contained in:
@@ -27,6 +27,9 @@
|
|||||||
water-volume or fabricated bathymetry metric was added.
|
water-volume or fabricated bathymetry metric was added.
|
||||||
- Added Docker, single-container Unraid and Dockerman template controls for
|
- Added Docker, single-container Unraid and Dockerman template controls for
|
||||||
SPW/PICC and UrbIS.
|
SPW/PICC and UrbIS.
|
||||||
|
- Fixed the governed Statbel review gate so a new probe timestamp does not
|
||||||
|
masquerade as a changed publication; release URL, identifier, version and
|
||||||
|
catalog content hash remain mandatory stable identity fields.
|
||||||
|
|
||||||
## Autonomous Belgium and North Sea RC program (2026-07-17)
|
## Autonomous Belgium and North Sea RC program (2026-07-17)
|
||||||
|
|
||||||
|
|||||||
@@ -340,6 +340,14 @@ def test_tampered_source_or_review_is_rejected(tmp_path: Path) -> None:
|
|||||||
def test_catalog_drift_and_outside_evidence_path_are_rejected(tmp_path: Path) -> None:
|
def test_catalog_drift_and_outside_evidence_path_are_rejected(tmp_path: Path) -> None:
|
||||||
args = arguments(tmp_path)
|
args = arguments(tmp_path)
|
||||||
original = decision(args)
|
original = decision(args)
|
||||||
|
checked_later = json.loads(json.dumps(original))
|
||||||
|
checked_later["catalog_identity"]["catalog_checked_at"] = "2026-07-19T01:00:00Z"
|
||||||
|
|
||||||
|
MANAGER.require_catalog_unchanged(
|
||||||
|
{"release": original["release"], "catalog_identity": original["catalog_identity"]},
|
||||||
|
checked_later,
|
||||||
|
)
|
||||||
|
|
||||||
changed = json.loads(json.dumps(original))
|
changed = json.loads(json.dumps(original))
|
||||||
changed["catalog_identity"]["capabilities_sha256"] = "b" * 64
|
changed["catalog_identity"]["capabilities_sha256"] = "b" * 64
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,10 @@
|
|||||||
- Rechecked MDK bathymetry. Strict hostname validation still fails because the
|
- Rechecked MDK bathymetry. Strict hostname validation still fails because the
|
||||||
configured official hostname presents a `*.l27powered.eu` certificate, so
|
configured official hostname presents a `*.l27powered.eu` certificate, so
|
||||||
acquisition remains truthfully disabled.
|
acquisition remains truthfully disabled.
|
||||||
|
- Live national Statbel staging exposed and fixed a release-review blocker:
|
||||||
|
`catalog_checked_at` was incorrectly compared as publication identity.
|
||||||
|
Reviews now tolerate a later probe timestamp while still blocking any
|
||||||
|
identifier, release URL/version or catalog content-hash change.
|
||||||
- Focused backend and frontend suites passed before full release validation;
|
- Focused backend and frontend suites passed before full release validation;
|
||||||
full local and Tower evidence follows in the final P5 gate.
|
full local and Tower evidence follows in the final P5 gate.
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,12 @@ DEFAULT_EVIDENCE_ROOT = Path("/app/storage/operator-evidence/statbel-population-
|
|||||||
YEAR_PATTERN = re.compile(r"^20[0-9]{2}$")
|
YEAR_PATTERN = re.compile(r"^20[0-9]{2}$")
|
||||||
SHA256_PATTERN = re.compile(r"^[0-9a-f]{64}$")
|
SHA256_PATTERN = re.compile(r"^[0-9a-f]{64}$")
|
||||||
ACTIONABLE_STATUSES = {"update_available", "not_loaded"}
|
ACTIONABLE_STATUSES = {"update_available", "not_loaded"}
|
||||||
|
STABLE_CATALOG_IDENTITY_FIELDS = (
|
||||||
|
"metadata_identifier",
|
||||||
|
"metadata_url",
|
||||||
|
"remote_version",
|
||||||
|
"capabilities_sha256",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def parse_args() -> argparse.Namespace:
|
def parse_args() -> argparse.Namespace:
|
||||||
@@ -431,7 +437,13 @@ def load_staged_plan(
|
|||||||
|
|
||||||
|
|
||||||
def require_catalog_unchanged(plan: dict[str, Any], decision: dict[str, Any]) -> None:
|
def require_catalog_unchanged(plan: dict[str, Any], decision: dict[str, Any]) -> None:
|
||||||
if plan.get("release") != decision.get("release") or plan.get("catalog_identity") != decision.get("catalog_identity"):
|
planned_identity = plan.get("catalog_identity") or {}
|
||||||
|
current_identity = decision.get("catalog_identity") or {}
|
||||||
|
identity_changed = any(
|
||||||
|
planned_identity.get(field) != current_identity.get(field)
|
||||||
|
for field in STABLE_CATALOG_IDENTITY_FIELDS
|
||||||
|
)
|
||||||
|
if plan.get("release") != decision.get("release") or identity_changed:
|
||||||
raise RuntimeError("The official Statbel release evidence changed; create and review a new staged plan")
|
raise RuntimeError("The official Statbel release evidence changed; create and review a new staged plan")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user