# 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 · destructive                                          │
│   $ rm -rf build/ && 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.
- **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?
