The postgres importer inserts records sequentially with FK enforcement live,
so it depends on records arriving in dependency order. That requirement was
implicit in each backend's export table list; make it explicit and enforced.
- Add EXPORT_KIND_ORDER (user, abode, resident, apikey, note) as a documented
single source of truth, with the FK dependency chain spelled out on its doc
comment, and note the ordering guarantee on the ExportEnvelope wire-format
doc. Derive the isExportKind set from it.
- Both backends' export() now iterate EXPORT_KIND_ORDER via a loader map
(postgres omits note by leaving it out of the map), so emission order is
tied to the constant and can't drift.
- Tests: a change-detector on EXPORT_KIND_ORDER, plus assertions that both the
sqlite and postgres (fake) exports emit record kinds grouped in FK-safe
order (kind rank non-decreasing down the stream, after the leading meta).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The export/import plan predated the postgres backend. Bring it up to parity:
- PostgresInterface implements Exportable + Importable, mirroring the sqlite
backend. Export is a signal-checked async generator (one query per table);
import drives a transaction via `WrappedPool.multi`, which rolls back on any
error/abort and commits only after the whole stream is consumed cleanly.
- The `note` kind is skipped on postgres (its note CRUD is still unimplemented,
so a pg database holds none) — a full dump from sqlite imports its
user/abode/resident/apikey records and drops notes.
- Unlike sqlite (PRAGMA foreign_keys=off), postgres keeps FK enforcement; the
FK-safe insertion order keeps a full dump valid, and truly-dangling partial
dumps will (correctly) fail.
- abode-import now resolves the backend via getDbInterface instead of
constructing SqliteInterface directly; isImportable keeps it from ever
running over the remote (api) interface, which has no import.
- Add isImportable(); make postgres selectClientApikeys' where optional.
- Tests: exercise the real PostgresInterface export/import paths against an
in-memory fake WrappedPgClient (no pg service in CI) — NDJSON shape,
meta.source, filtering, note-skip, insert dispatch, and error/abort rollback.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>