-- Backfill the new `documents.edit` and `files.edit` permission keys on -- every existing row in `roles.permissions`. The schema (RolePermissions -- in src/lib/db/schema/users.ts) added these keys to close the silent-403 -- traps on PATCH /api/v1/documents/[id], /cancel, /remind, /watchers, and -- PATCH /api/v1/files/[id] — each used a permission key that did not exist -- in the schema, so withPermission()'s `resourcePerms[action]` returned -- undefined and 403'd every non-superadmin call. -- -- Backfill rule: -- documents.edit ← documents.create (anyone who can create can edit) -- files.edit ← files.upload (same rationale) -- -- jsonb_set with create_missing=true (the default) inserts the key only -- when it's absent, so re-runs are idempotent and the migration is safe -- against a partial run. -- -- Note: per-port overrides live in `port_role_overrides.permission_overrides` -- and are PARTIAL — they only contain the keys a port flipped from the -- base role. The deepMerge resolver fills in `documents.edit` from the -- base role for any port that didn't override it, so we deliberately do -- NOT touch `port_role_overrides` here. Backfilling there would synthesize -- override entries that the operator never intended. UPDATE roles SET permissions = jsonb_set( permissions, '{documents,edit}', COALESCE(permissions->'documents'->'create', 'false'::jsonb), true ) WHERE permissions->'documents' IS NOT NULL AND NOT (permissions->'documents' ? 'edit'); UPDATE roles SET permissions = jsonb_set( permissions, '{files,edit}', COALESCE(permissions->'files'->'upload', 'false'::jsonb), true ) WHERE permissions->'files' IS NOT NULL AND NOT (permissions->'files' ? 'edit');