Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ Top to bottom:

1. **Brand row** — logo mark and a "New workbench" button (`+`) that
opens `/new`.
2. **Bench list** — **Agents**, then **Channels**. Agent rows are
`kind: chat` DMs (one per agent). Channel rows are `kind: workbench`
rooms. Search is built into the list itself. Nothing page-scoped
ever renders in this body; it lists conversations, not product
sections.
2. **Bench list** — one recency list of conversations, pins first.
Agent DMs (`kind: chat`, one per agent) mix with channels
(`kind: workbench` rooms). Not two labeled empty sections, and not
a list titled Workbenches. Search is built into the list itself.
Nothing page-scoped ever renders in this body; it lists
conversations, not product sections.
3. **Footer rail** — Mission Control is pinned above the rail as its own
row. The first-run rail below it is Routines, Files, Skills, Agents, in
that order (CL-6765). Insights and Evals join that rail only when
Expand Down Expand Up @@ -60,13 +61,15 @@ consumed by both). A route that gets renamed or relocated leaves a redirect
behind at its old path — old links and bookmarks always land somewhere
real, never a 404.

**`/new` is the shipped create surface** (`NewWorkbenchPickerRoute`). The
primary act is a prompt: say what the channel should do, or pick a
named-template shortcut underneath. Blank `+` / prompt mint an empty
channel and invite nobody. Named templates mint that same empty
channel, then invite existing agents (including Myra as a participant,
never as the mint host). The sidebar `+` and an empty-bench land on
`/` both open this route — there is no Describe door.
**`/new` is the shipped create surface** (`NewWorkbenchPickerRoute`),
not home. The primary act is a prompt: say what the channel should do,
or pick a named-template shortcut underneath. Blank `+` / prompt mint
an empty channel and invite nobody. Named templates mint that same
empty channel, then invite existing agents (including Myra as a
participant, never as the mint host). The sidebar `+` opens this
route. First-run after credential does not: `/` hops to Myra's one
DM (`openAgentDm` / find-or-reopen). There is no parallel Myra home
route and no Describe door.

**`/inbox` is gone as a page** (CL-6151). The path stays as a redirect
home so old links still resolve; it is not a live groups inbox.
Expand Down
65 changes: 37 additions & 28 deletions PRODUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ opens a multi-principal room (`kind: workbench`). There is no separate
"project" or "space" object sitting above the conversation; the
conversation is the unit of work.

This is why the sidebar is Agents then Channels, not one recency list
titled Workbenches. The same Interchange agent can sit in its DM and in
This is why the sidebar is one recency list of conversations (pins
first) — agent DMs mixed with channels — not two labeled sections, and
not a list titled Workbenches. That noun is the product name and the
mint verb. The same Interchange agent can sit in its DM and in
many channels; product reopens or invites, and does not clone the
definition or mint a sibling instance per room. See
[docs/GLOSSARY.md](docs/GLOSSARY.md) for the full term mapping and
Expand All @@ -34,43 +36,48 @@ team spaces); within a bench, they open, create, and work in workbenches.
Workbench is intentionally not a multi-pane IDE. The product surface is one
column at a time:

- **A sidebar of Agents then Channels** lists conversations in the
selected bench. Agent rows are DMs (`kind: chat`); channel rows are
rooms (`kind: workbench`). There is no one recency list titled
- **A sidebar of conversations** lists agent DMs and channels together
by recency, pins first, in the selected bench. Agent rows are DMs
(`kind: chat`); channel rows are shared rooms (`kind: workbench`).
There are not two labeled empty sections, and the list is not titled
Workbenches.
- **"New workbench" always creates.** It opens `/new` — the shipped
prompt-primary picker — never a picker of existing things to join.
Plus mints an empty channel. Nobody is auto-hosted.
prompt-primary picker — never home, and never a picker of existing
things to join. Plus mints an empty channel. Nobody is auto-hosted.
- **Agents are principals, not templates.** Opening Sales opens Sales —
the one 1:1 tenant with that agent. The same agent can sit in its DM
and in many channels. Product reopens or invites; it does not clone
the definition or mint a sibling instance per room. Myra is an agent
row, not a special home slot.
the definition or mint a sibling instance per room. Myra is the
first-run guide in her DM, not a special home slot and not a
parallel home route.
- The active workbench occupies the main column; a contextual panel beside
it carries account-wide surfaces (approvals, recent activity) that stay
visible regardless of which workbench is open.

## First run

A brand-new account is walked through login and a credential connect, then
lands on the create surface rather than an empty shell:
lands in Myra's one DM rather than `/new` or an empty shell:

1. **Login.**
2. **Credential** — connect a model provider (one-click OAuth for
supported providers, or a pasted API key); see `packages/onboarding`.
3. **Create** — `/` hops an empty bench (zero workbenches) to `/new`, the
prompt-primary picker (`apps/web/src/pages/new-workbench-picker.tsx`).
A prompt box is the primary act: typing a goal and submitting mints an
empty channel and sends that text as the first message; blank plus
invites nobody. Named-template rows underneath mint that same empty
channel, then invite existing principals (including Myra as a
participant, never as mint `definitionId`) — one-click shortcuts, not
a kind-then-Create second step. There is no Describe door and no
`describe-first-workbench.tsx`.

A bench that already has one or more workbenches skips create and lands
on `workbenches[0]` (see `apps/web/src/pages/home-page.tsx`). Myra is an
agent row, not a home slot.
3. **Myra's DM** — `/` hops an empty bench to that agent's one DM
(`openAgentDm` / find-or-reopen). There is no parallel Myra home
route. `/new` stays the create door (sidebar `+`), not this hop.

Create stays on `/new` (`apps/web/src/pages/new-workbench-picker.tsx`):
a prompt box is the primary act: typing a goal and submitting mints an
empty channel and sends that text as the first message; blank plus
invites nobody. Named-template rows underneath mint that same empty
channel, then invite existing principals (including Myra as a
participant, never as mint `definitionId`) — one-click shortcuts, not
a kind-then-Create second step. There is no Describe door and no
`describe-first-workbench.tsx`.

A bench that already has one or more workbenches skips first-run and
lands on `workbenches[0]` (see `apps/web/src/pages/home-page.tsx`). Myra
is the first-run guide in her DM, not a home slot.

The shell's first-run destinations stay small on purpose (CL-6765):
Mission Control is pinned above the footer rail; the rail itself is
Expand Down Expand Up @@ -158,13 +165,15 @@ as an explicit absence, never a fabricated zero. See `packages/insights`.
User-facing surfaces (UI, docs, support) use exactly these nouns:

- **Workbench** — the product name, and the mint verb ("New workbench").
A workbench is a conversation tenant: a DM or a channel.
- **Agent** — a coworker identity in the sidebar's Agents list. Opening
the row reopens that agent's one DM. Never "template."
A workbench is a conversation tenant: a DM or a channel. Do not title
the sidebar list Workbenches.
- **Agent** — a coworker principal. Opening the row reopens that agent's
one DM. Never "template." Myra is the first-run guide in her DM.
- **DM** — the one 1:1 conversation with an agent. Never cloned by a
second open.
- **Channel** — a multi-party room. Plus mints an empty one; nobody is
auto-hosted. Named templates invite existing agents into that room.
- **Channel** — a shared room between people and agents. Plus mints an
empty one; nobody is auto-hosted. Named templates invite existing
principals into that room.
- **Bench** — the shared team scope a person signs into and switches
between; shown in the bench switcher, never called a "workspace" or
"org" in copy.
Expand Down
61 changes: 34 additions & 27 deletions apps/web/src/pages/home-page.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
// Default land: `/` is a hop onto the most-recent existing workbench or
// `/new`, never a parallel Myra home. A bench that already has one or
// Myra's one DM, never the picker. A bench that already has one or
// more workbenches lands in the first listed row (`workbenches[0]`) in
// the main stage. A brand-new bench with zero workbenches has nothing to
// land in yet, so this hop sends it to the guided create surface
// (`NewWorkbenchPickerRoute`, CL-6342) instead of auto-minting an
// unlabeled "New Workbench" and dropping the person straight into it —
// that auto-mint (CL-6138) is exactly the confusing empty-bench landing
// this hop used to produce. Home as a dashboard does not earn its keep —
// `/` only exists as this hop onto `/w/:workbenchId` or `/new`. Deep
// links to other pages are unchanged.
// the main stage. A brand-new bench with zero workbenches waits for
// Myra's own definition to exist, then opens her DM the same way
// "Talk to Myra" does (`openAgentDmChat`) — never `/new`, never
// `ensureMyraWorkbench`. Home as a dashboard does not earn its keep —
// `/` only exists as this hop onto `/w/:workbenchId`. Deep links to
// other pages are unchanged.
//
// Right after a provider connect this hop is also the wait (CL-6457's
// deploys run in the background, so landing here can beat them). CL-6462
// settled what that wait looks like: one warm loader and nothing else. For
// a zero-workbench bench the wait is for Myra's own definition to exist at
// all — the picker's "Create workbench" needs it too, so checking here
// first means the picker never opens onto a create button that would just
// throw. The check is simply retried every few seconds, because Myra's
// readiness IS the test of whether the person can start — she is deployed
// first (`SETUP_AGENT_ASSET_NAME` leads `DEFAULT_WORKFLOWS`), so the moment
// she's ready we go, with every other seeded workflow still converging
// behind us. Readiness is read only to tell a wait from a genuine failure,
// never to draw a progress number: a seed count is an implementation
// detail, and "0 of 5" told a waiting person nothing.
// all — then we open her DM. The check is simply retried every few seconds,
// because Myra's readiness IS the test of whether the person can start —
// she is deployed first (`SETUP_AGENT_ASSET_NAME` leads `DEFAULT_WORKFLOWS`),
// so the moment she's ready we go, with every other seeded workflow still
// converging behind us. Readiness is read only to tell a wait from a
// genuine failure, never to draw a progress number: a seed count is an
// implementation detail, and "0 of 5" told a waiting person nothing.
//
// CL-6780: that wait is for the agent, never a workbench that does not
// exist yet — so the loader says "Preparing your agent", and a skip with
Expand All @@ -37,10 +33,13 @@ import { listAllWorkbenches, WorkbenchLoadingState } from "@corbits/chat-ui";
import { describeApiError } from "@corbits/api-query";

import { fetchAgentReadiness, hasActiveCredential } from "../onboarding";
import { listAgentDefinitions } from "../agents-api";
import { openAgentDmChat } from "../agent-dm-launch";
import { findMyraDefinition } from "../myra-workbench";
import { useBench } from "../bench-context";
import { workbenchPath } from "../workbench-path";
import { useNavigate } from "../navigation";
import { NEW_WORKBENCH_PATH, ONBOARDING_PATH } from "../routes";
import { ONBOARDING_PATH } from "../routes";

type LandState =
/** Working on it: the warm loader, whether we are reading the bench's
Expand Down Expand Up @@ -102,18 +101,26 @@ export function HomeRoute({
retryTimer = setTimeout(() => setAttempt((count) => count + 1), retryMs);
};

// Zero workbenches: wait for Myra's own definition to exist, then send
// the person to the picker rather than minting anything ourselves —
// "she can't start yet" and "here, go create your first workbench"
// are different messages, and only the readiness check tells them
// apart. Without a credential the drain never starts (CL-6780), so a
// not-ready status with no credential is an honest next step, not a
// forever spin on "getting ready".
// Zero workbenches: wait for Myra's own definition to exist, then
// open her DM the same way "Talk to Myra" does — never `/new`.
// "She can't start yet" and "here is her chat" are different
// messages, and only the readiness check plus a real definition
// tell them apart. Without a credential the drain never starts
// (CL-6780), so a not-ready status with no credential is an honest
// next step, not a forever spin on "getting ready".
const awaitFirstWorkbench = () => {
void fetchAgentReadiness().then((readiness) => {
if (cancelled) return;
if (readiness.kind === "ready" || readiness.kind === "chat-ready") {
navigate(NEW_WORKBENCH_PATH);
void listAgentDefinitions(selectedTenantId).then((definitions) => {
if (cancelled) return;
const myra = findMyraDefinition(definitions);
if (myra === undefined) {
waitAndRetry();
return;
}
void openAgentDmChat(selectedTenantId, myra.id, navigate);
});
return;
}
void hasActiveCredential(selectedTenantId).then((probe) => {
Expand Down
Loading
Loading