resource-control/docs/evidence/RESOURCE-WP-0002-T07-control-loop-2026-08-14.md
tegwick 10b988fa1c feat(wp-0002): complete T07 — control loop on the live backup resource
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>
2026-08-14 20:53:12 +02:00

117 lines
5.8 KiB
Markdown

# RESOURCE-WP-0002-T07 — fin-hub feed and optimization loop — 2026-08-14
## Result
The backup resource is live and proven, so the loop it exists to feed can now
run on real evidence instead of a model. T07 closes the cycle: observation →
thresholds → normalized fin-hub evidence → decided optimization case.
| Artifact | Purpose |
|---|---|
| `data/actuals/2026-08.json` | First real monthly observation for `platform:audit-storage` |
| `data/thresholds/platform-audit-storage.json` | Budget variance, abnormal growth, stale backup, and unused-commitment thresholds |
| `tools/thresholds.py`, `make thresholds PERIOD=…` | Fail-closed threshold evaluation |
| `tools/financial_exchange.py usage`, `make exchange-usage` | Normalized technical-usage evidence to fin-hub |
| `data/optimization/platform-audit-storage-2026-08.json` | The A/B/C case, now `approved` and executed |
| `data/resources/platform-audit-storage.json` | `ordered``active`, commissioned 2026-08-14 |
## The first period is honest about being four hours long
The bucket was created 2026-08-14T16:21:56Z and the first backup completed at
18:14:21Z. The 2026-08 row therefore covers about four hours of operation, and
says so. What is measured:
| Proxy | Value | Source |
|---|---|---|
| `database_gb` | 0.6365 | PGDATA 636 485 632 B |
| `stored_gb` | 0.0066 | 8 objects, 6 604 031 B in `platform-pg/` |
| `backup_success_pct` | 100 | 1 of 1 completed backups |
| `restore_rto_minutes` | 1.08 | 65 s full restore; PITR the same |
Five proxies are `null`, each with a named owner in `measurement_gaps`: WAL is
not separable from the base backup in the archive, restore egress bytes were
not recorded, no request metric is exposed to the scoped key, no invoice exists,
and no time record was kept for the procurement and drill work.
`internal_labor_hours` being null deserves a note: real operator hours were
spent this period on procurement, credential custody, and two restore drills.
Recording zero would have been false, and estimating would have been invention.
It is null with the reason attached, and the next period starts a time record.
## Why August produces no variance
`make variance ACTUAL=data/actuals/2026-08.json` returns `no-forecast` for the
period. The decision forecast begins at 2026-09, because the resource was not
expected to exist in August. This is the correct answer, not a defect: August
is a commissioning baseline and **2026-09 is the first comparable month**.
The threshold run reflects the same thing without pretending. Of nine
thresholds: two `within`, one `not_applicable`, six `unmeasured`, zero
breaches.
- `within``stale-backup` (100%) and `restore-rto-regression` (1.08 min against
a 3.24 min limit). These are the two things that were actually proven.
- `not_applicable``unused-commitment`. Scaleway Multi-AZ is usage-based with
no commitment, so there is nothing to under-use. Distinguished from passing.
- `unmeasured` — everything requiring an invoice, a request metric, a WAL split,
or a forecast row.
`unmeasured` is deliberately not `within`. A threshold that silently passes on
absent evidence is worse than no threshold: it reports safety it never checked.
## Thresholds set
Budget variance at 10% on infrastructure and 1 hour on labour; abnormal growth
at 20% on stored bytes and WAL; any egress not explained by a recorded drill;
backup success below 100% on any single run, not averaged across a month; WAL
archive gap above 15 minutes, three times the 5-minute `archive_timeout` beyond
which the RPO requirement fails; restore RTO above 3.24 minutes, three times the
measured baseline.
Two are declared rather than active. `wal_archive_gap_minutes` needs an archive
health metric from rapp-postgres. No euro budget ceiling exists: Scaleway has no
hard spend stop, the founder's email alert is the accepted backstop, and an
authoritative ceiling belongs to fin-hub.
## Feed to fin-hub
`financial_exchange.py usage` emits `technical_usage` records under the
contract's resource-control-to-fin-hub direction: resource, service, workload,
attribution key, period bounds, usage proxies, service evidence, and valued
internal labour — with `measurement_gaps` carried through so fin-hub sees why a
field is empty.
Infrastructure cost is absent by design. fin-hub owns the booked fact; an
uninvoiced period has no amount to send, and a `null` is never normalized to
`0.00`.
## The optimization case is now decided
`opt:platform-audit-storage:2026-08` moved from `blocked_on_evidence` to
`approved`, approver "founder (human financial authority)", 2026-08-14.
The schema gained `excluded` / `exclusion_reason` on an option, because the real
decision exposed a modelling gap: Host Europe never supplied written terms, and
the deciding authority chose to exclude it rather than delay recovery capability
indefinitely. Before this, an unevaluable alternative blocked its case forever,
which would have left the record claiming no decision had been made while the
bucket was in production. An excluded option no longer blocks, keeps its
recorded unknowns, and must state what would bring it back.
Hetzner remains a fully costed `reject` at €29.14/month dearer. The rationale
records two accepted risks at decision time: no euro hard-stop at Scaleway, and
the independent second copy required by the correlated-failure rule still
outstanding as T06.
## Boundary held
No booked cost was originated. The resource record moved to `active` on
operational fact — production backup enabled, `ContinuousArchivingSuccess`, both
restores verified — not on the purchase. Implementation of anything the case
approves is delegated to `railiance-platform` and `rapp-postgres`.
## Standing dependency
Total cost stays unknown until Scaleway invoices and fin-hub books it under
`FIN-WP-0004`. The loop runs monthly regardless; 2026-09 is the first period
that will produce real variance.