The backup is procured and proven, so the loop runs on real evidence. - data/actuals/2026-08.json: first real observation. database 0.6365 GB, stored 0.0066 GB over 8 objects, backup success 1/1, restore RTO 1.08 min. Five proxies null, each with a named owner in measurement_gaps. - data/thresholds/platform-audit-storage.json + tools/thresholds.py: budget variance, abnormal growth, stale backup, unused commitment. Fail-closed — an unmeasured value is reported as unmeasured, never as within. - financial_exchange.py gains a usage mode emitting technical_usage records to fin-hub, with measurement gaps carried through and no infrastructure amount: fin-hub owns the booked fact and a null is never sent as 0.00. - observation schema 0.2 allows null cost and usage proxies; variance.py fails closed rather than reporting a 100% favourable variance on a missing amount. - platform-audit-storage: ordered -> active, commissioned 2026-08-14, on operational fact rather than on the purchase. The optimization case is now approved by the founder. That needed a schema change: Host Europe never supplied written terms, so options gained excluded/exclusion_reason. Previously an unevaluable alternative blocked its case forever, leaving the record claiming no decision while the bucket was in production. An excluded option keeps its unknowns and must say what would bring it back. August produces no variance and should not: the decision forecast starts at 2026-09, so August is a commissioning baseline. Threshold run is 2 within, 1 not applicable, 6 unmeasured, 0 breaches. Also fixes a pre-existing test failure: reef-storage consumers_actual is now rapp-postgres, which the assertion still expected to be empty. 136 tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
211 lines
9.5 KiB
Python
211 lines
9.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Produce planning evidence and consume fin-hub booked-cost projections."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from calendar import monthrange
|
|
from datetime import date
|
|
from decimal import Decimal, ROUND_HALF_EVEN
|
|
from pathlib import Path
|
|
|
|
CENT = Decimal("0.01")
|
|
|
|
|
|
def money(value: object) -> Decimal:
|
|
return Decimal(str(value)).quantize(CENT, rounding=ROUND_HALF_EVEN)
|
|
|
|
|
|
def money_text(value: object) -> str:
|
|
return format(money(value), ".2f")
|
|
|
|
|
|
def forecast_records(payload: dict, *, resource_id: str, source_ref: str) -> list[dict]:
|
|
if payload.get("schema_version") != "0.1" or payload.get("record_type") != "forecast":
|
|
raise ValueError("expected a resource-control forecast v0.1")
|
|
if not resource_id.startswith("resource:"):
|
|
raise ValueError("resource_id must use the resource: namespace")
|
|
created_at = payload["created_at"]
|
|
version = f"{payload['provider_id']}:{created_at}"
|
|
records = []
|
|
for row in payload["rows"]:
|
|
infrastructure = money(row["infrastructure_eur"])
|
|
labor = money(row["internal_labor_eur"])
|
|
if money(row["total_eur"]) != infrastructure + labor:
|
|
raise ValueError("total_eur does not match infrastructure plus internal labor")
|
|
year, month = (int(part) for part in row["period"].split("-"))
|
|
period_start = date(year, month, 1)
|
|
period_end = date(year, month, monthrange(year, month)[1])
|
|
records.append({
|
|
"schema_version": "0.1",
|
|
"record_type": "forecast",
|
|
"record_id": f"forecast:{payload['provider_id']}:{payload['cost_attribution_key']}:{row['period']}:{created_at}",
|
|
"revision_of": payload.get("forecast_ref"),
|
|
"resource_id": resource_id,
|
|
"service_id": payload["provider_id"],
|
|
"workload_id": payload["workload"],
|
|
"tenant_id": None,
|
|
"environment": "production",
|
|
"cost_attribution_key": payload["cost_attribution_key"],
|
|
"period_start": period_start.isoformat(),
|
|
"period_end": period_end.isoformat(),
|
|
"currency": "EUR",
|
|
"source_evidence": [source_ref, *row.get("evidence", [])],
|
|
"created_at": created_at,
|
|
"scenario": payload.get("scenario") or "base",
|
|
"forecast_version": version,
|
|
"costs": {
|
|
"infrastructure": money_text(infrastructure),
|
|
"internal_labor": money_text(labor),
|
|
"external_services": "0.00",
|
|
"setup": "0.00",
|
|
"other": "0.00"
|
|
},
|
|
"uncertainty": None,
|
|
"assumptions": [
|
|
f"database_gb={row['database_gb']}",
|
|
f"stored_gb={row['stored_gb']}",
|
|
f"wal_gb={row['wal_gb']}",
|
|
f"restore_egress_gb={row['restore_egress_gb']}",
|
|
f"write_requests={row['write_requests']}",
|
|
f"read_requests={row['read_requests']}",
|
|
f"internal_labor_hours={row['internal_labor_hours']}"
|
|
]
|
|
})
|
|
return records
|
|
|
|
|
|
def usage_records(payload: dict, *, resource_id: str, source_ref: str) -> list[dict]:
|
|
"""Normalize a monthly technical observation for the fin-hub exchange.
|
|
|
|
Only the resource-control-to-fin-hub direction: technical usage and valued
|
|
internal labour. Infrastructure cost is deliberately absent — fin-hub owns
|
|
the booked fact, and an uninvoiced period carries no amount to send. A null
|
|
stays null; it is never sent as 0.00.
|
|
"""
|
|
if payload.get("schema_version") not in {"0.1", "0.2"}:
|
|
raise ValueError("expected a resource-control observation v0.1 or v0.2")
|
|
if payload.get("record_type") != "usage_observation":
|
|
raise ValueError("expected record_type usage_observation")
|
|
if not resource_id.startswith("resource:"):
|
|
raise ValueError("resource_id must use the resource: namespace")
|
|
|
|
records = []
|
|
for row in payload["rows"]:
|
|
year, month = (int(part) for part in row["period"].split("-"))
|
|
period_start = date(year, month, 1)
|
|
period_end = date(year, month, monthrange(year, month)[1])
|
|
labor = row["internal_labor_eur"]
|
|
records.append({
|
|
"schema_version": "0.1",
|
|
"record_type": "technical_usage",
|
|
"record_id": (
|
|
f"usage:{payload['provider_id']}:{payload['cost_attribution_key']}:{row['period']}"
|
|
),
|
|
"resource_id": resource_id,
|
|
"service_id": payload["provider_id"],
|
|
"workload_id": payload["workload"],
|
|
"tenant_id": None,
|
|
"environment": "production",
|
|
"cost_attribution_key": payload["cost_attribution_key"],
|
|
"period_start": period_start.isoformat(),
|
|
"period_end": period_end.isoformat(),
|
|
"currency": "EUR",
|
|
"created_at": payload["created_at"],
|
|
"source_evidence": [source_ref, *payload.get("evidence", [])],
|
|
"usage": {
|
|
key: row[key]
|
|
for key in (
|
|
"database_gb", "stored_gb", "wal_gb", "restore_egress_gb",
|
|
"write_requests", "read_requests",
|
|
)
|
|
},
|
|
"service_evidence": {
|
|
key: row.get(key)
|
|
for key in ("backup_success_pct", "restore_rto_minutes")
|
|
},
|
|
"costs": {
|
|
# resource-control originates internal labour only. Booked
|
|
# infrastructure cost is fin-hub's to record, not ours to echo.
|
|
"internal_labor": None if labor is None else money_text(labor),
|
|
"internal_labor_hours": row["internal_labor_hours"],
|
|
},
|
|
"measurement_gaps": row.get("measurement_gaps", []),
|
|
"notes": payload.get("notes", []),
|
|
})
|
|
return records
|
|
|
|
|
|
def validate_booked_cost(record: dict) -> dict:
|
|
required = {
|
|
"schema_version", "record_type", "financial_fact_id", "adjustment_kind",
|
|
"source_document_id", "source_line_id", "content_fingerprint", "provider",
|
|
"accounting_period", "currency", "gross_amount", "adjustment_amount",
|
|
"effective_amount", "source_evidence_ref", "recorded_at"
|
|
}
|
|
missing = required - record.keys()
|
|
if missing:
|
|
raise ValueError(f"booked-cost evidence missing {sorted(missing)}")
|
|
if record["schema_version"] != "0.1" or record["record_type"] != "booked_cost":
|
|
raise ValueError("unsupported booked-cost schema")
|
|
if len(record["currency"]) != 3 or record["currency"] != record["currency"].upper():
|
|
raise ValueError("currency must be an uppercase three-letter code")
|
|
if money(record["effective_amount"]) != money(record["gross_amount"]) + money(record["adjustment_amount"]):
|
|
raise ValueError("effective_amount must equal gross plus adjustment")
|
|
if record.get("tax_status") == "unknown" and record.get("tax_amount") is not None:
|
|
raise ValueError("unknown tax must not have a tax amount")
|
|
return record
|
|
|
|
|
|
def reconcile(forecasts: list[dict], booked_costs: list[dict]) -> list[dict]:
|
|
planned = {
|
|
(record["period_start"][:7], record["currency"], record.get("cost_attribution_key")):
|
|
money(record["costs"]["infrastructure"])
|
|
for record in forecasts if record["record_type"] == "forecast"
|
|
}
|
|
observed: dict[tuple[str, str, str | None], Decimal] = {}
|
|
fact_ids: dict[tuple[str, str, str | None], list[str]] = {}
|
|
for raw in booked_costs:
|
|
record = validate_booked_cost(raw)
|
|
key = (record["accounting_period"], record["currency"], record.get("cost_attribution_key"))
|
|
observed[key] = observed.get(key, Decimal("0")) + money(record["effective_amount"])
|
|
fact_ids.setdefault(key, []).append(record["financial_fact_id"])
|
|
rows = []
|
|
for key in sorted(set(planned) | set(observed), key=lambda item: (item[0], item[1], item[2] or "")):
|
|
forecast = planned.get(key)
|
|
actual = observed.get(key)
|
|
rows.append({
|
|
"period": key[0], "currency": key[1], "cost_attribution_key": key[2],
|
|
"forecast_infrastructure": None if forecast is None else money_text(forecast),
|
|
"booked_effective": None if actual is None else money_text(actual),
|
|
"variance": None if forecast is None or actual is None else money_text(actual - forecast),
|
|
"financial_fact_ids": fact_ids.get(key, []),
|
|
"status": "reconciled" if forecast is not None and actual is not None else "missing-booked-cost" if forecast is not None else "missing-forecast"
|
|
})
|
|
return rows
|
|
|
|
|
|
def main() -> int:
|
|
if len(sys.argv) < 3 or sys.argv[1] not in {"forecast", "usage", "reconcile"}:
|
|
print(
|
|
f"usage: {sys.argv[0]} forecast FORECAST.json [RESOURCE_ID]"
|
|
f" | usage OBSERVATION.json [RESOURCE_ID]"
|
|
f" | reconcile FORECAST_EVIDENCE.json BOOKED_COST.json",
|
|
file=sys.stderr,
|
|
)
|
|
return 2
|
|
if sys.argv[1] in {"forecast", "usage"}:
|
|
payload = json.loads(Path(sys.argv[2]).read_text())
|
|
resource_id = sys.argv[3] if len(sys.argv) > 3 else "resource:platform_audit_storage"
|
|
build = forecast_records if sys.argv[1] == "forecast" else usage_records
|
|
print(json.dumps(build(payload, resource_id=resource_id, source_ref=sys.argv[2]), indent=2))
|
|
return 0
|
|
forecasts = json.loads(Path(sys.argv[2]).read_text())
|
|
booked = json.loads(Path(sys.argv[3]).read_text())
|
|
print(json.dumps(reconcile(forecasts, booked), indent=2))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|