# Parley

An AI chat workspace for a small team: one conversation at a time, with the
files it is reading, the window it is filling and the money it is spending all
visible while you read it.

Sidebar shell · instant commit model on the conversation, the record screens and
the two workspace screens, staged on the personal settings and every auth form ·
sixteen product screens plus five auth · dark mode · verified at 1440, 1024, 768
and 375.

## What is here

| Route | Screen | Primary display |
|---|---|---|
| `/` | New chat | The composer, with the three most-used prompts under it |
| `/chats/:id` | A thread | The conversation, the composer, and the context rail |
| `/chats/archived` | Archived chats | Table, every row recessed |
| `/shared` | Shared with me | Other people's threads, then their projects |
| `/projects` | Projects | Cards in two column stacks |
| `/projects/:id` | A project | Its threads, its instructions, its files |
| `/library` | Library | Table of saved prompts |
| `/library/:id` | A prompt | The prompt itself, over its own figures |
| `/files` | Files | Table |
| `/files/:id` | A file | What carries it, and which turns cited it |
| `/usage` | Usage | The day chart over the model and person tables |
| `/search` | Search results | Grouped list, one card per kind, with the line that matched |
| `/settings/account`, `/settings/models` | Personal settings | Staged forms |
| `/settings/workspace` | Workspace | People table, invites, three policies |
| `/settings/billing` | Plan and billing | Cycle figures, plans, invoices, card, cap |
| `/signin` `/signup` `/forgot-password` `/check-email` `/signout` | Auth | Centred card, no navigation owner |

## The two numbers, and why they are different

Everything on the usage screen and everything in the context meter comes from
the same messages, but they are not the same quantity, and `metrics.js` keeps
them apart:

- **Context** is what the window holds: every message body in the thread plus
  every attached file that actually indexed. A file that failed to index carries
  nothing, which is the whole argument for fixing it.
- **Billing** is what the turns cost. A model re-reads the conversation on every
  turn, so an assistant turn is billed for the context that existed when it ran
  plus what it wrote. Nothing stores that — `turnBilling` derives it from the
  messages before it, which is why the usage screen cannot drift from the
  threads, and why sending a message moves the headline, the day chart, the
  thread's cost and the project's cost at once.

A failed turn produced nothing and is billed nothing: the billed turns are every
assistant message minus the one that failed.

Seats and the bill work the same way. A seat is held by a person or by an invite
still standing, so `seatsUsed` is `people.length` plus the unexpired invites —
which is why one dormant member and one expired invite tell completely different
stories, and why the invite button is disabled with the reason rather than
hidden. The cycle's charge is the plan's seat price times the paid seats plus the
turns that ran after the allowance was exhausted, over the same billed turns the
usage screen charts; the current cycle is never stored, so sending a message
moves it.

## Local additions

Built local to this product because the design system has no component for
them, reported here as AGENTS §9 asks:

- **`Prose` / `CodeBlock`** (`ParleyParts.jsx`) — one renderer for a message
  body. Blocks are `p`, `h`, `ul`, `ol`, `quote`, `code`; nothing renders raw
  markdown, and the code block carries its own copy button.
- **`AnchorRow`** — four anchor figures on one row definition (`subgrid`), so a
  wrapped label or a missing qualifier cannot shift its own column.
- **`Bar`**, **`DayChart`** — a proportion bar and the usage chart. Every bar is
  a figure and carries the tooltip that says what it counts.
- **`ContextRail`**, **`Composer`**, **`Turn`** (`ChatScreen.jsx`) — the
  conversation itself.
- **`Floating` / `FloatingTip`** (`lib/ui-runtime.js`) — carried over from the
  other templates in this system, unchanged.
- **`MissingRecord`** (`DiscoveryScreens.jsx`) — a record whose route resolves
  but whose fixture entry was deleted, which is not the same thing as an
  unregistered path.

- **A brand mark** (`assets/templates/logos/08-logo-demo.svg`) in the sidebar
  brand block and above the auth form, replacing the letter tile.

One change to a `lib/` file, made in every product's copy so they stay
byte-identical: `CommandPalette` takes an optional `footer` render prop, one
persistent row under the filtered list. It is domain-free — the row Parley puts
there is a destination, not a record.

Streaming is drawn by truncating the finished reply to the number of characters
revealed so far, through the same renderer a finished turn uses, so nothing
reflows when it lands. Stop keeps what was written and marks the turn `stopped`;
Continue starts a new turn from it.

## What is real

- **Upload** reads the file you actually pick — its name and size are what the
  row shows, and an archive lands `unsupported` exactly as it would in the
  product.
- **Export** downloads the thread as plain text.
- **Copy** on a code block and on an answer writes to the clipboard.
- **⌘K**, **⌘⇧O**, **↵** and **Esc** are all bound; the shortcuts dialog lists
  those four and nothing else. The palette's footer row leads to `/search`,
  which searches message bodies as well as titles and says which line matched.
- **Archiving** takes a thread out of the nested list and off the composer,
  keeping its turns on the usage screen — with an undo toast, because it is
  reversible.
- **Invites, access, retention, the plan, the card and the cap** all commit into
  the fixture: revoking an invite frees a seat, replacing an expired card retries
  the invoice that failed against it, and changing plan re-rates the cycle.

## What is not built

- No streaming from a real model — replies come from `fixtures.js`, one written
  per thread and a general one that works from what was typed and what is in
  context.
- No artefact or canvas pane, no voice, no image generation, no threads-inside-
  projects sharing rules beyond `private` / `workspace` / `link`.
- No SSO configuration behind the enforcement switch, no audit log, no seat
  purchasing: adding seats is named as the way out of a full workspace but the
  plan rows are what actually change capacity.
- No per-record notification or read state, which is why Shared with me carries
  no badge — nothing here records what you have already read.
