railiance-master/schemas
codex 28cbba1025 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>
2026-08-11 22:56:22 +02:00
..
rapp.schema.json Add rapp declaration schema with composition block (WP-0021 T02, T03) 2026-08-11 22:56:22 +02:00
README.md Add rapp declaration schema with composition block (WP-0021 T02, T03) 2026-08-11 22:56:22 +02:00

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.