| 1 | # Darwin - Team-Up Workspace Build Spec (binding) |
| 2 | |
| 3 | Build the multi-agent terminal workspace into Darwin's Face. This is the Cockpit replacement that |
| 4 | lets Zion retire Odysseus. **OBEY `/shared/projects/darwin/_ops/DESIGN_LOCK.md`** - brutalist |
| 5 | near-black/teal, NOT generic terminal-app or dashboard chrome. Run the design gate before "done." |
| 6 | |
| 7 | ## Zion's 5 requirements (all must be met) |
| 8 | 1. **Integrated** - agent terminals live INSIDE Darwin (a route/tab in the Face), not a separate app. |
| 9 | 2. **Scrollable** - each pane scrolls its full chat/terminal history (xterm scrollback, pane NOT |
| 10 | height-broken - the pane body must give xterm a real height; fix the Cockpit's constrained-cell bug). |
| 11 | 3. **Resizable** - draggable gutters between panes (use `react-resizable-panels` or equivalent). The |
| 12 | Cockpit never had this; add it. |
| 13 | 4. **Stable overlay** - NO absolute-inset takeover / sibling `display:none` hack. Use a real Next.js |
| 14 | route + normal flow + React state so it never glitches the layout. |
| 15 | 5. **Collapsible sidebar** + clean **1/2/3/4-up split**, each pane = an agent Zion picks |
| 16 | (Claude/Codex/OpenCode-GLM/shell), all resizable. Sidebar collapses to give a single full-bleed pane. |
| 17 | |
| 18 | ## Mobile (HARD requirement) |
| 19 | - Phone (≤820px): the split collapses to **swipeable single-pane tabs** (one agent at a time, tab/swipe |
| 20 | to switch) - the Cockpit's `matchMedia` pattern but cleaner. Sidebar becomes a bottom-sheet/drawer. |
| 21 | - Thumb-reachable launch controls, legible mono type, no horizontal scroll, tap targets ≥44px. |
| 22 | - On-brand per DESIGN_LOCK (brutalist editorial, near-black, teal) - NOT a generic mobile terminal app. |
| 23 | |
| 24 | ## Backend (LIVE - wire to it, don't rebuild) |
| 25 | `darwin-term` on **CT215:9894** (token-gated; token in `/opt/darwin/.env` on CT215 - read it, bake into |
| 26 | the Face server-side or proxy so the browser gets it; do NOT hardcode in client source committed to git - |
| 27 | use a Next.js server route/env). Protocol: |
| 28 | - **WS** `ws://REDACTED-IP:9894/ws?target=&agent=&mode=&session=&cwd=&token=` |
| 29 | - targets: `ct215`(claude), `ct241`(codex), `ct247`(opencode/GLM), + `shell`; modes `persistent|ephemeral` |
| 30 | - client→server: **binary** = keystrokes; **text JSON** `{"t":"resize","cols":N,"rows":N}` = resize |
| 31 | - server→client: **binary** = PTY output → `term.write()`; **text** = banner; **text JSON** |
| 32 | `{"_cockpit":"session","name":...}` = persistent session id (track for attach/close) |
| 33 | - `GET /targets?token=` → TARGETS/AGENT_CMD/COMMON_DIRS maps → build the launcher dynamically |
| 34 | - `GET /sessions?token=` → live tmux sessions (Attach/Kill list) |
| 35 | |
| 36 | ## Front-end stack & approach |
| 37 | - Next.js (existing Face app) + **`@xterm/xterm` + `@xterm/addon-fit` v5** (npm; the Cockpit used vendored |
| 38 | xterm v4 - use modern packages, same API). One xterm instance per pane; `term.onData`→binary WS send; |
| 39 | WS binary→`term.write`; on container resize → FitAddon.fit() (GUARD against fitting a hidden/0×0 pane - |
| 40 | only fit visible panes, else it sends a bogus tiny PTY resize and garbles TUIs) → send resize control frame. |
| 41 | - Layout: `react-resizable-panels` for the 1-4 split with drag gutters; React state for pane count/focus |
| 42 | (no manual DOM bookkeeping). A real route (e.g. `/workspace` or a top-level tab on the main surface). |
| 43 | - Launcher sidebar (collapsible): Source/Agent/Mode/cwd selects + Launch + a Local-models (3060) panel + |
| 44 | live-sessions list (Attach/Kill, poll /sessions). Sidebar collapse → single full pane. |
| 45 | - xterm theme from Darwin tokens (near-black bg, teal cursor/accent) - terminal must look ON-BRAND. |
| 46 | |
| 47 | ## Reuse notes (from recon) |
| 48 | - Port the Cockpit's agent-launch UX (source→agent→cwd→mode→launch) but rebuild the layout natively. |
| 49 | - Persistent mode = tmux (survives reload); ephemeral dies with socket. Surface the session id for reattach. |
| 50 | - Token: the browser needs it for the WS. Cleanest: a Next.js API route on the Face proxies/injects the |
| 51 | token (kept server-side) OR serve it via a server component env - never commit the token to client git. |
| 52 | |
| 53 | ## Verify / deliver |
| 54 | - Build on local ext4, redeploy Face to CT252 (`/opt/darwin/face`, restart `darwin-face`). |
| 55 | - Prove: open `/workspace`, launch a `shell` on ct215 → live terminal, type a command, see output, SCROLL |
| 56 | back, RESIZE a pane via gutter, collapse the sidebar, switch 1→2→3→4 split. Take desktop + mobile (390px) |
| 57 | screenshots to `/shared/projects/darwin/face/_review/` (`workspace-desktop.png`, `workspace-mobile.png`). |
| 58 | - Run the DESIGN_LOCK gate. If it looks like a generic terminal/dashboard, redo bolder. |