From 9f1e58a797e546556bb0f97df7ac1a3c2616d2b0 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 16 Aug 2026 02:52:05 +0200 Subject: [PATCH] Plan guardrail production rollout (TEN-WP-0007) Co-Authored-By: Claude Opus 5 --- ...EN-WP-0007-guardrail-production-rollout.md | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 workplans/TEN-WP-0007-guardrail-production-rollout.md diff --git a/workplans/TEN-WP-0007-guardrail-production-rollout.md b/workplans/TEN-WP-0007-guardrail-production-rollout.md new file mode 100644 index 0000000..4a42cfd --- /dev/null +++ b/workplans/TEN-WP-0007-guardrail-production-rollout.md @@ -0,0 +1,145 @@ +--- +id: TEN-WP-0007 +type: workplan +title: "Production rollout of the guardrail surface" +domain: infotech +repo: tenant-engine +status: ready +owner: claude +topic_slug: tenant-guardrails +created: "2026-08-16" +updated: "2026-08-16" +depends_on: + - TEN-WP-0006 +unblocks: [] +--- + +# TEN-WP-0007 - Guardrail production rollout + +TEN-WP-0006 deliberately left production rollout out of scope. The operator has +now authorized proceeding, so this workplan carries the deploy that TEN-WP-0006 +would not. + +## Sequencing (agreed with flex-auth, FLEX-WP-0014) + +1. **flex-auth first** — CI image from the nine-action commit, pin + `flex-auth-tenant-engine`. +2. **tenant-engine second** — pin the guardrail image. +3. **Joint live verification** against a disposable tenant. + +flex-auth goes first on purpose. If we pin first, the guardrail surface is live +but every call denies with `write_denied` — a state indistinguishable from a +broken deployment to anyone reading logs. Their step landing first makes each +step independently observable. + +## What is already true + +- Source revision `e5c1061` on `main`, TEN-WP-0006 finished, 231 tests passing. +- flex-auth's nine-action package is verified **in source** (17/17 Rego tests, + 23/23 fixtures, end-to-end against our unmodified `FlexAuthWriteAuthorizer`). +- Production flex-auth serves the **seven**-action image + `@sha256:9320df39…` — the four-action regression is resolved, but the + guardrail actions are not in that image. +- Production tenant-engine still runs the TEN-WP-0005 lifecycle image. + +## T01 - Confirm the CI image and its digest + +```task +id: TEN-WP-0007-T01 +status: todo +priority: high +``` + +`src/**` is a trigger path in `.forgejo/workflows/image.yaml` and the guardrail +commits are pushed, so CI should have built an image for `main-e5c1061`. +Confirm it exists and record the immutable digest. + +Blocked from this workstation: no registry credentials. Needs a shell with +forge access, or the digest relayed by whoever has one. + +Done when the digest is recorded here and the image is confirmed to contain +the guardrail routes (`GET /openapi.json` on the built image lists +`/tenants/{tenant_id}/guardrails`). + +## T02 - Wait for the flex-auth nine-action pin + +```task +id: TEN-WP-0007-T02 +status: wait +priority: high +``` + +Waiting on FLEX-WP-0014 step 1. Two things to collect when it lands: + +- the new flex-auth image digest; +- confirmation that the **seven pre-existing actions still allow** after the + pin. The last regression surfaced as a silent rollback, and lifecycle + authority is what user-engine is already shipping against — a second silent + rollback would break a live consumer, not just this workplan. + +Done when guardrail checks return allow instead of `deny unknown_action` +against the live decision surface. + +## T03 - Apply the migration and pin the tenant-engine image + +```task +id: TEN-WP-0007-T03 +status: wait +priority: high +``` + +The guardrail schema change is two new tables created by the same +`CREATE TABLE IF NOT EXISTS` script fresh and existing databases both take, so +the migration is forward-only and idempotent and needs no separate step. What +it does need is evidence that the existing SQLite database on the PVC survives +it with tenants, grants, and plans untouched. + +Rollback is `kubectl -n tenant-engine rollout undo`; last-known-good is the +TEN-WP-0005 lifecycle digest +`@sha256:08be0b1dcdc65575592b7be665c28e09a82316ea3d4c9b551ccb753f25360612`. +Rolling back is safe in one direction only: the new tables are additive and the +old image ignores them, but any override written while the new image is live +stops being enforced on rollback. Nothing reads guardrails yet, so today that +is inert — record it before that stops being true. + +Blocked from this workstation: `kubectl` returns `Unauthorized`. This is an +operator step. + +Done when the deployment is pinned to the T01 digest and the pre-existing +tenants are intact. + +## T04 - Live verification and consumer confirmation + +```task +id: TEN-WP-0007-T04 +status: wait +priority: high +``` + +Against a disposable tenant, in the TEN-WP-0005-T05 evidence style: + +- guardrail read returns the grouping default with provenance `grouping`; +- a `trial` tenant reads a **zero** spend ceiling — the ADR-0013 mandate, and + the one thing here that is enforceable with no meter; +- override set → read reflects it with provenance `override`; +- override cleared → falls back to the grouping default; +- `actor=ops` denied on both read and write; +- `flex-auth` allowed on read, denied on write (`action_not_granted`); +- retired tenant still reads, clamps to floor, loosening refused; +- idempotent replay returns the original result unchanged. + +Then confirm to flex-auth from our side — they verified the decision surface, +not our endpoints in production, and TEN-WP-0005 established that those are +different claims. + +Done when live evidence is recorded here and the confirmation is sent. + +## Out of scope + +- **Metering.** Still unowned by any repo. The guardrail read stays + ceiling-only until canon names a meter. +- **The plan-derived layer.** `adaptive-pricing` exposes no plan-derived + ceilings; the precedence layer exists and is tested but has no feed. +- **Grouping ceiling values.** The non-`trial` defaults are conservative + opening numbers pending product sign-off. Rolling out does not ratify them — + they are config, changeable without a deploy.