Skip to content

Workspace

The workspace is a drive-like tree of files and folders, scoped per namespace, where every file write produces a new immutable revision in an append-only chain. Mutations use optimistic concurrency control (OCC) — you pass the version you think you’re editing, and a stale guess is rejected with a conflict instead of silently overwriting.

A WorkspaceItem is either a file or a folder (src/workspace/types.ts:6-9). Folders nest via parent_id, so items form a tree inside one namespace. Each item carries a monotonic version (starts at 1, types.ts:16) and, for files, a pointer to the latest revision:

Field Meaning
kind file or folder
namespace the drive this item lives in
parent_id enclosing folder (absent = root)
version OCC counter, bumped on every item mutation
current_revision_id latest revision (files only)
current_revision_number revision count (files only)
deleted_at / deleted_by set on soft-delete; IDs and history preserved

Deletes are soft — workspace.delete_item soft-deletes the whole subtree, preserving IDs and revision history (workspace.ts:295-313).

A file’s content lives in WorkspaceRevision rows, not on the item. Each revision is written once and never mutated (types.ts:36-47): it has a revision_number, a previous_revision_id linking it to its predecessor, a storage_key pointing at the backing blob, and size_bytes / content_type. Writing a file appends a new revision and re-points current_revision_id; the old revision stays addressable. workspace.list_revisions (HTTP GET /workspace/items/:id/history) walks the chain.

Text content is gated by isTextContentType (src/workspace/utils.ts:18-27) — only text/* plus json/javascript/typescript/xml/yaml/markdown content types go through the text read/write commands (workspace.read_text / workspace.update_text). Anything else is binary.

There are two OCC tokens, one per kind of mutation:

  • Item mutations (update_item, move_item, delete_item) require expected_version (workspace.ts:120-134). The store compares it to the item’s current version; a mismatch returns a conflict result, surfaced as HTTP 409 (workspace.ts:75-85,257).
  • Text writes (update_text) require expected_revision_id — the id of the revision you read (workspace.ts:145,176). If a concurrent write moved the head, you get a revision conflict (409, workspace.ts:414,517).
read item v3 ──► edit ──► update_item(expected_version: 3)
│
head still v3 ─────────┼──► ok, item → v4
head now v4 ─────────┴──► 409 conflict (re-read, retry)

The mutation result is a discriminated union — ok | not_found | conflict | deleted (types.ts:102-112) — so callers can branch on the outcome rather than catching an exception for the expected-version case.