First delegated evidence from RESOURCE-WP-0003-T04 to land. railiance-platform delivered apps-pg capacity, utilization, consumers, and the apps-pg-dbbytes-v1 allocation driver, and correctly delivered no EUR. - data/resources/apps-pg.json: real capacity; allocation unattributed -> shared under apps-pg-dbbytes-v1; second consumer vergabe-teilnahme registered - data/control-cycle/apps-pg-2026-09-base.json: first operational control-cycle record in the repository - examples/control-cycle/apps-pg-*.json retired; the invented fixture collided with the real record's identifier - data/portfolio-coverage-2026-08-14.json: gap marked delivered with three residual unknowns still open The real evidence exposed a design gap in the T05 schema: v0.1 required a number for every cost field, so recording genuine usage without a booked cost meant inventing one. Schema 0.2 permits null costs, null unattributed_eur, a technical unattributed_share, and null measurements. Null is unknown, never zero; an unknown component makes the total null rather than the sum of the known parts; and the comparator classifies unknown amounts as data_quality instead of computing a variance. Existing 0.1 records are not rewritten. apps-pg is now measured (idle at 5.8% of volume) and attributed, and remains unpriced: delivered technical evidence does not create a booked cost. 86 tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
154 lines
7 KiB
Markdown
154 lines
7 KiB
Markdown
# Forecast-to-actual resource control
|
||
|
||
## Why the €2.89, €5.14, €62.89, and €65.14 figures differ
|
||
|
||
They describe two volumes and two cost scopes:
|
||
|
||
| Figure | Stored volume | Scope | Calculation |
|
||
| --- | ---: | --- | --- |
|
||
| €2.89 | 180 GB | infrastructure only | 180 × €0.01606 |
|
||
| €62.89 | 180 GB | infrastructure + internal labor | €2.89 + 1 h × €60 |
|
||
| €5.14 | 320 GB | infrastructure only | 320 × €0.01606 |
|
||
| €65.14 | 320 GB | infrastructure + internal labor | €5.14 + 1 h × €60 |
|
||
|
||
The older €62.89 estimate was not “storage without elastic cost”; it already
|
||
included the €60 monthly operator allowance. The normalized table introduced a
|
||
different stored volume and showed infrastructure separately. Future reports
|
||
must always label both `stored_gb` and cost scope.
|
||
|
||
## Control records
|
||
|
||
A forecast is a timestamped, immutable record of what was believed at decision
|
||
time. The initial base forecast is
|
||
`data/forecasts/platform-audit-storage-scaleway-base-2026-08.json`. Do not edit
|
||
it after actual evidence exists. If assumptions change, create a new forecast
|
||
with a new creation date and retain the prior one so forecast accuracy remains
|
||
auditable.
|
||
|
||
After every billing month, record a technical `usage_observation` under
|
||
`data/actuals/`. Authoritative booked financial facts come from fin-hub and
|
||
are joined by reference; resource-control does not originate a second booked
|
||
actual.
|
||
The required cost and usage proxies are:
|
||
|
||
- database size and provider stored bytes;
|
||
- WAL volume;
|
||
- write and read requests;
|
||
- restore-test egress;
|
||
- infrastructure invoice cost;
|
||
- internal operations hours and valued labor cost;
|
||
- total attributable cost;
|
||
- backup success, maximum observed RPO, and restore RTO when measured.
|
||
|
||
Provider stored bytes are intentionally distinct from logical database size.
|
||
They expose base-backup retention, WAL, compression, versioning, incomplete
|
||
multipart uploads, and lifecycle behavior. Invoice cost is distinct from the
|
||
provider's usage estimator and must reconcile to non-secret billing evidence.
|
||
|
||
## Error calculation and review
|
||
|
||
`make variance ACTUAL=data/actuals/YYYY-MM.json` reports signed error and
|
||
absolute percentage error for each numeric proxy. Initially review rather than
|
||
automatically rewrite assumptions:
|
||
|
||
- investigate infrastructure cost error above 10%;
|
||
- investigate stored-byte or WAL error above 20%;
|
||
- investigate any unplanned egress or request-class charge;
|
||
- investigate labor error above 1 hour or 25%;
|
||
- investigate every backup failure, RPO breach, or restore-RTO regression.
|
||
|
||
After three comparable months, calculate mean absolute percentage error by
|
||
proxy and recalibrate the next forecast. Avoid MAPE where the forecast is zero;
|
||
use absolute error and explain the new activity instead. Separate forecast
|
||
error from price variance: a bill can differ because usage was wrong, the rate
|
||
changed, tax/discount treatment differed, or an unmodeled SKU appeared.
|
||
|
||
Quarterly provider comparison should use the latest actual trailing three
|
||
months, the next 12-month forecast, one monthly restore, an exit event, and
|
||
observed—not aspirational—operator labor.
|
||
|
||
## General portfolio control record
|
||
|
||
The storage-specific v0.1 observation remains valid for the backup procurement
|
||
case. New portfolio controls use
|
||
`schemas/resource-control-cycle.schema.json`, whose resource-specific
|
||
`usage_proxies` allow the same comparison mechanism to cover storage, VMs or
|
||
cluster compute, shared platform services, managed services, and future
|
||
resource types without pretending they share the same utilization unit.
|
||
|
||
Each record separates:
|
||
|
||
- provisioned and used capacity, with a fixed, elastic, or hybrid model;
|
||
- infrastructure, internal labor, and external labor cost;
|
||
- booked financial facts, referenced rather than copied from fin-hub;
|
||
- allocation method, driver, version, and unattributed residual;
|
||
- service constraints and their units;
|
||
- low/base/high forecast scenario or observed actual;
|
||
- evidence and uncertainty.
|
||
|
||
Forecast records are append-only. A changed forecast receives a new
|
||
`record_id`, creation time, and `revision_of` reference. Actuals must name the
|
||
exact original `forecast_ref`; a later revision must never replace the
|
||
decision-time baseline when measuring forecast error.
|
||
|
||
Run a generic comparison with:
|
||
|
||
```sh
|
||
make control-cycle FORECAST=examples/control-cycle/storage-forecast.json \
|
||
ACTUAL=examples/control-cycle/storage-actual.json
|
||
```
|
||
|
||
The examples cover storage and `reef-railiance` cluster compute. Their numbers
|
||
are illustrative contract fixtures, not booked facts. Operational records
|
||
replace them once the owner workplans publish observations — the illustrative
|
||
`apps-pg` pair was retired on 2026-08-14 when `RAILIANCE-WP-0016` delivered the
|
||
real one to `data/control-cycle/apps-pg-2026-09-base.json`.
|
||
|
||
Illustrative fixtures live in `examples/control-cycle/`; operational records
|
||
live in `data/control-cycle/` and must cite the authoritative repository
|
||
evidence they came from and state their uncertainty. The validator enforces
|
||
both.
|
||
|
||
### Schema 0.2 — unknown is not zero
|
||
|
||
Real evidence arrives incomplete. `RAILIANCE-WP-0016` supplied apps-pg
|
||
capacity, utilization, consumers, and an allocation driver, and correctly
|
||
supplied no EUR: the host bill belongs to `resource:hosteurope:railiance01`,
|
||
whose price is still unknown. Schema 0.1 could not express that — every cost
|
||
field required a number, so the only way to record real usage was to invent a
|
||
cost.
|
||
|
||
Schema 0.2 permits `null` for each cost component, for `unattributed_eur`, and
|
||
for any measurement value, and adds `unattributed_share` so a shared-cost
|
||
residual can be recorded from technical evidence before any EUR exists. The
|
||
rules:
|
||
|
||
- `null` means unknown or unbounded, never zero;
|
||
- if any cost component is unknown, `total` is `null` — never the sum of the
|
||
parts that happen to be known;
|
||
- the comparator returns `status: "unknown"` for that component and classifies
|
||
it `data_quality`, overriding any explicit attribution, because a missing
|
||
amount is an evidence gap rather than a price or labour movement;
|
||
- known components still compute their variance alongside unknown ones.
|
||
|
||
Existing 0.1 records stay valid and are never rewritten. Immutability applies
|
||
to the record, not to the schema, so `schema_version` accepts both.
|
||
|
||
## Variance attribution
|
||
|
||
Every material variance is assigned to one of six controlled categories:
|
||
|
||
- `demand`: the amount of consumed service differed;
|
||
- `provider_price`: rate, discount, tax, currency, or billed SKU differed;
|
||
- `allocation`: a shared-cost driver or attribution changed;
|
||
- `labor`: internal or external effort differed;
|
||
- `model`: a formula, assumption, capacity behavior, or omitted component was
|
||
wrong;
|
||
- `data_quality`: evidence is missing, late, inconsistent, or uses a different
|
||
unit.
|
||
|
||
The comparator defaults usage differences to demand, infrastructure cost to
|
||
provider price, and labor fields to labor, while preserving explicit
|
||
attribution supplied with the actual. Missing proxies and unit mismatches fail
|
||
closed as data-quality errors. Attribution explains an error; it does not
|
||
rewrite the forecast.
|