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

5.1 KiB

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.