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>
100 lines
4.9 KiB
Markdown
100 lines
4.9 KiB
Markdown
# Railiance family declaration schemas
|
|
|
|
Normative schemas for the repo-family declaration files. `docs/repo-family-bootstrap-contract.md`
|
|
names the minimum fields; these schemas define their shapes and are the
|
|
authority where the two disagree.
|
|
|
|
| Family | Declaration path | Schema |
|
|
|---|---|---|
|
|
| `rapp-*` | `declarations/rapp.yaml` | `rapp.schema.json` |
|
|
| `rail-*` | `declarations/rail.yaml` | `rail.schema.json` (RAILIANCE-WP-0021-T04) |
|
|
| `reef-*` | `declarations/reef.yaml` | `reef.schema.json` (RAILIANCE-WP-0021-T04) |
|
|
|
|
Written under `RAILIANCE-WP-0021` from the drift survey routed by
|
|
`railiance-platform` under its `RAILIANCE-WP-0015` (State Hub messages
|
|
`04c776c4`, `f88f938d`).
|
|
|
|
## The two cardinalities
|
|
|
|
These are the load-bearing rules. Everything the validator can usefully check
|
|
follows from keeping them distinct.
|
|
|
|
- **Repos to rapps is many-to-many.** A repo may contribute to more than one
|
|
rapp — a shared library, or a service that is a member of one bundle and a
|
|
dependency of another.
|
|
- **Deployables to rapps is one-to-one.** Every running deployable has exactly
|
|
one rapp that owns its rollout.
|
|
|
|
The second is what makes the coverage question well-formed: *does every live
|
|
deployable belong to exactly one rapp?* That single check catches stale reef
|
|
bindings, unpackaged live workloads, and the workload-count gap at once. It is
|
|
what turns this schema from a one-shot lint into a standing control loop.
|
|
|
|
Rapp bounded context is therefore its own dimension, derived neither from
|
|
Forgejo organizations nor from State Hub domains. A repo lives in exactly one
|
|
Forgejo org, so `org:repo` is one-to-many, and a many-to-many grouping cannot
|
|
be derived from a one-to-many one. Domains fail in both directions. See
|
|
`docs/adr/ADR-0007` (T07) and decision `d07ee5f9`.
|
|
|
|
## Normative choices, and what they cost
|
|
|
|
The three live rapp declarations expressed their rollout, smoke and rollback
|
|
contracts three mutually unreadable ways. The schema picks one shape. Where it
|
|
picks, it says so and it says who pays.
|
|
|
|
**`default_mode` is kept** — all three declarations already carried it. No cost.
|
|
|
|
**`supported_commands` becomes `commands`** in all three contracts. Mechanical
|
|
rename for `rapp-openbao` and `rapp-postgres`.
|
|
|
|
**`smoke_contract.required` is required, and `commands` is optional.** Taken
|
|
from `rapp-qonto`. This is the one migration with real cost: the platform rapps
|
|
declare only commands, so their owners must name the outcomes those commands
|
|
establish. That cost is the point. A command list records what was run; a
|
|
required list records what must be true. Only the second can be checked by
|
|
anything other than the person who ran it.
|
|
|
|
**`rollback_contract.order` is required.** Taken from `rapp-qonto`. Recoverable
|
|
from the command sequences the platform rapps already document, so the cost is
|
|
low. `direct_kubernetes_fallback` generalizes to `fallback`.
|
|
|
|
**`workload_identity.name` is the workload, never the repo**, and the schema
|
|
forbids the `rapp-` prefix on it outright. `rapp-qonto` currently sets it to
|
|
`rapp-qonto`; the correct value is `qonto`.
|
|
|
|
**`contract_version`, `readiness_state`, `data_classification` and
|
|
`criticality` are required.** All four exist only in `rapp-qonto` today. The
|
|
first two are promoted because the rail family has carried them consistently in
|
|
both rails and has not drifted — that contrast is the whole argument for
|
|
schema. The second two are promoted because a package whose classification and
|
|
criticality are unstated cannot be admitted to a reef on evidence.
|
|
|
|
**`consumer_contract` is schema-defined but optional.** Only `rapp-postgres`
|
|
provisions isolated units to other services. Requiring it everywhere would be
|
|
false precision.
|
|
|
|
## Open question: what may `ownership_repo` name?
|
|
|
|
Currently `rapp-openbao` and `rapp-postgres` name `railiance-platform`, a layer
|
|
repo, and `rapp-qonto` names `qonto-assistant`, an application repo.
|
|
|
|
The schema admits **both**, requiring only that there be exactly one and that
|
|
it not be the rapp repo itself. This is deliberately the permissive reading,
|
|
taken so the schema does not silently invalidate two of three live declarations
|
|
on a question the survey cannot settle.
|
|
|
|
The tighter alternative is that `ownership_repo` must name the repo whose team
|
|
is accountable for the workload in production, which would likely disqualify a
|
|
layer repo that owns dozens of unrelated workloads. That is a modelling call
|
|
about where accountability sits, not a lint, and it belongs to the architecture
|
|
owner. Until it is made, treat the permissive rule as provisional.
|
|
|
|
## Validating
|
|
|
|
The validator lands under `tools/` in `RAILIANCE-WP-0021-T05`. Until then these
|
|
schemas are normative but unenforced — which is precisely the condition that
|
|
produced the drift, so T05 should not lag T02/T03 by long.
|
|
|
|
Two constraints are validator-only, because JSON Schema cannot express them:
|
|
`primary_rail` must appear in `supported_rails`, and reef `bound_rapps` must be
|
|
derivable from the rapp declarations rather than hand-listed.
|