ops-mason/workplans/MASON-WP-0006-readiness-tier-check.md
tegwick 36445ae679 Enforce readiness tiers and reconcile blocked workplans
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0e75a-fc5c-7913-9dba-9846210c766d
2026-09-28 11:40:57 +02:00

233 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: MASON-WP-0006
type: workplan
title: "Check the target's readiness tier before a direct Kubernetes apply"
domain: infotech
repo: ops-mason
status: blocked
flavor: implementation
owner: claude
topic_slug: ops-mason
created: "2026-09-21"
updated: "2026-09-28"
state_hub_workstream_id: "1b900161-51ff-544f-8412-ba7fcab64bb5"
---
# MASON-WP-0006 — Check the target's readiness tier before a direct Kubernetes apply
## Goal
Make phase 4 enforce the founder's Kubernetes change-gate decision
(`the-custodian/docs/kubernetes-change-gate-decision.md`, 2026-09-21, the
founder exercising `GOVERN @ estate`). Before any direct
`ADMINISTER @ realm:kubernetes/railiance01`, `ops-mason plane apply` must
establish the target's railiance-master ADR-0006 `readiness_state` and:
- allow a direct apply under `activation=APPROVED` (founder plan approval) when
the target is `declared`, `installed` or `verified`;
- refuse a direct apply to a production-tier target (`production-approved`, or
no readiness state at all), except:
- under `activation=BREAK_GLASS`, which is recorded and must be reconciled
back into the manifest repository afterwards; or
- for `rapp-policy-nexus` under the transition, which runs until 2026-12-21.
Each such change keeps `activation=APPROVED` and is recorded as
production-tier, so the transition does not look like conformance.
The production path itself (`CONSTRUCT @ manifest repository`, reconciled by
ArgoCD) belongs to railiance-platform. ops-mason only refuses; it does not
build that path.
## Boundaries
- **The executor's limits are not widened.** One expected namespace per plan,
`Pod` and `Secret` refused, no `data` or `stringData`. The decision relies on
them; widening any of them is a new founder decision.
- The readiness check is a quality gate, not an authorization decision. It does
not consult access-engine and must not be described as a PDP.
- No live change is part of this workplan. Tests use the injected runner.
## Design choice: how ops-mason obtains the readiness state
The authoritative states live in another repository,
`reef-railiance/bindings/rapps.yaml`. Three options were weighed:
1. **Read the sibling checkout at apply time.** Simple, but unpinned: the
evidence would name no revision, and the result depends on whatever the
workstation happens to have checked out.
2. **Vendor a copy of `rapps.yaml` into ops-mason.** Pinned, but it goes stale
silently. A rApp promoted to `production-approved` upstream would still read
as `verified` here and pass the gate: the failure mode is fail-open.
3. **Pin the reference in the bundle and verify it against the source at
preflight (chosen).** The bundle names the target and pins the readiness
source the same way it already pins manifests:
```yaml
readiness:
target: {kind: rapp, rapp_id: rapp-user-engine} # or {kind: platform}
source:
repo: reef-railiance
path: bindings/rapps.yaml
revision: <commit>
sha256: <digest of the file at that commit>
```
Preflight, through the existing runner:
- `git -C <reef-railiance> show <revision>:bindings/rapps.yaml` must hash to
`sha256`;
- `git -C <reef-railiance> diff --quiet <revision> HEAD -- bindings/rapps.yaml`
must show the file unchanged since the pin, so a later promotion cannot be
missed. If the file changed, the bundle is re-pinned through review;
- the `readiness_state` for the target is read from the pinned content.
**Fail closed.** A missing `readiness` block, `kind: platform`, a target not
listed in `rapps.yaml`, a source that cannot be found or verified, or an
unknown state value all resolve to the production tier. The decision says
platform objects without a readiness state default to production; an
unverifiable state is treated the same way.
The checkout location comes from a `--readiness-repo` CLI option, defaulting
to the sibling path `../reef-railiance`. It is a location, not a trust
anchor: the pinned revision and digest are.
The evidence record gains a `readiness` section: target, source repo, revision
and digest, resolved state, tier, `activation`, and a `transition` flag when the
policy-nexus rule was used.
## Open question for the founder: the whitehat plane
```task
id: MASON-WP-0006-T01
status: done
priority: high
state_hub_task_id: "7b6fe7b4-08f7-5ad0-899d-a4862b5ddb00"
```
`bundles/whitehat-foundational-plane.yaml` is the only bundle today, and its plan
is `built`. It targets the `whitehat` namespace, which is not a rApp and carries
no ADR-0006 `readiness_state`. Under the decision it therefore defaults to the
production tier, and once T03 lands, a further direct apply of it would be
refused.
This workplan was told the check must not change behaviour for today's
non-production plans. Whether the whitehat plane is one of them is not a
question ops-mason can answer for itself. The founder chooses one of:
- (a) it is a platform object and production-tier, as the default says; future
changes to it go through the manifest repository or `BREAK_GLASS`;
- (b) it gains a readiness state from its owner (whitehat-security), in which
case `readiness.target` names that record; or
- (c) an explicit, dated exception.
T03 must not merge before this is answered.
**Resolved 2026-09-21; implemented 2026-09-28.** The founder chose explicit
non-production placement for namespace whitehat. Decision and inbox receipt
are recorded in the September 28 review. This unblocks T03.
## Extend the bundle schema with the readiness block
```task
id: MASON-WP-0006-T02
status: done
priority: high
state_hub_task_id: "89d07bd5-5a92-5a06-9ecb-441799c14dd7"
```
Add the optional `readiness` block to `PlaneBundle.load` as described above. It
is included in the bundle digest, as the bundle file already is. Add a
`--activation {APPROVED,BREAK_GLASS}` option to `plane apply`, defaulting to
`APPROVED`, and a `--readiness-repo` option. `BREAK_GLASS` requires a
`--break-glass-reason` string. No existing refusal is relaxed.
**Done 2026-09-28.** Schema/CLI implemented; the mapping also requires an
explicit namespace matching the bundle. Whitehat now pins the source used to
check whether a binding supersedes its explicit placement.
## Enforce the tier in preflight and apply
```task
id: MASON-WP-0006-T03
status: done
priority: high
state_hub_task_id: "163a807b-29aa-5eb7-a9a8-ef1ac70c89d3"
```
Add a pure function, `resolve_tier(state, target, activation, today)`, returning
the tier and whether a direct apply is allowed, and call it from `apply` after
the plan-approval and digest checks and before any `kubectl` write. `preflight`
reports the resolved tier without refusing, so a production-tier plane can
still be validated.
Tests, all against the injected runner:
- a `verified` rApp target applies under `APPROVED` (non-production tier);
- a `production-approved` target is refused under `APPROVED`, with no `kubectl
apply` call made;
- the same target is allowed under `BREAK_GLASS`, and the evidence records it;
- `rapp-policy-nexus` is allowed under `APPROVED` on 2026-12-20, is recorded
with `transition: true` and tier production, and is refused on 2026-12-22;
- no `readiness` block, `kind: platform`, an unlisted rApp, a digest mismatch,
and a file changed since the pin each resolve to production and are refused;
- the existing forbidden-kind, `data`/`stringData` and namespace tests still pass
unchanged.
**Done 2026-09-28.** Source digest, ancestry, local freshness and complete
binding history are checked. Historical production approval remains production;
deprecation retains the last known tier. Unknowns fail closed. The policy-nexus
transition stops on December 21 itself. Preflight reports; apply refuses before
Kubernetes commands. Covered by injected-runner regression tests.
## Record BREAK_GLASS and its reconcile-back obligation
```task
id: MASON-WP-0006-T04
status: done
priority: medium
state_hub_task_id: "66349531-09ee-55b9-be47-537842c80f3b"
```
A `BREAK_GLASS` apply writes its reason, actor and time into the evidence
record and prints the follow-up that is owed: the same change, committed to the
manifest repository that ArgoCD reconciles. ops-mason does not open that change
itself.
**Done 2026-09-28.** Apply evidence and printed JSON include the emergency
reason, local account, UTC time and GitOps reconciliation obligation. Empty
reasons are refused without invoking the runner.
## Update the docs and the declaration
```task
id: MASON-WP-0006-T05
status: done
priority: medium
state_hub_task_id: "6d64e7f5-9cff-5bf9-9851-97d318d1df0a"
```
Update `docs/kubernetes-plane.md` with the tier table and the readiness block,
and change the `enforcement` line of the `kubernetes-plane-apply` entry in
`INTENT.md` from "not yet in code" to point at the check.
**Done 2026-09-28.** Documented tiers, pins, mapping, history, checkout
freshness limits and emergency procedure; removed the withdrawn
rail-kubernetes ownership/layer assertion from INTENT.md.
## Review on 2026-12-21
```task
id: MASON-WP-0006-T06
status: wait
priority: medium
state_hub_task_id: "49dd5f9a-307b-5baf-809e-bd3893c73341"
```
On 2026-12-21 the policy-nexus transition ends and the plan-approval exception
comes up for review. Confirm with railiance-platform that policy-nexus is
onboarded to ArgoCD. The date check in T03 ends the transition in code on that
date either way.
## Loose-end review — 2026-09-28
T01–T05 are done: the founder resolved the whitehat placement and the guarded readiness implementation, tests, emergency evidence and docs are complete. T06 remains wait until the 2026-12-21 platform/exception review; this workplan is blocked on that dated review.
Evidence and precise resumption conditions: [review](../docs/evidence/2026-09-28-loose-end-review.md). Existing tasks retain all remaining obligations; no new task or workplan was opened.