Skip to content

fix(BA-6896): stop RBAC backfill downgrades from deleting live permissions - #12881

Draft
jopemachine wants to merge 2 commits into
mainfrom
fix/BA-6896-rbac-backfill-downgrade-data-loss
Draft

fix(BA-6896): stop RBAC backfill downgrades from deleting live permissions#12881
jopemachine wants to merge 2 commits into
mainfrom
fix/BA-6896-rbac-backfill-downgrade-data-loss

Conversation

@jopemachine

@jopemachine jopemachine commented Jul 15, 2026

Copy link
Copy Markdown
Member

📚 Stacked PRs

Merge in order, bottom-up. Each PR is based on the one above it, so its diff shows only its own layer.

Problem

15 RBAC backfill migrations seed permissions / association_scopes_entities / object_permissions for a new entity type, and their downgrade() removes those rows with a blanket DELETE ... WHERE entity_type = %s.

Once the runtime starts using an entity type, operators grant and revoke permissions and attach entities to scopes of their own. Those rows are indistinguishable from the seeded ones, so a downgrade also erases real operator-managed grants — including the object permissions backing live vfolder invitations. The loss is unrecoverable: a later upgrade only re-creates the seed, not the operator's grants.

Verified against a real database

Not a static reading — reproduced with real alembic on a Postgres DB built at head via mgr schema oneshot. For each migration: seed a permission row on an operator-owned role (indistinguishable from a seeded one), stamp <rev>, then downgrade <parent> so only that migration's downgrade runs, then check whether the operator's row survived.

migration entity_type main (before) this PR (after)
30c8308738ee session DESTROYED survived
091d0274c2e3 agent DESTROYED survived
0e0723286a7a image DESTROYED survived
21159a293dfb keypair DESTROYED survived
2e42a745f939 container_registry DESTROYED survived
82e817b74ae4 resource_group DESTROYED survived
67f5338ff571 artifact DESTROYED survived
6d850788c7c8 artifact_registry DESTROYED survived
a5e87ed3b6d4 app_config DESTROYED survived
013a6676866c notification_channel DESTROYED survived
d0a3c0716970 model_deployment DESTROYED survived
2185ae0dd371 vfolder DESTROYED survived
f1a2b3c4d5e6 model_card DESTROYED survived
3b6297b1bd75 project_admin_page DESTROYED ¹ survived

¹ 3b6297b1bd75 deletes only from roles matching role_project_%_admin, so it needs a role matching that pattern to bite; with one, it destroys the row like the rest.

Solution

Make these downgrade()s no-ops with a short comment, and drop the now-dead delete/revert helpers.

This is not a new convention — it matches the forward-only precedent already set by sibling backfill migrations (6e5a7a62a687, 3632aad9d5d9, f2b9a4c7e103, 5a4e677aea42, e3fb172166dc), which are no-ops for exactly this reason.

Out of scope

Schema-reverting migrations (constraints, columns, tables) keep their existing downgrade() — dropping a column the upgrade added is a real, safe revert. Only data backfills are changed here.

7h4m7ygnaao1 also reverted ref edges back to auto on downgrade; that flips rows the operator may own too, so the whole downgrade is now a no-op.

End-to-end verification

Added once the downgrade chain was repaired (#12887, #12907, #12910, #12912 fixed six walls that made this impossible before). A single straight alembic downgrade from head to d86417a6bf7b175 revisions, no skipped steps — run on a seeded database. Operator-managed permission rows counted by entity_type, since role_id does not exist in the schema at that depth:

chain exit operator rows
with this PR 0 10 → 10 — all survived
this PR reverted 0 10 → 4 — session, vfolder, agent, image, container_registry, model_card destroyed

The four survivors are the pre-redesign entity types, whose downgrades never matched these rows in the first place.

Follow-up

Making these downgrades no-ops exposes a latent idempotency bug in six of them — fixed in the stacked PR #12883, which should land together with this one.

🤖 Generated with Claude Code

…sions

The RBAC backfill migrations seeded permissions, scope associations, and
object permissions for a new entity type, and their downgrades removed
them with a blanket DELETE by entity_type.

Once the runtime starts using an entity type, operators grant and revoke
permissions and attach entities to scopes of their own. Those rows are
indistinguishable from the seeded ones, so a downgrade also erased real
operator-managed grants -- including the object permissions backing live
vfolder invitations -- and a later upgrade only restored the seed.

Make these downgrades no-ops, matching the forward-only precedent already
set by the sibling backfill migrations, and drop the now-dead delete and
revert helpers. Schema-reverting migrations are untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the size:XL 500~ LoC label Jul 15, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp:manager Related to Manager component require:db-migration Automatically set when alembic migrations are added or updated size:XL 500~ LoC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant