221 lines
11 KiB
Markdown
221 lines
11 KiB
Markdown
# 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?
|