feat: restate the backup case in ITC-CAP terms; add evidence basis; publish consumption-mode signal
Three things. 1. CANON RESTATEMENT (info-tech-canon's ask after accepting our demand) data/capability/platform-audit-storage.json restates the backup case against ITC-CAP 0.2.0: requirement with profile, targets and the failure-domain constraint that decided the procurement; two provisions (data.object and data.backup); all four data.backup evidence hooks satisfied and measured; and consumption in native units — GB, hours, tokens — with unknown never zero. tools/capability.py reads their capabilities.yaml directly rather than copying it, so drift in either repo fails here. The requirement asks D5, the provision is D4, and the review reports below_requirement rather than inflating maturity. 2. EVIDENCE BASIS (tools/basis.py, docs/evidence-basis.md) Every value declares how it was obtained on an ordered scale: invoiced, measured, quoted, derived, projected, estimated, assumed, unknown. A derived value resolves to the weakest basis among its inputs, so precise arithmetic cannot launder weak assumptions. First application is a finding about our own biggest decision: the Scaleway vs Hetzner comparison, EUR 29.14/month stated to the cent, grades "indicative" — 1 of 4 load-bearing values evidenced, weakest "assumed". The direction is robust; the magnitude is a model output. The cheapest fix is recording real operator hours, not better arithmetic. 3. CONSUMPTION-MODE SIGNAL (railiance-platform RAILIANCE-WP-0017) settlement.py gains a consumption-mode command projecting statements into the signal they consume; make consumption-mode PERIOD=YYYY-MM publishes data/consumption-mode/current.json. Currently an empty list: no live charges for 2026-09, so no entity is restricted. Publishing the empty list makes that an assertion rather than an absence, which their contract distinguishes. The validator fails if the published signal is stale. 185 tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
d09924b3fc
commit
13c2b82281
11 changed files with 1163 additions and 3 deletions
147
tests/test_basis.py
Normal file
147
tests/test_basis.py
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parents[1] / "tools"))
|
||||
from basis import (
|
||||
BASIS_ORDER,
|
||||
decision_grade,
|
||||
is_evidenced,
|
||||
profile,
|
||||
rank,
|
||||
resolve,
|
||||
strongest,
|
||||
validate_value,
|
||||
weakest,
|
||||
)
|
||||
|
||||
|
||||
def value(basis="measured", **overrides):
|
||||
result = {"name": "v", "basis": basis, "value": 1}
|
||||
if basis == "unknown":
|
||||
result = {"name": "v", "basis": basis, "value": None, "gap": "not measured (owner: x)"}
|
||||
result.update(overrides)
|
||||
return result
|
||||
|
||||
|
||||
class OrderTest(unittest.TestCase):
|
||||
def test_order_runs_strongest_to_weakest(self):
|
||||
self.assertEqual("invoiced", BASIS_ORDER[0])
|
||||
self.assertEqual("unknown", BASIS_ORDER[-1])
|
||||
self.assertLess(rank("measured"), rank("estimated"))
|
||||
self.assertLess(rank("estimated"), rank("assumed"))
|
||||
|
||||
def test_weakest_and_strongest_pick_opposite_ends(self):
|
||||
bases = ["measured", "assumed", "quoted"]
|
||||
self.assertEqual("assumed", weakest(bases))
|
||||
self.assertEqual("measured", strongest(bases))
|
||||
|
||||
def test_empty_collection_is_unknown_not_an_error(self):
|
||||
self.assertEqual("unknown", weakest([]))
|
||||
|
||||
def test_only_observed_or_contracted_bases_count_as_evidenced(self):
|
||||
for basis in ("invoiced", "measured", "quoted"):
|
||||
self.assertTrue(is_evidenced(basis))
|
||||
for basis in ("derived", "projected", "estimated", "assumed", "unknown"):
|
||||
self.assertFalse(is_evidenced(basis))
|
||||
|
||||
def test_unknown_basis_name_is_rejected(self):
|
||||
with self.assertRaises(ValueError):
|
||||
rank("vibes")
|
||||
|
||||
|
||||
class PropagationTest(unittest.TestCase):
|
||||
"""A derived value is only as strong as its weakest input."""
|
||||
|
||||
def test_derivation_from_measured_inputs_stays_measured(self):
|
||||
derived = value("derived", derived_from=[value("measured"), value("measured")])
|
||||
self.assertEqual("measured", resolve(derived))
|
||||
|
||||
def test_one_assumed_input_drags_the_result_down(self):
|
||||
derived = value("derived", derived_from=[value("quoted"), value("assumed")])
|
||||
self.assertEqual("assumed", resolve(derived))
|
||||
|
||||
def test_propagation_is_recursive(self):
|
||||
inner = value("derived", derived_from=[value("measured"), value("estimated")])
|
||||
outer = value("derived", derived_from=[value("measured"), inner])
|
||||
self.assertEqual("estimated", resolve(outer))
|
||||
|
||||
def test_derived_without_inputs_is_rejected(self):
|
||||
with self.assertRaisesRegex(ValueError, "derived_from"):
|
||||
resolve(value("derived"))
|
||||
|
||||
def test_non_derived_value_resolves_to_itself(self):
|
||||
self.assertEqual("quoted", resolve(value("quoted")))
|
||||
|
||||
|
||||
class ValidationTest(unittest.TestCase):
|
||||
def test_unknown_must_not_carry_a_quantity(self):
|
||||
with self.assertRaises(ValueError):
|
||||
validate_value({"basis": "unknown", "value": 0, "gap": "g"})
|
||||
|
||||
def test_unknown_must_name_the_gap_and_owner(self):
|
||||
with self.assertRaisesRegex(ValueError, "name the gap"):
|
||||
validate_value({"basis": "unknown", "value": None})
|
||||
|
||||
def test_known_basis_must_carry_a_quantity(self):
|
||||
with self.assertRaisesRegex(ValueError, "must carry a quantity"):
|
||||
validate_value({"basis": "measured", "value": None})
|
||||
|
||||
def test_zero_is_a_measurement_not_an_absence(self):
|
||||
validate_value({"basis": "measured", "value": 0})
|
||||
|
||||
def test_derived_from_only_allowed_on_derived(self):
|
||||
with self.assertRaisesRegex(ValueError, "only a derived value"):
|
||||
validate_value(value("measured", derived_from=[value()]))
|
||||
|
||||
def test_empty_proxy_target_is_rejected_but_absent_one_is_fine(self):
|
||||
with self.assertRaisesRegex(ValueError, "proxy_for"):
|
||||
validate_value(value("measured", proxy_for=" "))
|
||||
validate_value(value("measured", proxy_for=None))
|
||||
validate_value(value("measured", proxy_for="cost of the shared host"))
|
||||
|
||||
|
||||
class DecisionGradeTest(unittest.TestCase):
|
||||
def test_all_evidenced_values_grade_evidenced(self):
|
||||
result = decision_grade([value("measured"), value("invoiced"), value("quoted")])
|
||||
self.assertEqual("evidenced", result["grade"])
|
||||
self.assertEqual(1.0, result["evidenced_ratio"])
|
||||
|
||||
def test_a_single_assumption_makes_the_whole_decision_indicative(self):
|
||||
result = decision_grade([value("measured"), value("measured"), value("assumed")])
|
||||
self.assertEqual("indicative", result["grade"])
|
||||
self.assertEqual("assumed", result["weakest"])
|
||||
|
||||
def test_an_unknown_makes_the_decision_insufficient(self):
|
||||
result = decision_grade([value("measured"), value("unknown")])
|
||||
self.assertEqual("insufficient", result["grade"])
|
||||
|
||||
def test_precise_arithmetic_does_not_upgrade_weak_inputs(self):
|
||||
"""The provider comparison: a two-decimal euro figure built on assumptions."""
|
||||
euros = value(
|
||||
"derived",
|
||||
name="labour_eur_month",
|
||||
value=60.0,
|
||||
derived_from=[value("assumed", name="hours"), value("assumed", name="rate")],
|
||||
)
|
||||
result = decision_grade([value("quoted", name="price"), euros])
|
||||
self.assertEqual("assumed", result["weakest"])
|
||||
self.assertEqual("indicative", result["grade"])
|
||||
|
||||
def test_projected_values_grade_separately_from_estimates(self):
|
||||
result = decision_grade([value("measured"), value("projected")])
|
||||
self.assertEqual("projected", result["grade"])
|
||||
|
||||
def test_proxies_are_reported_in_the_note(self):
|
||||
result = decision_grade([value("measured", proxy_for="cost of the shared host")])
|
||||
self.assertEqual(1, len(result["proxies"]))
|
||||
self.assertIn("proxy", result["note"])
|
||||
|
||||
def test_profile_counts_by_basis_in_canonical_order(self):
|
||||
result = profile([value("assumed"), value("measured"), value("measured")])
|
||||
self.assertEqual(["measured", "assumed"], list(result["by_basis"]))
|
||||
self.assertEqual(2, result["by_basis"]["measured"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
167
tests/test_capability.py
Normal file
167
tests/test_capability.py
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
import json
|
||||
import sys
|
||||
import unittest
|
||||
from copy import deepcopy
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).parents[1]
|
||||
sys.path.insert(0, str(ROOT / "tools"))
|
||||
from capability import (
|
||||
consumption_profile,
|
||||
evidence_coverage,
|
||||
load_canon,
|
||||
review,
|
||||
validate_provision,
|
||||
validate_requirement,
|
||||
)
|
||||
|
||||
RECORD = json.loads((ROOT / "data/capability/platform-audit-storage.json").read_text())
|
||||
|
||||
|
||||
def canon():
|
||||
return load_canon()
|
||||
|
||||
|
||||
class CanonBindingTest(unittest.TestCase):
|
||||
"""We read the canon rather than copying it; drift must fail here."""
|
||||
|
||||
def setUp(self):
|
||||
self.canon = canon()
|
||||
|
||||
def test_catalog_is_the_version_we_restated_against(self):
|
||||
self.assertEqual("0.2.0", self.canon["version"])
|
||||
self.assertEqual("0.3.0", self.canon["canon_version"])
|
||||
|
||||
def test_human_effort_and_intelligence_classes_exist_with_native_units(self):
|
||||
classes = self.canon["resource_classes"]
|
||||
self.assertEqual("hour", classes["H"]["native_unit"])
|
||||
self.assertEqual("internal", classes["H"]["supply"])
|
||||
self.assertEqual("constrained", classes["H"]["capacity_behaviour"])
|
||||
self.assertEqual("token", classes["I"]["native_unit"])
|
||||
|
||||
def test_unknown_capability_is_rejected(self):
|
||||
with self.assertRaisesRegex(ValueError, "unknown capability"):
|
||||
validate_requirement({"capability": "data.telepathy"}, self.canon)
|
||||
|
||||
|
||||
class RequirementTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.canon = canon()
|
||||
self.requirement = deepcopy(RECORD["requires"][0])
|
||||
|
||||
def test_the_real_requirement_validates(self):
|
||||
validate_requirement(self.requirement, self.canon)
|
||||
|
||||
def test_target_key_must_be_a_declared_quality_dimension(self):
|
||||
self.requirement["targets"]["chattiness"] = {"value": 1, "unit": "x"}
|
||||
with self.assertRaisesRegex(ValueError, "quality dimension"):
|
||||
validate_requirement(self.requirement, self.canon)
|
||||
|
||||
def test_constraint_dimension_must_be_declared(self):
|
||||
self.requirement["constraints"][0]["dimension"] = "vibes"
|
||||
with self.assertRaisesRegex(ValueError, "quality dimension"):
|
||||
validate_requirement(self.requirement, self.canon)
|
||||
|
||||
def test_predicate_must_be_in_the_closed_set(self):
|
||||
self.requirement["constraints"][0]["predicate"] = "sort_of_near"
|
||||
with self.assertRaisesRegex(ValueError, "closed set"):
|
||||
validate_requirement(self.requirement, self.canon)
|
||||
|
||||
def test_profile_must_be_declared_on_that_capability(self):
|
||||
self.requirement["profile"] = "volume-of-vibes"
|
||||
with self.assertRaisesRegex(ValueError, "profile"):
|
||||
validate_requirement(self.requirement, self.canon)
|
||||
|
||||
def test_the_constraint_that_decided_procurement_is_expressible(self):
|
||||
constraint = self.requirement["constraints"][0]
|
||||
self.assertEqual("geographical_separation", constraint["dimension"])
|
||||
self.assertEqual("not_in", constraint["predicate"])
|
||||
self.assertIn("railiance01", [entry["id"] for entry in constraint["of"]])
|
||||
|
||||
|
||||
class ProvisionTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.canon = canon()
|
||||
self.provisions = {p["capability"]: deepcopy(p) for p in RECORD["provisions"]}
|
||||
|
||||
def test_both_real_provisions_validate(self):
|
||||
for provision in self.provisions.values():
|
||||
validate_provision(provision, self.canon)
|
||||
|
||||
def test_consumption_unit_must_be_the_class_native_unit(self):
|
||||
provision = self.provisions["data.object"]
|
||||
row = next(r for r in provision["consumes"] if r["class"] == "H")
|
||||
row["quantity"]["unit"] = "eur"
|
||||
with self.assertRaisesRegex(ValueError, "native unit"):
|
||||
validate_provision(provision, self.canon)
|
||||
|
||||
def test_currency_cannot_be_smuggled_in_as_a_class(self):
|
||||
provision = self.provisions["data.object"]
|
||||
provision["consumes"].append(
|
||||
{"class": "EUR", "quantity": {"value": 7.35, "unit": "eur"}, "basis": "quoted"}
|
||||
)
|
||||
with self.assertRaisesRegex(ValueError, "unknown resource class"):
|
||||
validate_provision(provision, self.canon)
|
||||
|
||||
def test_unknown_consumption_may_not_be_recorded_as_zero(self):
|
||||
provision = self.provisions["data.backup"]
|
||||
row = next(r for r in provision["consumes"] if r["basis"] == "unknown")
|
||||
row["quantity"]["value"] = 0
|
||||
with self.assertRaises(ValueError):
|
||||
validate_provision(provision, self.canon)
|
||||
|
||||
def test_evidence_hook_must_be_declared_on_the_capability(self):
|
||||
provision = self.provisions["data.backup"]
|
||||
provision["evidence"][0]["hook"] = "vibes_check"
|
||||
with self.assertRaisesRegex(ValueError, "evidence hook"):
|
||||
validate_provision(provision, self.canon)
|
||||
|
||||
def test_duplicate_class_rows_are_rejected(self):
|
||||
provision = self.provisions["data.backup"]
|
||||
provision["consumes"].append(deepcopy(provision["consumes"][0]))
|
||||
with self.assertRaisesRegex(ValueError, "duplicate"):
|
||||
validate_provision(provision, self.canon)
|
||||
|
||||
def test_backup_provision_satisfies_all_four_declared_evidence_hooks(self):
|
||||
coverage = evidence_coverage(self.provisions["data.backup"], self.canon)
|
||||
self.assertTrue(coverage["complete"])
|
||||
self.assertEqual([], coverage["missing"])
|
||||
self.assertEqual(4, len(coverage["supplied"]))
|
||||
|
||||
def test_object_provision_is_honest_about_missing_hooks(self):
|
||||
coverage = evidence_coverage(self.provisions["data.object"], self.canon)
|
||||
self.assertFalse(coverage["complete"])
|
||||
self.assertIn("object_integrity_tests", coverage["missing"])
|
||||
|
||||
def test_effort_and_tokens_are_recorded_in_native_units(self):
|
||||
rows = {r["class"]: r for r in self.provisions["data.object"]["consumes"]}
|
||||
self.assertEqual("hour", rows["H"]["quantity"]["unit"])
|
||||
self.assertEqual("token", rows["I"]["quantity"]["unit"])
|
||||
self.assertEqual("unknown", rows["I"]["basis"])
|
||||
|
||||
|
||||
class ReviewTest(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.report = review(deepcopy(RECORD), canon())
|
||||
|
||||
def test_requirement_is_reported_as_below_the_asked_maturity(self):
|
||||
requirement = self.report["requirements"][0]
|
||||
self.assertEqual("D5", requirement["required"])
|
||||
self.assertEqual("D4", requirement["provided"])
|
||||
self.assertEqual("below_requirement", requirement["status"])
|
||||
|
||||
def test_provider_comparison_grades_as_indicative_not_evidenced(self):
|
||||
grade = self.report["alternatives_grade"]
|
||||
self.assertEqual("indicative", grade["grade"])
|
||||
self.assertEqual("assumed", grade["weakest"])
|
||||
self.assertEqual(0.25, grade["evidenced_ratio"])
|
||||
|
||||
def test_consumption_profiles_report_what_is_still_unknown(self):
|
||||
for provision in self.report["provisions"]:
|
||||
self.assertEqual("unknown", provision["consumption"]["weakest"])
|
||||
self.assertGreater(provision["consumption"]["count"], 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue