Skip to content

Collections

chronicle.collections groups assets and experiments under a named, ACL'd label — a research-library primitive for organizing work and scoping search.

A collection isn't a new storage concept: members are plain assets and experiments referenced by id, and the collection is a row the platform RBAC-protects. Authorization, scope resolution, and member visibility filtering all happen server-side — you don't filter for them.

Create and list

coll = chronicle.collections.create(
    "navier-stokes-2024",
    description="Turbulence-regime runs for the NS convergence study",
)
print(coll["id"])

scope_id is the owning principal — a user, team, or org id. Omit it and the collection lands in your active scope (a personal collection).

mine = chronicle.collections.list()                          # your personal scope
team = chronicle.collections.list(scope_id="team_4Hq", q="ns")  # name substring filter
list param Effect
scope_id Narrow to one owning scope (default: your personal scope)
q Case-insensitive substring filter on the name
include_archived Include soft-archived collections (default False)

get(collection_id) hydrates member_asset_count / member_experiment_count — but these reflect the members you can see, not the true totals.

Members

add and remove mutate membership; both take asset_ids and/or experiment_ids.

chronicle.collections.add(
    coll["id"],
    experiment_ids=["exp_7Qk", "exp_9Lm"],
    asset_ids=["asset_3Zp"],
)

add requires Write on the collection and Read on each member; a member you can't read (or that doesn't exist) is skipped with a warning rather than failing the batch, and adds are idempotent per member. It returns {added, warnings}. remove needs only Write on the collection — you can prune anything in your own collection — and returns {removed}.

chronicle.collections.remove(coll["id"], asset_ids=["asset_3Zp"])
result = chronicle.collections.members(coll["id"])  # {assets, experiments}

members(collection_id) is existence-filtered: a member you can't Read is omitted entirely (no membership oracle), so it returns the rows you can independently see.

reindex_mode

add and remove take a reindex_mode controlling only search-index freshness — the Postgres membership write is immediate either way.

reindex_mode Behavior
"lazy" (default) Restamp affected search docs on the normal background cadence. Cheap, eventually consistent.
"eager" Additionally restamp those docs promptly. Expensive (a Vertex patch fan-out).

Curate-then-search-now

Use reindex_mode="eager" only when you search right after curating and need the new membership reflected immediately. Otherwise "lazy" is correct.

Associate a collection with an experiment

associate links one or more collections to an experiment as a search anchor — distinct from membership.

chronicle.collections.associate("exp_7Qk", [coll["id"]])

It requires Write on the experiment and Read on each collection; an unreadable or missing collection is skipped with a warning. Returns {associated, warnings}. Associations are never indexed — they resolve at query time, so there's no reindex_mode here. Associate a collection at experiment setup and later searches with experiment_context boost toward it (and scope restricts to it).

See reference/client.md for full return shapes, and the search and datasets guides for the surfaces collections scope.