`make portfolio-report` renders the current portfolio view from
`tools/portfolio_report.py`. The report is derived entirely from committed
evidence: resource records in `data/resources/`, the latest
`data/portfolio-coverage-*.json`, and the optimization cases in
`data/optimization/`. Nothing in it is typed by hand.
## What the report answers
| Section | Question |
|---|---|
| `coverage` | Which service groups are represented, and which evidence gaps are still delegated and to whom. |
| `lifecycle` | How many resources sit in each lifecycle state, and whether any lacks an owner. |
| `utilization` | Provisioned versus observed capacity per metric, flagged `idle`, `normal`, or `saturated`; and which resources cannot be measured at all. |
| `cost` | Which resources carry price evidence, which do not, and which have no allocation method so their cost reaches no consumer. |
| `renewals` | Contract dates inside the 90-day horizon, and resources with no recorded dates at all. |
| `optimization` | Open cases, their per-option verdicts, and the named evidence blocking each one. |
| `next_actions` | The smallest set of evidence that would unblock the most decisions, addressed to the repository that owns it. |
## Not forcing false precision
Two rules the report holds to, because the alternative is a number that looks
authoritative and is not:
- **`known_monthly_spend_eur` is `null`, not `0`.** No booked cost has reached
this repository, and six of seven resources carry no price evidence. Summing
what happens to be known would report a portfolio spend an order of magnitude
below reality. The figure stays unknown until `fin-hub` delivers booked facts
under `docs/fin-hub-resource-control-contract-v0.1.md`.
- **Unattributed cost is a list, not a spread.** Four resources have allocation
mode `unattributed`. Their cost is not divided across consumers by a plausible
default; they are named, and the allocation driver is owed by the repository
that owns the resource.
Resources that cannot be measured appear in `utilization.unmeasured` with the
reason, rather than being silently omitted from the ratios.
## Cadence
| Rhythm | When | Inputs | Consumers | Output |
|---|---|---|---|---|
| **Monthly observation** | First week, for the closed month | Monthly actual records from delegated telemetry; booked costs from `fin-hub` | resource-control | `tools/control_cycle.py` variance per resource; a new optimization case where variance is attributed to demand, price, or allocation rather than data quality |
| **Monthly settlement** | With the observation, once entity association is live | Per-entity usage, delivered cost, transfer prices under `docs/TermsOfResourceProcurement.md`; recognized payments from `fin-hub` | consuming financial entities; Railiance operations; `fin-hub` | One settlement statement and credit-state evaluation (`open` or `restricted`) per consuming entity |
| **Monthly portfolio report** | With the observation | Resource records, coverage, optimization cases | Human operator; owning repositories | `make portfolio-report`; the `next_actions` list is re-sent to the repositories named in it |
| **Quarterly calibration** | End of quarter | Three months of variance | resource-control; `fin-hub` | Forecast revisions per `docs/forecast-actual-control.md` — revisions, never overwrites; refreshed provider prices in every open case |
| **Pre-renewal review** | At least 60 days before the earliest `renews_on` or `cancel_by` | Contract evidence, utilization, open cases | Human financial authority | A `renewal` case, decided while cancellation is still possible |
| **Event-driven** | Sustained variance beyond threshold, an incident exposing a failure domain, or a workload request | The triggering evidence | Owning repository | A case of the matching type per `docs/optimization-cases.md` |