How collaboration actually works

What is shared, what stays yours, and where the boundary is enforced.

Understanding what's atomic, what's trust-based, and what to expect when two people touch the same thing.

Live push (SSE)

When you launch rememd in remote mode, it opens a long-lived GET /events connection. The backend publishes change notifications on an in-memory bus; every connected client gets them. The client filters by the workspaces the user can see, then invalidates its index cache and reloads that workspace's index.

Events currently emitted:

  • workspace_changed - the blob's version bumped (index PUT).
  • task_claimed / task_released - a task's claim state flipped.
  • agent_run_started / agent_run_released / agent_run_finished - an unattended run took, dropped or reported back on a task. Separate from the task_* pair on purpose: a person picking work up and a machine writing against it are not the same event.
  • agent_run_outcome - what became of the pull request a finished run left: merged, or closed unmerged. The one agent_run_* event published while nothing is running.
  • presence_changed - someone connected or disconnected; payload lists the currently-online user IDs.

When dev B logs a session, edits a PRD, or claims a task, your view updates without a refresh click.

Task claim/release (server-enforced)

Claim is atomic: the server locks the blob row, checks no one else holds the task, writes assigneeUserId + claimedAt + assignee, bumps the version. If another user already holds it → 409 Conflict. Only the holder (or an admin) can release → 403 Forbidden otherwise.

In the UI: right-click a task. The context menu shows Claim task, Release task, or "Claimed by <name>" depending on state. Admins get a Force release (admin) option on any claimed task.

PRD / session-body writes (optimistic lock)

Each content document has its own version counter. When you save a PRD, the client PUTs with the version it last saw. If the server's at a newer version (someone edited in between):

  1. The client fetches the server's current body.
  2. Throws a DocumentConflict carrying both bodies.
  3. The app shows a three-button alert: Keep mine (overwrite), Discard mine (reload server), Cancel.

No silent overwrite, no silent last-write-wins. You always know when there's a conflict.

Workspace-index writes (optimistic lock + silent retry)

Index-level writes (creating a feature, adding a session metadata, claiming a task - anything that touches the blob) use the same optimistic-lock pattern but with a one-shot silent retry. For small metadata edits this is fine; for blob-level conflicts affecting content, the document-level conflict mechanism above catches the important cases.

Presence

A small green dot overlays the avatar when a teammate currently has rememd open and connected to this backend. Not full "they're viewing this feature right now" - just "they're online."

Author attribution

Notes and session events stamp authorUserId automatically when saved through RemoteStorage. In work-mode, the session timeline shows a colored avatar beside each event's timestamp, indicating who logged it. Hover to see the name.

Repo paths are per-user (important!)

rememd used to store absolute paths like /Users/dev-a/.../some-repo inside the workspace blob, which meant dev B's rememd would try to use your paths - and fail. As of the dockerlayer branch, paths live in each user's ~/.recall/repo-map.json, keyed by the shared focus-area / feature UUIDs.

Practically:

  • Each teammate git clones wherever they want on their disk.
  • Nothing about that path is shared with anyone.
  • rememd connect and the cwd-based commands (rememd note, rememd log) need the user to register their clone once: rememd link focus-area <name> <path>.
  • Pure CLI workflow (rememd ls, task claim, watch, prd edit) needs no path setup at all.

Legacy blob-stored paths still decode as fallback, so pre-refactor data doesn't break. The migration into your local map happens automatically on first launch: any path in the blob that exists on your disk gets copied into your local map; paths that don't exist on your disk are ignored (they belong to someone else).