audit: establish accuracy phase 1 baseline

This commit is contained in:
Jens
2026-08-01 18:57:38 +02:00
parent 0c019bb22f
commit 3d442ef43f
48 changed files with 15390 additions and 1 deletions
@@ -0,0 +1,159 @@
from __future__ import annotations
import argparse
from datetime import datetime, timezone
import hashlib
import json
from pathlib import Path
import subprocess
from typing import Any
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
DEFAULT_EVIDENCE_ROOT = REPOSITORY_ROOT / "artifacts" / "evidence" / "accuracy" / "P1"
DEFAULT_OUTPUT = DEFAULT_EVIDENCE_ROOT / "evidence-manifest.json"
PROGRAM_PATHS = (
"docs/accuracy-program/00-execution-contract.md",
"docs/accuracy-program/01-system-inventory.md",
"docs/accuracy-program/02-data-lineage.md",
"docs/accuracy-program/03-baseline-and-gaps.md",
"docs/accuracy-program/04-risk-register.md",
"docs/accuracy-program/05-metric-framework.md",
"docs/accuracy-program/06-implementation-roadmap.md",
"docs/accuracy-program/status.json",
"scripts/build_accuracy_phase1_evidence_manifest.py",
"scripts/collect_accuracy_phase1_inference_smoke.py",
"scripts/collect_accuracy_phase1_ml_lineage.py",
"scripts/collect_accuracy_phase1_runtime.py",
"scripts/reproduce_accuracy_phase1_findings.py",
"scripts/run_accuracy_phase1_baseline.py",
"scripts/verify_accuracy_phase1_evidence.py",
"tests/test_accuracy_phase1_baseline.py",
)
def sha256_file(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def role_for(path: Path) -> str:
name = path.name.lower()
if name.endswith(".junit.xml"):
return "test_report"
if name.endswith(".sql"):
return "migration_evidence"
if "runtime" in name or "gpu-inference" in name:
return "runtime_evidence"
if "lineage" in name:
return "lineage_evidence"
if "reproduction" in name:
return "defect_reproduction"
if "ruff" in name or "lint" in name:
return "lint_evidence"
if "test" in name or "vitest" in name or "golden-qa" in name:
return "test_evidence"
if path.suffix.lower() == ".json":
return "structured_inventory"
return "execution_log"
def record(path: Path, *, displayed_path: str, role: str) -> dict[str, Any]:
return {
"path": displayed_path,
"role": role,
"size_bytes": path.stat().st_size,
"sha256": sha256_file(path),
}
def git_head() -> str | None:
result = subprocess.run(
["git", "rev-parse", "HEAD"],
cwd=REPOSITORY_ROOT,
capture_output=True,
text=True,
check=False,
)
return result.stdout.strip() if result.returncode == 0 else None
def build_manifest(evidence_root: Path, output: Path) -> dict[str, Any]:
if not evidence_root.is_dir():
raise FileNotFoundError(f"Evidence root does not exist: {evidence_root}")
evidence_files = [
path
for path in evidence_root.rglob("*")
if path.is_file() and path.resolve() != output.resolve()
]
evidence_records = [
record(
path,
displayed_path=path.relative_to(REPOSITORY_ROOT).as_posix(),
role=role_for(path),
)
for path in sorted(evidence_files)
]
program_records = []
for relative in PROGRAM_PATHS:
path = REPOSITORY_ROOT / relative
if not path.is_file():
raise FileNotFoundError(f"Required Phase-1 program file is missing: {relative}")
program_records.append(record(path, displayed_path=relative, role="phase1_program"))
return {
"schema_version": 1,
"generated_at": datetime.now(timezone.utc).isoformat(),
"audited_repository_head": git_head(),
"claim_boundary": (
"This manifest proves retained-file identity and completeness. It does "
"not establish model accuracy, human label acceptance, split independence "
"or release readiness."
),
"evidence_root": evidence_root.relative_to(REPOSITORY_ROOT).as_posix(),
"evidence_file_count": len(evidence_records),
"evidence_total_bytes": sum(item["size_bytes"] for item in evidence_records),
"evidence_files": evidence_records,
"program_file_count": len(program_records),
"program_files": program_records,
}
def main() -> int:
parser = argparse.ArgumentParser(description="Build the immutable GeoIntel Accuracy P1 evidence manifest.")
parser.add_argument("--evidence-root", type=Path, default=DEFAULT_EVIDENCE_ROOT)
parser.add_argument("--output", type=Path, default=DEFAULT_OUTPUT)
args = parser.parse_args()
evidence_root = args.evidence_root.expanduser().resolve()
output = args.output.expanduser().resolve()
if output.exists():
parser.error(f"refusing to overwrite existing evidence manifest: {output}")
if output.parent != evidence_root:
parser.error("--output must be directly inside --evidence-root")
payload = build_manifest(evidence_root, output)
output.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
print(
json.dumps(
{
"status": "created",
"output": str(output),
"evidence_file_count": payload["evidence_file_count"],
"program_file_count": payload["program_file_count"],
},
indent=2,
sort_keys=True,
)
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,181 @@
from __future__ import annotations
import argparse
from collections import Counter
from datetime import datetime, timezone
import hashlib
import json
from pathlib import Path
import random
import sys
from time import perf_counter
from typing import Any
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
BACKEND_ROOT = REPOSITORY_ROOT / "backend"
if str(BACKEND_ROOT) not in sys.path:
sys.path.insert(0, str(BACKEND_ROOT))
from app.core.config import Settings # noqa: E402
from app.services.yolo_adapter import YoloDetectionAdapter # noqa: E402
def _sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def _raster_metadata(path: Path) -> dict[str, Any]:
try:
import rasterio
except ImportError:
return {"available": False, "reason": "rasterio_not_installed"}
with rasterio.open(path) as dataset:
return {
"available": True,
"bounds": [float(value) for value in dataset.bounds],
"count": int(dataset.count),
"crs": str(dataset.crs) if dataset.crs else None,
"dtypes": list(dataset.dtypes),
"height": int(dataset.height),
"nodata": dataset.nodata,
"transform": [float(value) for value in dataset.transform],
"width": int(dataset.width),
}
def _summarize_detections(detections: list[dict[str, Any]]) -> dict[str, Any]:
confidences = [float(item["confidence"]) for item in detections]
class_counts = Counter(str(item["class_name"]) for item in detections)
return {
"count": len(detections),
"class_counts": dict(sorted(class_counts.items())),
"confidence": {
"minimum": min(confidences) if confidences else None,
"maximum": max(confidences) if confidences else None,
"mean": sum(confidences) / len(confidences) if confidences else None,
},
"sample": detections[:10],
}
def main() -> int:
parser = argparse.ArgumentParser(
description=(
"Run one read-only production-adapter inference and emit forensic JSON. "
"This proves runtime execution only; it does not establish model accuracy."
)
)
parser.add_argument("--model-path", required=True)
parser.add_argument("--tile-path", required=True)
parser.add_argument("--manifest-path")
parser.add_argument("--confidence", type=float, default=0.5)
parser.add_argument("--image-size", type=int, default=640)
parser.add_argument("--max-detections", type=int, default=1000)
parser.add_argument("--device", default="cuda:0")
parser.add_argument("--seed", type=int, default=20260801)
args = parser.parse_args()
model_path = Path(args.model_path).expanduser().resolve()
tile_path = Path(args.tile_path).expanduser().resolve()
manifest_path = Path(args.manifest_path).expanduser().resolve() if args.manifest_path else None
for label, path in (("model", model_path), ("tile", tile_path)):
if not path.is_file():
parser.error(f"{label} path is not an existing file: {path}")
if manifest_path is not None and not manifest_path.is_file():
parser.error(f"manifest path is not an existing file: {manifest_path}")
if not 0.0 <= args.confidence <= 1.0:
parser.error("--confidence must be between 0 and 1")
import numpy as np
import torch
import ultralytics
random.seed(args.seed)
np.random.seed(args.seed)
torch.manual_seed(args.seed)
if torch.cuda.is_available():
torch.cuda.manual_seed_all(args.seed)
torch.cuda.reset_peak_memory_stats()
torch.use_deterministic_algorithms(True, warn_only=True)
settings = Settings().model_copy(
update={
"yolo_enabled": True,
"yolo_model_path": str(model_path),
"yolo_device": args.device,
"yolo_require_cuda": True,
"yolo_image_size": args.image_size,
"yolo_max_detections": args.max_detections,
}
)
adapter = YoloDetectionAdapter(settings)
adapter.validate_runtime()
started = perf_counter()
model = adapter.load_model(model_path)
model_loaded = perf_counter()
detections = adapter.predict_tile(model, tile_path, args.confidence)
if torch.cuda.is_available():
torch.cuda.synchronize()
finished = perf_counter()
device_index = torch.cuda.current_device() if torch.cuda.is_available() else None
payload = {
"schema_version": 1,
"captured_at": datetime.now(timezone.utc).isoformat(),
"status": "passed",
"claim_boundary": (
"One production-adapter inference completed on one existing tile. "
"No accuracy, calibration, geographic-generalization, or release claim follows from this smoke."
),
"read_only": True,
"configuration": {
"confidence": args.confidence,
"device": args.device,
"image_size": args.image_size,
"max_detections": args.max_detections,
"seed": args.seed,
"deterministic_algorithms": True,
},
"model": {
"path": str(model_path),
"sha256": _sha256(model_path),
"size_bytes": model_path.stat().st_size,
},
"input": {
"tile_path": str(tile_path),
"tile_sha256": _sha256(tile_path),
"tile_size_bytes": tile_path.stat().st_size,
"manifest_path": str(manifest_path) if manifest_path else None,
"manifest_sha256": _sha256(manifest_path) if manifest_path else None,
"raster": _raster_metadata(tile_path),
},
"runtime": {
"python": sys.version,
"torch": torch.__version__,
"ultralytics": ultralytics.__version__,
"cuda_available": torch.cuda.is_available(),
"cuda_runtime": torch.version.cuda,
"cuda_device_index": device_index,
"cuda_device_name": torch.cuda.get_device_name(device_index) if device_index is not None else None,
"cuda_peak_memory_bytes": torch.cuda.max_memory_allocated() if torch.cuda.is_available() else None,
},
"timing_seconds": {
"model_load": model_loaded - started,
"inference": finished - model_loaded,
"total": finished - started,
},
"output": _summarize_detections(detections),
}
print(json.dumps(payload, indent=2, sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,364 @@
from __future__ import annotations
import argparse
from collections import Counter, defaultdict
from datetime import datetime, timezone
from itertools import combinations
import hashlib
import json
from pathlib import Path
from typing import Any
def sha256_file(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def artifact_record(path: Path, *, include_hash: bool = True) -> dict[str, Any]:
stat = path.stat()
return {
"path": str(path),
"size_bytes": stat.st_size,
"modified_at": datetime.fromtimestamp(stat.st_mtime, timezone.utc).isoformat(),
"sha256": sha256_file(path) if include_hash else None,
}
def manifest_summary(path: Path) -> dict[str, Any]:
payload = json.loads(path.read_text(encoding="utf-8"))
samples = payload.get("samples") if isinstance(payload.get("samples"), list) else []
region_split = Counter(
f"{sample.get('region')}/{sample.get('split')}" for sample in samples
)
contexts = Counter(str(sample.get("context")) for sample in samples)
roles = Counter(str(sample.get("sample_role")) for sample in samples)
background_samples = [
{
"sample_slug": sample.get("sample_slug"),
"region": sample.get("region"),
"reference_feature_count": sample.get("reference_feature_count"),
"require_empty": sample.get("require_empty"),
}
for sample in samples
if sample.get("split") == "background-test"
]
pure_empty = [
sample
for sample in background_samples
if sample.get("require_empty") is True
and int(sample.get("reference_feature_count") or 0) == 0
]
cross_split_raster_hashes: dict[str, set[str]] = defaultdict(set)
cross_split_dataset_ids: dict[str, set[str]] = defaultdict(set)
for sample in samples:
split = str(sample.get("split"))
if sample.get("raster_sha256"):
cross_split_raster_hashes[str(sample["raster_sha256"])].add(split)
if sample.get("raster_dataset_id"):
cross_split_dataset_ids[str(sample["raster_dataset_id"])].add(split)
spatial = cross_split_spatial_summary(samples)
perceptual = cross_split_dhash_summary(samples)
return {
**artifact_record(path),
"schema_version": payload.get("schema_version"),
"dataset_version": payload.get("dataset_version"),
"immutable": payload.get("immutable"),
"sample_count": len(samples),
"region_split_counts": dict(sorted(region_split.items())),
"context_counts": dict(sorted(contexts.items())),
"sample_role_counts": dict(sorted(roles.items())),
"background_test_samples": background_samples,
"pure_empty_background_count": len(pure_empty),
"pure_empty_background_by_region": dict(
sorted(Counter(str(item["region"]) for item in pure_empty).items())
),
"exact_cross_split_raster_hash_duplicate_count": sum(
1 for splits in cross_split_raster_hashes.values() if len(splits) > 1
),
"cross_split_raster_dataset_id_duplicate_count": sum(
1 for splits in cross_split_dataset_ids.values() if len(splits) > 1
),
"spatial_independence": spatial,
"perceptual_duplicate_screen": perceptual,
}
def cross_split_spatial_summary(samples: list[dict[str, Any]]) -> dict[str, Any]:
try:
from pyproj import Transformer
from shapely.geometry import box
from shapely.ops import transform
except ImportError:
return {"available": False, "reason": "geospatial_dependencies_unavailable"}
transformer = Transformer.from_crs(4326, 31370, always_xy=True)
records: list[tuple[str, str, Any]] = []
for sample in samples:
bbox_values = sample.get("bbox_epsg4326")
if not isinstance(bbox_values, list) or len(bbox_values) != 4:
continue
geometry = transform(transformer.transform, box(*[float(value) for value in bbox_values]))
records.append((str(sample.get("sample_slug")), str(sample.get("split")), geometry))
distances: list[tuple[float, str, str, str, str]] = []
for left, right in combinations(records, 2):
if left[1] == right[1]:
continue
distance = float(left[2].distance(right[2]))
distances.append((distance, left[0], left[1], right[0], right[1]))
distances.sort(key=lambda item: item[0])
return {
"available": True,
"crs": "EPSG:31370",
"cross_split_pair_count": len(distances),
"minimum_distance_m": distances[0][0] if distances else None,
"pairs_below_64_m": sum(1 for item in distances if item[0] < 64.0),
"pairs_below_2000_m": sum(1 for item in distances if item[0] < 2000.0),
"closest_pairs": [
{
"distance_m": item[0],
"left_sample": item[1],
"left_split": item[2],
"right_sample": item[3],
"right_split": item[4],
}
for item in distances[:20]
],
"claim_boundary": (
"AOI bounding-box distance is a screening check only; it does not prove "
"municipality, flight-strip, building-instance or imagery independence."
),
}
def _dhash(path: Path) -> int:
from PIL import Image
with Image.open(path) as image:
resized = image.convert("L").resize((9, 8))
pixel_source = (
resized.get_flattened_data()
if hasattr(resized, "get_flattened_data")
else resized.getdata()
)
pixels = list(pixel_source)
value = 0
for row in range(8):
offset = row * 9
for column in range(8):
value = (value << 1) | int(pixels[offset + column] > pixels[offset + column + 1])
return value
def cross_split_dhash_summary(samples: list[dict[str, Any]]) -> dict[str, Any]:
hashes: list[tuple[str, str, int]] = []
failures: list[dict[str, str]] = []
for sample in samples:
raster_path = Path(str(sample.get("raster_path") or ""))
if not raster_path.is_file():
failures.append({"sample_slug": str(sample.get("sample_slug")), "reason": "missing_raster"})
continue
try:
hashes.append(
(
str(sample.get("sample_slug")),
str(sample.get("split")),
_dhash(raster_path),
)
)
except Exception as exc:
failures.append({"sample_slug": str(sample.get("sample_slug")), "reason": str(exc)})
matches: list[dict[str, Any]] = []
minimum: int | None = None
compared = 0
for left, right in combinations(hashes, 2):
if left[1] == right[1]:
continue
compared += 1
distance = (left[2] ^ right[2]).bit_count()
minimum = distance if minimum is None else min(minimum, distance)
if distance <= 4:
matches.append(
{
"left_sample": left[0],
"left_split": left[1],
"right_sample": right[0],
"right_split": right[1],
"hamming_distance": distance,
}
)
return {
"algorithm": "64-bit difference hash over 9x8 grayscale resize",
"screened_raster_count": len(hashes),
"cross_split_pair_count": compared,
"minimum_hamming_distance": minimum,
"pairs_at_or_below_4": matches,
"failures": failures,
"claim_boundary": (
"This bounded perceptual screen is not semantic, instance-level or "
"flight-strip deduplication and cannot establish split independence."
),
}
def corpus_audit_summary(path: Path) -> dict[str, Any]:
payload = json.loads(path.read_text(encoding="utf-8"))
keys = (
"status",
"dataset_version",
"manifest_immutable",
"sample_count",
"split_counts",
"input_feature_count",
"accepted_feature_count",
"decision_counts",
"temporal_unknown_sample_count",
"spatial_leakage_status",
"reviewed_sample_count",
"review_complete",
"failures",
)
return {
**artifact_record(path),
**{key: payload.get(key) for key in keys},
"review_queue_count": len(payload.get("review_queue") or []),
"claim_boundary": (
"Automated status and an empty failure list do not substitute for "
"human label acceptance."
),
}
def calibration_summary(path: Path) -> dict[str, Any]:
payload = json.loads(path.read_text(encoding="utf-8"))
sweeps = payload.get("sweeps") if isinstance(payload.get("sweeps"), list) else []
def sweep_at(threshold: float) -> dict[str, Any] | None:
return next(
(
{
"threshold": item.get("threshold"),
"aggregate": item.get("aggregate"),
"regions": item.get("regions"),
"pure_empty_false_positives": item.get("pure_empty_false_positives"),
}
for item in sweeps
if abs(float(item.get("threshold")) - threshold) < 1e-12
),
None,
)
return {
**artifact_record(path),
"model": payload.get("model"),
"summary": payload.get("summary"),
"split": payload.get("split"),
"match_iou": payload.get("match_iou"),
"tile_count": payload.get("tile_count"),
"inference_imgsz": payload.get("inference_imgsz"),
"sweep_count": len(sweeps),
"threshold_0_15": sweep_at(0.15),
"threshold_0_02": sweep_at(0.02),
"claim_boundary": (
"This is calibration-split, tile-level bbox evidence; it is not a "
"protected-test, unique-building or national release result."
),
}
def inventory(root: Path) -> dict[str, Any]:
model_files = sorted(
path
for path in (root / "models").glob("*")
if path.is_file() and path.suffix.lower() in {".pt", ".pth", ".onnx", ".engine", ".ckpt", ".safetensors"}
)
training_root = root / "storage" / "training"
checkpoints = sorted(
(
path
for path in training_root.glob("building-be-*/**/*")
if path.is_file() and path.suffix.lower() in {".pt", ".pth", ".ckpt", ".onnx", ".engine", ".safetensors"}
),
key=lambda path: path.stat().st_mtime,
reverse=True,
)
reports = sorted(
(
path
for path in training_root.glob("building-be-*/**/*.json")
if path.is_file()
),
key=lambda path: path.stat().st_mtime,
reverse=True,
)
manifests = sorted(
(root / "storage" / "operator-data").glob("building-be-*/operator_samples_manifest.json"),
key=lambda path: path.stat().st_mtime,
reverse=True,
)
return {
"model_assets": [artifact_record(path) for path in model_files],
"training_checkpoint_count": len(checkpoints),
"training_checkpoint_bytes": sum(path.stat().st_size for path in checkpoints),
"newest_training_checkpoints": [
artifact_record(path, include_hash=False) for path in checkpoints[:30]
],
"training_json_report_count": len(reports),
"newest_training_json_reports": [
artifact_record(path, include_hash=False) for path in reports[:100]
],
"operator_manifest_count": len(manifests),
"operator_manifests": [artifact_record(path) for path in manifests],
}
def main() -> int:
parser = argparse.ArgumentParser(
description="Collect a bounded, read-only ML/data-lineage snapshot from the deployed GeoIntel volume."
)
parser.add_argument("--app-root", default="/app")
args = parser.parse_args()
root = Path(args.app_root).resolve()
paths = {
"v56_audit": root / "storage/training/building-be-v56-corpus-audit-r1/belgium-building-corpus-audit.json",
"v56_manifest": root / "storage/operator-data/building-be-v56-hard-negative-instance-roofs-r1/operator_samples_manifest.json",
"v58_calibration": root / "storage/training/building-be-v58-v56-clean-pretrained-r1/preview-epoch-015/calibration.json",
"v62_calibration": root / "storage/training/building-be-v62-v58-flanders-balanced-transfer-r1/preview-epoch-006/calibration-routed.json",
"v66_manifest": root / "storage/operator-data/building-be-v66-lowrise-temporal-r1/operator_samples_manifest.json",
}
missing = [str(path) for path in paths.values() if not path.is_file()]
if missing:
raise SystemExit("Required lineage artifacts are missing: " + ", ".join(missing))
payload = {
"schema_version": 1,
"captured_at": datetime.now(timezone.utc).isoformat(),
"read_only": True,
"inventory": inventory(root),
"v56": {
"corpus_audit": corpus_audit_summary(paths["v56_audit"]),
"manifest": manifest_summary(paths["v56_manifest"]),
},
"v58": {"calibration": calibration_summary(paths["v58_calibration"])},
"v62": {"calibration": calibration_summary(paths["v62_calibration"])},
"v66": {"manifest": manifest_summary(paths["v66_manifest"])},
"global_claim_boundary": (
"Inventory and historical calibration evidence do not prove human "
"label acceptance, strict split independence, calibration, protected-test "
"performance, national validity or release readiness."
),
}
print(json.dumps(payload, indent=2, sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())
+334
View File
@@ -0,0 +1,334 @@
#!/usr/bin/env python3
"""Emit a bounded read-only database/runtime integrity snapshot as JSON.
Run inside the GeoIntel application container. Every SQL statement has a
timeout; the script never writes application rows or storage artifacts.
"""
from __future__ import annotations
import hashlib
import importlib.metadata
import json
import os
import platform
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
from sqlalchemy import text
from app.db.session import engine
TABLES = (
"projects",
"areas",
"datasets",
"dataset_versions",
"vector_features",
"jobs",
"analysis_runs",
"detections",
"segmentations",
"quality_checks",
"metrics",
"exports",
"detection_reviews",
"aoi_operations",
"aoi_operation_partitions",
)
PATH_QUERIES = {
"datasets": "SELECT id::text, storage_path FROM datasets WHERE storage_path IS NOT NULL",
"dataset_versions": "SELECT id::text, storage_path FROM dataset_versions WHERE storage_path IS NOT NULL",
"exports": "SELECT id::text, storage_path FROM exports WHERE storage_path IS NOT NULL",
}
def now() -> str:
return datetime.now(timezone.utc).isoformat()
def sha256_file(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def package_version(name: str) -> str | None:
try:
return importlib.metadata.version(name)
except importlib.metadata.PackageNotFoundError:
return None
def normalize_path(raw: str, storage_root: Path) -> Path:
candidate = Path(raw)
if candidate.is_absolute():
return candidate
normalized = raw.replace("\\", "/")
if normalized.startswith("storage/"):
normalized = normalized.removeprefix("storage/")
return storage_root / normalized
def rows(connection: Any, statement: str, parameters: dict[str, Any] | None = None) -> list[dict[str, Any]]:
result = connection.execute(text(statement), parameters or {})
return [dict(row._mapping) for row in result]
def scalar(connection: Any, statement: str) -> int:
return int(connection.execute(text(statement)).scalar() or 0)
def safe_query(connection: Any, name: str, statement: str) -> dict[str, Any]:
try:
return {"status": "ok", "rows": rows(connection, statement)}
except Exception as exc:
return {
"status": "error",
"error_type": type(exc).__name__,
"message": str(exc).splitlines()[0][:500],
"query_name": name,
}
def main() -> int:
storage_root = Path(os.environ.get("GEOINTEL_STORAGE_ROOT", "/app/storage")).resolve()
report: dict[str, Any] = {
"schema_version": 1,
"generated_at": now(),
"mode": "read_only",
"statement_timeout_ms": 30000,
"runtime": {
"python": platform.python_version(),
"platform": platform.platform(),
"packages": {
name: package_version(name)
for name in (
"geointel-backend",
"fastapi",
"sqlalchemy",
"geoalchemy2",
"shapely",
"pyproj",
"rasterio",
"geopandas",
"torch",
"torchvision",
"ultralytics",
)
},
},
"storage_root": str(storage_root),
}
try:
import torch
report["runtime"]["cuda"] = {
"available": torch.cuda.is_available(),
"runtime_version": torch.version.cuda,
"device_count": torch.cuda.device_count(),
"device_names": [
torch.cuda.get_device_name(index) for index in range(torch.cuda.device_count())
],
}
except Exception as exc:
report["runtime"]["cuda"] = {"available": False, "error": type(exc).__name__}
model_path = Path(os.environ.get("YOLO_MODEL_PATH", ""))
report["active_model"] = {
"configured_path": str(model_path) if str(model_path) else None,
"exists": model_path.is_file(),
"size_bytes": model_path.stat().st_size if model_path.is_file() else None,
"sha256": sha256_file(model_path) if model_path.is_file() else None,
"model_id": os.environ.get("YOLO_MODEL_ID"),
"model_version": os.environ.get("YOLO_MODEL_VERSION"),
"classes": os.environ.get("YOLO_MODEL_CLASSES"),
"device": os.environ.get("YOLO_DEVICE"),
"require_cuda": os.environ.get("YOLO_REQUIRE_CUDA"),
"validated_area_names": os.environ.get("YOLO_VALIDATED_AREA_NAMES"),
"validation_scope_enforced": os.environ.get("YOLO_ENFORCE_VALIDATION_SCOPE"),
}
with engine.connect().execution_options(isolation_level="AUTOCOMMIT") as connection:
connection.execute(text("SET statement_timeout TO '30s'"))
report["database"] = {
"version": connection.execute(text("SELECT version()")).scalar(),
"postgis_version": connection.execute(text("SELECT PostGIS_Full_Version()")).scalar(),
"migration_heads": [row["version_num"] for row in rows(connection, "SELECT version_num FROM alembic_version")],
"table_counts": {table: scalar(connection, f'SELECT count(*) FROM "{table}"') for table in TABLES},
}
report["database"]["dataset_statuses"] = rows(
connection,
"SELECT status, count(*)::bigint AS count FROM datasets GROUP BY status ORDER BY status",
)
report["database"]["job_statuses"] = rows(
connection,
"SELECT status, count(*)::bigint AS count FROM jobs GROUP BY status ORDER BY status",
)
report["database"]["analysis_statuses"] = rows(
connection,
"SELECT status, count(*)::bigint AS count FROM analysis_runs GROUP BY status ORDER BY status",
)
report["database"]["dataset_lineage_gaps"] = rows(
connection,
"""
SELECT
count(*)::bigint AS total,
count(*) FILTER (WHERE crs IS NULL OR btrim(crs) = '')::bigint AS missing_crs,
count(*) FILTER (WHERE checksum_sha256 IS NULL OR btrim(checksum_sha256) = '')::bigint AS missing_checksum,
count(*) FILTER (WHERE source_version IS NULL OR btrim(source_version) = '')::bigint AS missing_source_version,
count(*) FILTER (WHERE source_metadata IS NULL OR source_metadata::text = '{}')::bigint AS missing_source_metadata,
count(*) FILTER (WHERE provenance_metadata IS NULL OR provenance_metadata::text = '{}')::bigint AS missing_provenance_metadata,
count(*) FILTER (WHERE imported_at IS NULL)::bigint AS missing_imported_at,
count(*) FILTER (WHERE observed_at IS NULL)::bigint AS missing_observed_at
FROM datasets
""",
)
report["database"]["dataset_version_gaps"] = rows(
connection,
"""
SELECT
count(*)::bigint AS total,
count(*) FILTER (WHERE checksum_sha256 IS NULL OR btrim(checksum_sha256) = '')::bigint AS missing_checksum,
count(*) FILTER (WHERE source_metadata IS NULL OR source_metadata::text = '{}')::bigint AS missing_source_metadata,
count(*) FILTER (WHERE provenance_metadata IS NULL OR provenance_metadata::text = '{}')::bigint AS missing_provenance_metadata,
count(*) FILTER (WHERE storage_path IS NULL OR btrim(storage_path) = '')::bigint AS missing_storage_path
FROM dataset_versions
""",
)
report["database"]["geometry_integrity"] = {}
for table, nullable in (
("areas", False),
("vector_features", False),
("detections", True),
("segmentations", False),
):
where = "WHERE geometry IS NOT NULL" if nullable else ""
statement = f"""
SELECT
count(*)::bigint AS populated,
count(*) FILTER (WHERE ST_IsEmpty(geometry))::bigint AS empty,
count(*) FILTER (WHERE NOT ST_IsValid(geometry))::bigint AS invalid,
count(*) FILTER (WHERE ST_SRID(geometry) <> 4326)::bigint AS wrong_srid,
count(*) FILTER (
WHERE ST_XMin(Box3D(geometry)) < -180
OR ST_XMax(Box3D(geometry)) > 180
OR ST_YMin(Box3D(geometry)) < -90
OR ST_YMax(Box3D(geometry)) > 90
)::bigint AS outside_epsg4326_domain
FROM {table} {where}
"""
report["database"]["geometry_integrity"][table] = safe_query(
connection, f"{table}_geometry_integrity", statement
)
report["database"]["outside_domain_detection_records"] = rows(
connection,
"""
SELECT
d.id::text AS detection_id,
d.analysis_run_id::text AS analysis_run_id,
d.dataset_id::text AS dataset_id,
p.name AS project_name,
ds.name AS dataset_name,
ds.source_name AS dataset_source_name,
ar.analysis_type,
ar.status AS analysis_status,
d.model_name,
d.model_version,
d.class_name,
d.confidence,
d.source_tile_path,
d.bbox_json,
ST_XMin(Box3D(d.geometry)) AS min_x,
ST_YMin(Box3D(d.geometry)) AS min_y,
ST_XMax(Box3D(d.geometry)) AS max_x,
ST_YMax(Box3D(d.geometry)) AS max_y,
d.created_at
FROM detections d
LEFT JOIN analysis_runs ar ON ar.id = d.analysis_run_id
LEFT JOIN datasets ds ON ds.id = d.dataset_id
LEFT JOIN projects p ON p.id = d.project_id
WHERE d.geometry IS NOT NULL
AND (
ST_XMin(Box3D(d.geometry)) < -180
OR ST_XMax(Box3D(d.geometry)) > 180
OR ST_YMin(Box3D(d.geometry)) < -90
OR ST_YMax(Box3D(d.geometry)) > 90
)
ORDER BY d.created_at, d.id
LIMIT 100
""",
)
report["database"]["confidence_integrity"] = rows(
connection,
"""
SELECT
(SELECT count(*) FROM detections WHERE confidence < 0 OR confidence > 1)::bigint
AS detections_outside_unit_interval,
(SELECT count(*) FROM segmentations
WHERE confidence IS NOT NULL AND (confidence < 0 OR confidence > 1))::bigint
AS segmentations_outside_unit_interval
""",
)
report["database"]["metric_nulls"] = rows(
connection,
"""
SELECT metric_key, count(*)::bigint AS total,
count(*) FILTER (WHERE metric_value IS NULL)::bigint AS null_values
FROM metrics GROUP BY metric_key ORDER BY metric_key
""",
)
report["database"]["model_run_summary"] = rows(
connection,
"""
SELECT analysis_type, status, coalesce(model_name, '<none>') AS model_name,
coalesce(model_version, '<none>') AS model_version, count(*)::bigint AS count
FROM analysis_runs
GROUP BY analysis_type, status, model_name, model_version
ORDER BY count(*) DESC, analysis_type
LIMIT 100
""",
)
report["database"]["source_summary"] = rows(
connection,
"""
SELECT coalesce(source_name, source, '<none>') AS source_name,
status, count(*)::bigint AS count,
count(*) FILTER (WHERE dataset_role = 'reference')::bigint AS reference_count
FROM datasets
GROUP BY coalesce(source_name, source, '<none>'), status
ORDER BY count(*) DESC, source_name
LIMIT 200
""",
)
path_records = []
for table, statement in PATH_QUERIES.items():
for row in rows(connection, statement):
path = normalize_path(row["storage_path"], storage_root)
path_records.append({
"table": table,
"id": row["id"],
"storage_path": row["storage_path"],
"resolved_path": str(path),
"exists": path.is_file() or path.is_dir(),
})
missing = [row for row in path_records if not row["exists"]]
report["storage_references"] = {
"checked_count": len(path_records),
"missing_count": len(missing),
"missing_records": missing[:500],
"records_truncated": len(missing) > 500,
"scope": "direct datasets, dataset_versions and exports storage_path columns",
}
print(json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True, default=str))
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,271 @@
from __future__ import annotations
import json
from pathlib import Path
from types import SimpleNamespace
from uuid import UUID
from geoalchemy2.shape import to_shape
from shapely.geometry import Point, box
import sys
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
BACKEND_ROOT = REPOSITORY_ROOT / "backend"
if str(BACKEND_ROOT) not in sys.path:
sys.path.insert(0, str(BACKEND_ROOT))
from app.core.config import Settings # noqa: E402
from app.schemas.area import AreaUpdate # noqa: E402
from app.services.coverage_registry_service import ( # noqa: E402
CoverageRegistryService,
SOURCE_DEFINITIONS,
)
from app.services.detection_service import DetectionService # noqa: E402
from app.services.vector_feature_service import VectorFeatureService # noqa: E402
FIXED_UUID = UUID("00000000-0000-4000-8000-000000000001")
def _dataset(*, layer: str, bbox_values: list[float], source_name: str = "spw_picc") -> SimpleNamespace:
return SimpleNamespace(
id=FIXED_UUID,
status="ready",
source_name=source_name,
reference_layer_name=layer,
source_metadata={"coverage_zones": ["wallonia"], "bbox_epsg4326": bbox_values},
provenance_metadata={},
observed_at=None,
source_version="forensic-reproduction",
resolution_json=None,
checksum_sha256="forensic-only",
)
def _coverage_cross_theme_contamination() -> dict:
definition = next(
item for item in SOURCE_DEFINITIONS if item.contract.source_name == "spw_geoportail"
)
selection = box(4.50, 50.50, 4.70, 50.60)
building = _dataset(layer="buildings", bbox_values=[4.55, 50.52, 4.56, 50.53])
road = _dataset(layer="roads", bbox_values=[4.50, 50.50, 4.70, 50.60])
matches, fully_covered = CoverageRegistryService._matching_datasets(
[building, road],
definition,
"buildings",
"wallonia",
selection,
)
observed_ids = [str(item.id) for item in matches]
reproduced = observed_ids == [str(building.id)] and fully_covered is True
return {
"id": "P1-COV-001",
"severity": "critical",
"source": "backend/app/services/coverage_registry_service.py:481-505",
"expected": "A small buildings partition remains partial; a roads bbox cannot complete buildings coverage.",
"observed": {"matched_dataset_ids": observed_ids, "fully_covered": fully_covered},
"reproduced": reproduced,
}
def _meter_buffer_as_degrees() -> dict:
geometry = Point(5.0, 51.0)
buffered = geometry.buffer(100.0)
bounds = [float(value) for value in buffered.bounds]
reproduced = round(bounds[2] - bounds[0], 6) == 200.0
return {
"id": "P1-CRS-001",
"severity": "critical",
"source": "backend/app/services/vector_operations_service.py:179-203",
"expected": "A 100 metre buffer is projected to a metric CRS and spans roughly hundreds of metres.",
"observed": {"bounds_epsg4326": bounds, "longitude_span_degrees": bounds[2] - bounds[0]},
"reproduced": reproduced,
}
def _lambert_feature_mislabeled() -> dict:
row = VectorFeatureService._feature_row(
FIXED_UUID,
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [150000.0, 210000.0]},
"properties": {},
},
0,
None,
)
assert row is not None
geometry = to_shape(row.geometry)
reproduced = (
int(row.geometry.srid) == 4326
and float(geometry.x) == 150000.0
and float(geometry.y) == 210000.0
)
return {
"id": "P1-CRS-002",
"severity": "critical",
"source": "backend/app/services/vector_feature_service.py:271-299",
"expected": "Non-WGS84 input is transformed to EPSG:4326 or rejected before persistence.",
"observed": {
"stored_srid": int(row.geometry.srid),
"stored_coordinates": [float(geometry.x), float(geometry.y)],
},
"reproduced": reproduced,
}
def _authority_spoof() -> dict:
selection = box(4.9, 50.9, 5.0, 51.0)
dataset = SimpleNamespace(
id=FIXED_UUID,
status="ready",
source_name="grb",
reference_layer_name="buildings",
source_metadata={
"coverage_zones": ["flanders"],
"bbox_epsg4326": [4.8, 50.8, 5.1, 51.1],
},
provenance_metadata={"provided_by": "caller"},
observed_at=None,
source_version="caller-provided",
resolution_json=None,
checksum_sha256="caller-provided",
crs="EPSG:4326",
)
item = CoverageRegistryService._resolve_item(
zone="flanders",
theme="buildings",
datasets=[dataset],
selection=selection,
)
authority = item.evidence[0].authority_level if item.evidence else None
reproduced = item.status == "operational" and authority == "authoritative"
return {
"id": "P1-AUTH-001",
"severity": "critical",
"source": (
"backend/app/api/routes/datasets.py:142-163; "
"backend/app/services/coverage_registry_service.py:463-559"
),
"expected": "Only server-attested source identities can produce authoritative operational coverage.",
"observed": {
"caller_controlled_source_name": dataset.source_name,
"coverage_status": item.status,
"reported_authority": authority,
},
"reproduced": reproduced,
}
def _mutable_name_model_scope() -> dict:
area = SimpleNamespace(name="Mol validation bypass", geometry=box(-75.0, 35.0, -74.9, 35.1))
dataset = SimpleNamespace(id=FIXED_UUID, area_id=FIXED_UUID)
class FakeSession:
@staticmethod
def get(_model, _identifier):
return area
settings = Settings().model_copy(
update={"yolo_validated_area_names": "Mol,Kempen"}
)
accepted = True
try:
DetectionService._validate_model_area_scope(FakeSession(), dataset, settings)
except Exception:
accepted = False
return {
"id": "P1-AI-001",
"severity": "critical",
"source": "backend/app/services/detection_service.py:223-232",
"expected": "Validation scope is bound to immutable geometry/source/checksum evidence.",
"observed": {
"area_name": area.name,
"geometry_bounds": [float(value) for value in area.geometry.bounds],
"accepted": accepted,
},
"reproduced": accepted,
}
def _mutable_name_legal_scope() -> dict:
selection = box(4.9, 50.9, 5.0, 51.0)
flemish_geometry = box(2.5, 50.7, 5.9, 51.5)
canonical = [SimpleNamespace(name="Flanders", geometry=flemish_geometry)]
renamed = [SimpleNamespace(name="Vlaanderen", geometry=flemish_geometry)]
canonical_zones, canonical_outside = CoverageRegistryService._intersected_zones(
canonical, selection
)
renamed_zones, renamed_outside = CoverageRegistryService._intersected_zones(
renamed, selection
)
reproduced = (
canonical_zones == ["flanders"]
and canonical_outside is False
and renamed_zones == []
and renamed_outside is True
)
return {
"id": "P1-COV-002",
"severity": "high",
"source": "backend/app/services/coverage_registry_service.py:56-65,425-447",
"expected": "Renaming an Area cannot change its legal coverage-zone identity.",
"observed": {
"canonical": {"zones": canonical_zones, "outside": canonical_outside},
"renamed_same_geometry": {"zones": renamed_zones, "outside": renamed_outside},
},
"reproduced": reproduced,
}
def _area_patch_ignores_geometry() -> dict:
payload = AreaUpdate.model_validate(
{
"name": "Renamed",
"geometry": {
"type": "Polygon",
"coordinates": [[[4.0, 50.0], [5.0, 50.0], [5.0, 51.0], [4.0, 50.0]]],
},
}
)
parsed = payload.model_dump()
reproduced = "geometry" not in parsed
return {
"id": "P1-API-001",
"severity": "high",
"source": "backend/app/schemas/area.py:15-17; backend/app/services/area_service.py:154-171",
"expected": "PATCH /areas/{area_id} either validates and applies geometry or rejects the field.",
"observed": {"parsed_payload": parsed, "geometry_silently_ignored": reproduced},
"reproduced": reproduced,
}
def main() -> int:
findings = [
_coverage_cross_theme_contamination(),
_meter_buffer_as_degrees(),
_lambert_feature_mislabeled(),
_authority_spoof(),
_mutable_name_model_scope(),
_mutable_name_legal_scope(),
_area_patch_ignores_geometry(),
]
reproduced_count = sum(bool(item["reproduced"]) for item in findings)
payload = {
"schema_version": 1,
"purpose": "Read-only deterministic reproductions of Phase-1 contract violations.",
"findings": findings,
"summary": {
"total": len(findings),
"reproduced": reproduced_count,
"all_reproduced": reproduced_count == len(findings),
},
}
print(json.dumps(payload, indent=2, sort_keys=True))
return 0 if reproduced_count == len(findings) else 1
if __name__ == "__main__":
raise SystemExit(main())
+365
View File
@@ -0,0 +1,365 @@
#!/usr/bin/env python3
"""Create a bounded, read-only GeoIntel Phase-1 accuracy baseline."""
from __future__ import annotations
import argparse
import hashlib
import json
import re
import subprocess
from collections import Counter, defaultdict
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Iterable
SCHEMA_VERSION = 1
SKIP_DIRS = {".git", ".pytest_cache", ".ruff_cache", ".venv", "__pycache__", "dist", "node_modules"}
CODE_ROOTS = {
"backend": "backend/app",
"backend_tests": "backend/tests",
"frontend": "frontend/src",
"frontend_e2e": "frontend/e2e",
"root_tests": "tests",
"scripts": "scripts",
"migrations": "backend/alembic/versions",
}
CODE_SUFFIXES = {".py", ".ts", ".tsx", ".js", ".mjs", ".sh", ".ps1"}
ARTIFACT_ROOTS = ("models", "datasets", "data", "storage", "artifacts", "output")
MODEL_SUFFIXES = {".pt", ".pth", ".onnx", ".engine", ".safetensors"}
RASTER_SUFFIXES = {".tif", ".tiff", ".vrt", ".jp2"}
VECTOR_SUFFIXES = {".geojson", ".gpkg", ".shp", ".fgb"}
HASH_SUFFIXES = MODEL_SUFFIXES | RASTER_SUFFIXES | VECTOR_SUFFIXES | {
".json", ".yaml", ".yml", ".csv", ".txt", ".md", ".lock"
}
MARKERS = {
"fixture": re.compile(r"\bfixture\b", re.IGNORECASE),
"mock": re.compile(r"\bmock(?:ed|ing|s)?\b", re.IGNORECASE),
"placeholder": re.compile(r"\bplaceholder\b", re.IGNORECASE),
"heuristic": re.compile(r"\bheuristic(?:s)?\b", re.IGNORECASE),
"not_configured": re.compile(r"\bnot_configured\b", re.IGNORECASE),
"todo": re.compile(r"\bTODO\b"),
"fallback": re.compile(r"\b(?:fallback|fall back)\b", re.IGNORECASE),
}
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--output-dir", type=Path, required=True)
parser.add_argument("--max-hash-bytes", type=int, default=64 * 1024 * 1024)
return parser.parse_args()
def now() -> str:
return datetime.now(timezone.utc).isoformat()
def sha256_file(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def write_json(path: Path, payload: Any) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(
json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True) + "\n",
encoding="utf-8",
)
def git(repo: Path, *arguments: str) -> str:
result = subprocess.run(
["git", *arguments],
cwd=repo,
check=True,
capture_output=True,
text=True,
encoding="utf-8",
errors="replace",
)
return result.stdout
def iter_files(root: Path) -> Iterable[Path]:
if not root.is_dir():
return
for path in sorted(root.rglob("*")):
if path.is_file() and not any(part in SKIP_DIRS for part in path.parts):
yield path
def line_count(path: Path) -> int:
with path.open("r", encoding="utf-8", errors="replace") as handle:
return sum(1 for _ in handle)
def code_inventory(repo: Path) -> dict[str, Any]:
groups: dict[str, dict[str, int]] = {}
for name, relative in CODE_ROOTS.items():
files = [path for path in iter_files(repo / relative) if path.suffix.lower() in CODE_SUFFIXES]
groups[name] = {
"file_count": len(files),
"line_count": sum(line_count(path) for path in files),
}
pytest_pattern = re.compile(r"^\s*(?:async\s+)?def\s+test_", re.MULTILINE)
route_pattern = re.compile(r"@router\.(?:get|post|put|patch|delete)\s*\(")
pytest_count = 0
route_count = 0
frontend_test_count = 0
for base in (repo / "backend/tests", repo / "tests"):
for path in iter_files(base):
if path.suffix == ".py":
pytest_count += len(pytest_pattern.findall(path.read_text(encoding="utf-8", errors="replace")))
for path in iter_files(repo / "backend/app/api/routes"):
if path.suffix == ".py":
route_count += len(route_pattern.findall(path.read_text(encoding="utf-8", errors="replace")))
for path in iter_files(repo / "frontend"):
if ".test." in path.name.lower() or ".spec." in path.name.lower():
frontend_test_count += 1
return {
"groups": groups,
"pytest_test_function_count": pytest_count,
"frontend_test_file_count": frontend_test_count,
"api_route_decorator_count": route_count,
}
def migration_inventory(repo: Path) -> dict[str, Any]:
revision_re = re.compile(r'^revision\s*(?::[^=]+)?=\s*["\x27]([^"\x27]+)["\x27]', re.MULTILINE)
down_re = re.compile(
r'^down_revision\s*(?::[^=]+)?=\s*(?:["\x27]([^"\x27]+)["\x27]|None)',
re.MULTILINE,
)
rows = []
for path in iter_files(repo / "backend/alembic/versions"):
if path.suffix != ".py":
continue
text = path.read_text(encoding="utf-8", errors="replace")
revision = revision_re.search(text)
down = down_re.search(text)
rows.append({
"path": path.relative_to(repo).as_posix(),
"revision": revision.group(1) if revision else None,
"down_revision": down.group(1) if down and down.group(1) else None,
})
revisions = {row["revision"] for row in rows if row["revision"]}
parents = {row["down_revision"] for row in rows if row["down_revision"]}
return {
"count": len(rows),
"records": rows,
"heads_from_static_chain": sorted(revisions - parents),
"missing_revision_identifiers": sum(row["revision"] is None for row in rows),
}
def mirror_inventory(repo: Path, tracked: set[str]) -> dict[str, Any]:
identical = 0
different = []
for relative in sorted(item for item in tracked if not item.startswith("geointel/")):
mirror_relative = f"geointel/{relative}"
if mirror_relative not in tracked:
continue
source = repo / relative
mirror = repo / mirror_relative
if not source.is_file() or not mirror.is_file():
continue
source_hash = sha256_file(source)
mirror_hash = sha256_file(mirror)
if source_hash == mirror_hash:
identical += 1
else:
different.append({
"path": relative,
"root_sha256": source_hash,
"mirror_sha256": mirror_hash,
"root_size_bytes": source.stat().st_size,
"mirror_size_bytes": mirror.stat().st_size,
})
return {
"tracked_mirror_file_count": sum(item.startswith("geointel/") for item in tracked),
"paired_identical_file_count": identical,
"paired_different_file_count": len(different),
"different_files": different,
"risk": (
"The tracked geointel/ repository mirror can create ambiguous imports, stale tests "
"and local/deployment drift; Docker excludes it but local tools may not."
),
}
def marker_inventory(repo: Path) -> dict[str, Any]:
counts: Counter[str] = Counter()
examples: dict[str, list[dict[str, Any]]] = defaultdict(list)
for base in (repo / "backend/app", repo / "frontend/src", repo / "scripts"):
for path in iter_files(base):
if path.suffix.lower() not in CODE_SUFFIXES:
continue
relative = path.relative_to(repo).as_posix()
for number, text in enumerate(
path.read_text(encoding="utf-8", errors="replace").splitlines(), start=1
):
for name, pattern in MARKERS.items():
if pattern.search(text):
counts[name] += 1
if len(examples[name]) < 25:
examples[name].append({
"path": relative,
"line": number,
"text": text.strip()[:240],
})
return {
"counts": dict(sorted(counts.items())),
"examples": dict(sorted(examples.items())),
"interpretation": "Triage signals only; production impact requires a traced contract/runtime path.",
}
def artifact_role(path: Path) -> str:
suffix = path.suffix.lower()
name = path.name.lower()
if suffix in MODEL_SUFFIXES:
return "model_checkpoint"
if "manifest" in name or name in {"dataset.yaml", "data.yaml"}:
return "manifest"
if any(token in name for token in ("audit", "evaluation", "assessment", "metric", "report")):
return "evaluation_or_audit"
if any(token in name for token in ("contact_sheet", "review")) or suffix in {".png", ".jpg", ".jpeg"}:
return "visual_review"
if suffix in RASTER_SUFFIXES:
return "raster"
if suffix in VECTOR_SUFFIXES:
return "vector"
if suffix in {".db", ".sqlite", ".sqlite3", ".wal", ".shm"} or ".db-" in name:
return "database_runtime_state"
if suffix == ".txt" and "label" in path.as_posix().lower():
return "label"
return "other"
def artifact_inventory(
repo: Path,
tracked: set[str],
output: Path,
max_hash_bytes: int,
) -> dict[str, Any]:
records = []
output = output.resolve()
for root_name in ARTIFACT_ROOTS:
for path in iter_files(repo / root_name):
resolved = path.resolve()
if resolved == output or output in resolved.parents:
continue
stat = path.stat()
relative = path.relative_to(repo).as_posix()
can_hash = stat.st_size <= max_hash_bytes and path.suffix.lower() in HASH_SUFFIXES
records.append({
"path": relative,
"role": artifact_role(path),
"size_bytes": stat.st_size,
"modified_at": datetime.fromtimestamp(stat.st_mtime, timezone.utc).isoformat(),
"tracked": relative in tracked,
"sha256": sha256_file(path) if can_hash else None,
"hash_omission_reason": None if can_hash else "suffix_or_size_limit",
})
roles = Counter(row["role"] for row in records)
roots = Counter(row["path"].split("/", 1)[0] for row in records)
return {
"roots": list(ARTIFACT_ROOTS),
"file_count": len(records),
"total_size_bytes": sum(row["size_bytes"] for row in records),
"role_counts": dict(sorted(roles.items())),
"root_counts": dict(sorted(roots.items())),
"model_checkpoint_count": roles.get("model_checkpoint", 0),
"records": records,
"limitations": [
"Ignored Tower corpora and mounted model volumes can be absent locally.",
"Large/non-evidence files are inventoried without a SHA-256 above the configured ceiling.",
"Near-duplicate imagery and semantic label quality need dedicated corpus checks.",
],
}
def main() -> int:
args = parse_args()
repo = args.repo_root.expanduser().resolve()
output = args.output_dir.expanduser()
output = output.resolve() if output.is_absolute() else (repo / output).resolve()
if not (repo / ".git").exists():
raise SystemExit(f"Not a Git repository root: {repo}")
if output == repo:
raise SystemExit("Output directory must not equal the repository root")
tracked = set(filter(None, git(repo, "ls-files").splitlines()))
repository = {
"schema_version": SCHEMA_VERSION,
"generated_at": now(),
"repo_root": str(repo),
"git": {
"branch": git(repo, "branch", "--show-current").strip(),
"head": git(repo, "rev-parse", "HEAD").strip(),
"status_porcelain": [line for line in git(repo, "status", "--short").splitlines() if line],
"tracked_file_count": len(tracked),
"top_level_tracked_counts": dict(sorted(Counter(
item.split("/", 1)[0] for item in tracked
).items())),
},
"code": code_inventory(repo),
"migrations": migration_inventory(repo),
"tracked_mirror": mirror_inventory(repo, tracked),
}
signals = marker_inventory(repo)
artifacts = artifact_inventory(repo, tracked, output, args.max_hash_bytes)
findings = []
mirror = repository["tracked_mirror"]
if mirror["tracked_mirror_file_count"]:
findings.append({
"id": "P1-REPO-001",
"severity": "high",
"title": "Tracked nested repository mirror creates ambiguous source state",
"evidence": {
"tracked_mirror_file_count": mirror["tracked_mirror_file_count"],
"paired_different_file_count": mirror["paired_different_file_count"],
},
})
if artifacts["model_checkpoint_count"] == 0:
findings.append({
"id": "P1-ML-LOCAL-001",
"severity": "info",
"title": "No local checkpoint is available in the repository checkout",
"interpretation": "Production model truth must be verified on the mounted Tower volume.",
})
summary = {
"schema_version": SCHEMA_VERSION,
"generated_at": now(),
"status": "findings_present" if findings else "no_static_findings",
"finding_count": len(findings),
"findings": findings,
"baseline_scope": [
"tracked repository state",
"static code/test/migration inventory",
"tracked mirror comparison",
"local artifact inventory",
"mock/fixture/placeholder/fallback triage signals",
],
"separate_required_evidence": [
"Tower database/storage audit",
"Tower CUDA/model preflight and representative inference",
"corpus leakage/duplicate/label/time review",
"independent human visual review",
],
}
write_json(output / "repository-inventory.json", repository)
write_json(output / "local-artifact-inventory.json", artifacts)
write_json(output / "static-risk-signals.json", signals)
write_json(output / "phase1-baseline-summary.json", summary)
print(json.dumps(summary, ensure_ascii=False, indent=2, sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())
+128
View File
@@ -0,0 +1,128 @@
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
from typing import Any
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
DEFAULT_MANIFEST = (
REPOSITORY_ROOT / "artifacts" / "evidence" / "accuracy" / "P1" / "evidence-manifest.json"
)
REQUIRED_DOCUMENTS = tuple(
REPOSITORY_ROOT / "docs" / "accuracy-program" / f"{index:02d}-{name}.md"
for index, name in (
(0, "execution-contract"),
(1, "system-inventory"),
(2, "data-lineage"),
(3, "baseline-and-gaps"),
(4, "risk-register"),
(5, "metric-framework"),
(6, "implementation-roadmap"),
)
)
def sha256_file(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def verify_record(item: dict[str, Any]) -> list[str]:
errors: list[str] = []
relative = item.get("path")
if not isinstance(relative, str):
return ["manifest record has no string path"]
path = REPOSITORY_ROOT / relative
if not path.is_file():
return [f"missing file: {relative}"]
expected_size = item.get("size_bytes")
if path.stat().st_size != expected_size:
errors.append(
f"size mismatch for {relative}: expected {expected_size}, got {path.stat().st_size}"
)
expected_hash = item.get("sha256")
actual_hash = sha256_file(path)
if actual_hash != expected_hash:
errors.append(
f"sha256 mismatch for {relative}: expected {expected_hash}, got {actual_hash}"
)
return errors
def main() -> int:
parser = argparse.ArgumentParser(description="Verify GeoIntel Accuracy P1 evidence and program hashes.")
parser.add_argument("--manifest", type=Path, default=DEFAULT_MANIFEST)
args = parser.parse_args()
manifest_path = args.manifest.expanduser().resolve()
if not manifest_path.is_file():
parser.error(f"evidence manifest does not exist: {manifest_path}")
payload = json.loads(manifest_path.read_text(encoding="utf-8"))
errors: list[str] = []
evidence_records = payload.get("evidence_files")
program_records = payload.get("program_files")
if not isinstance(evidence_records, list) or not isinstance(program_records, list):
errors.append("manifest must contain evidence_files and program_files arrays")
evidence_records = []
program_records = []
for item in [*evidence_records, *program_records]:
if not isinstance(item, dict):
errors.append("manifest file record is not an object")
continue
errors.extend(verify_record(item))
evidence_root = REPOSITORY_ROOT / str(payload.get("evidence_root") or "")
listed = {
str(item.get("path"))
for item in evidence_records
if isinstance(item, dict) and isinstance(item.get("path"), str)
}
current = {
path.relative_to(REPOSITORY_ROOT).as_posix()
for path in evidence_root.rglob("*")
if path.is_file() and path.resolve() != manifest_path
}
for relative in sorted(current - listed):
errors.append(f"unlisted evidence file: {relative}")
for relative in sorted(listed - current):
errors.append(f"listed evidence file no longer exists: {relative}")
for document in REQUIRED_DOCUMENTS:
if not document.is_file() or document.stat().st_size == 0:
errors.append(f"required document missing or empty: {document.relative_to(REPOSITORY_ROOT)}")
status_path = REPOSITORY_ROOT / "docs" / "accuracy-program" / "status.json"
if not status_path.is_file():
errors.append("required status.json is missing")
else:
status = json.loads(status_path.read_text(encoding="utf-8"))
if status.get("phase1", {}).get("status") != "complete":
errors.append("status.json must mark Phase 1 complete")
if status.get("release", {}).get("status") != "blocked":
errors.append("status.json must keep release blocked")
if status.get("release", {}).get("promotion_allowed") is not False:
errors.append("status.json must keep promotion disallowed")
if status.get("scope", {}).get("national_building_validation") is not False:
errors.append("status.json must not claim national building validation")
summary = {
"schema_version": 1,
"status": "passed" if not errors else "failed",
"manifest": manifest_path.relative_to(REPOSITORY_ROOT).as_posix(),
"evidence_files_checked": len(evidence_records),
"program_files_checked": len(program_records),
"errors": errors,
}
print(json.dumps(summary, indent=2, sort_keys=True))
return 0 if not errors else 1
if __name__ == "__main__":
raise SystemExit(main())