# Private lanes

A Space can give every participant their **own private lane** beside the shared room: a place to think with an agent
that the other side never sees, from which nothing reaches the shared room without the human putting it there.

This is the [Bridge](./the-bridge.md)'s "prep room + co-signed crossing", generalized into a primitive any Space can
have.

---

## The structure

A lane is **its own Space**, not a scope inside the shared one. It has its own room id, its own key, its own ledger.
Two rooms, linked on the device that holds both keys.

That matters for the privacy claim. A "private scope" inside one room would put both sides' confidential reasoning in
a single sealed object, separated only by application logic — and the server would be the thing enforcing the
separation. Two rooms means a lane's contents are sealed to a key the other participant simply does not have.

**The server never joins the two rooms.** The device does. It holds both keys, reads the shared room, and appends a
labelled copy into the lane (`op: 'mirror'`, deduped by entry id, idempotent). To the platform these are two unrelated
Spaces.

## What the lane agent can and cannot do

Inside the lane, the agent sees the shared room **as an observer**. It is told so explicitly:

> THE SHARED ROOM — the live negotiation the other party sees. You OBSERVE it; you cannot post here.

To put something in the shared room it must call `cross_to_shared`, and that is a **high-stakes tool**: the agent
*proposes*, a human *approves*, and only then does it execute. Propose → approve → execute is the same delegated
authority rule the rest of the platform uses — see [Delegated authority](./delegated-authority.md).

So the failure mode people actually fear — "my assistant said the quiet part out loud to the other side" — is not
guarded by a prompt instruction. It is guarded by the agent having no path to the shared room that does not pass
through a human decision.

## What crosses is a message, not a transcript

`cross_to_shared` carries the text the human approved. The lane's reasoning, drafts, rejected phrasings and private
context stay in the lane. Crossing is an act of composition, not of forwarding.

The pending draft can be copied out as text before approving. Editing it in place was built and then removed: it made
"what exactly am I approving?" ambiguous, which is the one question the approval step exists to answer.

## In the room

- Lanes can be created for every participant **at room creation**, so the topology is the default rather than
  something to discover later.
- The lane opens as a docked panel beside the shared thread; the shared room's messages appear in it as compact,
  labelled, collapsible lines, so you can read one conversation without losing the other.
- A pending crossing renders as the message plus **Approve** / **Not now**.

## Code map

| Concern | Where |
|---|---|
| The crossing capability + guidance | `agent/spaceBridge.mjs` (`cross_to_shared`, `renderSharedForAgent`) |
| Provisioning + linking lanes | `agent/spaceProvision.mjs` |
| Turn path (bridged context) | `agent/spaceService.mjs`, `agent/asyncTurn.mjs` |
| Mirror op (device-side join) | `agent/spaceService.mjs` (`op: 'mirror'`) |
| Lane UI | `spaces/public/privateLane.html`, `spaces/public/space.html` (`?panel=1`) |

## Status

Live. All four phases — crossing, provisioning, agent runtime, UI — are built and deployed.

Design notes, including the alternatives that were rejected, are in `docs/spaces-private-lane.md`.
