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
statuslifecycle 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. Astatusfield carriesstages[], the lifecycle a record flows through.views: saved views over the records:board,table,list,calendar,gallery, each withgroup_by,filter,sort, andvisible_fields.criterion_rel_slugs: the Knowledge Topics the work reads before acting. This is the seam coming down: a lead pipeline readshow-we-sellandicp, 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 acontext_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: itsfields 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 examplecompany,person).name: its display name.dedup_key: the stable key that makes it unique within itskind.attributes: free-form typed values.
(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 asdriftless_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: thecriterion_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):
- Active — records are read and worked.
- Archived —
collection archiveretires an active collection. An archived collection is immutable: onlyrestore(back toactive) andpurgecan touch it.update, record writes andretrieveare rejected with409. - Purged —
collection purgeis permanent and owner/admin only. Run--dry-runfirst to read the impact (version, records,entity_links_removed, anddistilled_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 returnsalready_absent, never404.
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.