Skip to main content
A Collection is a configured table on the workspace spine; a Record is one typed row in it. This is the operational half of the workspace, where the work lives (leads, bugs, tickets, posts), as opposed to Topics, which capture what the team knows. The point of the substrate is that you don’t build apps. A CRM, a bug tracker, a support desk, and a content calendar are all the same primitive: a typed Record flowing through a configured Collection. A new use case is a configuration, not new code. Reads are active-only, bounded and brief by default across every surface. A collection list returns active collections in a bounded page; archived ones only surface when you explicitly opt in with --include-archived (CLI), include_archived: true (MCP), or ?include_archived=true (REST). A collection get comes back brief unless you ask for full. Archive, restore and purge are separate, explicit operations — lifecycle is never collapsed into update --status.
Collections are Driftless’s operational primitive: a human-run system of record augmented by AI. They are not built on Topics; instead, a Collection can reference criterion Knowledge that guides how its Records should be handled.

The three archetypes

Every use case collapses into one of three shapes:
  • Pipeline: records flow through a status lifecycle on a board: leads, bugs, support tickets, ad campaigns.
  • Analysis: a record is an artifact anchored to a source that can drift (the source changed) versus decay (no activity): call analysis, competitor tracking, SEO audits.
  • Content: a record with a publication lifecycle (draft → published): blog posts, product copy. The published artifact lives in your site/CMS; the collection tracks the work.

What a Collection holds

  • record_schema: the typed field definitions (the columns). Field types: text, long_text, number, currency, date, datetime, select, multi_select, status, relation, user, url, email, phone, file, anchor_ref, ai_field. A status field carries stages[], the lifecycle a record flows through.
  • views: saved views over the records: board, table, list, calendar, gallery, each with group_by, filter, sort, and visible_fields.
  • criterion_rel_slugs: the Knowledge Topics the work reads before acting. This is the seam coming down: a lead pipeline reads how-we-sell and icp, so the work happens with the team’s criteria.
  • distill_policy: a configuration field for how a terminal record should distill back into a Note (the seam going up): closing a bug or winning a deal can leave a durable learning in the vault. The distill returns a context_outcome (created · already_exists · skipped · failed), surfaced on the record’s create/update response — an observable, best-effort result that never blocks the write.

A Record

A Record is one row: its fields are the typed values (keyed by the Collection’s field keys), and its status is validated against the Collection’s own stages. Each Collection defines its own lifecycle, so a record can only sit in a stage its Collection declares.

Entities

A Record is a row inside one Collection. An Entity is an identity that can span Collections: the same customer who appears as a lead in the sales pipeline and a ticket in support. An entity is not a row in a Collection; it is a separate object that Records point at. An entity carries:
  • kind: the identity type (for example company, person).
  • name: its display name.
  • dedup_key: the stable key that makes it unique within its kind.
  • attributes: free-form typed values.
Entities upsert idempotently on (kind, dedup_key): writing the same pair twice updates the entity instead of creating a duplicate. A Record links to an entity by setting its top-level entity_id (never a field inside fields), so several Records across different Collections can resolve to one identity. Entities are available on three surfaces: the CLI (driftless collection entity add|list), MCP (driftless_entity, action: list | get | upsert), and REST (/workspaces/:slug/entities).
Connector import (via the Broker) maps a provider’s mirrored records into Collection Records. That is different from Broker index, which materializes connector documents for retrieve, not Collection Records. See Broker: reads and materialization.

Using collections

From the CLI (the surface also exists in MCP as driftless_collection and driftless_collection_record):

Retrieve before you act: the seam in one call

Before working a record, an agent reads the collection’s criterion Knowledge: the criterion_rel_slugs resolved into the actual topics (how-we-sell, icp), so the work happens with the team’s criteria, not from scratch. The retrieve action delivers both halves of the seam at once: the relevant records and the criterion to read first:
criterion is the team’s Knowledge to apply; criterion_missing flags any criterion slug that doesn’t resolve (a gap to close). Records paginate by keyset (nextCursor). Read the criterion, then act on a record with driftless_collection_record action:'update'. For just the criterion (no records), use driftless_collection action:'context'. This mirrors topic retrieve: one call returns what to read and what to work on, instead of hand-chaining a list and a separate criterion lookup.

Lifecycle, archive and purge

A collection moves through one of two lifecycle states (active · archived):
  1. Active — records are read and worked.
  2. Archivedcollection archive retires an active collection. An archived collection is immutable: only restore (back to active) and purge can touch it. update, record writes and retrieve are rejected with 409.
  3. Purgedcollection purge is permanent and owner/admin only. Run --dry-run first to read the impact (version, records, entity_links_removed, and distilled_notes_preserved — the distilled Notes the collection spawned are preserved, not deleted). Confirm with --expected-version <n> (compare-and-set on the version you just read). The call is audited and idempotent: a repeat after the collection is gone returns already_absent, never 404.
Safe record delete is the same shape: collection record rm only deletes a record in a non-archived collection, and --dry-run previews the Entity link that would be removed and the distilled Notes that are preserved (not deleted). The delete is workspace-membership gated, not owner/admin — any member may remove a record.
archive and restore are reversible; purge is the only permanent operation. They are separate commands/routes/actions on every surface — a update --status that tries to archive is rejected with 409 and names the lifecycle route to use instead.