Primitive spec
Korell
The bulk-import primitive. Korell is the substrate for moving entities into Foundation from external sources — tenant onboarding from a legacy system, periodic CSV pulls, one-time migrations — without each primitive growing its own ad-hoc importers. Upload a file (or a whole ZIP export of your old system), let the AI propose the mapping, preview the dry run, and import.
What it owns
Korell owns the lifecycle of a data-import job. An ImportTask is a reusable definition (source + target + mapping); an ImportRun is one execution of that task with a status, counts, and any per-row failure detail. The engine drives the run; source adapters read from the outside world; target adapters write into a primitive's normal write surface. Korell also owns the upload store the files land in (signed direct-to-storage uploads, never proxied through the application) and the cross-import identity map that keeps multi-file imports linked.
Korell does not bypass primitive validation. Targets are thin adapters over each primitive's existing controllers (or service-layer write APIs) — the same rules a single-item REST call would hit also apply to a 100,000-row import.
Concepts
- ImportTask
- A reusable import definition. Carries the source descriptor (where to read from), the target primitive + entity (where to write to), and a field-mapping document.
- ImportRun
- One execution of an
ImportTask. Tracks status (pending,running,succeeded,failed,partial), row counts, and structured per-row error detail. - Source adapter
- The "read" side.
CsvSourceReader, JSON (inline and file), a dependency-freeXlsxSourceReader, andS3ArchiveEntrySourceReader— run an import straight from one entry inside an uploaded ZIP. Each source produces a stream of normalised rows for the engine to consume. A direct database source reads a MySQL or PostgreSQL database straight into the same pipeline, with the same dry run, the same per-row validation and the same id remapping as a file. Each run picks up where the last one stopped, so it can be re-run to catch up changes until cutover — a cutover is a re-run rather than a freeze. The database host is checked at connect time, and the credentials are read-only ones you authorise; they are never stored on the import itself. It is set up with you during onboarding by our staff rather than as a self-serve button: we map your tables with you. Ask for it at hello@latticekit.app. - Upload store & archive inspector
- Files land via signed direct upload (
korell.create_upload_url/korell.inspect_upload) — the application never proxies the bytes. TheKorellArchiveInspectorenumerates a ZIP's entries so a whole legacy-system export becomes a set of importable files in one upload. - AI import planning
- The "Beth figures out the import" core.
korell.propose_import_planreads the uploaded files against a typed target field catalogue and proposes per-file targets + field mappings; the operator reviews and edits per file before anything runs. - Target adapter
- The "write" side. A target is a thin wrapper over a primitive's existing controllers or write services, each publishing its typed
TargetFieldcatalogue. Shipping today, by target key:terminus.personandterminus.employee— customers and staff.terminus.groupandterminus.group_member— households and other groups, and who belongs to them.hober.subscription— memberships and subscriptions. No card is imported: the first period is invoiced unpaid, so the customer adds a card to pay it.riose.shift— staff shifts.terminus.form_response— waivers, intake forms and other form responses already completed, validated against the form as it stands, so a member is not asked to sign the same waiver again.terminus.consent— consent history, marked as imported.mallow.stored_value— gift card and store credit balances, spendable the ordinary way once they land.seldon.booking— booking history.mallow.open_invoice— open invoices and opening balances.mannix.imported_login— existing logins, so people imported from another system sign in with the passwords they already have (where the old password format cannot be checked, the person is imported and invited to choose a new one). This one reads only from a direct database source, never from an uploaded file.trantor.resourceandhardin.product— bookable resources such as rooms and equipment, and the product catalogue.
- ImportRefMap
- The cross-import identity map. When file two references IDs from records file one created, the
ImportRefMapremaps source IDs to the entities an earlier run created, so multi-file imports stay linked across runs. - KorellEngine
- The orchestrator. Resolves the source + target adapters from an
ImportTask, opens anImportRun, streams rows through the mapping, and writes to the target with idempotency keys derived from the source row identity. A dry-run preview (korell.preview_import) exercises the same path without writing.
API surface
Korell's surface is published as typed capabilities in the platform capability registry — invoked by the Demerzel import wizard, by Beth over MCP, or through the generic capability invoke path, and gated to tenant operators by the DATA_IMPORT permission. There are no /korell/v1/ REST routes (and no Korell tag in the OpenAPI reference); the capabilities below are the API.
| Capability | Purpose |
|---|---|
korell.create_upload_url / korell.inspect_upload | Signed direct upload for a source file; inspect what landed (headers, sheets, archive entries). |
korell.inspect_archive | Enumerate the entries of an uploaded ZIP so each can become its own import. |
korell.propose_import_plan | AI-proposed per-file targets + field mappings against the target field catalogue. |
korell.create_import_task / korell.list_import_tasks / korell.get_import_task | Author and inspect reusable ImportTask definitions. |
korell.preview_import | Dry-run a task — full validation, row counts, and per-row errors with nothing written. |
korell.run_import_task | Open an ImportRun and execute the import. |
korell.list_import_runs / korell.get_import_run | Inspect run status, counts, and per-row failure detail. |
korell.list_import_targets | The target field catalogue — what can be imported, with typed fields. |
How it fits with the rest
flowchart TB
Up[Uploaded CSV / XLSX / ZIP] --> Src[Source adapter]
Db[(MySQL / PostgreSQL)] --> Src
AI[AI plan proposer] -. proposes mapping .-> Task[ImportTask]
Src --> Ko(Korell engine)
Task -. defines .-> Ko
Ko --> Tgt[Target adapter]
Tgt --> Te[Terminus]
Tgt --> Ha[Hardin]
Tgt --> T[Trantor]
Tgt --> Ho[Hober]
Tgt --> Ma[Mallow]
Tgt --> Se[Seldon]
Tgt --> Other[any primitive]
Korell writes to other primitives through their normal surfaces, so anything that ships in Foundation can be imported from elsewhere by adding a target adapter. Demerzel is the operator surface: an import wizard with per-file target + mapping editing and an inline run-in-chat card — or skip the wizard and drag a data export onto Beth's chat, and she proposes the plan herself via korell.propose_import_plan.