resource-control/docs/optimization-cases.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

105 lines
5.9 KiB
Markdown

# Optimization cases — review cadence and decision template
`resource-control` produces optimization cases. It does not approve them and it
does not implement them. A case turns portfolio evidence into a reproducible
comparison; a human or repository authority decides; the owning platform or
workload repository implements; `fin-hub` records the money.
Schema: `schemas/optimization-case.schema.json`.
Evaluator: `tools/optimization.py`. Cases live in `data/optimization/`.
## Case types
| Type | Question it answers |
|---|---|
| `rightsizing` | Is provisioned capacity materially larger or smaller than sustained demand? |
| `consolidation` | Can separate resources share one substrate without breaking failure domains? |
| `commitment` | Does a term or reserved commitment beat on-demand pricing at forecast demand? |
| `renewal` | Should this contract renew, renegotiate, or lapse? |
| `migration` | Should the workload move to different capacity at the same provider? |
| `retirement` | Is this resource still serving anything? |
| `provider_switch` | Should the same service come from a different provider? |
## Decision template
Every option — the baseline included — must present the same fields. The
baseline is a full option, not a footnote, because a comparison against an
undescribed status quo is not a comparison.
1. **Baseline** — what is in place now, on the same terms as every alternative.
2. **Alternatives** — at least one, each independently costed.
3. **One-time cost** — migration, setup, dual-running, and the labour to do it.
4. **Recurring cost** — split into infrastructure, internal labour, and external
labour. A cheaper unit price that doubles operator hours is not a saving.
5. **Utilization** — provisioned and used per capacity metric, with the
observation that supports it.
6. **Uncertainty**`low`, `medium`, or `high`, with the reasons written out.
7. **Service-level constraints** — what the option must still satisfy.
8. **Failure domains** — the evaluator reports which the alternative removes and
which it newly introduces.
9. **Exit path** — how to leave the option. An option with no known exit path is
blocked, not merely riskier.
10. **Expected payback** — computed, never asserted.
### Unknowns are first-class
Any unknown cost, utilization, or exit path is `null`, and each blocking gap is
named in the option's `unknowns` list with the repository or authority that owns
it. The evaluator is fail-closed: one unknown makes the affected comparison
`blocked_on_evidence` rather than optimistic. A case that cannot conclude is a
valid, useful output — it names exactly what evidence would unblock it.
### Verdicts
| Verdict | Meaning |
|---|---|
| `blocked_on_evidence` | A required decision field is unknown. No recommendation. |
| `recommend` | Fully costed and saves more than EUR 5 per month. |
| `no_material_change` | Fully costed, difference within EUR 5 per month. |
| `reject` | Fully costed and more expensive than the baseline. |
`reject` is a cost verdict only. An option that costs more may still be right —
the three-node cluster case buys availability, not savings — and that argument
belongs in `decision.rationale`, made by the deciding authority against a
declared service objective, not by the calculator.
### Decision states
`blocked_on_evidence``proposed``approved` | `rejected``superseded`.
The evaluator enforces the transitions it can check: a case cannot be `proposed`
while any comparison is blocked, cannot be `approved` while blocked, and cannot
be decided without a named `approver` and `approved_on`.
## Cadence
| Rhythm | Trigger | Scope |
|---|---|---|
| Monthly | `cadence` | Review variance from `tools/control_cycle.py`. Open a case where forecast error is attributed to demand, price, or allocation rather than data quality. |
| Quarterly | `cadence` | Re-run every open case against refreshed evidence. Recompute all provider comparisons; a stale price is not evidence. |
| Pre-renewal | `renewal` | Open at least 60 days before `lifecycle.renews_on` or `cancel_by`, whichever is earlier, so cancellation stays possible. |
| On variance | `variance` | Sustained variance beyond the thresholds in `docs/forecast-actual-control.md`. |
| On demand | `request`, `procurement`, `incident` | A workload repository, a procurement decision, or an incident that exposed a failure domain. |
## Closing the loop
An approved case is not finished when it is approved.
1. `financial_handoff` carries the cost attribution key and the reference to
`fin-hub` under the contract in `docs/fin-hub-resource-control-contract-v0.1.md`.
2. `decision.delegated_to` names the repositories that implement. Implementation
detail is theirs; this repository states the interface and the acceptance
evidence only.
3. `outcome.feeds_forecast` names the control-cycle forecast records the decision
changes. The original forecast is never overwritten — a decision produces a
revision, per `docs/forecast-actual-control.md`.
4. `outcome.actual_refs` collects the monthly actuals that later show whether the
predicted saving materialised. A case whose actuals never arrive is an
unverified case, and the next quarterly review should say so.
## Current cases
| Case | Type | State | Why |
|---|---|---|---|
| `opt:platform-audit-storage:2026-08` | `provider_switch` | `blocked_on_evidence` | Hetzner computes and is EUR 29.14 per month dearer than Scaleway at month-12 base demand, on labour rather than storage price. Host Europe has no usable price, no cancellation terms, and shares a failure domain with the host it would protect. The procurement decision itself belongs to `RESOURCE-WP-0002-T03`. |
| `opt:reef-railiance-k3s:2026-08` | `rightsizing` | `blocked_on_evidence` | Observed utilization is 14 percent CPU and 37 percent memory — a real signal from one sample. Nothing can be costed while the railiance01 booked price is unknown, so no payback exists. Blocked against `RAIL-HO-WP-0008` and `RAIL-BS-WP-0014`. |