Make CONSTITUTION/AGENTS/STANDARDS framework-owned (overwritten on upgrade) while
never losing user data:
- FRAMEWORK_OWNED vs USER_SEEDED lists (append-friendly per Lead's ask)
- reconcile_framework_files: overwrite framework-owned from defaults/, backing up a
divergent copy ONCE to <file>.pre-constitution.bak (advisory); seed-if-absent for
USER_SEEDED (TOOLS.md)
- anchor rsync preserve excludes to top-level (/<file>) so defaults/<file> still syncs
- never delete *.pre-constitution.bak across upgrades (rsync + cp-fallback)
- snapshot -> sync -> restore-on-failure (ERR/INT/TERM trap) for crash safety
- FRAMEWORK_VERSION 2 -> 3 + v2->v3 migration advisory
- MOSAIC_SYNC_ONLY hook for testability (file phase only, no env side effects)
Fixture suite (test-install-migration.sh) green 7/7: fresh, legacy-edited AGENTS
(overwrite + backup + SOUL/creds survive + idempotent .bak), tuned STANDARDS,
no-TTY, failure-path data integrity. Two real bugs caught + fixed by the fixtures
(unanchored exclude blocking the overwrite; backup deletion on re-upgrade).
file-adapter.ts TS parity + the vitest fixture matrix land in P4 (2/2).
Refs #542
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fresh `mosaic gateway install` (npm) left the gateway DB schema empty —
sign-in 500'd with `relation "users" does not exist`, and every entry
point (auth, bootstrap setup) failed because they all query the users
table first. Five stacked bugs on the local (PGlite) tier:
1. `packages/db/package.json` `files: ["dist"]` excluded the `drizzle/`
SQL migrations from the published tarball.
2. `runMigrations()` only supports postgres-js — unusable for embedded
PGlite.
3. `apps/gateway/src/database/database.module.ts` never invoked
migrations at startup.
4. `createPgliteDb` didn't load pgvector, so migration 0001's
`CREATE EXTENSION vector` failed.
5. Drizzle's PG migrator wraps every migration in one outer
transaction, which trips Postgres' `check_safe_enum_use` on
migration 0009 (`ALTER TYPE ADD VALUE 'pending'` → `SET DEFAULT
'pending'` in the same tx).
Changes:
- Ship `drizzle/` in the published tarball.
- `createPgliteDb` loads `@electric-sql/pglite/vector`.
- New `runPgliteMigrations(handle)` walks the Drizzle journal and
runs each statement-breakpoint chunk through PGlite's `client.exec()`
(autocommit per statement). Records into `drizzle.__drizzle_migrations`
for interop with the postgres-js path. Per-statement try/catch
surfaces which statement of which migration failed.
- `DatabaseModule` runs migrations in `OnModuleInit` before
`app.listen()`. Local tier: explicit `runPgliteMigrations` then
`storageAdapter.migrate()`. Postgres tier: just `storageAdapter.migrate()`,
which already calls `runMigrations(url)` internally — no double-call.
- Removed `packages/storage/src/test-utils/pglite-with-vector.ts`. The
"intentionally not exported" rationale is moot now that migration
0001 forces pgvector load anyway. The integration test uses
`createPgliteDb` + `runPgliteMigrations` from `@mosaicstack/db`.
Tests: BetterAuth tables exist after migrate; idempotent (re-runs 0009);
partial-failure surfaces statement-level context and leaves no ledger row.
QA on a fresh PGlite install:
- `Applying PGlite schema migrations...` then `Initializing storage
adapter (pglite)...` in startup log.
- `GET /api/bootstrap/status` → `{"needsSetup":true}` HTTP 200 (was 500).
- `POST /api/bootstrap/setup` reaches Zod validator (was 500).
Scope: this PR fixes the local (PGlite) tier. Postgres-tier first
install still has the outer-transaction problem and a journal ordering
bug (0009's `when` < 0008's). Documented inline as TODO and in the
scratchpad — needs a separate change with real-Postgres validation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>