railiance-platform/workplans/RPF-WP-0020-ccr-schema-drift.md
codex ea2e9ec97d
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
fix(workplans): adopt ADR-007 derived identifiers for unregistered records
These workplans exist only in the retired local hub. Their random pre-ADR-007
identifiers are refused by C-06 as stale references, so they cannot be
registered. Deriving from the canonical record id takes no identity from
anything: central does not hold them and the old ids die with the cache.

Records central already holds were deliberately left untouched.

Refs CUST-WP-0068-T06

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-25 20:20:45 +02:00

162 lines
7 KiB
Markdown

---
id: RPF-WP-0020
type: workplan
title: "Close CCR schema drift: one active lane unmigrated, one draft the suite cannot express"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
created: "2026-08-18"
updated: "2026-08-21"
related:
- RPF-WP-0014
origin: residual
origin_ref: RPF-WP-0019
state_hub_workstream_id: "99bdb617-b577-5de3-a9d8-4990ac676e28"
---
# RPF-WP-0020 — CCR schema drift
## Goal
Make `tests/test_credential_change.py::test_all_repo_ccrs_validate` pass for
the right reason: because every credential-change request in the repo is
either valid or explicitly declared in-flight — not because the assertion was
loosened until it stopped complaining.
## Why this is its own workplan
The test has been failing on `main`. It was found while closing
`RPF-WP-0019` and confirmed to predate that work (it fails at `HEAD` with the
apps-pg changes stashed), so it is neither a regression from that workplan nor
something to fix inside it.
A single red test is hiding **two unrelated problems with opposite remedies**.
That is the reason to split them out and name them, rather than fix whichever
one makes the suite green first.
## The two problems
**P1 — an active credential lane uses an unrepresented auth mode.**
`credential-change-requests/CCR-2026-0010-email-connect-transactional.yaml`
carries `status: active` and `readiness: ready`. It has always declared the
live transitional ESO token Secret, bootstrap script, policy and TTL, but the
validator understood only OIDC and Kubernetes auth and consequently reported
the role/bound-claim fields for those modes as missing.
This is the one that matters. A live lane whose declaration does not describe
how the workload authenticates is a governance gap, not a lint failure — the
document that is supposed to be the authority on the lane cannot answer the
first question anyone would ask of it. The live lane is working, which is
exactly what made this representation gap easy to leave.
**P2 — a genuine in-flight request the suite could not express.**
`CCR-2026-0011-scaleway-object-storage-bootstrap.yaml` carries
`status: apply_pending` and an out-of-enum placeholder readiness string.
It is a founder-bootstrap credential still holding placeholder values, and
`ops-warden` already tracks it as a draft lane. Its errors include a
`readiness` value outside the permitted enum, which is the file honestly
reporting that it is not finished.
This one is not a defect. The defect is that a suite asserting *all* CCRs
validate has no way to say "this one is deliberately incomplete", so an honest
draft and a real gap produce identical output.
## Boundaries
- **No secret values are read, written or rotated.** This is declaration
hygiene. Anything requiring a mint, a rotation or an apply belongs to the
credential-change approval flow in `docs/credential-change-approval.md`.
- P2 is not closed by filling in placeholders. The Scaleway bootstrap key is
a live commercial credential with an owner outside this repo.
- The validator's rules are not relaxed to accommodate either file.
## Tasks
```task
id: RPF-WP-0020-T01
status: done
priority: high
state_hub_task_id: "4763622c-78aa-556a-ba8e-9b556a2e78fc"
```
**Migrate CCR-2026-0010 to the current schema.** Determine the actual
authentication path for the `email-connect` transactional lane — Kubernetes
auth mount, role, bound claims, and the policy name that must match
`openbao.policy_name` — from the live OpenBao configuration and the existing
policy file, not by inventing plausible values. If the live configuration and
the declaration disagree, the live configuration is the fact and the
disagreement is the finding.
Completed 2026-08-21. Live read-only Kubernetes metadata showed
`ClusterSecretStore/openbao-email-connect` using
`external-secrets/openbao-email-connect-eso-token`, limited to namespace
`email-connect`, and reporting Valid; its ExternalSecret reported
SecretSynced. The schema now represents this transitional token mode directly,
requires the ESO policy artifact, Secret reference, bootstrap script, bounded
TTL and explicit Kubernetes-auth follow-up, and keeps delegated token creation
out of scope. No Secret value was read.
```task
id: RPF-WP-0020-T02
status: done
priority: medium
state_hub_task_id: "a897b2f5-b7ae-5dbf-9f51-54b2db5ca6fc"
```
**Give the suite a way to express a draft.** Add an explicit in-flight state
so `CCR-2026-0011` is skipped *by declaration* rather than by exception list —
a `status` the validator recognises as not-yet-complete, with the test
asserting that such files are still well-formed in every other respect. An
allowlist of filenames would work today and rot on the next draft.
Completed 2026-08-21. Added the non-applyable `in_flight` status. It requires a
named owner, blocking reason and exact `missing_fields`; only
`openbao.policy_file` and `openbao.auth` may be declared incomplete, while all
other CCR structure remains validated. CCR-2026-0011 now uses this state and a
valid non-resolvable front-door readiness without inventing policy/auth
metadata or filling credential placeholders.
```task
id: RPF-WP-0020-T03
status: done
priority: medium
state_hub_task_id: "44959415-a917-5dfb-a5ab-80ab166b9096"
```
**Report the drift rather than only fixing it.** If the `openbao.auth`
requirement was added without migrating existing active CCRs, other repos
carrying CCRs may have the same gap and no failing test to reveal it. Confirm
whether the requirement originated here or upstream, and notify accordingly.
Completed 2026-08-21. Git history traces the validator requirement to local
commit `815b124`; a filesystem-wide declaration search found CCR files and the
validator/schema implementation only in `railiance-platform`. There is no
upstream CCR implementation to migrate or notify. The migration obligation is
now documented locally.
```task
id: RPF-WP-0020-T04
status: done
priority: low
state_hub_task_id: "d85eeb8a-3147-59e1-bc2a-e84244dde93d"
```
**Make the suite green and keep it that way.** With T01 and T02 done, the full
suite passes. Record in `docs/credential-change-approval.md` that a new
required field obliges a migration pass over existing active CCRs — the
omission that produced P1.
Completed 2026-08-21. Regression coverage proves the live token-auth shape,
explicit in-flight omissions, rejection of undeclared omissions, and safe
status/summary rendering. The approval guide now requires a migration pass over
all existing CCRs whenever required fields are added or strengthened. The full
repository test suite passes.
## Risks
**T01 invents values to make the test pass.** The likeliest failure and the
worst one: a declaration that is well-formed and wrong is more dangerous than
one that is visibly incomplete, because it stops anyone looking. Mitigation is
that T01 reads live configuration and treats disagreement as a finding.
**T02 becomes a way to silence future failures.** Mitigation is that the
in-flight state still requires the file to be well-formed in every other
respect, so it suppresses the completeness assertion and nothing else.