Rename RAILIANCE-WP-0017..0021 to RMASTER-WP-* so railiance-master IDs no longer collide with railiance-platform's RAILIANCE-WP series. Hub UUIDs are unchanged.
494 lines
22 KiB
Markdown
494 lines
22 KiB
Markdown
---
|
|
id: RMASTER-WP-0021
|
|
type: workplan
|
|
title: "Rapp Declaration Schema and Family Declaration Validator"
|
|
domain: financials
|
|
repo: railiance-master
|
|
status: finished
|
|
owner: codex
|
|
topic_slug: railiance
|
|
planning_priority: high
|
|
planning_order: 21
|
|
created: "2026-08-11"
|
|
updated: "2026-08-13"
|
|
related_repos:
|
|
- railiance-master
|
|
- railiance-platform
|
|
- rapp-openbao
|
|
- rapp-postgres
|
|
- rapp-qonto
|
|
- rail-kubernetes
|
|
- rail-knative
|
|
- reef-railiance
|
|
- the-custodian
|
|
state_hub_workstream_id: "37667c23-1fa9-430e-8d80-d67d98f90e38"
|
|
---
|
|
|
|
# RMASTER-WP-0021 - Rapp Declaration Schema and Family Declaration Validator
|
|
|
|
## Goal
|
|
|
|
Give the `rapp-*` family a machine-checkable declaration contract, and stand up
|
|
a validator over all three family declaration files (`rail.yaml`, `rapp.yaml`,
|
|
`reef.yaml`) so that architectural drift fails loudly instead of accumulating
|
|
quietly.
|
|
|
|
The deliverable is not more prose. `docs/repo-family-bootstrap-contract.md`
|
|
already names the required `rapp.yaml` fields; naming them in prose is exactly
|
|
what failed. The deliverable is a schema plus a check that runs.
|
|
|
|
## Why This Exists
|
|
|
|
`RMASTER-WP-0017` through `RMASTER-WP-0019` established the four-axis model
|
|
and materialized the first concrete family repos. The model held up. Its
|
|
enforcement did not.
|
|
|
|
`railiance-platform` surveyed all six family repos and the live Railiance01
|
|
cluster on 2026-08-11 under its `RAILIANCE-WP-0015` and routed the findings
|
|
here, on the grounds that `railiance-master` owns the model and an
|
|
implementation repo should not author it. State Hub messages
|
|
`04c776c4-52fa-494a-9761-942123f28a6e` (proposal) and
|
|
`f88f938d-1ebc-4ba0-91ee-9395e44a7cb3` (amendment) carry the detail.
|
|
|
|
Reported drift across the three live rapps:
|
|
|
|
- rollout, smoke, and rollback contracts are mutually unreadable —
|
|
`rapp-openbao` and `rapp-postgres` use `supported_commands` lists,
|
|
`rapp-qonto` uses `default_mode` / `production_gate`, a `required:` smoke
|
|
list and an `order:` rollback list
|
|
- `contract_version`, `readiness_state`, `data_classification` and
|
|
`criticality` appear only in `rapp-qonto`, though both rails carry
|
|
`contract_version` / `readiness_state` consistently
|
|
- `consumer_contract` / `consumers` exist only in `rapp-postgres`
|
|
- `workload_identity.name` is the workload name in two rapps and the repo name
|
|
in the third
|
|
- `ownership_repo` points at a layer repo twice and at an app repo once
|
|
- `reef-railiance` `bound_rapps` lists `rapp-qonto` only, while `rapp-openbao`
|
|
and `rapp-postgres` are both live on Railiance01 — a hand-maintained registry
|
|
with nothing checking it
|
|
- roughly 17 live non-system workloads in-cluster against 3 rapps
|
|
|
|
The `rail-*` family received a tighter contract in wave 1 and has stayed clean.
|
|
That contrast is the argument for schema rather than for more documentation.
|
|
|
|
## Current Starting Point
|
|
|
|
- no `schemas/` directory exists in this repo
|
|
- `docs/repo-family-bootstrap-contract.md` lists eight required `rapp.yaml`
|
|
fields with no shapes, no types, and no cardinality rules
|
|
- `docs/rapp-first-wave-candidates.md` still reads as though the planned first
|
|
wave (`rapp-openbao`, `rapp-vergabe-teilnahme`, `rapp-forgejo`) happened; the
|
|
actual population is `rapp-openbao`, `rapp-postgres`, `rapp-qonto`, the
|
|
latter two created ad hoc from immediate need
|
|
- `rapp-vergabe-teilnahme` was never built, so the user-facing wrapper pattern
|
|
it existed to prove remains unproven while the platform-service pattern has
|
|
been proven twice
|
|
- canon does not yet admit the family prefixes, so `fix-consistency` cannot
|
|
call a family validator until `the-custodian` lands its side
|
|
- `railiance-platform` is holding `RAILIANCE-WP-0015-T02` (bringing
|
|
`rapp-openbao` and `rapp-postgres` onto one shape) until this schema exists
|
|
|
|
## Target Outcome
|
|
|
|
When this workplan is complete:
|
|
|
|
1. `schemas/rapp.schema.json` defines every required and optional `rapp.yaml`
|
|
field with one normative shape per contract.
|
|
2. A `composition:` block expresses what a grouped rapp actually is: a stated
|
|
purpose, first-party member repos, and pinned third-party components.
|
|
3. The two cardinalities are written down and enforced separately — repos are
|
|
many-to-many with rapps, deployables are one-to-one.
|
|
4. A validator runs over `rail.yaml`, `rapp.yaml`, and `reef.yaml` and fails on
|
|
drift.
|
|
5. Reef `bound_rapps` is derived from rapp declarations rather than
|
|
hand-listed.
|
|
6. `docs/rapp-first-wave-candidates.md` records what was actually built versus
|
|
planned, and carries the grouped wave-2 inventory.
|
|
7. Downstream repos have a single shape to converge on, and know it.
|
|
|
|
## Boundaries
|
|
|
|
This workplan owns the schema, the validator, the cardinality rules, and the
|
|
framework documents that describe them.
|
|
|
|
It does not migrate any rapp repo onto the new shape — that stays with each
|
|
owning repo, coordinated through `railiance-platform` `RAILIANCE-WP-0015-T02`.
|
|
|
|
It does not touch canon. Admitting the family prefixes into
|
|
`repo-classification-standard_v1.0` is `the-custodian`'s call, routed there
|
|
separately as `cc64764a`.
|
|
|
|
It does not address the Forgejo organization split. All repos currently sit
|
|
under a single `coulomb/` org and a split into stewardship orgs is a
|
|
URL-breaking change across every remote, CI config and ArgoCD source. It is an
|
|
independent dimension from rapp grouping and needs its own work record.
|
|
|
|
## Tasks
|
|
|
|
## T01 - Accept the routed proposal and record the ownership decision
|
|
|
|
```task
|
|
id: RMASTER-WP-0021-T01
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "af45d82d-c1e3-47b3-81ae-2974bc65cc5d"
|
|
```
|
|
|
|
Read and acknowledge State Hub messages `04c776c4` and `f88f938d`, mark them
|
|
read, and reply to `railiance-platform` confirming that `railiance-master`
|
|
accepts ownership of the schema and that this workplan is the vehicle.
|
|
|
|
Record the framework decision that rapp bounded context is a grouping dimension
|
|
in its own right, derived neither from Forgejo organizations nor from State Hub
|
|
domains, with the cardinality argument as rationale: a repo lives in exactly
|
|
one Forgejo org, so `org:repo` is one-to-many, while a repo may legitimately
|
|
contribute to more than one rapp, so `rapp:repo` is many-to-many — and 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.
|
|
|
|
Reference OAS P1 in `canon/standards/orthogonal-architecture_v1.0.md`: separate
|
|
independent perspectives into orthogonal dimensions.
|
|
|
|
**Done 2026-08-11.** Both messages marked read; reply `a9a8109f` sent to
|
|
`railiance-platform` accepting ownership and confirming this workplan as the
|
|
vehicle. No decision record was created here: `d07ee5f9` already carries the
|
|
dimension decision from the `railiance-platform` side, and duplicating it would
|
|
create two records of one decision. The rationale lands instead in `ADR-0007`
|
|
under T07. One question was returned to `railiance-platform` — whether they can
|
|
emit the live deployable inventory that T06 needs, so this repo does not
|
|
acquire a cluster dependency.
|
|
|
|
## T02 - Author schemas/rapp.schema.json
|
|
|
|
```task
|
|
id: RMASTER-WP-0021-T02
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "87ae4551-1fac-4613-b258-28c8b22bbb82"
|
|
```
|
|
|
|
Create `schemas/` and write `rapp.schema.json` covering:
|
|
|
|
- the eight fields already required by the bootstrap contract: `rapp_id`,
|
|
`workload_identity`, `ownership_repo`, `supported_rails`,
|
|
`runtime_dependencies`, `rollout_contract`, `smoke_contract`,
|
|
`rollback_contract`
|
|
- `contract_version` and `readiness_state`, matching the shape the rail family
|
|
already carries
|
|
- `data_classification` and `criticality`, promoted from `rapp-qonto`-only to
|
|
required
|
|
- `consumer_contract`, promoted from `rapp-postgres`-only to optional but
|
|
schema-defined
|
|
|
|
Settle the two definitional questions the survey exposed. `workload_identity.name`
|
|
is the workload, never the repo. `ownership_repo` names the repo that owns the
|
|
workload's lifecycle; state explicitly whether a layer repo is a legal value,
|
|
since current declarations disagree.
|
|
|
|
Pick one normative shape for `rollout_contract`, `smoke_contract` and
|
|
`rollback_contract` rather than admitting all three observed variants. Note in
|
|
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: RMASTER-WP-0021-T03
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "8b130ff8-0601-40bb-8539-d6f5847c2f41"
|
|
```
|
|
|
|
Supersede the flat `members:` list from the original proposal with the
|
|
composition block from the amendment:
|
|
|
|
```yaml
|
|
composition:
|
|
purpose: <what this coherent workload does>
|
|
member_repos: # first-party, each sharing rollout and rollback fate
|
|
- repo: <slug>
|
|
role: <what it contributes>
|
|
deployables: [<names>]
|
|
upstream_components: # third-party, pinned
|
|
- name: <chart|image|operator>
|
|
source: <repo/chart or registry ref>
|
|
version: <pin>
|
|
```
|
|
|
|
Encode both cardinalities. A repo may appear in the `member_repos` of more than
|
|
one rapp. A deployable must belong to exactly one rapp — each running
|
|
deployable has exactly one rapp owning its rollout. Keeping these distinct is
|
|
what makes the coverage check in T06 well-defined.
|
|
|
|
Carry the operator decision of 2026-08-11 into the schema: rapp granularity is
|
|
grouped-by-bounded-context, one rapp per cohesive group that deploys, versions
|
|
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
|
|
id: RMASTER-WP-0021-T04
|
|
status: done
|
|
priority: medium
|
|
state_hub_task_id: "50ffbff9-f8f0-4085-975a-6c5123d9c2f0"
|
|
```
|
|
|
|
Write `schemas/rail.schema.json` and `schemas/reef.schema.json` from the
|
|
minimum field sets in `docs/repo-family-bootstrap-contract.md`. The rail family
|
|
has stayed clean, so this is largely codifying what already works; do not
|
|
tighten it beyond current practice without cause.
|
|
|
|
Change `reef.yaml` `bound_rapps` from a hand-maintained list to a derived
|
|
projection of the rapp declarations that name the reef, and record the
|
|
migration path for `reef-railiance`, whose current list is already known stale.
|
|
|
|
**Done 2026-08-13.** `schemas/rail.schema.json` and `schemas/reef.schema.json`
|
|
written from the live declarations and the composition/substrate contracts,
|
|
not from the bootstrap field lists (those lists used an older
|
|
`substrate_prerequisites` name the live rails do not carry).
|
|
|
|
Both live rails conform. The live reef conforms as a document.
|
|
`bound_rapps` is optional and derived from a new required `rapp.bound_reefs`
|
|
field (empty list means declared but not placed). The live
|
|
`reef-railiance` hand-list `[rapp-qonto]` fails the derivation check, which
|
|
is the intended result. Migration for that repo: delete the field now, or
|
|
replace it with the derived set once the three live rapps name the reef.
|
|
|
|
Adjacent ownership, recorded so it is not mistaken for a schema hole: the
|
|
postgres backup object store is a consumed capability (`s3-backup-target`),
|
|
not a rapp-owned resource. Bucket = `resource-control` / `RESOURCE-WP-0002`.
|
|
Credential = `railiance-platform` OpenBao `Secret platform-pg-backup-s3`.
|
|
Purchase = decision `9c21c0e0`. `make postgres-backup-deploy` stays
|
|
fail-closed until that handoff lands.
|
|
|
|
## T05 - Build the family declaration validator
|
|
|
|
```task
|
|
id: RMASTER-WP-0021-T05
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "0a212d33-2169-4472-80d0-69ec4edba5cb"
|
|
```
|
|
|
|
Add a validator under `tools/` that walks the family repos, locates each
|
|
`declarations/*.yaml`, and validates it against the matching schema.
|
|
|
|
Checks in priority order, per the routed proposal:
|
|
|
|
1. every declaration conforms to its schema
|
|
2. every declared member repo and upstream pin resolves
|
|
3. reef `bound_rapps` is derivable from rapp declarations rather than
|
|
hand-listed
|
|
4. deployable coverage — deferred to T06, which needs cluster state
|
|
|
|
Exit non-zero on any failure and name the offending file and field. Make it
|
|
runnable standalone now, and callable from `fix-consistency` later; do not
|
|
block on canon admitting the prefixes, since that is `the-custodian`'s
|
|
sequencing, not ours.
|
|
|
|
**Done 2026-08-13.** `tools/validate-family-declarations.py` walks
|
|
`rail-*` / `rapp-*` / `reef-*` siblings of `--root` (default: parent of
|
|
this repo). `--self-test` passes against `tools/testdata/family-declarations`.
|
|
|
|
First live scan of `/home/worsch` (no inventory): 6 declarations, 31
|
|
errors. The two rails are clean. The three declared rapps fail on the
|
|
already-known shape drift plus the new `bound_reefs` field. The reef fails
|
|
only on stale `bound_rapps`. The three undeclared engine repos fail as
|
|
prefix-without-declaration. That is exactly the drift the schema exists to
|
|
make loud.
|
|
|
|
## T06 - Add the deployable coverage check
|
|
|
|
```task
|
|
id: RMASTER-WP-0021-T06
|
|
status: done
|
|
priority: medium
|
|
state_hub_task_id: "519c2838-c4af-46e9-81fa-612fb09e91e3"
|
|
```
|
|
|
|
Extend the validator with the standing control-loop question: does every live
|
|
deployable belong to exactly one rapp?
|
|
|
|
This is the check that turns the schema from a one-shot lint into an ongoing
|
|
control loop. Per the amendment it catches all three drift findings at once —
|
|
stale reef bindings, unpackaged live workloads, and the roughly
|
|
17-workloads-versus-3-rapps gap.
|
|
|
|
Decide how live cluster state reaches the check without `railiance-master`
|
|
acquiring a cluster dependency it should not have. Consuming an inventory
|
|
produced by an implementation repo is preferable to querying Railiance01 from
|
|
here.
|
|
|
|
Expect the first run to report a large uncovered set. That output is the wave-2
|
|
worklist, not a failure to suppress.
|
|
|
|
**Done 2026-08-13.** The validator takes `--inventory PATH`. This repo does
|
|
not query a cluster. The file is a JSON mapping with `source`,
|
|
`captured_at`, optional `reef`, and `deployables` as a list of `{name,
|
|
namespace?, kind?}` (bare name strings also accepted). Live names that no
|
|
rapp claims are errors labelled as wave-2 worklist items. Declared names
|
|
missing from the inventory are warnings.
|
|
|
|
The first live run is not in this repo: it needs `railiance-platform` to
|
|
emit the inventory T01 asked for. The check itself is implemented and
|
|
covered by `--self-test`.
|
|
|
|
## T07 - Update the bootstrap contract to point at the schemas
|
|
|
|
```task
|
|
id: RMASTER-WP-0021-T07
|
|
status: done
|
|
priority: medium
|
|
state_hub_task_id: "aa71bc2f-7d9e-4351-8130-8b6e23b8ee36"
|
|
```
|
|
|
|
Revise `docs/repo-family-bootstrap-contract.md` so the minimum-field lists cite
|
|
the schema files as normative rather than restating fields in prose. Prose that
|
|
duplicates a schema will drift from it.
|
|
|
|
Record an ADR (next free number, `ADR-0007`) for the framework decisions this
|
|
workplan settles: bounded rapp context as an independent dimension, the two
|
|
cardinalities, and grouped-by-bounded-context granularity.
|
|
|
|
**Done 2026-08-13.** Bootstrap contract now points at the three schema files
|
|
and refuses to restate their fields. `docs/adr/ADR-0007-rapp-declaration-contract.md`
|
|
records the dimension, the two cardinalities, grouped-by-bounded-context
|
|
granularity, and that the schemas are normative. Decision `d07ee5f9` remains
|
|
the platform-side record; this ADR is the framework record, not a second
|
|
decision.
|
|
|
|
## T08 - Refresh rapp-first-wave-candidates.md
|
|
|
|
```task
|
|
id: RMASTER-WP-0021-T08
|
|
status: done
|
|
priority: medium
|
|
state_hub_task_id: "28e4ef82-62ea-41d2-a059-05c047292240"
|
|
```
|
|
|
|
Correct the document to record what was actually built against what was
|
|
planned: `rapp-openbao` as planned, then `rapp-postgres` and `rapp-qonto` ad
|
|
hoc from need, and `rapp-vergabe-teilnahme` never built.
|
|
|
|
State the consequence plainly — the platform-service pattern is proven twice
|
|
and the user-facing wrapper pattern is unproven — and carry the operator
|
|
decision to build `rapp-vergabe-teilnahme` to prove the user-facing shape
|
|
before mass extraction.
|
|
|
|
Add the grouped wave-2 inventory in the new composition shape. Indicative
|
|
candidates: `rapp-netkingdom-identity` (sso, mfa, user-engine, tenant-engine,
|
|
flex-auth), `rapp-forgejo` (forgejo, runner, registry), `rapp-coulomb-social`,
|
|
`rapp-helix-forge`.
|
|
|
|
**Done 2026-08-13.** The document now leads with the plan-versus-outcome
|
|
table, states that the user-facing wrapper is unproven, keeps the 2026-07-25
|
|
assessment as history, and carries the wave-2 inventory in composition
|
|
shape. The three undeclared engine repos are named so they are not invented
|
|
again as separate rapps. `rapp-forgejo` placement cites `d151d817`.
|
|
|
|
## T09 - Release the schema to downstream repos
|
|
|
|
```task
|
|
id: RMASTER-WP-0021-T09
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "b8dc2452-8ec0-4301-98b7-4ec96411f428"
|
|
```
|
|
|
|
Waiting on T02, T03 and T05.
|
|
|
|
Notify `railiance-platform` that the shape is settled and
|
|
`RAILIANCE-WP-0015-T02` is unblocked, and notify `rapp-openbao`,
|
|
`rapp-postgres`, `rapp-qonto` and `reef-railiance` of the migration they each
|
|
own. Include the normative shape chosen for the rollout, smoke and rollback
|
|
contracts and what each repo must change.
|
|
|
|
Migration itself belongs to the owning repos. This task ends when they have
|
|
what they need, not when they are done.
|
|
|
|
**Done 2026-08-13.** Release messages sent. Downstream now has the shape and
|
|
the per-repo change list; migration itself is not this task.
|
|
|
|
| To | Message | What they received |
|
|
|---|---|---|
|
|
| `railiance-platform` | `072c5820` | WP-0015-T02 unblocked; inventory shape for T06; S3 split restated |
|
|
| `rapp-openbao` | `093f2f73` | 11-error migration (metadata, composition, `bound_reefs`, contract rename) |
|
|
| `rapp-postgres` | `7b1cb9b7` | same 11-error set; S3 is a consumed destination, not a hole |
|
|
| `rapp-qonto` | `16742192` | 5-error migration (`composition`, `bound_reefs`, `package_type`, name, `fallback`) |
|
|
| `reef-railiance` | `6b395e26` | delete or replace stale `bound_rapps` |
|
|
| `resource-control` | `aa8393e3` | coordination only: bucket is theirs (`RESOURCE-WP-0002`) |
|