--- id: MASON-WP-0006 type: workplan title: "Check the target's readiness tier before a direct Kubernetes apply" domain: infotech repo: ops-mason status: proposed flavor: implementation owner: claude topic_slug: ops-mason created: "2026-09-21" updated: "2026-09-21" 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: sha256: ``` Preflight, through the existing runner: - `git -C show :bindings/rapps.yaml` must hash to `sha256`; - `git -C diff --quiet 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: wait 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. ## Extend the bundle schema with the readiness block ```task id: MASON-WP-0006-T02 status: todo 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. ## Enforce the tier in preflight and apply ```task id: MASON-WP-0006-T03 status: todo 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. ## Record BREAK_GLASS and its reconcile-back obligation ```task id: MASON-WP-0006-T04 status: todo 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. ## Update the docs and the declaration ```task id: MASON-WP-0006-T05 status: todo 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. ## 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.