Genesis: Birthing an LQMS Installation with a Base Library¶
The end-to-end procedure for standing up an installation whose QMS arrives as a portability bundle (ADR-0125/-0127/-0128, format 1.3). Written from the first real run; names below are the fictional MERIDIAN convention — substitute your instance's.
Phases¶
0 — Preflight (on the outgoing instance, if one exists).
Final backup (deploy/backup.sh) — it becomes the ARCHIVE OF RECORD for everything the rebuild
does not carry (ADR-0125 §3). Export every corpus that survives: the QMS scope
(/portability-export), each project scope. Split the QMS bundle by the layer table
(scripts/import/split_bundle.py): --keep base-governing,base-derive-master,base-then-revoke
→ the library bundle; --keep stay → the records bundle.
1 — Reset. Stop the app; drop and recreate the application database. The identity realm (Keycloak) survives untouched — accounts and passwords persist; all in-app state goes.
2 — Deploy. The current release boots the full migration chain onto the empty database: the GLOBAL scope, the bootstrap administrator, the Base Reader role. Verify the Flyway log.
3 — Found the organizations through the product API (audited): the QMS owner's org (internal) and each customer org.
4 — Land the library: load_arrival.py --land-into GLOBAL --mandator-id <owner-org> with
the library bundle. Ids arrive verbatim under the bundle's namespace; roles adopt; counters
seed. Preview first, always.
5 — Inspect and accept. The one-shot discipline: browse the landed library; nothing acts in GLOBAL until the operator's owner accepts.
6 — The retirement wave: revoke the tool-era documents (the migrate-then-revoke class of ADR-0127 §2) each WITH ITS REASON — the library's first witnessed acts, closing the window.
7 — Land the records scope: the records bundle into a scope under the owner's org, ordinary
same-code adoption. Its records' citations to governing documents resolve through the citation
rule (own scope → the library's released face) — verify by opening one record's doc: link.
8 — Land each project under its customer org, its own code and ids.
9 — Staffing: invited users receive Base Reader automatically; project roles per project. Library (GLOBAL) staffing of the QMS team is safe by construction since ADR-0128/-0129: a GLOBAL assignment grants library work and administration — never content reach or workflow authority inside anyone's project.
10 — Verification: per-scope count diffs against each bundle's manifest (documents, versions, released/revoked/draft, attachments, folders); zero RELEASED/REVOKED audit rows in freshly-landed scopes; a reduced-rights member's probe (reads the library, sees only their project); one citation walk record→library.
11 — Close-out: STATUS/worklog, a fresh DR snapshot, and the pre-genesis backup labelled as the historical archive.
What genesis deliberately loses¶
The outgoing instance's working-record layer and audit trail (ADR-0125 §3 — the final backup is their archive), and any scope not exported in phase 0.
Lessons from the first run (2026-08-29)¶
- Recreating the database skips the initdb scripts (PG15+: schema
publicis owner-only in a new database). Aftercreate database, replay the DATABASE-level init statements before the app boots:ALTER SCHEMA public OWNER TO lqms_migration; GRANT ALL PRIVILEGES ON DATABASE lqms TO lqms_migration; GRANT ALL ON SCHEMA public TO lqms_migration; - Name the tenant truthfully BEFORE first boot. The bootstrap structurally founds the
installation's own organization;
LQMS_ORG_NAMEnames it exactly once. A wrongly-named tenant cannot be dissolved after its first trail row (a dissolution's own record makes its org permanent — the design defending itself), only renamed. - Every landing writes its own role mapping. Preview → confirm → finalize, per bundle;
--skip-previewwithout a--mappingfor THAT batch is refused by design. - The bootstrap admin holds no Base Reader (it never accepts an invitation) — grant it explicitly if the operator should read the library.