Stdlib-only CronJob entry point that reads the Kubernetes API (get/list on nodes, pods, PVCs, PVs, storageclasses, metrics.k8s.io) and Prometheus, reuses build_observation / reconcile.join / build_report / retain unchanged, and retains ISO-week reports, per-attempt inputs and an append-only receipt log. Telemetry window ends Monday 06:00 UTC so weeks stay comparable across DST. Reconcile now imports PyYAML only in its CLI. Not deployed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
153 lines
8.8 KiB
Markdown
153 lines
8.8 KiB
Markdown
# Retained weekly allocation assessment
|
||
|
||
`scripts/weekly_allocation_report.py` implements the offline evidence join for
|
||
CUST-WP-0071-T05. It reads the existing cluster collector, Custodian allocation
|
||
reconcile, Prometheus query-response capture and resource-control inventory.
|
||
It performs no collection, deployment, network calls, task writes or delivery.
|
||
It is suitable for an accepted durable executor; it does not install one.
|
||
|
||
## Inputs and retained evidence
|
||
|
||
The required inputs are:
|
||
|
||
- `--cluster`: `railiance.cluster-resource-observation.v1`, produced by
|
||
railiance-cluster's `make cluster-observe REMOTE=railiance01`.
|
||
- `--allocation`: `custodian.allocation-reconcile.v1` from
|
||
`scripts/reconcile_railiance_allocation.py`, matching the same capture and
|
||
workload inventory. Run both against the same retained observation.
|
||
- `--telemetry`: the existing September 28 query-capture shape: `captured_at`,
|
||
`window: 7d`, `step: 5m`, and `queries` mapping each metric name to its exact
|
||
`promql` and Prometheus `response`. Preserve original sample evaluation times;
|
||
do not restamp historical query results. These describe the query window,
|
||
not independently verified raw scrape completeness.
|
||
- `--inventory`: resource-control's `data/resources/reef-railiance-k3s.json`.
|
||
Dated portfolio capacity is retained as portfolio context, never summed with
|
||
the live cluster or treated as current scheduler evidence.
|
||
- `--provenance`: exact source collector revisions and `source_observation`
|
||
matching the cluster capture, as in the September 28 allocation provenance.
|
||
- `--as-of`: explicit evaluation timestamp; identical retries use the same value.
|
||
- `--output`: a new retained JSON path. Exact identical retries succeed; changed
|
||
content at that path fails and requires an explicit revision path. Writes use
|
||
a complete temporary file and atomic create-without-replacement publication.
|
||
|
||
Optional `--previous` selects the previous retained report. Deltas require the
|
||
same resource, exact query texts, sample step, seven-day window and consecutive
|
||
capture endpoints within one sample step. Other comparisons stay unknown; the
|
||
report still records disappeared workloads. Recommendation identity depends on
|
||
resource/namespace/workload/review purpose, not the date or recommendation text.
|
||
Existing receiving-task links survive retries and subsequent reports.
|
||
|
||
Metric names are `cpu_p95_m`, `cpu_peak_m`, `memory_p95_bytes`,
|
||
`memory_peak_bytes`, `cpu_throttle_ratio`, `latency_p95_seconds`, `errors`,
|
||
`request_count`, `restarts`, `oom_events`, `cpu_sample_count` and
|
||
`memory_sample_count`. Metrics are nonnegative, finite instant-vector aggregates.
|
||
Workload queries must carry exactly `namespace` and `workload` identity labels
|
||
(an optional Prometheus `__name__` is allowed). Extra labels or duplicate series
|
||
are not silently aggregated. Namespace-only results are retained separately;
|
||
they never become per-workload values, even for a currently single-workload
|
||
namespace. Namespace peaks and percentiles are not additive.
|
||
|
||
Missing series, failed queries and non-finite values remain null with explicit
|
||
reasons. Observed zero remains zero. Evaluation counts never prove raw scrape
|
||
coverage. CPU/memory limits and instantaneous observations come from the cluster
|
||
snapshot; historical latency, request/errors, OOM or throttling are not inferred
|
||
from that snapshot. Unknown operating margin remains null; reservation residual
|
||
does not establish processing capacity or release acceptance.
|
||
|
||
## Owner decisions
|
||
|
||
Without a complete current owner review the result is `investigate`, with low
|
||
confidence and explicit evidence gaps. `--reviews` accepts a JSON mapping keyed
|
||
by the stable recommendation ID. A review provides `decision` (keep, increase,
|
||
decrease or investigate), `owner`, `period_end` matching telemetry capture,
|
||
`reviewed_at`, `evidence_refs`, `receiving_task`, `configuration`, `rollback_ref`,
|
||
`coverage_ref`, `service_acceptance_ref`, and `margin_ref`. Optional `outcome`
|
||
records what happened to the preceding recommendation. These references are
|
||
owner assertions to be checked during acceptance, not proof generated by this
|
||
adapter. Wrong-owner/window reviews, missing workload metrics, zero CPU requests,
|
||
unknown owners and stale/future evidence keep the result at investigate and
|
||
retain the proposal for review. No idle sample automatically recommends a cut.
|
||
|
||
Only new observation evidence and verified owner disposition can complete T02–T04.
|
||
In particular, the historical Vergabe sample does not establish the attended
|
||
two-user p95/error acceptance target or incremental database attribution.
|
||
|
||
## Disabled execution contract
|
||
|
||
The owner-source candidate is
|
||
`activity-core/activity-definitions/weekly-railiance-allocation-review.md`.
|
||
Its fixed command is:
|
||
|
||
```sh
|
||
python3 scripts/weekly_allocation_report.py --request config/weekly-allocation-request.json
|
||
```
|
||
|
||
The request is intentionally unbound and the command fails until the accepted
|
||
executor supplies real input paths, a fixed `as_of`, and a durable output path.
|
||
Paths resolve relative to the request file. The executor must snapshot this
|
||
request per run, keep it stable for retries, bind a real previous report, and
|
||
retain reports beyond the seven-day raw-metric retention. The committed null
|
||
template is not a production storage location or collection implementation.
|
||
|
||
Activity Core source `4ae00fc` selects its existing ADR-007 bounded-operation
|
||
executor with fixed owner commands; both definitions remain disabled. The prior
|
||
coding profile cannot run Python or reach the Hub. Runtime owners must still
|
||
bind accepted source/fleet mounts, separate private retained state and scoped
|
||
network access. Activity Core owns the Monday 08:00 Europe/Berlin schedule and
|
||
bounded-activity/context/progress-sink evidence, with one Temporal attempt.
|
||
The full binding contract is `activity-core/docs/custodian-bounded-operations.md`.
|
||
The adapter's matching recommendation IDs are the downstream upsert keys for
|
||
existing owner work records; it does not itself create those records. Delivery,
|
||
explicit owner acknowledgment, independent missed/failed-run detection and
|
||
recovery remain unbound. A report file or completed bounded activity alone proves none
|
||
of them. T05 needs the first unattended schedule receipt and exercised recovery.
|
||
|
||
## Verification
|
||
|
||
```sh
|
||
python3 -m unittest discover -s tests -p test_weekly_allocation_report.py -v
|
||
```
|
||
|
||
The tests cover historical namespace-only input, missing/zero/NaN, invalid
|
||
aggregation, query failure, compatible/incompatible periods, stable identities
|
||
and owner-task links, stale/future evidence, owner review, resource/capture
|
||
mismatch and retained retry/collision behavior.
|
||
|
||
## In-cluster runner (CUST-WP-0071-T05, founder decision 2026-10-21)
|
||
|
||
`docs/decisions/2026-10-21-cust-wp-0071-delivery-interview.md` selects an
|
||
in-cluster Kubernetes CronJob for T05 and supersedes the Activity Core
|
||
placement above. `scripts/weekly_allocation_incluster.py` is the job entry
|
||
point. It is standard-library only and reuses the existing owner code
|
||
unchanged: railiance-cluster `build_observation`, `reconcile.join` (PyYAML is
|
||
now imported only by the reconcile CLI) and this adapter's `build_report` and
|
||
`retain`.
|
||
|
||
- Reads: the Kubernetes API through the pod ServiceAccount, with get/list on
|
||
nodes, pods, persistentvolumeclaims, persistentvolumes, storageclasses and
|
||
metrics.k8s.io nodes/pods only. Prometheus is reached through
|
||
`PROMETHEUS_URL` with `config/weekly-allocation-queries.json`.
|
||
- Week identity: the ISO week of (run time - 6h). The telemetry window ends at
|
||
Monday 06:00 UTC, so consecutive windows are exactly seven days apart in CET
|
||
and CEST. Prometheus keeps seven days of data, so the run must happen on the Monday. A
|
||
2026-10-21 (Wednesday) read-only dry run against railiance01 retained the
|
||
report, but per-namespace CPU sample counts reached at most 1512 of 2016,
|
||
because the window start had already expired.
|
||
- Retention: `<data>/reports/<YYYY>-W<ww>.json` (create-without-replacement),
|
||
per-attempt inputs under `<data>/runs/<week>/<attempt>/`, and an append-only
|
||
`<data>/receipts.jsonl` with status `retained`, `already-retained` or
|
||
`failed`, previous week, `missed_weeks`, comparability and failed queries.
|
||
- Idempotency: a retained week is a no-op success. A failed attempt leaves no
|
||
report, so the Job retry or a manual run collects again.
|
||
- Not done: delivery, owner acknowledgment and owner-task creation
|
||
(`delivery: not emitted`). Workload rows stay `investigate` because the queries
|
||
are namespace-level. kube-state-metrics does not export `kube_pod_labels`, so
|
||
no workload-labelled series exist yet.
|
||
|
||
Executor manifests (suspended, not registered with ArgoCD) live in
|
||
railiance-platform `argocd/platform-addons/custodian-allocation-review/`, with
|
||
the draft Application in `argocd/railiance01/drafts/`.
|
||
|
||
```sh
|
||
python3 -m pytest tests/test_weekly_allocation_incluster.py tests/test_weekly_allocation_report.py -q
|
||
```
|