2026-08-18 17:00:07 +02:00
|
|
|
---
|
|
|
|
|
id: RMASTER-ADR-0007
|
|
|
|
|
title: "Rapp Declaration Contract"
|
|
|
|
|
status: accepted
|
2026-08-23 01:38:29 +02:00
|
|
|
revision: "accepted-2"
|
2026-08-18 17:00:07 +02:00
|
|
|
owner: railiance-master
|
2026-08-23 01:38:29 +02:00
|
|
|
last_reviewed: "2026-08-23"
|
2026-08-18 17:00:07 +02:00
|
|
|
review_interval: 6m
|
|
|
|
|
---
|
|
|
|
|
|
2026-08-13 15:29:07 +02:00
|
|
|
# ADR-0007: Rapp Declaration Contract
|
|
|
|
|
|
|
|
|
|
Date: 2026-08-13
|
|
|
|
|
Status: Accepted
|
|
|
|
|
|
|
|
|
|
## Context
|
|
|
|
|
|
2026-08-14 14:29:18 +02:00
|
|
|
`RMASTER-WP-0017` through `RMASTER-WP-0019` established the four-axis
|
2026-08-13 15:29:07 +02:00
|
|
|
model and materialized the first family repos. The model held up. Its
|
|
|
|
|
enforcement did not.
|
|
|
|
|
|
|
|
|
|
A 2026-08-11 survey by `railiance-platform` found that the three live
|
|
|
|
|
`rapp.yaml` files were mutually unreadable: rollout, smoke, and rollback
|
|
|
|
|
contracts used different shapes; metadata that both rails carry consistently
|
|
|
|
|
appeared in only one rapp; `reef-railiance` `bound_rapps` listed `rapp-qonto`
|
|
|
|
|
only, while `rapp-openbao` and `rapp-postgres` were already live on the same
|
|
|
|
|
reef. `docs/repo-family-bootstrap-contract.md` named fields in prose and
|
|
|
|
|
could not catch any of this.
|
|
|
|
|
|
|
|
|
|
The same survey treated rapp grouping as something that might be derived from
|
|
|
|
|
Forgejo organizations or from State Hub domains. Neither works. A repo lives
|
|
|
|
|
in exactly one Forgejo org, so `org:repo` is one-to-many. A repo may
|
|
|
|
|
legitimately contribute to more than one rapp, so `rapp:repo` is
|
|
|
|
|
many-to-many. A many-to-many grouping cannot be derived from a one-to-many
|
|
|
|
|
one. Domains fail in both directions. The three dimensions also change at
|
|
|
|
|
different speeds.
|
|
|
|
|
|
|
|
|
|
Canon OAS P1 requires independent perspectives to be separate orthogonal
|
|
|
|
|
dimensions. Decision `d07ee5f9` already recorded this from the
|
|
|
|
|
`railiance-platform` side; this ADR is the framework record.
|
|
|
|
|
|
|
|
|
|
## Decision
|
|
|
|
|
|
|
|
|
|
1. **Bounded rapp context is its own dimension.** It is derived neither from
|
|
|
|
|
Forgejo organizations nor from State Hub domains. Grouping is a first-class
|
|
|
|
|
declaration, not a projection of some other axis.
|
|
|
|
|
|
|
|
|
|
2. **Two cardinalities stay distinct.** Repos to rapps is many-to-many: a
|
|
|
|
|
repo may appear in the `composition.member_repos` of more than one rapp.
|
|
|
|
|
Deployables to rapps is one-to-one: every running deployable has exactly
|
|
|
|
|
one rapp that owns its rollout. The coverage question — *does every live
|
|
|
|
|
deployable belong to exactly one rapp?* — is well-formed only if these
|
|
|
|
|
stay distinct.
|
|
|
|
|
|
|
|
|
|
3. **Granularity is grouped-by-bounded-context.** One rapp per cohesive group
|
|
|
|
|
that deploys, versions, and rolls back together, not one rapp per
|
|
|
|
|
deployable. Grouping is legitimate only where members share rollout and
|
|
|
|
|
rollback fate. A single-repo rapp is the one-member case of the same
|
|
|
|
|
`composition` block, not a second shape.
|
|
|
|
|
|
|
|
|
|
4. **The schema is normative.** `schemas/rapp.schema.json`,
|
|
|
|
|
`schemas/rail.schema.json`, and `schemas/reef.schema.json` define the
|
|
|
|
|
shapes. Framework prose cites those files. It does not restate their
|
|
|
|
|
fields. Reef `bound_rapps` is a derived projection of `rapp.bound_reefs`,
|
|
|
|
|
not a hand-maintained registry.
|
|
|
|
|
|
2026-08-23 01:38:29 +02:00
|
|
|
5. **Coverage includes every managed running deployable.** Application,
|
|
|
|
|
operational, and tooling runtimes participate in the same exactly-one-rapp
|
|
|
|
|
invariant when they are installed, scheduled, or otherwise operated as a
|
|
|
|
|
managed deployable. This includes a managed one-shot Job; it does not turn
|
|
|
|
|
a human command or approval act into a workload. Human access, credential
|
|
|
|
|
patterns, broker actions, one-off operational acts, and infrastructure
|
|
|
|
|
resources that are not workloads retain their native actor, lane, activity,
|
|
|
|
|
or resource identity.
|
|
|
|
|
|
|
|
|
|
A running deployable that predates rapp extraction is migration debt. Until
|
|
|
|
|
an authoritative declaration claims it, workload-based controls report it
|
|
|
|
|
as `unknown`; they do not infer a rapp from its repository, namespace, path,
|
|
|
|
|
labels, or apparent owner. A subject explicitly established as not being a
|
|
|
|
|
workload is `not-applicable`. `unknown` and `not-applicable` are different
|
|
|
|
|
outcomes and omission must not collapse them.
|
|
|
|
|
|
|
|
|
|
Railiance Master remains the sole owner of the normative rapp vocabulary and
|
|
|
|
|
schemas. Consumer catalogs may store explicit references and integration
|
|
|
|
|
owners may resolve them, but neither creates a parallel declaration surface
|
|
|
|
|
or copies rapp metadata as another source of truth.
|
|
|
|
|
|
2026-08-13 15:29:07 +02:00
|
|
|
The detailed shapes, including the single normative form of the rollout,
|
|
|
|
|
smoke, and rollback contracts, live in the schema files and
|
|
|
|
|
`schemas/README.md`.
|
|
|
|
|
|
|
|
|
|
## Consequences
|
|
|
|
|
|
|
|
|
|
- Drift across family declarations fails in
|
|
|
|
|
`tools/validate-family-declarations.py` instead of accumulating in prose.
|
|
|
|
|
- `railiance-platform` `RAILIANCE-WP-0015-T02` can converge `rapp-openbao`
|
|
|
|
|
and `rapp-postgres` onto one shape. Migration belongs to the owning repos;
|
|
|
|
|
this ADR does not move any declaration.
|
|
|
|
|
- `reef-railiance` must stop treating `bound_rapps: [rapp-qonto]` as source
|
|
|
|
|
of truth. The list is already stale.
|
|
|
|
|
- Three further `rapp-*` repos (`rapp-secrets-engine`, `rapp-tenant-engine`,
|
|
|
|
|
`rapp-user-engine`) carry the family prefix and no declaration. They are
|
|
|
|
|
visible to the validator as undeclared and must be declared, renamed, or
|
|
|
|
|
retired by their owners.
|
2026-08-23 01:38:29 +02:00
|
|
|
- Operational and tooling deployables are not exempt from family coverage.
|
|
|
|
|
Existing pre-rapp runtimes may continue during migration, but their workload
|
|
|
|
|
identity remains visibly `unknown` to controls until declared.
|
|
|
|
|
- Runtime inventory is still required to prove universal coverage. Repository
|
|
|
|
|
discovery alone cannot establish that every running unit has exactly one
|
|
|
|
|
authoritative rapp.
|
2026-08-13 15:29:07 +02:00
|
|
|
- Calling the validator from `fix-consistency` still waits on
|
|
|
|
|
`the-custodian` admitting the family prefixes into the classification
|
|
|
|
|
standard. That sequencing is not this repo's.
|