Cockpit Inbox Design
internal prototype · canonical JSON + Dreamborn Forge HTML
internal generated
design_doc · markdown

Cockpit Inbox Design

Cockpit Inbox Design Date: 2026 04 27 Status: Approved Branch: feature/marketing agents Overview Replace the "Exec Inbox" section in the cockpit right sidebar with a nav level inbox badge and a 40% width slide in drawer. The drawer provides a two pane interface for reading messages, approving exec gates, replying to agents, and composing ad hoc messages to a...

Cockpit Inbox Design

Date: 2026-04-27 Status: Approved Branch: feature/marketing-agents

---

Overview

Replace the "Exec Inbox" section in the cockpit right sidebar with a nav-level inbox badge and a 40%-width slide-in drawer. The drawer provides a two-pane interface for reading messages, approving exec gates, replying to agents, and composing ad hoc messages to any agent.

---

Nav Badge

A ✉ N badge is added to the nav bar between the "testnet" badge and the clock.

  • Count = unread inbox rows where to_agent = 'justin' and processed = false + agent_tasks rows where role = 'exec' and status = 'assigned'
  • Badge pulses amber when count > 0
  • Clicking the badge toggles the drawer open/closed
  • Badge shows is-open styling when drawer is open
  • The existing "Exec Inbox" label and container in the right sidebar are removed

---

Drawer
  • Width: 40% of viewport
  • Position: Fixed to the right edge, full height below the nav
  • Behavior: Slides in with a 200ms ease transition; clicking the overlay or pressing Escape closes it
  • Border: Left edge uses --color-accent to signal it is an interactive layer above the cockpit
  • A semi-transparent overlay covers the rest of the cockpit when the drawer is open (does not block interaction — just visual depth)
Drawer Header

`` ✉ Inbox · N unread [+ Compose] [✕] ``

  • Title shows live unread count
  • + Compose button opens the compose form in the right pane; it gets an is-active style while compose is open
  • closes the drawer

---

Two-Pane Layout

The drawer body is split into two columns:

| Column | Width | Content | |--------|-------|---------| | Message list | 38% | Scrollable list of all inbox items | | Detail pane | 62% | Selected message body + action area |

Message List

Messages are ordered: exec gate tasks first (green), then unread inbox messages by created_at desc, then read/processed messages dimmed at the bottom.

  • From agent name (amber for regular; green for exec gates) + unread dot if unprocessed
  • Subject (truncated to one line)
  • Meta: message_type · ref_id · time ago

Clicking an item selects it (amber border) and loads it in the detail pane. Read messages are dimmed (opacity 0.55).

Detail Pane
  • From → To header
  • message_type · ref_id · timestamp
  • Subject line
  • Full message body (scrollable)
  • HashScan link for the ref topic_id
  • Action area pinned to the bottom (see below)

---

Exec Gate (agent_tasks role=exec, status=assigned)

Action area shows two buttons:

  • Posts { type: 'task.complete', task_id: topicId, agent: 'justin', output: 'Approved via cockpit' } to the task's topic_id via POST /post-hcs-message
  • Button changes to ✓ Approved (green) on success
  • Re-renders the message list after 2 seconds
  • Clicking Block expands a reason text input inline (replaces the button row)
  • User types a reason; a Confirm Block button posts { type: 'task.blocked', task_id: topicId, agent: 'justin', error_message: reason } to the task's topic_id via HCS
  • Cancel collapses back to Approve/Block buttons
Inbox Message (inbox table, message_type != exec.gate)

Action area shows a reply textarea:

`` Reply to <agent> [textarea — placeholder: "Type your reply…"] sends as justin → <agent> via HCS [Dismiss] [Send] ``

  • Looks up the sender agent's inbound_topic_id from agent_definitions
  • Posts { type: 'inbox.message', from: 'justin', to: senderAgent, subject: 'Re: ' + originalSubject, body: replyText, ref_id: originalRefId, ref_type: originalRefType } via POST /post-hcs-message
  • Listener picks up the HCS message and writes it to the inbox table
  • On success: textarea clears, message item dims (marked read)
  • Patches inbox row processed = true directly via Supabase REST (no HCS needed — just clears the unread state)
  • Message dims in the list; badge count decrements

---

Compose (Ad Hoc Message)

Clicking + Compose replaces the detail pane with a compose form. Cancelling returns to the previously selected message.

Fields:

| Field | Type | Options | |-------|------|---------| | To | Dropdown | All agents from AGENT_DEPT map | | Type | Dropdown | question, feedback, fyi, decision.needed, review.request | | Subject | Text input | One-line summary | | Message | Textarea | Full message body |

  • Looks up the selected agent's inbound_topic_id from agent_definitions
  • Posts { type: 'inbox.message', from: 'justin', to: selectedAgent, message_type: selectedType, subject, body } via POST /post-hcs-message
  • Listener writes to inbox table on receipt
  • On success: compose form clears, drawer shows empty detail pane with "Message sent" confirmation

Cancel — returns to previously selected message (or empty detail if none was selected)

---

Loading inbox items

```js // Inbox messages sb.from('inbox') .select('id, from_agent, message_type, subject, payload, ref_id, ref_type, priority, processed, created_at') .eq('to_agent', 'justin') .order('created_at', { ascending: false }) .limit(50)

// Exec gate tasks sb.from('agent_tasks') .select('topic_id, brief, project_id, created_at') .eq('role', 'exec') .eq('status', 'assigned') .order('created_at', { ascending: true }) ```