/** * Seed the `roles` table with the roles the platform's own code names. * * Runs from `postdb:push`, so every machine gets the rows on its next schema push rather than needing a * per-server step in a deploy plan. Bun fires `pre`/`post` hooks for colon-named scripts — the same * mechanism `backfill-session-models.ts` relies on. * * ── Why these four are `isSystem` ── * * Each is named by name somewhere in the code: `rolePermitsDevTools()` tests for `'Developer'`, * `bootstrap.ts` writes `'Super Admin'`, `users.role` defaults to `'Member'`, and `'Admin'` is the one * `rolePermitsDevTools` deliberately excludes. Renaming or deleting one of those is not a data change, it * is a code change that has not happened — so the flag exists for the delete and rename paths to refuse on, * rather than for the failure to turn up later as a missing database role or an account that cannot log in. * * ── What this deliberately does NOT do ── * * No foreign key from `users.role`. That change cannot go through `db:push`: push creates the table empty * and adds the constraint in the same run, so every existing account violates it — verified against a * scratch database on 2026-08-23, where push reported the violation, created the table, skipped the * constraint, and exited 0. The FK is a separate release, once every host holds these rows. * * So today `users.role` is still a plain text column and nothing enforces that its value is a real role. * That is the intended state of this phase: additive, unable to fail, and safe to run on five hosts * unattended. * * Never overwrites. A description edited on one server must not be reverted by the next deploy. */ import { seedRole, waitForDatabase } from '../src/databases/officer_db/src/index'; const SEED: { name: string; description: string }[] = [ { name: 'Super Admin', description: 'Full authority over the platform, and an OS account with sudo' }, { name: 'Admin', description: 'Administers the platform. No operating-system privilege' }, { name: 'Developer', description: 'A member who is also trusted with developer tooling on the machine' }, { name: 'Member', description: 'The default. Reaches only what their role has been granted' }, ]; async function main(): Promise { await waitForDatabase(); let created = 0; for (const role of SEED) { if (await seedRole({ ...role, isSystem: true })) created += 1; } console.log(`[seed:roles] ${created} seeded, ${SEED.length - created} already present (of ${SEED.length})`); } // Never fatal. This runs from a `post` hook after a schema push, and a seed that cannot write must not make // a successful push look like a failed one — the rows are additive and the next push tries again. main() .catch((err: unknown) => { console.error('[seed:roles] could not seed:', err instanceof Error ? err.message : err); }) .finally(() => process.exit(0));