# Spec 001: shared state and live Work

Status: draft revision 3 for review
Date: 2026-08-08
Depends on: [Spec 000](spec-000-system-overview.md)

## Outcome

General Chat, shared project conversations, agent state, and results update live
without refresh. Reconnecting a browser or Space returns to the same durable
state without duplicating messages or agents.

This spec owns collaboration, persistence, and the live browser protocol. It
does not own sandbox provisioning or crew behavior; those belong to Specs 002
and 003.

## Two conversation scopes

`private` conversations belong to one user. General Chat is private and may be
used before a project is selected.

`project` conversations belong to a project. Every active member sees the same
messages, executions, results, and agent chats according to their role.

When General Chat starts real work, the user must select a project. The command,
execution, child chats, and artifacts become project-scoped; General Chat keeps
only the user's request and links to shared work. Private context is never
silently copied into the project.

## Ownership

Godloop owns durable product state: users, membership, conversations, commands,
executions, results, and events.

Herdr owns local workspaces, tabs, panes, terminals, agent processes, detection,
and live lifecycle. A user-owned Space connector is the only bridge between
Herdr and Godloop. The server never reaches into a user's local Herdr socket.

The connector reports observations; it does not decide project authorization,
command order, or canonical state.

## Minimal runtime

```text
Vue browser ── HTTP + SSE ── Go monolith ── SQLite
                                 │
                         authenticated connector
                                 │
                         Herdr + Git + Crabbox
```

One Go process, one SQLite database, one browser reducer, and one in-process
wakeup hub are enough. The database event log provides replay; the hub only
wakes connected consumers after commit. There is no external broker.

## Durable records

The first slice needs only:

- `users`;
- `projects` and `project_members`;
- `spaces` and explicit `project_spaces` contributions;
- `conversations` and ordered `messages`;
- observed `agent_sessions`;
- `commands`, `executions`, and `results`;
- append-only `events`.

Detailed columns belong in migrations and tests, not in this architecture spec.
Every scoped row carries `user_id` or `project_id`. Every mutation carries an
actor and idempotency key.

## One mutation rule

An application command validates authority, changes canonical rows, and appends
the resulting event in one SQLite transaction. The in-process hub is notified
only after commit.

HTTP handlers, connectors, FirstMate, Loops, Telegram, and browser components
are adapters. None may mutate the same domain state through a second path.

An event is a committed product fact, not a request and not a copy of terminal
output. A consumer can replay from its last event ID or recover from a fresh
snapshot.

## Live protocol

The browser needs two endpoints per scope:

```text
GET /api/v2/snapshot
GET /api/v2/events        # SSE with Last-Event-ID
```

Project endpoints add the project ID and authorize through active membership in
the resource query. A snapshot returns current conversations, agents,
executions, results, and the latest event ID. SSE replays later events and then
stays live.

One client reducer handles all durable events. Views do not poll or invent
their own refresh rules. Terminal frames use a separate lossy stream because
they are observation, not canonical history.

Initial event families are deliberately small:

```text
conversation.changed
agent.changed
execution.changed
result.changed
project.changed
```

Payloads include only the identity and revision needed to update or refetch the
aggregate. New features extend these families before adding new infrastructure.

## Herdr reconciliation

On connect, the Space connector takes a Herdr snapshot and subscribes to its
events without leaving a gap. Herdr events wake reconciliation; snapshots are
the recovery source after reconnect or a missed event.

Observed agents are keyed by the best stable Herdr agent-session identity, plus
the Space. Pane, tab, label, cwd, and lifecycle are mutable observations. A pane
label is never identity.

A project-linked Herdr agent appears as a durable Work chat. Moving or ending
the pane updates that chat; it does not delete it. Manually started agents are
shown as `trusted local` and are read-only to teammates unless the Space owner
explicitly makes them callable.

## Collaboration rules

The Project is the authorization boundary. Roles are:

- `owner`: membership, contributed Spaces, project settings, and editor work;
- `editor`: chat, dispatch, steer, review, and apply;
- `viewer`: read only.

A Space remains owned by one user. Sharing a project does not share a machine.
Its owner must explicitly contribute it and may revoke future execution at any
time. Revocation preserves prior chats and results.

Messages use server sequence numbers. One agent accepts one active prompt at a
time; concurrent prompts enter a visible FIFO queue. SQLite decides order.
Browser selection, drafts, panel layout, and scroll position remain per-user.

## Browser shape

Build contains:

- a project selector and collaborator presence;
- a Work rail of durable human and agent chats;
- one transcript and role-aware composer;
- a visible command queue;
- a live terminal for the selected active agent;
- execution mode, blocked state, result, and unread indicators.

New agents appear without stealing another user's selected chat or changing
Herdr focus.

General Chat sits outside the project selector. Selecting FirstMate there is
specified in [Spec 003](spec-003-agent-orchestration.md).

## Recovery rules

- Browser reconnect: replay SSE; fetch a snapshot when replay is unavailable.
- Server restart: rebuild from SQLite and resume the hub.
- Connector reconnect: reconcile a Herdr snapshot before sending changes.
- Duplicate delivery: return the prior idempotent result.
- Unknown Herdr schema: stop the bridge visibly; never fall back to polling.
- Removed member: close their project stream and reject later access.
- Lost terminal stream: reconnect it without losing messages or results.

## Acceptance

1. Two users join one project as owner and editor.
2. The owner contributes one Space linked to a Herdr workspace.
3. A new Herdr agent appears as the same Work chat in both browsers within two
   seconds and without refresh.
4. Messages, queue order, lifecycle, and final result match in both browsers.
5. Moving and ending the Herdr pane update the chat without replacing it.
6. Refresh both browsers, restart v2, and reconnect the Space; nothing
   duplicates and history remains.
7. Change the editor to viewer, then remove them; permissions and live streams
   change immediately while historical attribution remains.
8. General Chat stays private and links to project work without leaking private
   context into it.

## Not in this spec

- sandbox implementation or patch apply;
- FirstMate policy or agent-to-agent delegation;
- Loops, scheduling, Telegram, marketplace, billing, or v1 migration;
- shared drafts, collaborative raw terminal typing, or external pub/sub.
