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
|
|
@ -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 1–3 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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
```
|
||||
104
docs/evidence/RESOURCE-WP-0003-optimization-cases-2026-08-14.md
Normal file
104
docs/evidence/RESOURCE-WP-0003-optimization-cases-2026-08-14.md
Normal 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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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`.
|
||||
47
docs/fin-hub-resource-control-contract-v0.1.md
Normal file
47
docs/fin-hub-resource-control-contract-v0.1.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# fin-hub ↔ resource-control contract v0.1 — resource-control ratification
|
||||
|
||||
Status: authority and schema boundary accepted on 2026-08-11
|
||||
Fin-hub implementation reviewed: commit `0034330`
|
||||
Workplans: `RESOURCE-WP-0003-T02`, `FIN-WP-0004`
|
||||
|
||||
The jointly reviewed contract is maintained in fin-hub at
|
||||
`docs/fin-resource-authority-contract-v0.1.md`. This record is the
|
||||
resource-control-side ratification and identifies the artifacts for which this
|
||||
repository is authoritative.
|
||||
|
||||
## Authority
|
||||
|
||||
- fin-hub owns booked financial facts and corrections, tax and currency
|
||||
treatment, budgets, commitments, burn, runway, and viability signals.
|
||||
- resource-control owns resource identity and lifecycle, technical usage,
|
||||
allocation evidence, demand and cost forecasts, and optimization evidence.
|
||||
- workload and platform repositories remain authoritative for native workload
|
||||
identity, service requirements, provisioning, operation, and source
|
||||
telemetry.
|
||||
- neither repository approves contracts, executes payment, or turns planning
|
||||
evidence into booked spend.
|
||||
|
||||
## Artifacts
|
||||
|
||||
- `schemas/planning-evidence.schema.json` is the authoritative
|
||||
resource-control outbound schema.
|
||||
- `tools/financial_exchange.py forecast` converts the existing immutable
|
||||
monthly backup forecast into canonical `ForecastEvidence` records.
|
||||
- `tools/financial_exchange.py reconcile` validates fin-hub booked-cost
|
||||
projections and compares attributable infrastructure cost by accounting
|
||||
period. Booked facts are referenced projections here, never a second ledger.
|
||||
- `schemas/monthly-resource-observation.schema.json` uses
|
||||
`usage_observation` for technical observations. “Booked actual” is reserved
|
||||
for fin-hub facts.
|
||||
|
||||
Fin-hub owns its outbound `BookedCostEvidence` executable schema. This
|
||||
repository validates the contract fields and arithmetic it consumes, but does
|
||||
not redefine financial authority.
|
||||
|
||||
## Acceptance boundary
|
||||
|
||||
The 12-row `platform:audit-storage` forecast can complete an idempotent
|
||||
planning-evidence preflight before procurement. `FIN-WP-0004-T05` and the
|
||||
operational round trip remain open until an attributable provider charge or
|
||||
credit exists. Fixture or synthetic money may test the seam but cannot satisfy
|
||||
that evidence gate.
|
||||
123
docs/forecast-actual-control.md
Normal file
123
docs/forecast-actual-control.md
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
# Forecast-to-actual resource control
|
||||
|
||||
## Why the €2.89, €5.14, €62.89, and €65.14 figures differ
|
||||
|
||||
They describe two volumes and two cost scopes:
|
||||
|
||||
| Figure | Stored volume | Scope | Calculation |
|
||||
| --- | ---: | --- | --- |
|
||||
| €2.89 | 180 GB | infrastructure only | 180 × €0.01606 |
|
||||
| €62.89 | 180 GB | infrastructure + internal labor | €2.89 + 1 h × €60 |
|
||||
| €5.14 | 320 GB | infrastructure only | 320 × €0.01606 |
|
||||
| €65.14 | 320 GB | infrastructure + internal labor | €5.14 + 1 h × €60 |
|
||||
|
||||
The older €62.89 estimate was not “storage without elastic cost”; it already
|
||||
included the €60 monthly operator allowance. The normalized table introduced a
|
||||
different stored volume and showed infrastructure separately. Future reports
|
||||
must always label both `stored_gb` and cost scope.
|
||||
|
||||
## Control records
|
||||
|
||||
A forecast is a timestamped, immutable record of what was believed at decision
|
||||
time. The initial base forecast is
|
||||
`data/forecasts/platform-audit-storage-scaleway-base-2026-08.json`. Do not edit
|
||||
it after actual evidence exists. If assumptions change, create a new forecast
|
||||
with a new creation date and retain the prior one so forecast accuracy remains
|
||||
auditable.
|
||||
|
||||
After every billing month, record a technical `usage_observation` under
|
||||
`data/actuals/`. Authoritative booked financial facts come from fin-hub and
|
||||
are joined by reference; resource-control does not originate a second booked
|
||||
actual.
|
||||
The required cost and usage proxies are:
|
||||
|
||||
- database size and provider stored bytes;
|
||||
- WAL volume;
|
||||
- write and read requests;
|
||||
- restore-test egress;
|
||||
- infrastructure invoice cost;
|
||||
- internal operations hours and valued labor cost;
|
||||
- total attributable cost;
|
||||
- backup success, maximum observed RPO, and restore RTO when measured.
|
||||
|
||||
Provider stored bytes are intentionally distinct from logical database size.
|
||||
They expose base-backup retention, WAL, compression, versioning, incomplete
|
||||
multipart uploads, and lifecycle behavior. Invoice cost is distinct from the
|
||||
provider's usage estimator and must reconcile to non-secret billing evidence.
|
||||
|
||||
## Error calculation and review
|
||||
|
||||
`make variance ACTUAL=data/actuals/YYYY-MM.json` reports signed error and
|
||||
absolute percentage error for each numeric proxy. Initially review rather than
|
||||
automatically rewrite assumptions:
|
||||
|
||||
- investigate infrastructure cost error above 10%;
|
||||
- investigate stored-byte or WAL error above 20%;
|
||||
- investigate any unplanned egress or request-class charge;
|
||||
- investigate labor error above 1 hour or 25%;
|
||||
- investigate every backup failure, RPO breach, or restore-RTO regression.
|
||||
|
||||
After three comparable months, calculate mean absolute percentage error by
|
||||
proxy and recalibrate the next forecast. Avoid MAPE where the forecast is zero;
|
||||
use absolute error and explain the new activity instead. Separate forecast
|
||||
error from price variance: a bill can differ because usage was wrong, the rate
|
||||
changed, tax/discount treatment differed, or an unmodeled SKU appeared.
|
||||
|
||||
Quarterly provider comparison should use the latest actual trailing three
|
||||
months, the next 12-month forecast, one monthly restore, an exit event, and
|
||||
observed—not aspirational—operator labor.
|
||||
|
||||
## General portfolio control record
|
||||
|
||||
The storage-specific v0.1 observation remains valid for the backup procurement
|
||||
case. New portfolio controls use
|
||||
`schemas/resource-control-cycle.schema.json`, whose resource-specific
|
||||
`usage_proxies` allow the same comparison mechanism to cover storage, VMs or
|
||||
cluster compute, shared platform services, managed services, and future
|
||||
resource types without pretending they share the same utilization unit.
|
||||
|
||||
Each record separates:
|
||||
|
||||
- provisioned and used capacity, with a fixed, elastic, or hybrid model;
|
||||
- infrastructure, internal labor, and external labor cost;
|
||||
- booked financial facts, referenced rather than copied from fin-hub;
|
||||
- allocation method, driver, version, and unattributed residual;
|
||||
- service constraints and their units;
|
||||
- low/base/high forecast scenario or observed actual;
|
||||
- evidence and uncertainty.
|
||||
|
||||
Forecast records are append-only. A changed forecast receives a new
|
||||
`record_id`, creation time, and `revision_of` reference. Actuals must name the
|
||||
exact original `forecast_ref`; a later revision must never replace the
|
||||
decision-time baseline when measuring forecast error.
|
||||
|
||||
Run a generic comparison with:
|
||||
|
||||
```sh
|
||||
make control-cycle FORECAST=examples/control-cycle/storage-forecast.json \
|
||||
ACTUAL=examples/control-cycle/storage-actual.json
|
||||
```
|
||||
|
||||
The examples cover storage, `reef-railiance` cluster compute, and the shared
|
||||
`apps-pg` service. Their numbers are illustrative contract fixtures, not booked
|
||||
facts. Operational records replace their evidence references after the owner
|
||||
workplans publish observations.
|
||||
|
||||
## Variance attribution
|
||||
|
||||
Every material variance is assigned to one of six controlled categories:
|
||||
|
||||
- `demand`: the amount of consumed service differed;
|
||||
- `provider_price`: rate, discount, tax, currency, or billed SKU differed;
|
||||
- `allocation`: a shared-cost driver or attribution changed;
|
||||
- `labor`: internal or external effort differed;
|
||||
- `model`: a formula, assumption, capacity behavior, or omitted component was
|
||||
wrong;
|
||||
- `data_quality`: evidence is missing, late, inconsistent, or uses a different
|
||||
unit.
|
||||
|
||||
The comparator defaults usage differences to demand, infrastructure cost to
|
||||
provider price, and labor fields to labor, while preserving explicit
|
||||
attribution supplied with the actual. Missing proxies and unit mismatches fail
|
||||
closed as data-quality errors. Attribution explains an error; it does not
|
||||
rewrite the forecast.
|
||||
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`. |
|
||||
74
docs/portfolio-operating-cadence.md
Normal file
74
docs/portfolio-operating-cadence.md
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# Portfolio reporting and operating cadence
|
||||
|
||||
`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. |
|
||||
| `risks` | Concentrated failure domains, unpriced resources, unattributed cost, idle and saturated capacity. |
|
||||
| `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 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` |
|
||||
|
||||
## Reading the current report
|
||||
|
||||
As of 2026-08-14 an operator asking the four questions this cadence exists to
|
||||
answer gets these answers, and the honest ones are the useful ones:
|
||||
|
||||
- **Material spend?** Unknown, and explicitly so. Only the proposed backup
|
||||
storage carries price evidence. Every other resource is waiting on
|
||||
`RAIL-HO-WP-0008` and `FIN-WP-0004`.
|
||||
- **Idle or saturated capacity?** `railiance01` and the `reef-railiance` k3s
|
||||
cluster are idle on every measured metric — 14 percent CPU, 37 percent memory.
|
||||
Nothing is saturated. Five resources cannot be measured at all.
|
||||
- **Forecast error?** Not yet computable. The control-cycle mechanism is proven
|
||||
on paired examples, but no operational actual observation exists, so there is
|
||||
no variance to report and none is fabricated.
|
||||
- **Approaching commitments?** None visible — and that is itself the finding:
|
||||
six active resources have no renewal or cancellation date recorded, so the
|
||||
cancellation window cannot be respected for any of them.
|
||||
- **Next evidence-backed action?** The `next_actions` list, led by the seven
|
||||
delegated workplans that own the missing evidence.
|
||||
|
||||
The single largest structural risk the report surfaces is concentration: six of
|
||||
seven resources share `host:railiance01`, including the backup storage's own
|
||||
intended protection target. That is the standing argument for placing backup
|
||||
storage outside the Host Europe failure domain, and it is visible in the report
|
||||
rather than only in prose.
|
||||
Loading…
Add table
Add a link
Reference in a new issue