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:
tegwick 2026-08-14 09:28:44 +02:00
parent 54dd45c926
commit 2c2a6073ff
63 changed files with 4662 additions and 69 deletions

View file

@ -0,0 +1,54 @@
# PostgreSQL backup demand and cost model — 2026-08-10
## Observed baseline
A read-only query against `platform-pg-1` at `2026-08-10T15:54:12Z` measured
39,573,871 bytes across connectable databases. `pg_stat_wal` reported
19,744,568 bytes since `2026-08-10T13:37:53Z`, an extrapolated 0.208 GB/day.
The cluster is less than one day old, so this is a seed observation rather than
a representative trend. No credential value was read or recorded.
The governed requirements are a 30-day recovery window, daily physical base
backup, continuous WAL archive, one monthly restore drill, and a complete exit
calculation. The scenario inputs live in
`data/demand/platform-audit-storage.json` and deliberately floor demand above
the tiny initial observation:
| Scenario | Initial DB | Monthly growth | WAL/day | Restore egress | Labor/month |
| --- | ---: | ---: | ---: | ---: | ---: |
| low | 1 GB | 5% | 0.25 GB | 1 GB | 0.5 h |
| base | 5 GB | 10% | 1 GB | 5 GB | 1 h |
| high | 20 GB | 20% | 5 GB | 20 GB | 2 h |
The conservative stored-byte formula is `30 × current database size + 30 ×
daily WAL`. It does not claim compression savings. Operator labor is valued at
€60/hour; provider-specific labor floors capture relative operational effort.
The exit event adds full stored-byte egress plus four operator hours.
Run `make forecast` for all 12 months, scenarios, and providers. A `null` total
is intentional: it means a required price is unknown and prevents an unknown
from silently becoming free.
The catalog also includes self-managed Garage on 13 Host Europe/Hetzner VMs
and managed AWS, Azure, Google Cloud, and STACKIT price points. See
`RESOURCE-WP-0002-expanded-storage-comparison-2026-08-10.md` for assumptions
and topology warnings.
Calculator rows split `monthly_infrastructure_eur` from
`monthly_internal_labor_eur` and split internal setup labor from external setup
services. The top-level `comparison_320gb` array provides a normalized running
cost at 320 GB using the base request and restore pattern.
## Initial comparison
- Scaleway Standard Multi-AZ is fully calculable at €0.01606/GB-month, with
75 GB/month free egress then €0.01/GB. Prices exclude tax.
- Hetzner is calculable while usage remains inside its €6.49 monthly minimum,
which includes 1 TB storage and 1 TB egress. Excess unit prices still need to
be captured before high-growth or exit usage crosses the included quota.
- Host Europe cannot be calculated until it confirms that the historical S3
Cloud Storage product is currently orderable and supplies a current quote.
Because labor dominates this very small workload, selection should emphasize
restore compatibility, independent failure domain, encryption, and exit proof
before sub-euro storage differences.

View file

@ -0,0 +1,106 @@
# Expanded object-storage comparison — 2026-08-10
This adds self-managed S3-compatible storage and four large-cloud price points
to the managed-provider comparison. All recurring totals include operator labor
at €60/hour. Taxes are excluded. USD prices use the 2026-08-10 ECB reference
rate of 1 EUR = 1.1555 USD.
## Self-managed reference architecture
Garage is the reference open-source implementation. It has an S3-compatible
API and is designed for distributed deployments. Garage explicitly says its
single-node quick-start has no redundancy and must not be used in production.
CNPG/Barman compatibility remains a live preflight requirement.
The initial fixed-size estimates use locally attached storage:
- Host Europe: 8 vCPU, 16 GB RAM, 400 GB NVMe at €37.99/month including 19%
VAT, normalized to €31.92 net per node. Network traffic is flat-rate.
- Hetzner: CX53, 16 vCPU, 32 GB RAM, 320 GB local SSD at €29.49/month plus
€0.50 IPv4, net per node. The June 2026 price adjustment is used.
Replication consumes the extra nodes; it does not multiply usable capacity.
All nodes at one provider/location still share provider and site failure risk.
| Topology | Infrastructure/month | Internal labor/month | Running total at 320 GB | Internal setup | External setup | Usable ceiling | Assessment |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | --- |
| Host Europe, 1 node | €31.92 | 2 h / €120 | €151.92 | 8 h / €480 | not quoted | 400 GB | no redundancy; not production |
| Host Europe, 2 nodes | €63.85 | 3 h / €180 | €243.85 | 12 h / €720 | not quoted | 400 GB | replicated, but poor failure/quorum shape |
| Host Europe, 3 nodes | €95.77 | 4 h / €240 | €335.77 | 16 h / €960 | not quoted | 400 GB | production candidate; single provider/site risk |
| Hetzner, 1 node | €30.09 | 2 h / €120 | €150.09 | 8 h / €480 | not quoted | 320 GB | no redundancy; not production |
| Hetzner, 2 nodes | €60.18 | 3 h / €180 | €240.18 | 12 h / €720 | not quoted | 320 GB | replicated, but poor failure/quorum shape |
| Hetzner, 3 nodes | €90.27 | 4 h / €240 | €330.27 | 16 h / €960 | not quoted | 320 GB | production candidate; single location risk |
These sizes cover the base scenario's first month (180 GB), but not its month
12 estimate (458 GB). The calculator therefore returns `null` once the fixed
capacity is exceeded. A larger Host Europe 800 GB node tier or Hetzner Volumes
would be required; obtain account quotes before treating that expansion as a
decision price. Hetzner also documents that its Volumes are already replicated
across three physical servers, which means combining three Garage replicas with
three replicated Volumes would buy nested redundancy at additional cost.
## Managed object-storage price points
Azure Blob and Google Cloud Storage are Barman-supported object stores but are
not S3-compatible APIs. AWS and STACKIT are direct S3 API comparisons. The
following table fixes stored volume at **320 GB**, uses the base request pattern
(2,500 writes and 1,000 reads/month), and includes one 5 GB restore drill. This
puts elastic per-GB products, Hetzner's bundled minimum, and Garage's fixed
capacity on the same running-cost basis.
| Service | Infrastructure at 320 GB | Internal labor/month | Running total at 320 GB | Internal setup | External setup |
| --- | ---: | ---: | ---: | ---: | ---: |
| Scaleway Standard Multi-AZ | €5.14 | 1 h / €60 | **€65.14** | none | €0 assumed |
| Hetzner Object Storage | €6.49 | 1.5 h / €90 | **€96.49** | none | €0 assumed |
| AWS S3 Standard, Frankfurt | €6.80 | 1.5 h / €90 | **€96.80** | 4 h / €240 | €0 assumed |
| STACKIT Object Storage Premium-EU01 | €8.52 | 1.5 h / €90 | **€98.52** | 4 h / €240 | €0 assumed |
| Google Cloud Storage Standard, Frankfurt | €6.05 | 2 h / €120 | **€126.05** | 6 h / €360 | €0 assumed |
| Azure Blob Hot ZRS, Germany West Central | €6.72 | 2 h / €120 | **€126.72** | 6 h / €360 | €0 assumed |
Cloud request costs are negligible here; operator effort and egress assumptions
matter more. Azure/GCP egress figures are explicit planning estimates rather
than captured retail meters, so they must be refreshed in an account calculator
before decision approval.
"Internal setup" values the federation's own implementation time at €60/hour.
"External setup" means paid consulting, vendor professional services, or a
contractor. No such service is assumed for managed self-service onboarding. It
is **not quoted**, rather than zero, for Garage because an external security or
operations review may be prudent and no scope or rate has been obtained.
The apparent difference between the earlier €62.89 Scaleway total and the
normalized €5.14 infrastructure figure is reconciled in
`docs/forecast-actual-control.md`: €62.89 is 180 GB infrastructure (€2.89) plus
€60 labor, while €5.14 is infrastructure alone at 320 GB. The comparable 320 GB
total is €65.14.
## Interpretation
Self-management does not win at this workload once even two hours/month of
patching, monitoring, certificate handling, upgrades, disk replacement,
capacity management, and restore support are valued. Three-node Garage costs
roughly €330€336/month plus €960 setup labor and still shares a provider/site
failure domain. It may become attractive if the platform wants the capability
for broader workloads, can amortize operations across them, or deliberately
values open-source control above the cost difference.
For this backup alone, Scaleway remains the lowest modeled managed option.
STACKIT is especially interesting as a sovereign three-AZ S3-compatible
alternative: its documentation now states automatic replication across all
three availability zones, versioning, lifecycle rules, server-side encryption,
bucket policies, and Object Lock.
## Primary sources
- Host Europe VM pricing: https://www.hosteurope.de/Server/Virtual-Server/
- Hetzner June 2026 pricing: https://docs.hetzner.com/general/infrastructure-and-availability/price-adjustment/
- Hetzner Volume replication: https://docs.hetzner.com/cloud/volumes/overview/
- Garage single-node warning: https://garagehq.deuxfleurs.fr/documentation/
- AWS S3 price list: https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonS3/current/eu-central-1/index.json
- AWS egress/free allowance: https://aws.amazon.com/ec2/pricing/on-demand/
- Azure retail pricing API: https://learn.microsoft.com/en-us/rest/api/cost-management/retail-prices/azure-retail-prices
- Google Cloud Storage pricing: https://cloud.google.com/storage/pricing
- STACKIT storage pricing: https://www.stackit.de/de/preise__trashed/cloud-services/iaas/stackit-storage/
- STACKIT object-storage capabilities: https://docs.stackit.cloud/de/products/storage/object-storage/basics/introduction/
- STACKIT network pricing: https://www.stackit.de/de/preise__trashed/cloud-services/iaas/stackit-network/
- ECB daily FX rate: https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml

View file

@ -0,0 +1,71 @@
# Object-storage provider due diligence — 2026-08-10
Status vocabulary: `yes`, `no`, `unknown`, or `test`. `Test` means documentation
is promising but the live CNPG/Barman preflight remains mandatory.
| Requirement | Host Europe Cloud Storage | Scaleway Standard Multi-AZ | Hetzner Object Storage |
| --- | --- | --- | --- |
| Currently orderable for account | unknown — written confirmation required | yes | yes |
| S3-compatible HTTPS / SigV4 | unknown | yes / test | yes / test |
| EU region considered | unknown | Amsterdam `nl-ams` | Nuremberg `nbg1` |
| Different provider from reef-railiance | no | yes | yes |
| Multi-AZ / availability | unknown | yes; 99.9% service availability | unknown; docs describe Ceph and infrastructure redundancy, not a quantified service SLA |
| Published durability | unknown | 99.999999999% | unknown |
| Scoped access key | unknown | yes; bucket policy test required | yes; key defaults to all project buckets, bucket policy required |
| Private bucket | unknown | yes | yes |
| Multipart/list/get/put/delete | unknown | yes / test | yes / test |
| Versioning | unknown | yes | yes |
| Lifecycle expiration | unknown | yes | yes |
| Object lock | unknown | yes; cannot later disable | yes; enable at bucket creation |
| TLS encryption | unknown | yes; enforceable by bucket policy | yes / test |
| Encryption at rest | unknown | yes: SSE-ONE, SSE-C, or SSE-KMS | **no default**; SSE-C is documented |
| Initial 100 GiB without long commitment | unknown | yes, usage-based | yes, hourly base billing |
| Operations pricing | unknown | no separate request charge found; confirm invoice model | PUT/GET/DELETE free |
| Storage / egress pricing | unknown | €0.01606/GB-month; 75 GB egress free, then €0.01/GB | €6.49 minimum includes 1 TB storage and 1 TB egress; excess prices still to capture |
| Cancellation / support terms | unknown | contract review required | contract review required |
| Provider status visibility | unknown | yes | yes |
| CNPG 1.25/Barman compatibility | unknown | test | test |
## Evidence
- Host Europe Backup Storage supports FTP, SCP, SFTP, and rsync, so it is a
secondary logical-copy candidate, not the primary Barman object store:
https://www.hosteurope.de/faq/server/virtual-server/backup-storage/
- Host Europe's S3 claim is currently only backed by an older specification:
https://www.hosteurope.de/download/PVL/Preis_und_Leistungsverzeichnis_CloudStorage_1-0-1.pdf
- Scaleway pricing: https://www.scaleway.com/en/pricing/storage/
- Scaleway capabilities and durability:
https://www.scaleway.com/en/object-storage/
- Scaleway regions/lifecycle/multipart:
https://www.scaleway.com/en/docs/object-storage/concepts/
- Scaleway object lock:
https://www.scaleway.com/en/docs/object-storage/how-to/use-object-lock/
- Hetzner overview, endpoints, quotas, operations, and limits:
https://docs.hetzner.com/storage/object-storage/overview/
- Hetzner supported S3 actions and encryption limitation:
https://docs.hetzner.com/storage/object-storage/supported-actions/
- Hetzner encryption FAQ:
https://docs.hetzner.com/storage/object-storage/faq/general/
- CNPG 1.25 S3-compatible endpoint and lifecycle guidance:
https://cloudnative-pg.io/docs/1.25/appendixes/object_stores/
## Blocking evidence requests
1. Host Europe: ask account support to confirm current orderability, endpoint,
region, SigV4, supported API, versioning/lifecycle, encryption, durability,
availability, price, support, and cancellation terms in writing.
2. Scaleway and Hetzner: obtain account-visible contractual/support terms and
confirm tax treatment. Capture Hetzner excess prices.
3. Run a disposable live preflight for each finalist: private bucket, scoped
positive and negative credentials, multipart CRUD, versioning/lifecycle,
Barman empty-WAL-archive check, and deletion/exit behavior.
## Provisional conclusion
Host Europe is not selectable as primary while current S3 orderability is
unknown. Hetzner fails the current acceptance requirement for provider-managed
encryption at rest unless client-side/SSE-C custody is explicitly accepted.
Scaleway Standard Multi-AZ is therefore the provisional primary, subject to a
live Barman preflight, contract review, and human purchase approval. Hetzner
remains a price comparator; Host Europe Backup Storage or the governed
Nextcloud lane remains the independent logical-copy candidate.

View file

@ -0,0 +1,25 @@
# RESOURCE-WP-0003 delegated evidence workplans — 2026-08-11
## Result
Every owned gap from the initial portfolio discovery now points to a live,
repository-local work record. Resource-control retains the inventory,
forecast, actual-observation, allocation, and optimization contract; source
repositories retain authority for infrastructure and workload evidence; and
fin-hub retains booked financial facts.
| Evidence owner | Workplan | State Hub workplan ID | Interface outcome |
|---|---|---|---|
| railiance-infra | `RAIL-HO-WP-0008` | `7122657f-87c8-46b5-a725-a1af1ba0af12` | Host identity, lifecycle, commercial and host-capacity evidence |
| railiance-cluster | `RAIL-BS-WP-0014` | `ea6ec98a-0d65-4afa-b6c5-6e7ed34011ac` | Cluster capacity, utilization, storage/failure-domain and allocation evidence |
| railiance-forge | `RAILIANCE-WP-0002` | `72f935a5-d921-48a4-96e4-2365556c7374` | Forgejo demand, utilization, labor/SLO and allocation evidence |
| railiance-platform | `RAILIANCE-WP-0016` | `49084fb8-de63-4f32-a4a9-3a42d4e708ac` | apps-pg utilization, recovery, labor and consumer evidence |
| coulomb-social | `CSOC-WP-0005` | `00de1578-6150-40d3-b193-e852666ed945` | Workload forecasts, observations, objectives and labor evidence |
| rapp-qonto | `RAPP-QONTO-WP-0002` | `25b3b715-9a56-499e-a85c-a09ebd9bbb79` | Scale-to-zero usage, external service cost inputs and labor evidence |
| fin-hub | `FIN-WP-0004-T04..T06` | `67b6de6c-4820-4478-9789-f50260204c27` | Budgets/constraints, forecast-to-actual loop and generalized attribution |
The authoritative machine-readable mapping is
`data/portfolio-coverage-2026-08-11.json`. State Hub synchronization completed
without failures. Its warnings concern pre-existing fleet prefix collisions,
unregistered legacy task prefixes, and missing optional DoR metadata; none
prevented registration of these plans.

View file

@ -0,0 +1,64 @@
# RESOURCE-WP-0003 fin-hub contract preflight
Date: 2026-08-11
Resource-control task: `RESOURCE-WP-0003-T02`
Fin-hub tasks: `FIN-WP-0004-T01`, `T02`, `T03`, `T07`, `T08`, `T09`
Fin-hub commit: `0034330`
## Result
Resource-control accepts the authority and schema boundary in the joint v0.1
contract. Fin-hub's remediation was reviewed against the timestamped
assessment and its full suite passed:
```text
46 passed, 1 third-party deprecation warning
```
Resource-control's suite passed after adding its producer-owned schema,
forecast exporter, booked-cost consumer validation, and reconciliation seam:
```text
13 passed
resource-control declarations: valid
```
## Cross-repository check
The live immutable backup forecast
`data/forecasts/platform-audit-storage-scaleway-base-2026-08.json` was processed
through both implementations using:
- resource-control `tools/financial_exchange.py::forecast_records`; and
- fin-hub `fin_hub.services.exchange::ingest_resource_forecast`.
Observed result:
```json
{
"producer_consumer_records": 12,
"idempotent_store_rows": 12,
"payloads_equal": true,
"booked_cost_gate": "awaits attributable provider fact"
}
```
Replaying the same forecast did not add rows. All canonical record payloads
matched, including IDs, periods, cost breakdown, attribution, assumptions, and
provenance reference.
## Residual acceptance gate
This preflight proves the planning direction; it is not evidence of purchased
storage or actual spend. `FIN-WP-0004-T05` remains waiting until
`RESOURCE-WP-0002` produces a real provider financial fact attributable to
`platform:audit-storage`. That round trip must then demonstrate:
1. exactly one authoritative booked fact in fin-hub;
2. a non-ledger projection consumed by resource-control;
3. a join to the immutable forecast and technical observation;
4. infrastructure-cost variance with the financial fact ID preserved; and
5. duplicate delivery and any correction leaving effective totals correct.
Synthetic records remain suitable for automated contract tests but cannot
satisfy this operational gate.

View file

@ -0,0 +1,36 @@
# RESOURCE-WP-0003 generalized control cycle — 2026-08-11
## Result
`RESOURCE-WP-0003-T05` generalized the backup-specific forecast-to-actual
method into a provider-neutral portfolio contract.
The implementation consists of:
- `schemas/resource-control-cycle.schema.json` for immutable forecast and
actual records;
- `tools/control_cycle.py` for resource-neutral comparison and controlled
variance attribution;
- six paired examples covering storage, fixed cluster compute, and a hybrid
shared PostgreSQL service;
- tests for the three resource classes, immutable forecast binding, explicit
cost attribution, and fail-closed data-quality handling;
- updated operating guidance in `docs/forecast-actual-control.md`.
## Boundary with fin-hub
Actual records carry `booked_cost_refs`; resource-control does not reproduce
the ledger fact. Fin-hub message `d33f2452-8dc6-4249-a3d2-84fda938103a`
confirmed explicit `financial_fact_id -> resource_id` binding, budget and
constraint signals, and generalized exchange health. The first operational
backup round trip remains correctly gated on `RESOURCE-WP-0002` procurement;
that does not invalidate these illustrative contract fixtures.
## Verification
- `make test`: 25 tests passed and declarations validated.
- All three example pairs completed through `make control-cycle`.
- `git diff --check`: clean.
The examples are intentionally marked illustrative. Their values must not be
reported as actual consumption or booked cost.

View file

@ -0,0 +1,81 @@
# RESOURCE-WP-0003 initial portfolio discovery
Date: 2026-08-11
Task: `RESOURCE-WP-0003-T03`
Coverage data: `data/portfolio-coverage-2026-08-11.json`
## Result
The first real portfolio slice contains seven inventory records:
| Resource | Owner | Role |
| --- | --- | --- |
| `resource:hosteurope:railiance01` | `railiance-infra` | provider compute host |
| `resource:railiance:reef-railiance:k3s` | `railiance-cluster` | shared single-node Kubernetes runtime |
| `resource:railiance:forgejo` | `railiance-forge` | Helix Forge source/package infrastructure |
| `resource:railiance:apps-pg` | `railiance-platform` | shared application database service |
| `resource:tenant:coulomb:coulomb-social` | `coulomb-social` | Coulomb reference-tenant application |
| `resource:tenant:friendly:binky:rapp-qonto` | `rapp-qonto` | representative tenant workload |
| `resource:platform:audit-storage` | `resource-control` | proposed backup storage |
Helix Forge does not itself operate production infrastructure; its repository
scope explicitly delegates runtime operation. Its current infrastructure
coverage is therefore the shared Forgejo service and its underlying cluster
and host, not a fictional Helix Forge server.
## Live substrate observation
A read-only observation of `railiance01` and the reef-railiance Kubernetes API
at `2026-08-11T09:05:25Z` confirmed:
- one Ready k3s control-plane/etcd node on `railiance01`;
- 4 allocatable vCPU, approximately 15.62 GiB allocatable memory, 183.06 GiB
allocatable ephemeral storage, and a 110-pod limit;
- node usage of 564m CPU and 5,975 MiB memory at observation time;
- a 192.69 GiB root filesystem, 66.77 GiB used;
- active Forgejo and Coulomb Social deployments with one ready replica each;
- active `apps-pg` with one ready CNPG instance and a 10 GiB volume;
- a healthy `rapp-qonto` Knative Service with min-scale 0 and max-scale 1;
- a continuously running Qonto egress proxy; and
- all discovered persistent volumes using `local-path`, therefore sharing the
host failure domain.
No credential values or secret-bearing resources were read or recorded.
## Source authority
- `railiance-cluster` owns the k3s runtime and publishes the substrate profile.
- `railiance-apps` supplies current Forgejo and Coulomb Social deployment
values; `railiance-forge` is the declared forge-operation owner.
- `railiance-platform` owns the shared applications PostgreSQL definition.
- `coulomb-social` owns tenant and application behavior.
- `rapp-qonto` owns the Binky tenant binding, resource requests, Knative
package, and runtime requirements.
- the live operating system and Kubernetes API supplied timestamped capacity
and workload-state evidence.
## Material findings
The portfolio currently has one physical/virtual host and one Kubernetes node.
Compute, local persistent storage, databases, Forgejo, Coulomb Social, and the
tenant workload therefore share one provider/host failure domain. The planned
ThreePhoenix HA work remains backlog; the inventory does not describe its
target three nodes as current capacity.
The active cluster has significant point-in-time headroom, but a single CPU and
memory sample is not a utilization history and cannot support rightsizing.
Commercial identifiers, booked server price, renewal dates, operations labor,
and shared allocation methods remain unknown.
Each gap has an authoritative repository owner in the coverage data. Creating
live delegated work records for those gaps is `RESOURCE-WP-0003-T04`; this
discovery does not silently assign implementation through prose.
## Verification
```text
20 tests passed
resource-control declarations: valid
all inventory relationships resolve
git diff --check: pass
```

View file

@ -0,0 +1,104 @@
# 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.

View file

@ -0,0 +1,71 @@
# RESOURCE-WP-0003 portfolio model v0.2
Date: 2026-08-11
Task: `RESOURCE-WP-0003-T01`
Schema: `schemas/resource-inventory.schema.json`
## Result
The inventory model is generalized from a storage purchase record into a
managed-infrastructure portfolio record. Version 0.2 represents:
- provider-managed elastic storage;
- self-managed services hosted on rented capacity;
- Kubernetes or other shared capacity;
- shared platform services; and
- future compute, network, database, managed-service, and license records.
The existing proposed Scaleway backup resource was migrated as the only
authoritative inventory record. The Garage, Kubernetes-capacity, and ingress
records live under `examples/portfolio/`, carry `record_scope: example`, and
contain explicit example evidence. They are schema proofs, not claims about
live infrastructure.
## Model decisions
- Resource identity uses the stable `resource:` namespace independently of
provider-native identifiers.
- Lifecycle separates proposed, ordered, commissioning, active, suspended,
retiring, retired, and rejected states.
- Provider-managed, self-managed, and shared-capacity management models are
explicit.
- Capacity is a set of typed dimensions and distinguishes provisioned, usable,
allocated, observed, limiting, and unknown values.
- Ownership supports multiple workloads, optional tenant identity, and a
dedicated, shared, or unattributed allocation mode.
- Shared allocations require a driver and versioned method. Unknown allocation
remains explicit rather than receiving an invented key.
- Relationships express hosting, composition, dependency, replacement,
capacity consumers, and backup placement without embedding another
repository's graph authority.
- Requirements and evidence carry their kind, reference, observation date,
and authoritative source.
- Exit path is mandatory for every resource class.
## Semantic controls
`tools/portfolio.py` adds controls not conveniently expressed as shape alone:
- valid lifecycle transitions;
- ordered lifecycle dates;
- commissioned dates for operational resources;
- order evidence for operational provider-managed resources;
- allocation-key and shared-driver consistency;
- unique capacity metric/kind dimensions;
- prevention of self-relationships; and
- unmistakable example evidence.
Verification:
```text
18 tests passed
resource-control declarations: valid
git diff --check: pass
```
## Next use
`RESOURCE-WP-0003-T03` should now discover real portfolio records for Helix
Forge, Coulomb Social, shared Railiance services, and one representative tenant
workload. It must replace example assumptions with source evidence and preserve
unknown values where authority is not yet available.

View file

@ -0,0 +1,74 @@
# RESOURCE-WP-0003 portfolio reporting and cadence — 2026-08-14
## Result
`RESOURCE-WP-0003-T07` established the portfolio view and the operating cadence.
The implementation consists of:
- `tools/portfolio_report.py` — coverage, lifecycle, utilization, cost,
renewals, risks, open optimization cases, and next actions, derived entirely
from committed evidence;
- `docs/portfolio-operating-cadence.md` — monthly observation, monthly report,
quarterly calibration, pre-renewal review, and event-driven triggers, each
with named inputs, consumers, and outputs;
- `make portfolio-report`;
- 22 tests in `tests/test_portfolio_report.py`, including a suite that renders
the real committed portfolio rather than fixtures.
Nothing in the report is typed by hand. It reads `data/resources/`, the latest
`data/portfolio-coverage-*.json`, and `data/optimization/`, and it re-validates
every record it reads.
## Not forcing false precision
T07 requires reporting missing evidence and unapportioned cost rather than
forcing false precision. Two decisions carry that requirement:
- **`known_monthly_spend_eur` is `null`, never `0`.** Six of seven resources
carry no price evidence and no booked cost has arrived from `fin-hub`.
Summing the one priced resource would report a portfolio spend roughly an
order of magnitude below reality while looking authoritative. The field stays
unknown, accompanied by a note stating how many resources are unpriced.
- **Unattributed cost is a list of resources, not a spread.** Four resources
have allocation mode `unattributed`. They are named, with the repository that
owes the allocation driver; their cost is not divided across consumers by a
plausible default.
Resources whose capacity cannot be measured appear in `utilization.unmeasured`
with the reason, rather than being dropped from the ratios and thereby
flattering the portfolio average.
## What the current report tells an operator
T07 is done when an operator can identify material spend, idle or saturated
capacity, forecast error, approaching commitments, and the next evidence-backed
action. Against the portfolio as committed on 2026-08-14:
| Question | Answer |
|---|---|
| Material spend | **Unknown, explicitly.** Only the proposed backup storage carries price evidence; the rest waits on `RAIL-HO-WP-0008` and `FIN-WP-0004`. |
| Idle capacity | `resource:hosteurope:railiance01` and `resource:railiance:reef-railiance:k3s`, idle on every measured metric at 14 percent CPU and 37 percent memory. Five resources are unmeasurable. |
| Saturated capacity | None. |
| Forecast error | **Not yet computable.** The control-cycle mechanism is proven on paired examples, but no operational actual observation exists, so no variance is reported and none is fabricated. |
| Approaching commitments | None visible within 90 days — and the finding is that six active resources have **no** renewal or cancellation date at all, so no cancellation window can be respected. |
| Next action | The `next_actions` list: seven delegated workplans, the booked-cost dependency on `fin-hub`, and six missing contract-date records, each addressed to its owner. |
## Structural risk surfaced
The risk section reports concentration mechanically rather than in prose: six of
seven resources share `host:railiance01` and five share `cluster:reef-railiance`.
That includes every workload the proposed backup storage exists to protect,
which is the standing argument against same-provider placement recorded in
`opt:platform-audit-storage:2026-08`. Thresholds are explicit in the tool —
idle at or below 35 percent, saturated at or above 85 percent, renewal horizon
90 days, failure-domain concentration above two resources.
## Boundaries held
The report reads; it does not write to any other repository. Every unknown it
prints is attributed to the repository or authority that owns it, and the
`next_actions` list is the message sent back to those owners on the monthly
cadence. No booked financial fact is reproduced here; spend remains
`fin-hub`'s authority under
`docs/fin-hub-resource-control-contract-v0.1.md`.