feat(portfolio): fold in RAILIANCE-WP-0016 apps-pg evidence

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>
This commit is contained in:
tegwick 2026-08-14 09:36:57 +02:00
parent b64b5683df
commit 17de8b831e
16 changed files with 908 additions and 71 deletions

View file

@ -10,7 +10,7 @@
| --- | --- | --- | --- | --- |
| workplan | RESOURCE-WP-0001 | finished | — | workplans/RESOURCE-WP-0001-statehub-bootstrap.md |
| workplan | RESOURCE-WP-0002 | active | — | workplans/RESOURCE-WP-0002-procure-postgres-backup-storage.md |
| workplan | RESOURCE-WP-0003 | active | — | workplans/RESOURCE-WP-0003-managed-infrastructure-portfolio-control.md |
| workplan | RESOURCE-WP-0003 | finished | — | workplans/RESOURCE-WP-0003-managed-infrastructure-portfolio-control.md |
| task | RESOURCE-WP-0001-T01 | done | — | workplans/RESOURCE-WP-0001-statehub-bootstrap.md |
| task | RESOURCE-WP-0001-T02 | done | — | workplans/RESOURCE-WP-0001-statehub-bootstrap.md |
| task | RESOURCE-WP-0001-T03 | done | — | workplans/RESOURCE-WP-0001-statehub-bootstrap.md |
@ -26,5 +26,5 @@
| task | RESOURCE-WP-0003-T03 | done | — | workplans/RESOURCE-WP-0003-managed-infrastructure-portfolio-control.md |
| task | RESOURCE-WP-0003-T04 | done | — | workplans/RESOURCE-WP-0003-managed-infrastructure-portfolio-control.md |
| task | RESOURCE-WP-0003-T05 | done | — | workplans/RESOURCE-WP-0003-managed-infrastructure-portfolio-control.md |
| task | RESOURCE-WP-0003-T06 | todo | — | workplans/RESOURCE-WP-0003-managed-infrastructure-portfolio-control.md |
| task | RESOURCE-WP-0003-T07 | todo | — | workplans/RESOURCE-WP-0003-managed-infrastructure-portfolio-control.md |
| task | RESOURCE-WP-0003-T06 | done | — | workplans/RESOURCE-WP-0003-managed-infrastructure-portfolio-control.md |
| task | RESOURCE-WP-0003-T07 | done | — | workplans/RESOURCE-WP-0003-managed-infrastructure-portfolio-control.md |

View file

@ -0,0 +1,69 @@
{
"schema_version": "0.2",
"record_id": "forecast:apps-pg:2026-09:base:v1",
"record_type": "forecast",
"resource_id": "resource:railiance:apps-pg",
"resource_class": "shared_platform_service",
"period": "2026-09",
"created_at": "2026-08-14T00:00:00Z",
"scenario": "base",
"forecast_ref": null,
"revision_of": null,
"usage_proxies": {
"consumer_database_gb": { "value": 0.0155, "unit": "GiB" },
"pgdata_gb": { "value": 0.5766, "unit": "GiB" },
"wal_gb": { "value": 0.5469, "unit": "GiB" },
"sessions": { "value": 7, "unit": "count" }
},
"capacity": {
"model": "hybrid",
"provisioned": {
"storage": { "value": 10, "unit": "GiB" },
"instances": { "value": 1, "unit": "count" },
"max_connections": { "value": 100, "unit": "count" }
},
"used": {
"storage": { "value": 0.5766, "unit": "GiB" },
"instances": { "value": 1, "unit": "count" },
"sessions": { "value": 7, "unit": "count" }
}
},
"costs": {
"currency": "EUR",
"infrastructure": null,
"internal_labor": 30.0,
"external_labor": 0,
"total": null,
"booked_cost_refs": []
},
"allocation": {
"method": "proportional",
"driver": "database_gb",
"method_version": "apps-pg-dbbytes-v1",
"unattributed_eur": null,
"unattributed_share": 0.948
},
"service_constraints": {
"rpo_minutes": { "value": null, "unit": "minutes" },
"rto_minutes": { "value": null, "unit": "minutes" }
},
"uncertainty": {
"level": "high",
"notes": [
"First operational control-cycle record for this resource, from railiance-platform RAILIANCE-WP-0016 captured 2026-08-14T00:04:19Z",
"Infrastructure cost is null: apps-pg is a share of resource:hosteurope:railiance01, whose booked price is unknown (RAIL-HO-WP-0008)",
"Internal labour is 0.5 declared operator-hours per month converted at the EUR 60 per hour rate used in data/demand/platform-audit-storage.json; the hours are an estimate, not a timesheet",
"Total is null because one component is unknown; it is not the sum of the known parts",
"94.8 percent of used volume is WAL and engine catalogs attributed to platform:apps-pg-overhead, not to a consumer",
"vergabe_db has never recorded a transaction, so its 0.4614 byte share reflects a reserved empty database rather than realised demand",
"Five days of cluster life and no metrics retention: this is a snapshot carried forward, not a trend",
"RPO is unbounded and RTO undefined — no ScheduledBackup exists and the cluster is not an Option A logical-dump target"
]
},
"evidence": [
"/home/worsch/railiance-platform/docs/evidence/RAILIANCE-WP-0016-apps-pg-observation.json",
"/home/worsch/railiance-platform/docs/evidence/RAILIANCE-WP-0016-apps-pg-resource-evidence.md",
"RAILIANCE-WP-0016",
"data/resources/apps-pg.json"
]
}

View file

@ -0,0 +1,115 @@
{
"schema_version": "0.1",
"observed_at": "2026-08-14T00:04:19Z",
"inventory_records": 7,
"coverage": [
{
"group": "helix-forge",
"status": "covered-by-shared-forge",
"resource_ids": [
"resource:railiance:forgejo",
"resource:railiance:reef-railiance:k3s",
"resource:hosteurope:railiance01"
]
},
{
"group": "coulomb-social",
"status": "covered",
"resource_ids": [
"resource:tenant:coulomb:coulomb-social",
"resource:railiance:apps-pg",
"resource:railiance:reef-railiance:k3s",
"resource:hosteurope:railiance01"
]
},
{
"group": "shared-railiance",
"status": "covered-substrate-and-selected-services",
"resource_ids": [
"resource:railiance:reef-railiance:k3s",
"resource:railiance:forgejo",
"resource:railiance:apps-pg",
"resource:hosteurope:railiance01",
"resource:platform:audit-storage"
]
},
{
"group": "representative-tenant",
"status": "covered",
"tenant_id": "tenant:friendly:binky",
"resource_ids": [
"resource:tenant:friendly:binky:rapp-qonto",
"resource:railiance:reef-railiance:k3s",
"resource:hosteurope:railiance01"
]
}
],
"owned_gaps": [
{
"owner": "railiance-infra",
"gap": "Host Europe product, provider resource ID, account reference, country/region evidence, contract price, tax treatment, order date, renewal and cancellation dates",
"delegated_workplan": "RAIL-HO-WP-0008",
"state_hub_workplan_id": "7122657f-87c8-46b5-a725-a1af1ba0af12",
"status": "open"
},
{
"owner": "railiance-cluster",
"gap": "cluster-wide requested/used capacity history, allocation driver, generic persistent-storage contract, and independent failure domains",
"delegated_workplan": "RAIL-BS-WP-0014",
"state_hub_workplan_id": "ea6ec98a-0d65-4afa-b6c5-6e7ed34011ac",
"status": "open"
},
{
"owner": "railiance-forge",
"gap": "Forgejo resource requests/utilization, storage growth, operations labor, and cost-allocation driver",
"delegated_workplan": "RAILIANCE-WP-0002",
"state_hub_workplan_id": "72f935a5-d921-48a4-96e4-2365556c7374",
"status": "open"
},
{
"owner": "railiance-platform",
"gap": "apps-pg utilization, backup/restore evidence, operations labor, and consumer allocation driver",
"delegated_workplan": "RAILIANCE-WP-0016",
"state_hub_workplan_id": "49084fb8-de63-4f32-a4a9-3a42d4e708ac",
"status": "delivered",
"delivered_on": "2026-08-14",
"interface": [
"/home/worsch/railiance-platform/docs/evidence/RAILIANCE-WP-0016-apps-pg-observation.json",
"/home/worsch/railiance-platform/docs/evidence/RAILIANCE-WP-0016-apps-pg-resource-evidence.md"
],
"refresh": "make apps-pg-evidence",
"residual_unknowns": [
"booked EUR for the apps-pg share of railiance01 (owner: fin-hub, FIN-WP-0004; upstream RAIL-HO-WP-0008)",
"operator hours are an estimate, not a timesheet (owner: railiance-platform)",
"no restore drill, no ScheduledBackup: RPO unbounded and RTO undefined (owner: railiance-platform)"
]
},
{
"owner": "coulomb-social",
"gap": "declared CPU/memory demand, usage proxies, service objectives, and application operations labor",
"delegated_workplan": "CSOC-WP-0005",
"state_hub_workplan_id": "00de1578-6150-40d3-b193-e852666ed945",
"status": "open"
},
{
"owner": "rapp-qonto",
"gap": "request/cold-start frequency, external API costs, runtime utilization, and recurring operator labor",
"delegated_workplan": "RAPP-QONTO-WP-0002",
"state_hub_workplan_id": "25b3b715-9a56-499e-a85c-a09ebd9bbb79",
"status": "open"
},
{
"owner": "fin-hub",
"gap": "booked Host Europe financial facts joined to resource:hosteurope:railiance01",
"delegated_workplan": "FIN-WP-0004",
"state_hub_workplan_id": "67b6de6c-4820-4478-9789-f50260204c27",
"delegated_tasks": [
"FIN-WP-0004-T04",
"FIN-WP-0004-T05",
"FIN-WP-0004-T06"
],
"status": "open"
}
],
"supersedes": "data/portfolio-coverage-2026-08-11.json"
}

View file

@ -1,15 +1,162 @@
{
"schema_version": "0.2", "record_scope": "inventory",
"id": "resource:railiance:apps-pg", "resource_class": "database",
"status": "active", "management_model": "self_managed",
"provider": {"name": "Railiance", "account_ref": null, "product_ref": "cloudnative-pg", "provider_resource_id": "kubernetes:databases/cluster/apps-pg"},
"service": {"name": "Shared applications PostgreSQL", "service_id": "apps-pg", "class": "CloudNativePG database service", "capacity_model": "shared"},
"location": {"region": null, "country": null, "failure_domains": ["cluster:reef-railiance", "host:railiance01", "storage:local-path"], "residency": null},
"capacity": [{"metric": "instances", "value": 1, "unit": "count", "kind": "observed", "observed_at": "2026-08-11"}, {"metric": "storage", "value": 10, "unit": "GiB", "kind": "provisioned", "observed_at": "2026-08-11"}],
"ownership": {"owner": "railiance-platform", "environment": "production", "workload_ids": ["coulomb-social"], "tenant_id": null, "allocation": {"mode": "unattributed", "cost_attribution_key": null, "driver": null, "method_version": null}},
"cost": {"currency": "EUR", "tax_status": "not_applicable", "billing_model": "shared cluster and operations cost; allocation method unknown", "commitment_ref": null, "price_evidence": null},
"lifecycle": {"proposed_on": null, "ordered_on": null, "commissioned_on": "2026-08-09", "renews_on": null, "cancel_by": null, "retired_on": null, "exit_path": "Restore databases to a replacement PostgreSQL service, rotate application connections, verify consumers, then retire the cluster and volume."},
"relationships": [{"type": "hosted_on", "resource_id": "resource:railiance:reef-railiance:k3s"}],
"requirements": [{"kind": "recovery", "ref": "/home/worsch/railiance-platform/docs/apps-pg.md"}],
"evidence": [{"kind": "telemetry", "ref": "kubernetes:reef-railiance/databases/cluster/apps-pg@2026-08-11", "observed_at": "2026-08-11", "authority": "reef-railiance Kubernetes API"}, {"kind": "workload", "ref": "/home/worsch/railiance-platform/helm/apps-pg-cluster.yaml", "observed_at": "2026-08-11", "authority": "railiance-platform"}]
"schema_version": "0.2",
"record_scope": "inventory",
"id": "resource:railiance:apps-pg",
"resource_class": "database",
"status": "active",
"management_model": "self_managed",
"provider": {
"name": "Railiance",
"account_ref": null,
"product_ref": "cloudnative-pg",
"provider_resource_id": "kubernetes:databases/cluster/apps-pg"
},
"service": {
"name": "Shared applications PostgreSQL",
"service_id": "apps-pg",
"class": "CloudNativePG database service",
"capacity_model": "shared"
},
"location": {
"region": null,
"country": null,
"failure_domains": [
"cluster:reef-railiance",
"host:railiance01",
"storage:local-path"
],
"residency": null
},
"capacity": [
{
"metric": "instances",
"value": 1,
"unit": "count",
"kind": "provisioned",
"observed_at": "2026-08-14"
},
{
"metric": "instances",
"value": 1,
"unit": "count",
"kind": "observed",
"observed_at": "2026-08-14"
},
{
"metric": "storage",
"value": 10,
"unit": "GiB",
"kind": "provisioned",
"observed_at": "2026-08-14"
},
{
"metric": "storage_used",
"value": 0.5766,
"unit": "GiB",
"kind": "observed",
"observed_at": "2026-08-14"
},
{
"metric": "wal_since_init",
"value": 0.5469,
"unit": "GiB",
"kind": "observed",
"observed_at": "2026-08-14"
},
{
"metric": "cpu_usage",
"value": 0.009,
"unit": "vCPU",
"kind": "observed",
"observed_at": "2026-08-14"
},
{
"metric": "memory_usage",
"value": 0.0996,
"unit": "GiB",
"kind": "observed",
"observed_at": "2026-08-14"
},
{
"metric": "max_connections",
"value": 100,
"unit": "count",
"kind": "limit",
"observed_at": "2026-08-14"
},
{
"metric": "sessions",
"value": 7,
"unit": "count",
"kind": "observed",
"observed_at": "2026-08-14"
}
],
"ownership": {
"owner": "railiance-platform",
"environment": "production",
"workload_ids": [
"coulomb-social",
"vergabe-teilnahme"
],
"tenant_id": null,
"allocation": {
"mode": "shared",
"cost_attribution_key": "platform:apps-pg",
"driver": "database_gb",
"method_version": "apps-pg-dbbytes-v1"
}
},
"cost": {
"currency": "EUR",
"tax_status": "not_applicable",
"billing_model": "shared cluster and operations cost allocated by declared-consumer database bytes (apps-pg-dbbytes-v1: coulomb-social 0.5386, vergabe-teilnahme 0.4614); the 94.8 percent WAL and catalog residual is attributed to platform:apps-pg-overhead and no booked cost exists yet",
"commitment_ref": null,
"price_evidence": null
},
"lifecycle": {
"proposed_on": null,
"ordered_on": null,
"commissioned_on": "2026-08-09",
"renews_on": null,
"cancel_by": null,
"retired_on": null,
"exit_path": "Restore databases to a replacement PostgreSQL service, rotate application connections, verify consumers, then retire the cluster and volume."
},
"relationships": [
{
"type": "hosted_on",
"resource_id": "resource:railiance:reef-railiance:k3s"
}
],
"requirements": [
{
"kind": "recovery",
"ref": "/home/worsch/railiance-platform/docs/evidence/RAILIANCE-WP-0016-apps-pg-resource-evidence.md#t02--recovery-and-operations"
},
{
"kind": "other",
"ref": "/home/worsch/railiance-platform/docs/apps-pg.md"
}
],
"evidence": [
{
"kind": "telemetry",
"ref": "/home/worsch/railiance-platform/docs/evidence/RAILIANCE-WP-0016-apps-pg-observation.json",
"observed_at": "2026-08-14",
"authority": "railiance-platform (reef-railiance Kubernetes API + postgres catalog)"
},
{
"kind": "workload",
"ref": "/home/worsch/railiance-platform/docs/evidence/RAILIANCE-WP-0016-apps-pg-resource-evidence.md",
"observed_at": "2026-08-14",
"authority": "railiance-platform, RAILIANCE-WP-0016"
},
{
"kind": "workload",
"ref": "/home/worsch/railiance-platform/helm/apps-pg-cluster.yaml",
"observed_at": "2026-08-11",
"authority": "railiance-platform"
}
]
}

View file

@ -0,0 +1,90 @@
# RESOURCE-WP-0003 apps-pg evidence fold-in — 2026-08-14
## Result
`railiance-platform` finished `RAILIANCE-WP-0016` and published the apps-pg
evidence that `RESOURCE-WP-0003-T04` delegated. This is the first delegated
evidence to land, and folding it in exercised the whole loop end to end:
delegation, delivery, ingestion, allocation, control record, and report.
Source, captured 2026-08-14T00:04:19Z, refreshed with `make apps-pg-evidence`:
- `/home/worsch/railiance-platform/docs/evidence/RAILIANCE-WP-0016-apps-pg-observation.json`
- `/home/worsch/railiance-platform/docs/evidence/RAILIANCE-WP-0016-apps-pg-resource-evidence.md`
## What changed here
| Artifact | Change |
|---|---|
| `data/resources/apps-pg.json` | Real capacity and utilization; allocation moved from `unattributed` to `shared` under `apps-pg-dbbytes-v1`; second consumer `vergabe-teilnahme` registered; evidence repointed at the platform interface |
| `data/control-cycle/apps-pg-2026-09-base.json` | **New.** First operational control-cycle record in the repository |
| `examples/control-cycle/apps-pg-{forecast,actual}.json` | **Retired.** The invented fixture collided with the real record's identifier — the clearest possible signal it had been superseded |
| `schemas/resource-control-cycle.schema.json` | Widened to 0.2: nullable costs, nullable `unattributed_eur`, new `unattributed_share`, nullable measurement values |
| `tools/control_cycle.py` | Unknown amounts return `status: "unknown"` and classify as `data_quality` instead of computing a variance |
| `data/portfolio-coverage-2026-08-14.json` | **New.** Supersedes the 2026-08-11 observation; the railiance-platform gap is `delivered` with three residual unknowns |
| `tools/portfolio_report.py` | Delivered gaps stay visible with their residuals; `storage`/`storage_used` added to the measurable pairs |
## The schema gap the real evidence exposed
railiance-platform delivered technical facts and, correctly, no EUR — the host
bill belongs to `resource:hosteurope:railiance01`, whose price is unknown, and
they wrote plainly: "resource-control may convert hours at its own labor rate.
Do not treat these hours as booked cost."
Schema 0.1 could not represent that. Every cost field required a number, so the
only way to record real usage was to invent a cost — precisely what this
repository refuses to do everywhere else. That is a genuine design gap, found
by real evidence rather than by review.
Schema 0.2 permits `null` for cost components, `unattributed_eur`, and
measurement values, and adds `unattributed_share` so a residual can be recorded
from technical evidence alone. `null` means unknown, never zero; if any
component is unknown the total is `null` rather than the sum of the known
parts; and the comparator classifies an unknown amount as `data_quality`,
overriding any explicit attribution, because a missing amount is an evidence
gap and not a price movement.
Existing 0.1 records were not rewritten. `schema_version` accepts both:
immutability applies to the record, not to the schema.
## The first operational record
`forecast:apps-pg:2026-09:base:v1` carries what is known and refuses the rest:
| Field | Value | Basis |
|---|---|---|
| `consumer_database_gb` | 0.0155 GiB | `pg_database_size` of the two declared consumer databases |
| `pgdata_gb` | 0.5766 GiB | `du -sb` on PGDATA |
| storage used / provisioned | 0.5766 of 10 GiB | PVC `apps-pg-1`, `local-path` |
| `internal_labor` | EUR 30.00 | 0.5 declared operator-hours at the EUR 60/hour rate already used in `data/demand/platform-audit-storage.json` |
| `infrastructure` | **null** | Share of railiance01; price unknown (`RAIL-HO-WP-0008`) |
| `total` | **null** | One component unknown; not the sum of the known parts |
| `unattributed_share` | 0.948 | WAL and engine catalogs under `platform:apps-pg-overhead` |
| `rpo_minutes`, `rto_minutes` | **null** | No ScheduledBackup, no restore drill, not an Option A target |
Uncertainty is `high` and stated in eight notes, including the one that matters
most for allocation: `vergabe_db` has never recorded a transaction, so its
0.4614 byte share reflects a reserved empty database rather than realised
demand. The method is recorded as delivered; whether it survives contact with a
second month is a question for the quarterly calibration.
## Effect on the portfolio report
- apps-pg leaves `utilization.unmeasured` and enters `utilization.idle` at
5.8 percent of its volume — a third idle resource alongside railiance01 and
the k3s cluster.
- apps-pg leaves `cost.unattributed_allocation`, which drops from four
resources to three.
- apps-pg remains in `cost.unpriced`, and `known_monthly_spend_eur` remains
`null`. Delivered technical evidence does not create a booked cost.
- The railiance-platform gap moves to `delivered_gaps` and still generates
three next actions from its residual unknowns.
## Boundary held
No booked cost was originated here. The labour figure is a conversion of
declared hours at a rate this repository already publishes, recorded as an
estimate with its basis, not as a ledger fact. Recovery risk was recorded, not
resolved: apps-pg has RPO unbounded and RTO undefined on single-host local-path
storage, which stays railiance-platform's to close and is gated on the same
object-store decision as `RESOURCE-WP-0002`.

View file

@ -98,10 +98,41 @@ make control-cycle FORECAST=examples/control-cycle/storage-forecast.json \
ACTUAL=examples/control-cycle/storage-actual.json
```
The examples cover storage, `reef-railiance` cluster compute, and the shared
`apps-pg` service. Their numbers are illustrative contract fixtures, not booked
facts. Operational records replace their evidence references after the owner
workplans publish observations.
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

View file

@ -47,6 +47,19 @@ reason, rather than being silently omitted from the ratios.
| **Pre-renewal review** | At least 60 days before the earliest `renews_on` or `cancel_by` | Contract evidence, utilization, open cases | Human financial authority | A `renewal` case, decided while cancellation is still possible |
| **Event-driven** | Sustained variance beyond threshold, an incident exposing a failure domain, or a workload request | The triggering evidence | Owning repository | A case of the matching type per `docs/optimization-cases.md` |
## Delivered evidence stays visible
A delegated gap that is answered does not vanish from the report. It moves from
`coverage.unresolved_gaps` to `coverage.delivered_gaps`, keeping the interface
that answered it and the **residual unknowns** it did not, and each residual
still generates a next action. Partial delivery must not read as full coverage.
`RAILIANCE-WP-0016` is the first example: railiance-platform delivered apps-pg
capacity, utilization, consumers, and the `apps-pg-dbbytes-v1` allocation
driver, and deliberately delivered no EUR. Three residuals remain open — the
booked host share, the labour estimate, and the absent recovery objectives — so
apps-pg is now measurable and attributed while still counting as unpriced.
## Reading the current report
As of 2026-08-14 an operator asking the four questions this cadence exists to
@ -55,12 +68,15 @@ answer gets these answers, and the honest ones are the useful ones:
- **Material spend?** Unknown, and explicitly so. Only the proposed backup
storage carries price evidence. Every other resource is waiting on
`RAIL-HO-WP-0008` and `FIN-WP-0004`.
- **Idle or saturated capacity?** `railiance01` and the `reef-railiance` k3s
cluster are idle on every measured metric — 14 percent CPU, 37 percent memory.
Nothing is saturated. Five resources cannot be measured at all.
- **Forecast error?** Not yet computable. The control-cycle mechanism is proven
on paired examples, but no operational actual observation exists, so there is
no variance to report and none is fabricated.
- **Idle or saturated capacity?** `railiance01`, the `reef-railiance` k3s
cluster, and `apps-pg` are idle on every measured metric — 14 percent CPU,
37 percent memory, and 5.8 percent of the apps-pg volume. Nothing is
saturated. Four resources cannot be measured at all.
- **Forecast error?** Not yet computable. The first operational forecast record
exists for apps-pg, but its period is 2026-09 and no actual has been observed
against it, so there is no variance to report and none is fabricated. Its
infrastructure cost is `null`, so even when the actual lands, cost variance
will read `unknown` until a booked fact arrives.
- **Approaching commitments?** None visible — and that is itself the finding:
six active resources have no renewal or cancellation date recorded, so the
cancellation window cannot be respected for any of them.

View file

@ -1 +0,0 @@
{"schema_version":"0.1","record_id":"actual:apps-pg:2026-09","record_type":"actual","resource_id":"resource:railiance:apps-pg","resource_class":"shared_platform_service","period":"2026-09","created_at":"2026-10-02T00:00:00Z","scenario":"observed","forecast_ref":"forecast:apps-pg:2026-09:base:v1","revision_of":null,"usage_proxies":{"database_gb":{"value":6,"unit":"GB"},"connections":{"value":18,"unit":"peak"}},"capacity":{"model":"hybrid","provisioned":{"storage":{"value":20,"unit":"GiB"}},"used":{"storage":{"value":6,"unit":"GB"}}},"costs":{"currency":"EUR","infrastructure":15,"internal_labor":90,"external_labor":0,"total":105,"booked_cost_refs":["financial-fact:example-shared-allocation-2026-09"]},"allocation":{"method":"proportional","driver":"database_gb","method_version":"1","unattributed_eur":5},"service_constraints":{"rpo_minutes":{"value":5,"unit":"minutes"},"rto_minutes":{"value":60,"unit":"minutes"}},"variance_attribution":{"database_gb":"demand","connections":"model","costs.internal_labor":"labor"},"evidence":["illustrative actual; replace with RAILIANCE-WP-0016 evidence"]}

View file

@ -1 +0,0 @@
{"schema_version":"0.1","record_id":"forecast:apps-pg:2026-09:base:v1","record_type":"forecast","resource_id":"resource:railiance:apps-pg","resource_class":"shared_platform_service","period":"2026-09","created_at":"2026-08-11T00:00:00Z","scenario":"base","forecast_ref":null,"revision_of":null,"usage_proxies":{"database_gb":{"value":5,"unit":"GB"},"connections":{"value":20,"unit":"peak"}},"capacity":{"model":"hybrid","provisioned":{"storage":{"value":20,"unit":"GiB"}},"used":{"storage":{"value":5,"unit":"GB"}}},"costs":{"currency":"EUR","infrastructure":15,"internal_labor":60,"external_labor":0,"total":75,"booked_cost_refs":[]},"allocation":{"method":"proportional","driver":"database_gb","method_version":"1","unattributed_eur":5},"service_constraints":{"rpo_minutes":{"value":5,"unit":"minutes"},"rto_minutes":{"value":60,"unit":"minutes"}},"uncertainty":{"level":"high","notes":["Illustrative until platform evidence arrives"]},"evidence":["RAILIANCE-WP-0016"]}

View file

@ -4,81 +4,286 @@
"title": "Immutable resource forecast or actual observation",
"type": "object",
"additionalProperties": false,
"required": ["schema_version", "record_id", "record_type", "resource_id", "resource_class", "period", "created_at", "usage_proxies", "capacity", "costs", "allocation", "service_constraints", "evidence"],
"required": [
"schema_version",
"record_id",
"record_type",
"resource_id",
"resource_class",
"period",
"created_at",
"usage_proxies",
"capacity",
"costs",
"allocation",
"service_constraints",
"evidence"
],
"properties": {
"schema_version": {"const": "0.1"},
"record_id": {"type": "string", "minLength": 1},
"record_type": {"enum": ["forecast", "actual"]},
"resource_id": {"type": "string", "pattern": "^resource:"},
"resource_class": {"enum": ["storage", "vm", "cluster_compute", "shared_platform_service", "managed_service", "other"]},
"period": {"type": "string", "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])$"},
"created_at": {"type": "string", "format": "date-time"},
"scenario": {"enum": ["low", "base", "high", "observed"]},
"forecast_ref": {"type": ["string", "null"]},
"revision_of": {"type": ["string", "null"]},
"schema_version": {
"enum": [
"0.1",
"0.2"
],
"description": "0.2 permits null cost components for facts the authoritative repository has not supplied. Existing 0.1 records stay valid and are never rewritten; immutability applies to the record, not to the schema."
},
"record_id": {
"type": "string",
"minLength": 1
},
"record_type": {
"enum": [
"forecast",
"actual"
]
},
"resource_id": {
"type": "string",
"pattern": "^resource:"
},
"resource_class": {
"enum": [
"storage",
"vm",
"cluster_compute",
"shared_platform_service",
"managed_service",
"other"
]
},
"period": {
"type": "string",
"pattern": "^[0-9]{4}-(0[1-9]|1[0-2])$"
},
"created_at": {
"type": "string",
"format": "date-time"
},
"scenario": {
"enum": [
"low",
"base",
"high",
"observed"
]
},
"forecast_ref": {
"type": [
"string",
"null"
]
},
"revision_of": {
"type": [
"string",
"null"
]
},
"usage_proxies": {
"type": "object",
"minProperties": 1,
"additionalProperties": {"$ref": "#/$defs/measurement"}
"additionalProperties": {
"$ref": "#/$defs/measurement"
}
},
"capacity": {
"type": "object",
"additionalProperties": false,
"required": ["model", "provisioned", "used"],
"required": [
"model",
"provisioned",
"used"
],
"properties": {
"model": {"enum": ["fixed", "elastic", "hybrid"]},
"provisioned": {"type": "object", "additionalProperties": {"$ref": "#/$defs/measurement"}},
"used": {"type": "object", "additionalProperties": {"$ref": "#/$defs/measurement"}}
"model": {
"enum": [
"fixed",
"elastic",
"hybrid"
]
},
"provisioned": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/measurement"
}
},
"used": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/measurement"
}
}
}
},
"costs": {
"type": "object",
"additionalProperties": false,
"required": ["currency", "infrastructure", "internal_labor", "external_labor", "total"],
"required": [
"currency",
"infrastructure",
"internal_labor",
"external_labor",
"total"
],
"properties": {
"currency": {"const": "EUR"},
"infrastructure": {"type": "number", "minimum": 0},
"internal_labor": {"type": "number", "minimum": 0},
"external_labor": {"type": "number", "minimum": 0},
"total": {"type": "number", "minimum": 0},
"booked_cost_refs": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
}
"currency": {
"const": "EUR"
},
"infrastructure": {
"type": [
"number",
"null"
],
"minimum": 0,
"description": "EUR, or null when the amount is not yet known. Null is unknown, never zero."
},
"internal_labor": {
"type": [
"number",
"null"
],
"minimum": 0,
"description": "EUR, or null when the amount is not yet known. Null is unknown, never zero."
},
"external_labor": {
"type": [
"number",
"null"
],
"minimum": 0,
"description": "EUR, or null when the amount is not yet known. Null is unknown, never zero."
},
"total": {
"type": [
"number",
"null"
],
"minimum": 0,
"description": "EUR, or null when the amount is not yet known. Null is unknown, never zero."
},
"booked_cost_refs": {
"type": "array",
"items": {
"type": "string"
},
"uniqueItems": true
}
},
"description": "Cost components split so a cheaper unit price that raises operator hours is visible. Any component may be null when the authoritative repository or fin-hub has not supplied it; total must then also be null, and variance for that component is reported as unknown."
},
"allocation": {
"type": "object",
"additionalProperties": false,
"required": ["method", "driver", "method_version", "unattributed_eur"],
"required": [
"method",
"driver",
"method_version",
"unattributed_eur"
],
"properties": {
"method": {"type": "string", "minLength": 1},
"driver": {"type": "string", "minLength": 1},
"method_version": {"type": "string", "minLength": 1},
"unattributed_eur": {"type": "number", "minimum": 0}
"method": {
"type": "string",
"minLength": 1
},
"driver": {
"type": "string",
"minLength": 1
},
"method_version": {
"type": "string",
"minLength": 1
},
"unattributed_eur": {
"type": [
"number",
"null"
],
"minimum": 0,
"description": "EUR that reaches no consumer under the stated method, or null when the underlying cost itself is unknown. Null is unknown, never zero."
},
"unattributed_share": {
"type": [
"number",
"null"
],
"minimum": 0,
"maximum": 1,
"description": "Fraction of the allocation driver that reaches no consumer. Recordable from technical evidence alone, before any EUR amount exists."
}
}
},
"service_constraints": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/measurement"
}
},
"service_constraints": {"type": "object", "additionalProperties": {"$ref": "#/$defs/measurement"}},
"uncertainty": {
"type": "object",
"additionalProperties": false,
"required": ["level", "notes"],
"required": [
"level",
"notes"
],
"properties": {
"level": {"enum": ["low", "medium", "high"]},
"notes": {"type": "array", "items": {"type": "string"}}
"level": {
"enum": [
"low",
"medium",
"high"
]
},
"notes": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"variance_attribution": {
"type": "object",
"additionalProperties": {"enum": ["demand", "provider_price", "allocation", "labor", "model", "data_quality"]}
"additionalProperties": {
"enum": [
"demand",
"provider_price",
"allocation",
"labor",
"model",
"data_quality"
]
}
},
"evidence": {"type": "array", "minItems": 1, "items": {"type": "string"}}
"evidence": {
"type": "array",
"minItems": 1,
"items": {
"type": "string"
}
}
},
"$defs": {
"measurement": {
"type": "object",
"additionalProperties": false,
"required": ["value", "unit"],
"required": [
"value",
"unit"
],
"properties": {
"value": {"type": "number", "minimum": 0},
"unit": {"type": "string", "minLength": 1}
"value": {
"type": [
"number",
"null"
],
"minimum": 0,
"description": "Null means the quantity is unknown or unbounded, never zero. A declared service constraint with a null value records that no objective exists yet."
},
"unit": {
"type": "string",
"minLength": 1
}
}
}
}

View file

@ -1,3 +1,4 @@
import json
import sys
import unittest
from pathlib import Path
@ -50,5 +51,77 @@ class ControlCycleTest(unittest.TestCase):
self.assertEqual("data_quality", result["usage_proxies"]["stored_gb"]["category"])
class UnknownCostTest(unittest.TestCase):
"""Schema 0.2: a cost the authoritative repository has not supplied is
unknown, not zero, and must not produce a fabricated variance."""
def test_unknown_cost_reports_status_rather_than_an_error_figure(self):
forecast, actual = record(), record("actual")
forecast["costs"].update(infrastructure=None, total=None)
actual["costs"].update(infrastructure=None, total=None)
result = compare(forecast, actual)["costs"]["infrastructure"]
self.assertEqual("unknown", result["status"])
self.assertNotIn("error", result)
def test_unknown_cost_is_a_data_quality_gap_not_a_price_movement(self):
forecast, actual = record(), record("actual")
actual["costs"].update(infrastructure=None, total=None)
actual["variance_attribution"] = {"costs.infrastructure": "provider_price"}
result = compare(forecast, actual)["costs"]["infrastructure"]
self.assertEqual("data_quality", result["category"])
def test_known_components_still_compute_beside_an_unknown_one(self):
forecast, actual = record(), record("actual")
forecast["costs"].update(infrastructure=None, total=None)
actual["costs"].update(infrastructure=None, internal_labor=90, total=None)
result = compare(forecast, actual)["costs"]
self.assertEqual("unknown", result["infrastructure"]["status"])
self.assertEqual(30, result["internal_labor"]["error"])
def test_one_sided_unknown_does_not_subtract_against_a_missing_amount(self):
forecast, actual = record(), record("actual")
actual["costs"].update(infrastructure=None, total=None)
result = compare(forecast, actual)["costs"]["infrastructure"]
self.assertEqual("unknown", result["status"])
self.assertEqual(10, result["forecast"])
self.assertIsNone(result["actual"])
class OperationalRecordTest(unittest.TestCase):
"""The first real apps-pg record, folded in from RAILIANCE-WP-0016."""
def setUp(self):
path = Path(__file__).parents[1] / "data" / "control-cycle" / "apps-pg-2026-09-base.json"
self.record = json.loads(path.read_text())
def test_usage_is_known_while_infrastructure_cost_is_not(self):
costs = self.record["costs"]
self.assertIsNone(costs["infrastructure"])
self.assertIsNone(costs["total"])
self.assertEqual(30.0, costs["internal_labor"])
self.assertTrue(self.record["usage_proxies"])
def test_total_is_not_the_sum_of_the_known_parts(self):
costs = self.record["costs"]
known = costs["internal_labor"] + costs["external_labor"]
self.assertNotEqual(known, costs["total"])
self.assertIsNone(costs["total"])
def test_shared_overhead_is_recorded_as_a_share_before_any_eur_exists(self):
allocation = self.record["allocation"]
self.assertEqual("apps-pg-dbbytes-v1", allocation["method_version"])
self.assertIsNone(allocation["unattributed_eur"])
self.assertEqual(0.948, allocation["unattributed_share"])
def test_absent_recovery_objectives_are_null_not_optimistic_defaults(self):
constraints = self.record["service_constraints"]
self.assertIsNone(constraints["rpo_minutes"]["value"])
self.assertIsNone(constraints["rto_minutes"]["value"])
def test_record_cites_the_authoritative_platform_evidence(self):
self.assertIn("RAILIANCE-WP-0016", self.record["evidence"])
self.assertEqual("high", self.record["uncertainty"]["level"])
if __name__ == "__main__":
unittest.main()

View file

@ -159,5 +159,40 @@ class LivePortfolioTest(unittest.TestCase):
self.assertEqual(self.report, build(ROOT, TODAY))
class DeliveredEvidenceTest(unittest.TestCase):
"""RAILIANCE-WP-0016 delivered apps-pg evidence; partial delivery must not
read as full coverage."""
@classmethod
def setUpClass(cls):
cls.report = build(ROOT, TODAY)
def test_delivered_gap_leaves_the_open_list(self):
owners = {gap["owner"] for gap in self.report["coverage"]["unresolved_gaps"]}
self.assertNotIn("railiance-platform", owners)
def test_delivered_gap_stays_visible_with_its_interface(self):
delivered = self.report["coverage"]["delivered_gaps"]
self.assertEqual(1, len(delivered))
self.assertEqual("railiance-platform", delivered[0]["owner"])
self.assertTrue(delivered[0]["interface"])
def test_residual_unknowns_still_produce_next_actions(self):
residuals = [a for a in self.report["next_actions"] if "delivered, still open" in a]
self.assertEqual(3, len(residuals))
def test_apps_pg_is_now_measurable_and_idle(self):
self.assertIn("resource:railiance:apps-pg", self.report["utilization"]["idle"])
unmeasured = {row["resource_id"] for row in self.report["utilization"]["unmeasured"]}
self.assertNotIn("resource:railiance:apps-pg", unmeasured)
def test_apps_pg_cost_is_now_attributed_but_still_unpriced(self):
unattributed = {row["resource_id"] for row in self.report["cost"]["unattributed_allocation"]}
self.assertNotIn("resource:railiance:apps-pg", unattributed)
unpriced = {row["resource_id"] for row in self.report["cost"]["unpriced"]}
self.assertIn("resource:railiance:apps-pg", unpriced)
self.assertIsNone(self.report["cost"]["known_monthly_spend_eur"])
if __name__ == "__main__":
unittest.main()

View file

@ -11,7 +11,11 @@ COST_FIELDS = ("infrastructure", "internal_labor", "external_labor", "total")
ATTRIBUTIONS = {"demand", "provider_price", "allocation", "labor", "model", "data_quality"}
def delta(forecast: float, actual: float) -> dict:
def delta(forecast: float | None, actual: float | None) -> dict:
# A missing amount is unknown, not zero: subtracting against it would
# manufacture a variance the evidence does not support.
if forecast is None or actual is None:
return {"forecast": forecast, "actual": actual, "status": "unknown"}
error = actual - forecast
return {
"forecast": forecast,
@ -50,7 +54,12 @@ def compare(forecast: dict, actual: dict) -> dict:
costs = {}
for name in COST_FIELDS:
default_category = "labor" if "labor" in name else "provider_price"
costs[name] = {**delta(forecast["costs"][name], actual["costs"][name]), "currency": "EUR", "category": attribution.get(f"costs.{name}", default_category)}
result = delta(forecast["costs"][name], actual["costs"][name])
# An unknown amount is a data-quality gap, not a price or labour movement.
category = "data_quality" if result.get("status") == "unknown" else attribution.get(
f"costs.{name}", default_category
)
costs[name] = {**result, "currency": "EUR", "category": category}
if forecast["allocation"] != actual["allocation"]:
costs["allocation_method"] = {"status": "changed", "category": attribution.get("allocation", "allocation")}

View file

@ -27,6 +27,7 @@ USAGE_PAIRS = {
"cpu": "cpu_usage",
"memory": "memory_usage",
"root_filesystem": "root_filesystem_used",
"storage": "storage_used",
}
@ -59,9 +60,23 @@ def coverage_section(coverage: dict | None) -> dict:
{"group": group["group"], "status": group["status"], "resources": len(group["resource_ids"])}
for group in coverage["coverage"]
],
# A delivered gap stays visible with its residual unknowns rather than
# disappearing, so partial delivery is not read as full coverage.
"unresolved_gaps": [
{"owner": gap["owner"], "gap": gap["gap"], "delegated_workplan": gap["delegated_workplan"]}
for gap in coverage["owned_gaps"]
if gap.get("status", "open") == "open"
],
"delivered_gaps": [
{
"owner": gap["owner"],
"delegated_workplan": gap["delegated_workplan"],
"delivered_on": gap.get("delivered_on"),
"interface": gap.get("interface", []),
"residual_unknowns": gap.get("residual_unknowns", []),
}
for gap in coverage["owned_gaps"]
if gap.get("status", "open") == "delivered"
],
}
@ -250,6 +265,9 @@ def next_actions(report: dict) -> list[str]:
actions = []
for gap in report["coverage"]["unresolved_gaps"]:
actions.append(f"{gap['owner']}: deliver {gap['delegated_workplan']}{gap['gap']}")
for gap in report["coverage"].get("delivered_gaps", []):
for residual in gap["residual_unknowns"]:
actions.append(f"{gap['owner']}: {gap['delegated_workplan']} delivered, still open — {residual}")
if report["cost"]["unpriced"]:
actions.append(
"resource-control: no portfolio spend figure exists until at least one booked cost arrives "

View file

@ -44,16 +44,30 @@ def main() -> int:
for resource in resources:
assert not required - resource.keys(), f"missing fields: {required - resource.keys()}"
validate_record(resource)
operational_records = [load(str(path)) for path in Path("data/control-cycle").glob("*.json")]
control_records += operational_records
record_ids = {record["record_id"] for record in control_records}
assert len(record_ids) == len(control_records)
assert {record["resource_class"] for record in control_records} == {"storage", "cluster_compute", "shared_platform_service"}
assert {record["resource_class"] for record in control_records} >= {"storage", "cluster_compute", "shared_platform_service"}
for record in control_records:
assert record["schema_version"] == "0.1"
assert record["schema_version"] in {"0.1", "0.2"}
assert record["resource_id"].startswith("resource:")
costs = record["costs"]
assert costs["total"] == round(costs["infrastructure"] + costs["internal_labor"] + costs["external_labor"], 2)
components = [costs["infrastructure"], costs["internal_labor"], costs["external_labor"]]
if any(component is None for component in components):
# An unknown component makes the total unknown; it is never the sum
# of the parts that happen to be known.
assert record["schema_version"] == "0.2", "null costs require schema 0.2"
assert costs["total"] is None
else:
assert costs["total"] == round(sum(components), 2)
if record["record_type"] == "actual":
assert record["forecast_ref"] in record_ids
# Operational records assert real facts and must cite the authoritative
# repository evidence they came from.
for record in operational_records:
assert record["evidence"], f"{record['record_id']} cites no evidence"
assert record["uncertainty"]["notes"], f"{record['record_id']} states no uncertainty"
case_schema = load("schemas/optimization-case.schema.json")
assert case_schema["$schema"].endswith("2020-12/schema")
assert case_schema["properties"]["schema_version"]["const"] == "0.1"

View file

@ -287,6 +287,23 @@ resources with no contract dates at all, and six of seven resources sharing
- [x] At least two optimization cases complete the evidence-to-decision loop.
- [x] Portfolio reporting exposes unknown and unattributed values explicitly.
## Post-completion record
2026-08-14: `railiance-platform` delivered `RAILIANCE-WP-0016`, the first
delegated evidence from T04 to land. Folded in: the apps-pg resource record now
carries real capacity and the `apps-pg-dbbytes-v1` allocation driver, the first
operational control-cycle record replaced the illustrative apps-pg fixture, and
coverage moved to `data/portfolio-coverage-2026-08-14.json` with the gap marked
delivered and its 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 was
impossible without inventing one. Schema 0.2 permits null costs, a null
unattributed amount, a technical `unattributed_share`, and null measurements;
the comparator reports unknown amounts as `data_quality` rather than computing a
variance. Existing 0.1 records were not rewritten. Evidence:
`docs/evidence/RESOURCE-WP-0003-apps-pg-evidence-foldin-2026-08-14.md`.
## Standing gate
Both optimization cases are `blocked_on_evidence` by design: the mechanism is