Skip to content

Data model overview

Postgres via Drizzle; every tenant-owned table carries workspace_id and every repository method takes it as a required first argument — queries physically cannot forget the tenancy filter (DATA_ACCESS).

Core spine:

user ─ workspace_member ─ workspace ─ project ─ work_item ─ comment / attachment / activity
│ └ parent_id (one-level sub-tasks)
├ workflow_state (categories: backlog/unstarted/started/completed/cancelled)
├ cycle (date-only timeboxes)
└ saved_view
workspace ─ objective / module (roadmap) · notification · audit_event · outbox_event · email_outbox · idempotency_key

Semantics worth knowing before changing anything:

  • Identifiers (SKY-42) mint from project.next_item_seq under lock — unique, sequential, immutable.
  • Revisions: work_item.revision backs compare-and-set updates (409 with the authoritative body on staleness). Same contract on cycles.
  • Soft deletes (deleted_at) on items/comments; listings always filter them.
  • Activity + outbox rows commit in the mutation’s transaction — proven by fault-injection tests.
  • Migrations live in packages/db; CI runs both fresh-install and upgrade paths.

Full model: DATA_MODEL.