Files
nucleic/docs/UX_MACOS.md
T

221 lines
11 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.
# Nucleic — macOS UX & Information Architecture (v0)
How the runtime state ([RUNTIME_ARCHITECTURE](RUNTIME_ARCHITECTURE.md)) becomes screens and
flows. Altitude: navigation model, the core screens as wireframes, and the few interaction
flows that define the product. Not a pixel spec.
**North star:** the user is running *many* agents at once. The UI must answer, at a glance,
"which session needs me?" and make answering it (approve / unblock / integrate) one action
away.
---
## 1. Navigation model
Three-column macOS app (`NavigationSplitView`):
```
┌────────────┬────────────────────────────┬──────────────────────────┐
│ SIDEBAR │ SESSION LIST / DASHBOARD │ SESSION DETAIL │
│ │ │ │
│ ▸ Project A│ ● auth-refactor ⚠ needs │ [Transcript | Diff | Log]│
│ Project B│ ◐ flaky-tests running │ │
│ ▸ Project C│ ✓ docs-pass done │ …active tab… │
│ │ ○ spike-graphql idle │ │
│ ───────── │ │ │
│ Dashboard │ [ + New Session ] │ [approval bar / input] │
│ Devices │ │ │
└────────────┴────────────────────────────┴──────────────────────────┘
```
- **Sidebar:** projects (expandable to their sessions) + global destinations (Dashboard,
Devices, Settings).
- **Middle:** the session list for the selected project, or the cross-project **Dashboard**.
- **Detail:** the selected session.
Status is encoded once, consistently, everywhere a session appears:
| Glyph | Status | Meaning |
| --- | --- | --- |
| `⚠` (amber, pulsing) | `awaitingApproval` | **Needs you now** — blocking on a permission |
| `◔` (amber) | `awaitingInput` | Turn done, waiting for your next prompt |
| `◐` (blue, animated) | `running` | Agent working |
| `✓` (green) | `finished` | Done; ready to integrate |
| `○` (gray) | `idle` | Provisioned, not started |
| `✕` (red) | `error` | Failed / interrupted |
"Needs you" states (`⚠`, `◔`, `✕`) sort to the top and drive badge counts.
---
## 2. Dashboard (the "N agents at once" view)
The answer to "what needs me?" across every project. A density-first grid of session cards:
```
┌─ Needs attention (2) ───────────────────────────────────────────────┐
│ ⚠ auth-refactor ProjA "Run: npm publish" [Deny] [Allow ▸] │
│ ◔ payment-flow ProjC waiting for input [Open] │
├─ Running (4) ───────────────────────────────────────────────────────┤
│ ◐ flaky-tests ProjA +312/40 · editing test_runner.py · 2m │
│ ◐ graphql ProjB +88/12 · running tests · 40s │
│ … │
├─ Done, ready to integrate (3) ──────────────────────────────────────┤
│ ✓ docs-pass ProjA +120/5 [Review & Merge ▸] │
└──────────────────────────────────────────────────────────────────────┘
```
- **Inline approvals**: low-risk approvals can be answered straight from the dashboard
without opening the session (high-risk ones require opening detail — see §4).
- Each card shows live one-liner activity (current tool / last action) + diffstat + elapsed.
- Sectioned by urgency, not by project, so triage is global.
---
## 3. Session detail
Header: title, project, backend badge (Claude/Codex), branch, status, controls
(`Interrupt`, `Resume`, `⋯`). Body is three tabs over one shared bottom action area.
### 3.1 Transcript tab (default)
Renders the canonical transcript stream:
- **Assistant prose** — markdown, streaming token-by-token when `verbosity=.full`.
- **Thinking/reasoning** — collapsed by default, expandable.
- **Tool calls** — a compact row per call (`▸ Bash git status`) that expands to show input +
result; file-editing tools link to the Diff tab. Color-coded by `Risk`.
- **System/log** markers inline (setup output, retries, interrupts).
Auto-scrolls while running; "jump to latest" pill when scrolled up.
### 3.2 Diff tab
The live worktree diff vs `base_sha` (WORKTREE_MANAGER §4):
- File tree (left) with per-file `+/`, split/unified toggle, syntax highlight.
- Shows uncommitted *and* committed work (so it's accurate even if the agent never commits).
- Per-hunk navigation; this is also where the user reviews before integrating.
### 3.3 Log tab
Raw stdio / native event tail for debugging an adapter or a stuck session. Power-user surface.
### 3.4 Bottom action area (context-sensitive)
- `running` → live status + `Interrupt`.
- `awaitingApproval` → the **approval bar** (§4).
- `awaitingInput` / `finished` → a prompt composer (send a follow-up turn) **and** an
`Integrate ▸` button.
---
## 4. Approval flow (the defining interaction)
When a session hits `awaitingApproval`, the approval bar slides in (and a notification fires
if the app/session isn't foreground):
```
┌─ ⚠ Permission requested ──────────────────────────────────────────────┐
│ Bash · execute · network │
│ $ npm publish │
│ │
│ [ Deny ] [ Allow once ] [ Allow always ▾ ] (answered by you) │
│ ├ this command, this session │
│ ├ any Bash, this session │
│ └ Bash matching `npm *`, this session │
└───────────────────────────────────────────────────────────────────────┘
```
- **Risk-aware emphasis:** `destructive`/`network` requests are visually loud and *cannot* be
answered from the dashboard — they require opening detail (deliberate friction).
- **`Allow always`** writes an `always_rule` (RUNTIME §5) at the chosen scope; future matches
auto-resolve silently. **Not offered on `destructive` requests** (rm, force-push, `reset
--hard`, …): each must be a deliberate, one-off `Allow`, and a destructive call is never
satisfied by a pre-existing broader rule either — the `ApprovalCoordinator` re-surfaces it.
- **Modify-and-allow** (Claude only; `canModifyToolInput`) reveals an editable input field.
Hidden for backends without the capability — the UI reads `BackendCapabilities`.
- **Multi-client:** if the iPhone answers first, the bar collapses with "Approved on iPhone"
(first-responder-wins, SYNC §5.4).
- **Non-interactive (Codex `exec`)**: instead of an approval bar, a banner — "This session
runs under a fixed policy; blocked actions fail automatically" — set at creation.
---
## 5. New-session flow
`+ New Session` sheet:
```
Project: [ ProjA ▾ ] Base branch: [ main ▾ ]
Backend: ( ● Claude ○ Codex ) Model: [ default ▾ ]
Approvals: ( ● Interactive ○ Fixed policy ) [Codex: Sandbox ▾]
Title: [ auth-refactor ] → branch nucleic/auth-refactor
Prompt: ┌─────────────────────────────────────────────┐
│ Refactor the auth layer to… │
└─────────────────────────────────────────────┘
[ Cancel ] [ Start ]
```
On `Start`: provision worktree → run setup script (output streams into a fresh session detail
view as it provisions) → backend starts. Capability-driven fields (Sandbox only for Codex;
"Fixed policy" warns it disables interactive approvals).
---
## 6. Integration flow
From `awaitingInput`/`finished``Integrate ▸`:
```
Review diff (Diff tab) → choose:
◦ Strategy: ( ● Merge ○ Rebase ○ Squash )
◦ Into: [ main ▾ ]
◦ Commit pending work: [auto message: "auth-refactor"] (editable)
—or— [ Open Pull Request ▸ ] [ Discard… ]
```
- **Clean** → success toast, session → `finished/merged`, offer to clean up the worktree.
- **Conflict** → switches to a conflict view listing conflicted paths with two outs:
*Resolve in worktree* (optionally **"Ask the agent to resolve"** → spins a new turn) or
*Abort* (WORKTREE_MANAGER §6.2).
- **Discard** → confirm if unmerged/dirty, then remove worktree + branch.
---
## 7. Devices & ambient surfaces
- **Devices screen:** paired iPhones (label, scope, last seen), pairing via a displayed QR
(SYNC §4.2), and **revoke**.
- **Menu-bar item:** count of "needs you" sessions; click → mini list jumping into approvals.
- **Notifications:** on `awaitingApproval`/`awaitingInput`; actionable (Allow/Deny) for
low-risk; clicking opens the session.
- **Dock badge:** total attention count.
---
## 8. Mapping to the runtime
| UI surface | Source | Intent it sends |
| --- | --- | --- |
| Session list / dashboard | `AppStore` session summaries (`@Observable`) | select, inline-approve |
| Transcript tab | scoped event stream from `SessionController` | scroll only (read) |
| Diff tab | `WorktreeManager.diff` (on demand, debounced) | — |
| Approval bar | `approval` queue + `BackendCapabilities` | `respondToApproval(id, Decision)` |
| Prompt composer | — | `sendInput(AgentInput)` |
| Integrate sheet | `WorktreeManager.status` | `requestIntegrate(strategy, target, commit)` |
UI holds **no canonical state** — it observes the `SessionController` and sends intents
(RUNTIME §3). This keeps Mac and iPhone as two renderers of the same authority.
---
## 9. Open questions
1. **Inline vs. detail-only approvals** — exact risk threshold that forces opening detail
(proposed: `destructive`/`network` → detail; `readOnly`/`write`/`execute` → inline-OK).
2. **Transcript rendering budget** — virtualization for very long sessions; how much history
to keep mounted vs. page in (ties RUNTIME open-Q #3).
3. **Diff refresh cadence** — on every `fileChange` event vs. on turn boundaries vs. manual,
balancing liveness against churn on big diffs.
4. **Density** — is a grid dashboard enough at 20+ sessions, or do we need a compact "mission
control" list mode and saved filters?