#!/usr/bin/env python3 """Evidence basis: how a value was obtained, and how far it can be trusted. Every quantity in this repository is one of a small number of epistemic kinds. A counted object and an assumed hourly rate are both numbers; they are not both knowledge. This module names the difference and propagates it. The central rule is that a derived value is only as strong as its weakest input. Without it, a precise-looking figure launders weak assumptions: EUR 30.00 of monthly labour reads like a measurement, when it is another repository's estimate of hours multiplied by a rate we chose. """ from __future__ import annotations import json import sys from pathlib import Path # Ordered strongest to weakest. The order is the whole point: it is what makes # "weakest input wins" computable. BASIS_ORDER = ( "invoiced", # a booked financial fact, authoritative from fin-hub "measured", # directly observed from the authoritative system "quoted", # stated by a provider or counterparty in a citable source "derived", # computed from other values by a stated rule "projected", # interpolated between, or extrapolated beyond, observations "estimated", # human judgement, neither observed nor computed "assumed", # a modelling constant we chose "unknown", # no value exists ) BASES = frozenset(BASIS_ORDER) _RANK = {name: index for index, name in enumerate(BASIS_ORDER)} # Bases that assert an observed or contracted fact about the world. EVIDENCED = frozenset({"invoiced", "measured", "quoted"}) def rank(basis: str) -> int: if basis not in _RANK: raise ValueError(f"unknown evidence basis {basis!r}") return _RANK[basis] def weakest(bases) -> str: """The weakest basis in a collection. Empty means nothing is known.""" bases = list(bases) if not bases: return "unknown" return max(bases, key=rank) def strongest(bases) -> str: bases = list(bases) if not bases: return "unknown" return min(bases, key=rank) def is_evidenced(basis: str) -> bool: """True when the value asserts an observed or contracted fact.""" return basis in EVIDENCED def resolve(value: dict) -> str: """Effective basis of a value, propagating through derivation. A `derived` value resolves to the weakest basis among its inputs: deriving GB from measured bytes stays measured, while deriving euros from estimated hours and an assumed rate is no better than assumed. """ basis = value.get("basis", "unknown") if basis not in BASES: raise ValueError(f"unknown evidence basis {basis!r}") if basis != "derived": return basis inputs = value.get("derived_from") or [] if not inputs: raise ValueError("a derived value must record derived_from") return weakest(resolve(item) for item in inputs) def validate_value(value: dict) -> None: basis = value.get("basis") if basis not in BASES: raise ValueError(f"unknown evidence basis {basis!r}") if basis == "derived" and not value.get("derived_from"): raise ValueError("a derived value must record derived_from") if basis != "derived" and value.get("derived_from"): raise ValueError("only a derived value may record derived_from") if basis == "unknown": if value.get("value") is not None: raise ValueError("a value with basis unknown must not carry a quantity") if not value.get("gap"): raise ValueError("an unknown value must name the gap and its owner") elif value.get("value") is None: raise ValueError("a known basis must carry a quantity; use basis unknown instead") # A proxy measures a different quantity than the one named. That does not # weaken the measurement, but it does weaken the inference drawn from it. proxy_for = value.get("proxy_for") if proxy_for is not None and not str(proxy_for).strip(): raise ValueError("proxy_for must name the quantity actually wanted") for item in value.get("derived_from") or []: validate_value(item) def profile(values) -> dict: """Summarize a set of values for decision review.""" resolved = [] proxies = [] for value in values: validate_value(value) effective = resolve(value) resolved.append(effective) if value.get("proxy_for"): proxies.append({"name": value.get("name"), "proxy_for": value["proxy_for"]}) counts: dict[str, int] = {} for basis in resolved: counts[basis] = counts.get(basis, 0) + 1 evidenced = [b for b in resolved if is_evidenced(b)] return { "count": len(resolved), "by_basis": {k: counts[k] for k in BASIS_ORDER if k in counts}, "weakest": weakest(resolved), "evidenced": len(evidenced), "evidenced_ratio": round(len(evidenced) / len(resolved), 4) if resolved else None, "proxies": proxies, } def decision_grade(values) -> dict: """Grade a decision by the weakest evidence it actually rests on. A conclusion is not stronger than its weakest load-bearing input, however precise the arithmetic between them looks. """ summary = profile(values) basis = summary["weakest"] if basis == "unknown": grade, note = "insufficient", "at least one load-bearing value is unknown" elif is_evidenced(basis): grade, note = "evidenced", "every load-bearing value is observed, invoiced, or quoted" elif basis == "projected": grade, note = "projected", "the conclusion rests on values projected from observations" else: grade, note = "indicative", f"the conclusion is no stronger than an {basis} value" if basis[0] in "aeiou" else f"the conclusion is no stronger than a {basis} value" if summary["proxies"]: note += f"; {len(summary['proxies'])} value(s) measure a proxy rather than the quantity named" return {**summary, "grade": grade, "note": note} def main() -> int: if len(sys.argv) != 2: print(f"usage: {sys.argv[0]} VALUES.json", file=sys.stderr) return 2 payload = json.loads(Path(sys.argv[1]).read_text()) values = payload["values"] if isinstance(payload, dict) else payload print(json.dumps(decision_grade(values), indent=2)) return 0 if __name__ == "__main__": raise SystemExit(main())