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.

Status: Live, in production. The full import pipeline is operator-usable end to end — presigned S3 uploads, CSV / JSON / XLSX / ZIP-archive sources, AI-proposed import plans, dry-run preview, and a Demerzel import wizard (or drag a file onto Beth's chat). Korell's surface is published as typed capabilities in the platform capability registry rather than REST routes, so it has no tag in the OpenAPI reference; target coverage (today: Terminus persons + employees, Trantor resources, Hardin products) grows adapter by adapter.

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 (presigned S3 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-free XlsxSourceReader, and S3ArchiveEntrySourceReader — run an import straight from one entry inside an uploaded ZIP. Each source produces a stream of normalised rows for the engine to consume.
Upload store & archive inspector
Files land via presigned S3 upload (korell.create_upload_url / korell.inspect_upload) — the application never proxies the bytes. The KorellArchiveInspector enumerates 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_plan reads 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 TargetField catalogue. Shipping today: TerminusPersonImportTarget, TerminusEmployeeImportTarget, TrantorResourceImportTarget, HardinProductImportTarget. Adding a new importable entity is a target adapter, not a new endpoint.
ImportRefMap
The cross-import identity map. When file two references file one's IDs (customers → their bookings), the ImportRefMap remaps 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 an ImportRun, 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.

CapabilityPurpose
korell.create_upload_url / korell.inspect_uploadPresigned S3 upload for a source file; inspect what landed (headers, sheets, archive entries).
korell.inspect_archiveEnumerate the entries of an uploaded ZIP so each can become its own import.
korell.propose_import_planAI-proposed per-file targets + field mappings against the target field catalogue.
korell.create_import_task / korell.list_import_tasks / korell.get_import_taskAuthor and inspect reusable ImportTask definitions.
korell.preview_importDry-run a task — full validation, row counts, and per-row errors with nothing written.
korell.run_import_taskOpen an ImportRun and execute the import.
korell.list_import_runs / korell.get_import_runInspect run status, counts, and per-row failure detail.
korell.list_import_targetsThe target field catalogue — what can be imported, with typed fields.

How it fits with the rest

flowchart LR
  Up[Uploaded CSV / XLSX / ZIP] --> Src[Source adapter]
  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 --> 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.