resource-control/docs/optimization-cases.md

106 lines
5.9 KiB
Markdown
Raw Normal View History

# 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`. |