Skip to content

Workstreams

A workstream is a durable container for one effort. It holds nothing itself — its job is to bind the resources scattered across the rest of Chorus (signals, memory, artifacts, workspace items, tasks) into a single namespace-scoped unit and expose them as one threaded view. Think of it as the long-lived “project folder” that a task (short-lived) hangs off of.

Each workstream is namespace-scoped using the same grammar as memory — agent:{identity}, ring:{ring}, or org:{org} (src/memory/acl.ts). Within a namespace, (namespace, slug) is unique (022-workstream.surql:24).

Field Notes
namespace required; resolves aliases, then ACL-gated (workstreams.ts:231-235)
project_tag required at the catalog; normalized to a slug (classification.ts:19)
title required
slug derived from slug → kickoff-signal tags → title if omitted (workstreams.ts:244-249)
status open | blocked | paused | done | cancelled, default open (types.ts:8-14)
priority low | normal | high | critical, default normal (types.ts:17-22)
owner_identity optional; accepts a name or identity:… ref
summary, metadata optional free-form

created_by is inferred from your authenticated identity — you never pass it (workstreams.ts:264).

A workstream collects resources through workstream_link rows. Each link points at one of 10 resource kinds — signal, artifact, workspace_item, workspace_revision, workstream, memory, task, run_attempt, delivery, checkout (resource-ref.ts:3-22, mirrored exactly by the DB ASSERT in 023-workstream-link.surql:6) — with a link_role:

Role Meaning
primary the kickoff / driving resource
context background the effort draws on (default)
output something the effort produced

Links are unique on (workstream_id, resource_kind, resource_id) — re-linking the same resource returns WORKSTREAM_LINK_DUPLICATE (409). For namespaced kinds (memory, artifact, workspace item/revision, nested workstream) the target must live in the same namespace as the workstream, or you get WORKSTREAM_RESOURCE_NAMESPACE_MISMATCH (409) (service.ts:128-198).

get_context is the payoff. It walks every link, resolves each target, and returns a pre-bucketed view: primary_signals, context_signals, linked_memory, linked_artifacts, linked_workspace_items, and outputs, sorted primary → context → output, then newest-first (service.ts:58-126, 257-270). This is what gives an agent the whole effort in one read instead of chasing N resource ids.

All business logic (validation, policy, ACL, slug derivation, signal kickoff) lives in one catalog handler; every transport is a thin framer over it (workstreams.ts:1-19).

Command Does
workstreams.create create, optionally seeded from_signal (links it as primary)
workstreams.list namespace-filtered list; filters by project/status/priority/owner + time window
workstreams.get read one by id (ACL-gated)
workstreams.get_context the threaded, bucketed view above
workstreams.update change mutable fields (≥1 field required)
workstreams.link attach a resource
workstreams.unlink remove a resource link

create, update, link, unlink require the workstream:manage policy; the reads require workstream:inspect (workstreams.ts:68-74).

Transport How
HTTP GET/POST /workstreams, GET/PATCH /workstreams/:id, GET /workstreams/:id/context, POST/DELETE /workstreams/:id/links (routes.ts:19-116)
JSON-RPC workstreams.{create,list,get,get_context,update,link,unlink} (methods.ts:1360-1366)
MCP workstreams_create, workstreams_list, workstreams_get, workstreams_get_context, workstreams_update, workstreams_link, workstreams_unlink (workstream-tools.ts:100-106)
SDK client.workstreams.* (packages/sdk/src/resources/workstreams.ts)
CLI chorus workstreams {list,create,show,update,link,unlink} (packages/cli/src/commands/workstreams.ts:326-339, registered in packages/cli/src/main.ts:61-62)