Files
paseo/PLAN_PROJECTS_WORKSPACES_TABS.md
T

449 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Projects → Workspaces → Tabs (Full-Stack Plan)
This plan replaces the current “agents list + single chat view” IA with:
- **Projects** (grouped by a stable `projectKey`)
- **Workspaces** per project (today: local Paseo worktrees + main checkout; future: cloud VM)
- A **workspace main view** that is a **tabbed panel** where each tab is either an **agent** chat or a **terminal**
No legacy UI is kept: we redirect and fully rework the current routes and RPCs that don’t make sense anymore.
---
## TL;DR
- **Sidebar** becomes **Projects → Workspaces**; still supports drag reorder (projects + workspaces) persisted on-device.
- **Project key**: **remote when available**; otherwise **local repo root / workspace root** (CWD fallback).
- **Workspaces**: show **main checkout + all Paseo worktrees**, including empty workspaces.
- **Main view**: opening a workspace shows a **tab bar** with **all agents in that workspace**; horizontally scrollable.
- **Tabs** are a discriminated union: `agent` or `terminal` (terminals are not special).
- **Right sidebar** stays **Changes + Files**, scoped to the current workspace; **terminals removed** from the right sidebar.
- **File Explorer RPC**: replace agent-scoped explorer/download-token RPC with **workspace-scoped** versions.
- **Agent data** is loaded once at daemon startup and kept queryable in-memory (no expensive directory rescans at runtime).
- **Splits**: not implemented now, but abstractions should not block future VSCode-style splits.
---
## Definitions & IDs
### Project
A **Project** is a grouping container keyed by `projectKey`.
**`projectKey` derivation (must match user decisions):**
1. If git remote is available: use normalized remote key (e.g. `remote:github.com/org/repo`).
2. Else (git with no remote OR not git): use the **workspace/repo root path** (local key).
Notes:
- For Paseo-owned worktrees, prefer grouping via the **shared repo root** when available (so multiple workspaces still group under one project even without a remote).
- For plain directories, the root path is the directory itself.
### Workspace
A **Workspace** is an environment you can “open” and run agents/terminals within.
Today:
- **Local main checkout** (the normal repo root) is a workspace and **cannot be archived**.
- **Local Paseo worktrees** are workspaces (including empty ones).
Future:
- Workspace could point to a **cloud VM** or other remote compute target.
Workspace identity:
- `workspaceId` should be stable and unique within a server.
- For local: derive from canonical absolute `workspaceRoot` (and/or from the worktree metadata id when we add one).
### Tab
Tabs are the only top-level “things” in the workspace main view.
**Discriminated union (locked):**
```ts
type WorkspaceTab =
| { kind: "agent"; tabId: string; agentId: string }
| { kind: "terminal"; tabId: string; terminalId: string }
```
Terminals are not special: they are just `kind: "terminal"` tabs.
### Panel (future splits)
We do **not** implement splits now. We still design tab/panel state so it can evolve into a layout model:
```ts
type PanelLayout =
| { kind: "single"; panelId: string }
// later: | { kind: "split"; direction: "row"|"column"; a: PanelLayout; b: PanelLayout; sizes: number[] }
```
For this milestone, `PanelLayout` is always `{ kind: "single" }`.
---
## UX (Agreed)
### Sidebar (Left)
- Shows **Projects** (project icon + name + status dot).
- Each project expands to show **Workspaces**.
- Workspaces list includes:
- **Main checkout workspace** (always present, not archivable)
- **All Paseo worktrees** for that project (show all, including empty workspaces)
- Workspace row shows:
- **Branch name**
- **Created at**
- **No path**
- **Drag reorder**:
- Projects reorder globally
- Workspaces reorder within a project
- Persisted on-device
- **No agents** are shown in the sidebar (agents live in tabs + in the All Agents list).
### Main View (Workspace)
- Opening a workspace shows a workspace-scoped screen.
- Top area contains a **tab bar** with all agent tabs in that workspace:
- horizontally scrollable
- switching tabs focuses the selected agent/terminal
- **Remember last focus**:
- When returning to a workspace, restore the last focused tab (agent or terminal).
- When opening a workspace for the first time: pick the most recent agent, else open the draft agent flow.
### Tabs + Icons
- Each tab type provides its own icon.
- Terminal tabs: terminal icon.
- Agent tabs: status icon (running/idle/needs-attention/error) or provider icon if needed.
### Right Sidebar (Explorer Sidebar)
- Remains **Changes + Files** only.
- Scope is the **workspace you are looking at**.
- Selection of agent/terminal tab does **not** change the right sidebar scope.
- Terminals are removed from the right sidebar entirely.
- Gestures/overlay behavior are unchanged (mobile).
### Mobile-specific
- Keep existing overlay sidebars and gestures unchanged.
- Use a **tab switcher** pattern (bottom sheet / switcher) for tab navigation.
- Header is simplified:
- shows only workspace name (branch name)
- has space for a **plus** button
- Plus button:
- opens the **draft agent flow**, pre-scoped to the current workspace.
---
## Data Model (Client)
### On-device persisted state
Persist (per `serverId`):
- `projectOrder: projectKey[]`
- `workspaceOrderByProject: Record<projectKey, workspaceId[]>`
- `lastOpenedWorkspaceByProject: Record<projectKey, workspaceId>`
- `lastFocusedTabByWorkspace: Record<workspaceId, tabId>`
- `openTabsByWorkspace: Record<workspaceId, WorkspaceTab[]>` (optional; can be derived initially from agents/terminals list but should be persistable)
Default ordering:
- If no persisted order:
- Projects sorted by `createdAt/firstSeenAt` (or by name as fallback)
- Workspaces sorted by **createdAt** (locked decision)
### Derived view models
Compute:
- `projects[]` from workspace + checkout metadata
- `projectStatusDot` aggregated from agent statuses in all workspaces in that project
- `workspaceStatusDot` aggregated from agent statuses in that workspace
---
## Data Model (Server)
### Agents: load once, query often
Current pain:
- Hydrating agents by scanning directories is expensive.
Target behavior:
- On daemon startup, perform a single load/scan and build an in-memory index:
- by `agentId`
- by `workspaceRoot`
- by `projectKey`
- by status (running, attention, errored, etc.)
- No periodic rescans at runtime.
- When an agent is created/updated/removed, update the in-memory index incrementally.
- This aligns with the PID lock: we don’t support concurrent disk mutation anyway.
- Future: replace with local SQL DB, but not in this milestone.
### Workspaces (local)
Source of truth:
- Main checkout: derived from repo root / current connection cwd policy.
- Paseo worktrees: `paseo_worktree_list_*` plus metadata.
Worktree metadata needs `createdAt`:
- Add `createdAt` to Paseo worktree metadata (new version).
- Backfill for existing worktrees using filesystem stat of metadata file or worktree directory.
---
## RPC / Protocol Changes (Server ↔ App)
### Replace: File Explorer request (agent-scoped → workspace-scoped)
**Current (to remove):**
- `file_explorer_request` requires `agentId`.
**New (replacement; same message type name or updated schema, but old behavior removed):**
- `file_explorer_request` keyed by `workspaceId` (or `workspaceRoot`) instead of `agentId`.
- Requests can succeed even if there are **no agents** in the workspace (empty workspace is valid).
Rules:
- All explorer operations are rooted at `workspaceRoot`.
- Path traversal is prevented (no `..`, enforce prefix).
### Replace: File download token request (agent-scoped → workspace-scoped)
**Current (to remove):**
- `file_download_token_request` requires `agentId`.
**New:**
- `file_download_token_request` requires `workspaceId` (or `workspaceRoot`) and `path`.
- Token resolution is workspace-rooted with the same traversal protections.
### Worktree list RPC: include `createdAt`
Update `paseo_worktree_list_response` to include:
- `createdAt` (ISO string)
- `workspaceId` (optional if client can derive; preferred to include for stability)
Ensure the list includes:
- all Paseo worktrees for the requested repo/project
- enough info to render workspace row: branch name + createdAt
### Checkout lite payload: ensure it contains enough for project grouping
For workspace/project grouping on the client, ensure we have:
- `isGit`
- `remoteUrl` (nullable)
- `currentBranch`
- `isPaseoOwnedWorktree`
- `mainRepoRoot` (when worktree-owned)
- (optional) `repoRoot` for non-worktree git repos
---
## App Changes (packages/app)
### Routes
Replace/redirect:
- `/h/:serverId` should no longer redirect to “draft agent”; it should land on:
- last opened workspace, or
- a workspace picker (the new sidebar is the primary picker)
- `/h/:serverId/agent` and `/h/:serverId/agent/:agentId` are replaced with:
- `/h/:serverId/workspace/:workspaceId` (main workspace screen)
- `/h/:serverId/workspace/:workspaceId/draft-agent` (draft flow, pre-scoped)
No legacy route behavior remains; old routes redirect.
### Left Sidebar rewrite
Replace the current agent list sidebar with:
- Project list (icon + name + status dot)
- Nested workspace list (branch name + createdAt)
- Drag reorder persisted on-device
### Workspace screen
New top-level screen composition:
- Header: workspace name (branch) + plus button
- Tab strip (horizontal)
- Active tab content (agent chat or terminal UI)
- Right sidebar: changes + file explorer (workspace-scoped)
### Tabs system
Implement `WorkspaceTab` union:
- `agent` tabs map to existing agent chat component (but now hosted within the workspace screen).
- `terminal` tabs map to existing terminal component previously shown in the right sidebar.
Remember focus:
- Persist `lastFocusedTabByWorkspace`.
### Terminal migration
- Remove terminal UI from right sidebar.
- Expose terminals through tabs:
- create terminal -> adds a `terminal` tab
- show terminal output in the main area
### File explorer state migration
Update file explorer client state/actions:
- Key state by `workspaceId` (not `agentId`).
- Update all call sites to use the new RPC parameters.
---
## Status Dots (Sidebar)
### Agent → Workspace → Project aggregation
Define a simple severity ordering, e.g.:
1. Error
2. Needs attention
3. Running
4. Idle / none
Compute:
- workspace status = max severity across agents in that workspace
- project status = max severity across all workspaces in that project
No agents are displayed in the sidebar; only aggregated signal.
---
## User Flows (Agreed)
### Open app → open a workspace
1. User opens app.
2. Left sidebar shows Projects.
3. User selects a Project.
4. User selects a Workspace (main checkout or a worktree).
5. Workspace screen opens:
- tab strip shows all agents in that workspace
- restores last focused tab if available
- otherwise picks most recent agent
- otherwise opens draft agent flow pre-scoped to the workspace
### Create a new agent in a workspace
1. User is inside a workspace.
2. User taps plus in header.
3. Draft agent flow opens, pre-scoped to that workspace root.
4. On create:
- new agent appears as a new `agent` tab in the workspace
- it becomes focused
### Open a terminal
1. User taps plus (or a “new tab” affordance).
2. Selects “Terminal”.
3. A `terminal` tab is created and focused.
4. Terminal is fully usable inside the tab panel.
### Switch tabs (mobile)
1. User opens tab switcher.
2. Selects an agent/terminal tab.
3. Workspace focuses that tab and persists focus.
### Reorder projects and workspaces
1. User drags a project to reorder.
2. Order persists on-device and survives app restart.
3. User expands a project and drags a workspace row to reorder within the project.
4. Order persists on-device and survives app restart.
---
## Acceptance Criteria
### Sidebar / Navigation
- Projects are grouped by `projectKey`:
- remote when available
- otherwise local repo/workspace root key
- Sidebar shows **project icon** for each project.
- Each project lists workspaces (main checkout + all Paseo worktrees), including empty workspaces.
- Workspace rows show **branch name + createdAt**, and **do not show path**.
- Projects and workspaces can be drag-reordered; order is persisted on-device.
- Sidebar does not list agents anywhere.
### Workspace main view
- Opening a workspace shows a tabbed main view (not a single chat screen).
- Tab bar contains all agents in the workspace; horizontally scrollable.
- Last focused tab is restored when returning to a workspace.
- Header shows workspace name and a plus button.
- Plus opens draft agent flow pre-scoped to workspace.
- Mobile uses a tab switcher; sidebars/gestures unchanged.
### Tabs / Terminals
- Tabs are a discriminated union: `agent` or `terminal`.
- Terminals are accessible only as tabs (no terminal section in the right sidebar).
- Each tab type can render a custom icon; agent tabs can render status.
### Explorer / Changes / Files
- Right sidebar contains only Changes + Files, scoped to the opened workspace.
- File explorer and download token requests are workspace-scoped (no agentId in protocol).
- File explorer works for empty workspaces (no agents yet).
### Migration / No legacy
- Old agent-centric routes redirect to the new workspace routes.
- Old agent-scoped file explorer RPC is removed/replaced (no legacy behavior left behind).
---
## Implementation Phases (Suggested)
### Phase 0 — Scaffolding
- Add shared types: `Project`, `Workspace`, `WorkspaceTab`.
- Add on-device storage keys and migration scaffolding (no UI yet).
### Phase 1 — Server protocol + data
- Replace file explorer + download token RPCs to be workspace-scoped.
- Ensure checkout-lite has enough fields for grouping.
- Add worktree `createdAt` and include it in worktree list response.
- Ensure “main checkout workspace” can be represented as a workspace.
- Ensure agent index loads once at startup and is queryable without rescans.
### Phase 2 — App navigation rewrite
- Introduce workspace routes and redirect old routes.
- Add new workspace screen layout (header + tabs + explorer sidebar).
### Phase 3 — Sidebar rewrite
- Implement Projects → Workspaces sidebar UI.
- Implement project icon wiring.
- Implement drag reorder + persisted ordering.
- Implement project/workspace status dots from agent status aggregation.
### Phase 4 — Tabs + terminals migration
- Implement tab state + last focused persistence.
- Move terminals out of right sidebar and into tabs.
- Implement mobile tab switcher.
### Phase 5 — Polish + verification
- Verify flows end-to-end (open workspace, tabs, explorer, plus flows).
- Confirm no legacy route/UI remains.
- Typecheck and targeted runtime smoke tests.
---
## Verification Checklist
- `npm run typecheck` passes after each phase.
- Explorer sidebar still behaves the same (gestures/overlay).
- No references remain to “file explorer by agentId” or “download token by agentId”.
- Terminals are not rendered in the right sidebar.
- Workspace list includes empty worktrees and main checkout.
- Reorder persistence works and survives app restart.