Add rapp declaration schema with composition block (WP-0021 T02, T03)

schemas/rapp.schema.json defines one normative shape for the rollout, smoke and
rollback contracts in place of the three mutually unreadable variants found
across the live rapps, promotes contract_version, readiness_state,
data_classification and criticality to required, and forbids the rapp- prefix
on workload_identity.name.

composition replaces the flat members list per amendment f88f938d: purpose,
member_repos with deployables, and pinned upstream_components. Repos are
many:many with rapps; deployables are 1:1, which is what makes the T06 coverage
check well-formed.

ownership_repo left permissive pending an architecture-owner call; the tighter
alternative is written up in schemas/README.md.

Validated against all three live declarations: openbao 10 errors, postgres 10,
qonto 4 — precisely the reported drift and nothing else.

Also found: three further rapp-* repos (secrets-engine, tenant-engine,
user-engine) carry no declarations at all, which the routed survey missed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
codex 2026-08-11 22:56:22 +02:00
parent 8fc92bd7d2
commit 28cbba1025
3 changed files with 525 additions and 2 deletions

View file

@ -162,7 +162,7 @@ acquire a cluster dependency.
```task
id: RAILIANCE-WP-0021-T02
status: todo
status: done
priority: high
state_hub_task_id: "87ae4551-1fac-4613-b258-28c8b22bbb82"
```
@ -190,11 +190,62 @@ Pick one normative shape for `rollout_contract`, `smoke_contract` and
the schema which existing declaration the chosen shape most resembles, so
migration cost is visible.
**Done 2026-08-11.** `schemas/rapp.schema.json` (draft 2020-12) and
`schemas/README.md` written from the three live declarations rather than from
the survey summary, which turned out to understate the drift — see the extra
finding below.
Normative choices, with who pays:
- `default_mode` kept; all three declarations already carried it. No cost.
- `supported_commands``commands` in all three contracts. Mechanical.
- `smoke_contract.required` is required, `commands` optional — taken from
`rapp-qonto`. This is the one migration with real cost: the platform rapps
declare only commands and their owners must now name the outcomes those
commands establish. A command list records what was run; a required list
records what must be true, and only the second is checkable by anyone other
than the person who ran it.
- `rollback_contract.order` required, also from `rapp-qonto`, recoverable from
the command sequences already documented. `direct_kubernetes_fallback`
generalizes to `fallback`.
- `workload_identity.name` is the workload, never the repo; the schema forbids
the `rapp-` prefix outright.
- `contract_version`, `readiness_state`, `data_classification`, `criticality`
all promoted to required.
- `consumer_contract` schema-defined but optional.
`ownership_repo` is left permissive — exactly one repo, not the rapp repo
itself, layer and application repos both legal — so the schema does not
silently invalidate two of three live declarations on a question the survey
cannot settle. The tighter alternative and its consequence are written up in
`schemas/README.md` for the architecture owner. Treat the permissive rule as
provisional.
Validated against all three live declarations with `jsonschema` 4.10.3. The
schema is well-formed and reproduces precisely the drift the survey reported,
which is the intended result — it fails on real drift and not on anything else:
| Declaration | Errors | Character |
|---|---|---|
| `rapp-openbao` | 10 | four missing metadata fields, no composition, three contracts on the old shape |
| `rapp-postgres` | 10 | identical set — the two platform rapps drifted together |
| `rapp-qonto` | 4 | composition, `direct_kubernetes_fallback`, missing `package_type`, and `workload_identity.name` = `rapp-qonto` |
**Extra finding, not in the routed survey.** There are six `rapp-*` repos, not
three: `rapp-secrets-engine`, `rapp-tenant-engine` and `rapp-user-engine` exist
and carry no `declarations/` directory at all. The survey counted only declared
rapps, so the real gap is wider than the roughly 17-workloads-versus-3-rapps
figure suggests — three further repos claim the family prefix while declaring
nothing. Raised to `railiance-platform`; whether these are stubs, abandoned, or
undeclared live workloads should be settled before T06 draws its coverage
baseline, since undeclared repos are invisible to a coverage check that reads
declarations.
## T03 - Define the composition block and its cardinality rules
```task
id: RAILIANCE-WP-0021-T03
status: todo
status: done
priority: high
state_hub_task_id: "8b130ff8-0601-40bb-8539-d6f5847c2f41"
```
@ -226,6 +277,28 @@ and rolls back together, not one per deployable. Grouping is legitimate only
where members share rollout and rollback fate; make that testable rather than
advisory where possible.
**Done 2026-08-11.** `composition` is required on every rapp, in
`schemas/rapp.schema.json` under `$defs/composition`. A single-repo rapp
declares exactly one member, so there is no second shape for the ungrouped
case — the composition block is the only way to state membership.
`purpose` carries a `minLength` and is documented as the bounded-context
statement: if it cannot be written without "and also", the grouping is probably
wrong. That is the closest this schema gets to testing shared rollout fate;
the substantive check is structural, in that `deployables` is required on every
member, which is what T06 consumes.
Both cardinalities are encoded where they bite. `member_repos[].repo` is
unconstrained across rapps, so a repo may appear in several — many-to-many.
`member_repos[].deployables` is documented as one-to-one and enforced by the
validator's coverage check rather than by the schema, since no single-file
schema can see across declarations. `upstream_components` requires an exact
`version` pin, with the schema stating that ranges and floating tags are not
pins.
`workload_identity.package_type` gains a `grouped-composition` value for rapps
whose members carry their own package types.
## T04 - Schematize rail.yaml and reef.yaml, and derive bound_rapps
```task