<p><code>RMASTER-WP-0017</code> through <code>RMASTER-WP-0019</code> established the four-axis model and materialized the first family repos. The model held up. Its enforcement did not.</p>
<p>A 2026-08-11 survey by <code>railiance-platform</code> found that the three live <code>rapp.yaml</code> files were mutually unreadable: rollout, smoke, and rollback contracts used different shapes; metadata that both rails carry consistently appeared in only one rapp; <code>reef-railiance</code><code>bound_rapps</code> listed <code>rapp-qonto</code> only, while <code>rapp-openbao</code> and <code>rapp-postgres</code> were already live on the same reef. <code>docs/repo-family-bootstrap-contract.md</code> named fields in prose and could not catch any of this.</p>
<p>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 <code>org:repo</code> is one-to-many. A repo may legitimately contribute to more than one rapp, so <code>rapp:repo</code> 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.</p>
<p>Canon OAS P1 requires independent perspectives to be separate orthogonal dimensions. Decision <code>d07ee5f9</code> already recorded this from the <code>railiance-platform</code> side; this ADR is the framework record.</p>
</section>
<sectionid="decision"><h2>Decision</h2>
<ol><li><strong>Bounded rapp context is its own dimension.</strong> 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.</li></ol>
<ol><li><strong>Two cardinalities stay distinct.</strong> Repos to rapps is many-to-many: a repo may appear in the <code>composition.member_repos</code> 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 — <em>does every live deployable belong to exactly one rapp?</em> — is well-formed only if these stay distinct.</li></ol>
<ol><li><strong>Granularity is grouped-by-bounded-context.</strong> 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 <code>composition</code> block, not a second shape.</li></ol>
<ol><li><strong>The schema is normative.</strong><code>schemas/rapp.schema.json</code>, <code>schemas/rail.schema.json</code>, and <code>schemas/reef.schema.json</code> define the shapes. Framework prose cites those files. It does not restate their fields. Reef <code>bound_rapps</code> is a derived projection of <code>rapp.bound_reefs</code>, not a hand-maintained registry.</li></ol>
<ol><li><strong>Coverage includes every managed running deployable.</strong> 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.</li></ol>
<p>A running deployable that predates rapp extraction is migration debt. Until an authoritative declaration claims it, workload-based controls report it as <code>unknown</code>; 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 <code>not-applicable</code>. <code>unknown</code> and <code>not-applicable</code> are different outcomes and omission must not collapse them.</p>
<p>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.</p>
<p>The detailed shapes, including the single normative form of the rollout, smoke, and rollback contracts, live in the schema files and <code>schemas/README.md</code>.</p>
<ul><li>Drift across family declarations fails in <code>tools/validate-family-declarations.py</code> instead of accumulating in prose.</li><li><code>railiance-platform</code><code>RAILIANCE-WP-0015-T02</code> can converge <code>rapp-openbao</code> and <code>rapp-postgres</code> onto one shape. Migration belongs to the owning repos; this ADR does not move any declaration.</li><li><code>reef-railiance</code> must stop treating <code>bound_rapps: [rapp-qonto]</code> as source of truth. The list is already stale.</li><li>Three further <code>rapp-*</code> repos (<code>rapp-secrets-engine</code>, <code>rapp-tenant-engine</code>, <code>rapp-user-engine</code>) 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.</li><li>Operational and tooling deployables are not exempt from family coverage. Existing pre-rapp runtimes may continue during migration, but their workload identity remains visibly <code>unknown</code> to controls until declared.</li><li>Runtime inventory is still required to prove universal coverage. Repository discovery alone cannot establish that every running unit has exactly one authoritative rapp.</li><li>Calling the validator from <code>fix-consistency</code> still waits on <code>the-custodian</code> admitting the family prefixes into the classification standard. That sequencing is not this repo's.</li></ul>