Files
nucleic/docs/UX_MACOS.md
T

11 KiB
Raw Blame History

Nucleic — macOS UX & Information Architecture (v0)

How the runtime state (RUNTIME_ARCHITECTURE) 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/finishedIntegrate ▸:

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?