Keep orthophoto release evidence immutable
This commit is contained in:
@@ -25,6 +25,9 @@
|
||||
- Made official `YYYY.NN` orthophoto Datasets take precedence over rolling
|
||||
acquisition markers in source-catalog comparison. No API, migration,
|
||||
scheduler, browser fetch or automatic refresh was added.
|
||||
- Made staged, reviewed and applied release evidence write-once. An idempotent
|
||||
apply validates and reuses the existing evidence instead of changing its
|
||||
timestamp or authorization history.
|
||||
|
||||
## Sprint 230 Governed orthophoto release preflight (2026-07-17)
|
||||
|
||||
|
||||
@@ -277,6 +277,9 @@ def test_stage_fetches_one_allowlisted_map_and_writes_reviewable_rgb_evidence(tm
|
||||
assert manifest["normalized_geotiff"]["crs"] == "EPSG:31370"
|
||||
assert manifest["normalized_geotiff"]["band_count"] == 3
|
||||
assert Path(manifest["review_preview"]["path"]).read_bytes().startswith(b"\x89PNG")
|
||||
with pytest.raises(RuntimeError, match="will not be overwritten"):
|
||||
MANAGER.stage_artifacts(args, release_report, request, opener=opener)
|
||||
assert len(calls) == 1
|
||||
|
||||
|
||||
def test_getmap_redirect_and_response_limits_fail_closed(tmp_path: Path) -> None:
|
||||
@@ -397,6 +400,47 @@ def test_official_flight_dates_are_persisted_without_inventing_a_catalog_date()
|
||||
MANAGER.parse_flight_dates(["spring 2025"])
|
||||
|
||||
|
||||
def test_applied_evidence_is_immutable_on_idempotent_retry(tmp_path: Path) -> None:
|
||||
args, plan_path, plan, staged = staged_plan(tmp_path)
|
||||
manifest = MANAGER.validate_staged_artifacts(args, Path(staged["manifest_path"]))
|
||||
args.approve = True
|
||||
args.reviewer = "Jens"
|
||||
review = MANAGER.build_review_evidence(args, plan_path, plan, manifest)
|
||||
review_path = MANAGER.default_review_path(args, "2025.04")
|
||||
dataset = {
|
||||
"id": "dataset-1",
|
||||
"checksum_sha256": manifest["normalized_geotiff"]["sha256"],
|
||||
}
|
||||
final_report = report(status="current", local="2025.04")
|
||||
final_report["release"]["comparison_status"] = "same"
|
||||
|
||||
path, first = MANAGER.applied_evidence(
|
||||
args,
|
||||
plan_path,
|
||||
plan,
|
||||
review_path,
|
||||
review,
|
||||
dataset,
|
||||
final_report,
|
||||
reused=False,
|
||||
)
|
||||
original_bytes = path.read_bytes()
|
||||
_, second = MANAGER.applied_evidence(
|
||||
args,
|
||||
plan_path,
|
||||
plan,
|
||||
review_path,
|
||||
review,
|
||||
dataset,
|
||||
final_report,
|
||||
reused=True,
|
||||
)
|
||||
|
||||
assert second == first
|
||||
assert path.read_bytes() == original_bytes
|
||||
assert second["dataset_status"] == "imported"
|
||||
|
||||
|
||||
def test_release_manager_is_packaged_and_compiled_by_readiness() -> None:
|
||||
dockerfile = (ROOT / "deploy" / "unraid" / "Dockerfile.all-in-one").read_text(encoding="utf-8")
|
||||
readiness = (ROOT / "scripts" / "run_readiness_check.sh").read_text(encoding="utf-8")
|
||||
|
||||
@@ -9787,6 +9787,8 @@ Implemented:
|
||||
- Made the latest official `YYYY.NN` Dataset authoritative for source-catalog
|
||||
comparison even when a newer-imported rolling marker also exists. No API,
|
||||
migration, release table, Job type or frontend behavior changed.
|
||||
- Made stage, review and applied evidence write-once. Exact apply retries reuse
|
||||
the original checksum-bound evidence and never rewrite approval history.
|
||||
|
||||
Validation so far:
|
||||
- 38 focused Sprint 222/230/231 tests pass with deprecations treated as errors.
|
||||
|
||||
@@ -168,7 +168,9 @@ flight-day preflight identities; review binds a named approval; applied
|
||||
evidence binds both to the immutable Dataset id and checksum. Paths outside
|
||||
this root, changed files and changed provider/local state fail closed. Only the
|
||||
normalized GeoTIFF enters ordinary Dataset storage through DatasetService.
|
||||
Existing `most_recent_at_*` raster metadata is never rewritten.
|
||||
Existing release evidence and `most_recent_at_*` raster metadata are never
|
||||
rewritten. An exact apply retry validates and returns the original applied
|
||||
evidence rather than replacing it.
|
||||
|
||||
DHMV II DTM/DSM outputs are also normal raster Dataset files. The provider WCS
|
||||
returns multipart coverage data; GeoIntel retains response and extracted
|
||||
|
||||
@@ -446,6 +446,10 @@ def stage_artifacts(
|
||||
geotiff_path = directory / f"orthophoto_{edition}_{selection_key(list(args.bbox))}.tif"
|
||||
preview_path = directory / "review-preview.png"
|
||||
manifest_path = directory / "staged-manifest.json"
|
||||
if directory.exists() and any(directory.iterdir()):
|
||||
raise RuntimeError(
|
||||
f"Governed orthophoto evidence already exists and will not be overwritten: {directory}"
|
||||
)
|
||||
max_bytes = args.max_response_mb * 1024 * 1024
|
||||
content, content_type, final_url = fetch_map(
|
||||
request_identity,
|
||||
@@ -888,6 +892,22 @@ def applied_evidence(
|
||||
*,
|
||||
reused: bool,
|
||||
) -> tuple[Path, dict[str, Any]]:
|
||||
path = plan_path.with_name("applied-evidence.json")
|
||||
if path.is_file():
|
||||
existing = json.loads(path.read_text(encoding="utf-8"))
|
||||
if existing.get("applied_evidence_sha256") != canonical_sha256(existing, "applied_evidence_sha256"):
|
||||
raise RuntimeError("Existing orthophoto applied evidence checksum is invalid")
|
||||
if (
|
||||
existing.get("project_id") != args.project_id
|
||||
or existing.get("scope") != args.scope
|
||||
or existing.get("edition") != plan["edition"]
|
||||
or existing.get("staged_plan_sha256") != plan["plan_sha256"]
|
||||
or existing.get("review_sha256") != review["review_sha256"]
|
||||
or existing.get("dataset_id") != dataset["id"]
|
||||
or existing.get("dataset_checksum_sha256") != dataset["checksum_sha256"]
|
||||
):
|
||||
raise RuntimeError("Existing orthophoto applied evidence does not match this approved Dataset")
|
||||
return path, existing
|
||||
payload: dict[str, Any] = {
|
||||
"schema_version": 1,
|
||||
"status": "applied",
|
||||
@@ -908,7 +928,6 @@ def applied_evidence(
|
||||
"existing_snapshots_retained": True,
|
||||
}
|
||||
payload["applied_evidence_sha256"] = canonical_sha256(payload, "applied_evidence_sha256")
|
||||
path = plan_path.with_name("applied-evidence.json")
|
||||
write_json(path, payload)
|
||||
return path, payload
|
||||
|
||||
@@ -939,10 +958,12 @@ def main() -> int:
|
||||
edition = require_edition(args, report)
|
||||
if args.action == "stage":
|
||||
authorization = authorize_stage(args, report)
|
||||
plan_path = governed_path(args, args.plan_path or default_plan_path(args, edition))
|
||||
if plan_path.exists():
|
||||
raise RuntimeError(f"Governed orthophoto plan already exists and will not be overwritten: {plan_path}")
|
||||
request_identity = map_request(report)
|
||||
staged = stage_artifacts(args, report, request_identity)
|
||||
plan = build_staged_plan(args, report, authorization, staged)
|
||||
plan_path = governed_path(args, args.plan_path or default_plan_path(args, edition))
|
||||
write_json(plan_path, plan)
|
||||
print(
|
||||
json.dumps(
|
||||
@@ -958,6 +979,10 @@ def main() -> int:
|
||||
if args.action == "review":
|
||||
review = build_review_evidence(args, plan_path, plan, manifest)
|
||||
review_path = governed_path(args, args.review_path or default_review_path(args, edition))
|
||||
if review_path.exists():
|
||||
raise RuntimeError(
|
||||
f"Governed orthophoto review already exists and will not be overwritten: {review_path}"
|
||||
)
|
||||
write_json(review_path, review)
|
||||
print(json.dumps({"status": "approved", "review_path": str(review_path), **review}, ensure_ascii=False, indent=2))
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user