# Plugin hooks and contract surface Everything a plugin may import from the core lives in `shopdb.api` (ADR-001), versioned by `__contract_version__` (ADR-002). Adding a name is a minor bump; removing one is major. This file records the non-obvious hooks - the ones a plugin author would not guess from the name alone. It is not the full export list; read `shopdb/api/__init__.py` for that. ## Reversible bulk-import journal Contract version 0.30.0. A plugin that drives a bulk import - a site asset adoption, an integration sync - records the primitive changes it makes so the whole batch can be reversed later. The journal is import-agnostic: it stores primitive mutations (set a cell, insert a row) tied to a batch id, and a generic undo replays them in reverse. Nothing in core learns what the import means. Exposed on `shopdb.api`: - `ImportBatch` - one row per applied import. `batchid` is the identity that the approval step, the write, and the undo all reference. `status` is `applied` or `undone`. - `ImportJournalEntry` - one reversible primitive mutation. `operation` is `update` (a cell change, carrying `oldvalue`/`newvalue` so it can be put back) or `insert` (a row this batch created, so undo can delete strictly what the import created). - `BATCH_APPLIED`, `BATCH_UNDONE` - the two `ImportBatch.status` values. - `OP_UPDATE`, `OP_INSERT` - the two `ImportJournalEntry.operation` values. What a plugin gets from this, and what it does NOT: - A plugin's import UI reads `ImportBatch` to list applied batches (kind, row count, who applied it, when) and offers an "Undo import" per batch. - A plugin does NOT write the journal or run undo through these model imports. The write/undo logic lives with the import writer (see `scripts/site_imports/journal.py`), which records entries in the SAME transaction as the mutation they describe and reverses them conservatively: an `update` is put back only if the cell still holds the value the import wrote (a value a human changed since is left as-is and reported), and an `insert` is hard-deleted only when nothing outside the batch still references the created row. The tables are core-owned (migration `7d37_import_batches_and_journal`), so they exist regardless of which plugins are installed.