resource-control/docs/evidence/RESOURCE-WP-0003-optimization-cases-2026-08-14.md
tegwick 2c2a6073ff feat(portfolio): complete RESOURCE-WP-0003 T06 optimization cases and T07 reporting
T06: optimization-case schema, fail-closed evaluator, cadence and decision
template. Every option including the baseline must present all ten decision
fields; one unknown blocks the comparison. Validated on the storage case
(Hetzner computes and loses to Scaleway by EUR 29.14/month on operator labour;
Host Europe blocks on four named gaps) and on the non-storage reef-railiance
k3s rightsizing case (low utilization is real, but nothing is costable while
the railiance01 price is unknown).

T07: portfolio report over coverage, lifecycle, utilization, cost, renewals,
risks, open cases, and next actions, derived only from committed evidence.
Portfolio spend is reported null rather than as a partial sum, unattributed
cost is a named list rather than a spread, and unmeasurable resources are
reported rather than dropped.

RESOURCE-WP-0003 is finished; both cases remain blocked_on_evidence against
live delegated records in other repositories. RESOURCE-WP-0002 is untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 09:28:44 +02:00

104 lines
5.1 KiB
Markdown

# RESOURCE-WP-0003 optimization cases — 2026-08-14
## Result
`RESOURCE-WP-0003-T06` established the review cadence, the decision template,
and a fail-closed evaluator for optimization cases, and validated the process
against the backup case and one non-storage portfolio candidate.
The implementation consists of:
- `schemas/optimization-case.schema.json` — baseline and alternatives carry
identical decision fields, so a comparison against an undescribed status quo
is structurally impossible;
- `tools/optimization.py` — comparison, payback, failure-domain delta, verdicts,
and decision-state validation;
- `data/optimization/platform-audit-storage-2026-08.json` — the storage case;
- `data/optimization/reef-railiance-k3s-2026-08.json` — the non-storage case;
- `docs/optimization-cases.md` — case types, decision template, cadence, and the
loop that returns outcomes to the next forecast;
- 25 tests in `tests/test_optimization.py`, plus schema and case checks in
`tools/validate.py`.
## Decision fields
T06 requires every recommendation to show baseline, alternative, one-time cost,
recurring infrastructure and labour cost, utilization, uncertainty,
service-level constraints, failure domains, exit path, and expected payback.
The schema requires all ten on every option, and the evaluator refuses to
recommend while any is `null`. Payback is computed from one-time cost and
realised monthly saving; it is never asserted.
Recurring cost is split into infrastructure, internal labour, and external
labour. This is not cosmetic: in the storage case Hetzner has the lower
infrastructure price (EUR 6.49 against EUR 7.35) and is still EUR 29.14 per
month more expensive, entirely on operator hours. A comparison on unit price
alone would have inverted the result.
## Fail-closed behaviour
One unknown anywhere in the compared pair makes the comparison
`blocked_on_evidence`. Unknowns are `null` values plus a named `unknowns` list
that attributes each gap to an owning repository or authority. The evaluator
also refuses invalid decision states: a blocked case cannot be `proposed` or
`approved`, a decided case must name `approver` and `approved_on`, and a case
with no blockers cannot claim to be blocked.
`reject` is a cost verdict only. The three-node cluster option raises recurring
cost by design and buys availability instead; that argument belongs to the
deciding authority against a declared service objective, not to the calculator.
## Case 1 — `opt:platform-audit-storage:2026-08` (storage)
Baseline is Scaleway Standard Multi-AZ, the provisional primary from T02 due
diligence, at month-12 base-scenario demand from `tools/cost_model.py`.
| Option | Recurring EUR/month | Verdict |
|---|---|---|
| Scaleway Standard Multi-AZ (baseline) | 67.35 | — |
| Hetzner Object Storage | 96.49 | `reject` (+29.14) |
| Host Europe Cloud Storage | unknown | `blocked_on_evidence` |
Host Europe is blocked on four named gaps: current S3 orderability for this
account, five price fields, cancellation terms, and correlated-failure
acceptance. It has no known exit path, and it shares `provider:host-europe`
with the compute host the backup is meant to protect.
This case exercises the process against real cost-model output. It is
deliberately **not** the procurement decision, which `RESOURCE-WP-0002-T03`
owns and human financial authority approves. The case state is
`blocked_on_evidence` and recommends nothing.
## Case 2 — `opt:reef-railiance-k3s:2026-08` (non-storage)
Baseline is the current single-node k3s on railiance01, with the live capacity
observation from T03 discovery: 0.564 of 4 vCPU, 5.83 of 15.62 GiB, 66.77 of
192.69 GiB root filesystem.
Utilization is genuinely low — 14 percent CPU, 37 percent memory — and that is
a real rightsizing signal. Every comparison is nevertheless blocked, for two
independent reasons the case records rather than works around:
1. The railiance01 booked price is unknown, so no option has a recurring cost,
no saving exists, and no payback can be computed.
2. The utilization figure is a single sample taken on 2026-08-11. Mean usage is
not a sizing basis; peak load over a billing period is owed by
`RAIL-BS-WP-0014`.
Both alternatives — a smaller single server and the RAIL-BS-WP-0007 three-node
cluster — are recorded with their unknowns attributed to `railiance-infra`
(`RAIL-HO-WP-0008`) and `railiance-cluster` (`RAIL-BS-WP-0014`).
A case that refuses to conclude is the correct output here, and it is a useful
one: it names exactly which evidence would unblock a rightsizing decision, and
it is visible in the portfolio report until that evidence arrives.
## Boundaries held
`resource-control` produced both cases and decided neither. Financial
implications reach `fin-hub` through `financial_handoff` under
`docs/fin-hub-resource-control-contract-v0.1.md`; neither case has sent one,
because neither is approved. Implementation is delegated through
`decision.delegated_to`, which names `railiance-infra` and `railiance-cluster`
for the cluster case. `outcome.feeds_forecast` binds each case to the
control-cycle records that will later show whether it was right.