← Files Planner5D: Plan, Build, CreateARCHIVED FILE

skills/planner5d-floorplan-recheck/scripts/compare_measurements.py

3.31 KB · Oct 10, 2026 · 18:24 UTC

↓ Download file

See the change to this file →

#!/usr/bin/env python3
"""Compare explicit same-basis length evidence; no network or scene mutation."""
import json
import math
import sys
from decimal import Decimal
from pathlib import Path


class MeasurementComparison:

	BASES: frozenset[str] = frozenset({"clear_span", "wall_path", "outer_span", "opening_width"})
	SOURCES: frozenset[str] = frozenset({"labeled_dimension", "user_measurement", "measured_scan"})

	def assess(self, ledger: object) -> dict[str, object]:
		if not isinstance(ledger, dict) or ledger.get("version") != 1:
			raise ValueError("Expected measurement ledger version 1")
		rows: object = ledger.get("measurements")
		if not isinstance(rows, list) or not rows:
			raise ValueError("Expected a nonempty measurements array")
		checks: list[dict[str, object]] = [self.compare(row) for row in rows]
		states: set[object] = {row["status"] for row in checks}
		status: str = "passed"
		if "failed" in states:
			status = "failed"
		elif "insufficient_evidence" in states:
			status = "insufficient_evidence"
		return {"version": 1, "status": status, "scope": "listed_length_measurements_only",
				"target": ledger.get("target"), "checks": checks}

	def compare(self, row: object) -> dict[str, object]:
		if not isinstance(row, dict):
			raise ValueError("Every measurement must be an object")
		result: dict[str, object] = {key: row.get(key) for key in
				  ("label", "objectId", "expectedCm", "observedCm", "toleranceCm",
				   "referenceBasis", "observedBasis", "sourceRef", "observedMethod")}
		missing: list[str] = []
		for key in ("expectedCm", "observedCm", "toleranceCm"):
			value: object = row.get(key)
			if not self.validNumber(value, key == "toleranceCm"):
				missing.append(f"{key} must be a finite {'nonnegative' if key == 'toleranceCm' else 'positive'} number")
		if row.get("referenceBasis") not in self.BASES or row.get("referenceBasis") != row.get("observedBasis"):
			missing.append("matching explicit dimension bases required")
		sourceRef: object = row.get("sourceRef")
		if row.get("sourceKind") not in self.SOURCES or not isinstance(sourceRef, str) or not sourceRef.strip():
			missing.append("reliable source provenance required")
		observedMethod: object = row.get("observedMethod")
		if not isinstance(observedMethod, str) or not observedMethod.strip():
			missing.append("observed measurement method required")
		if missing:
			return {**result, "status": "insufficient_evidence", "reasons": missing}
		deviation: Decimal = Decimal(str(row["observedCm"])) - Decimal(str(row["expectedCm"]))
		return {**result, "deviationCm": float(deviation),
				"status": "passed" if abs(deviation) <= Decimal(str(row["toleranceCm"])) else "failed"}

	def validNumber(self, value: object, allowZero: bool) -> bool:
		if isinstance(value, bool) or not isinstance(value, (int, float)):
			return False
		try:
			return math.isfinite(value) and value >= 0 and (allowZero or value > 0)
		except OverflowError:
			return False


if __name__ == "__main__":
	try:
		if len(sys.argv) != 2:
			raise ValueError("Usage: compare_measurements.py <ledger.json>")
		payload: object = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
		print(json.dumps(MeasurementComparison().assess(payload), indent=2, allow_nan=False))
	except (OSError, ValueError, TypeError) as error:
		print(json.dumps({"status": "invalid_input", "message": str(error)}))
		sys.exit(2)

SHA-256: f1c10ce71198cdba4fc694d8eb3c5037e763b2690afd3823380e24ca7bbf02bb