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>
This commit is contained in:
parent
54dd45c926
commit
2c2a6073ff
63 changed files with 4662 additions and 69 deletions
105
docs/optimization-cases.md
Normal file
105
docs/optimization-cases.md
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
# 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`. |
|
||||
Loading…
Add table
Add a link
Reference in a new issue