{
  "id": "2026-04-27-cockpit-inbox-design-5d5847e98c",
  "scope": "redkey",
  "source_of_truth": "repo",
  "source_path": "docs/superpowers/specs/2026-04-27-cockpit-inbox-design.md",
  "source_kind": "markdown",
  "visibility": "internal",
  "renderer_id": "design_doc.dreamborn-forge.generated.v1",
  "design_system": "dreamborn-design-system:forge",
  "generated_at": "2026-05-09T13:00:55.770Z",
  "artifact_type": "design_doc",
  "schema_version": "design_doc.generated.v1",
  "title": "Cockpit Inbox Design",
  "summary": "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...",
  "format_source": "markdown",
  "sections": [
    {
      "title": "Cockpit Inbox Design",
      "level": 1,
      "body": "**Date:** 2026-04-27  \n**Status:** Approved  \n**Branch:** feature/marketing-agents\n\n---"
    },
    {
      "title": "Overview",
      "level": 2,
      "body": "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.\n\n---"
    },
    {
      "title": "Nav Badge",
      "level": 2,
      "body": "A `✉ N` badge is added to the nav bar between the \"testnet\" badge and the clock.\n\n- **Count** = unread `inbox` rows where `to_agent = 'justin'` and `processed = false` + `agent_tasks` rows where `role = 'exec'` and `status = 'assigned'`\n- Badge pulses amber when count > 0\n- Clicking the badge toggles the drawer open/closed\n- Badge shows `is-open` styling when drawer is open\n- The existing \"Exec Inbox\" label and container in the right sidebar are removed\n\n---"
    },
    {
      "title": "Drawer",
      "level": 2,
      "body": "- **Width:** 40% of viewport\n- **Position:** Fixed to the right edge, full height below the nav\n- **Behavior:** Slides in with a 200ms ease transition; clicking the overlay or pressing Escape closes it\n- **Border:** Left edge uses `--color-accent` to signal it is an interactive layer above the cockpit\n- A semi-transparent overlay covers the rest of the cockpit when the drawer is open (does not block interaction — just visual depth)"
    },
    {
      "title": "Drawer Header",
      "level": 3,
      "body": "```\n✉ Inbox · N unread       [+ Compose]  [✕]\n```\n\n- Title shows live unread count\n- `+ Compose` button opens the compose form in the right pane; it gets an `is-active` style while compose is open\n- `✕` closes the drawer\n\n---"
    },
    {
      "title": "Two-Pane Layout",
      "level": 2,
      "body": "The drawer body is split into two columns:\n\n| Column | Width | Content |\n|--------|-------|---------|\n| Message list | 38% | Scrollable list of all inbox items |\n| Detail pane | 62% | Selected message body + action area |"
    },
    {
      "title": "Message List",
      "level": 3,
      "body": "Messages are ordered: exec gate tasks first (green), then unread inbox messages by `created_at desc`, then read/processed messages dimmed at the bottom.\n\nEach list item shows:\n- **From** agent name (amber for regular; green for exec gates) + unread dot if unprocessed\n- **Subject** (truncated to one line)\n- **Meta:** `message_type · ref_id · time ago`\n\nClicking an item selects it (amber border) and loads it in the detail pane. Read messages are dimmed (opacity 0.55)."
    },
    {
      "title": "Detail Pane",
      "level": 3,
      "body": "Displays:\n- From → To header\n- `message_type · ref_id · timestamp`\n- Subject line\n- Full message body (scrollable)\n- HashScan link for the ref topic_id\n- Action area pinned to the bottom (see below)\n\n---"
    },
    {
      "title": "Exec Gate (agent_tasks role=exec, status=assigned)",
      "level": 3,
      "body": "Action area shows two buttons:\n\n**Approve**\n- Posts `{ type: 'task.complete', task_id: topicId, agent: 'justin', output: 'Approved via cockpit' }` to the task's `topic_id` via `POST /post-hcs-message`\n- Button changes to `✓ Approved` (green) on success\n- Re-renders the message list after 2 seconds\n\n**Block**\n- Clicking Block expands a reason text input inline (replaces the button row)\n- 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\n- Cancel collapses back to Approve/Block buttons"
    },
    {
      "title": "Inbox Message (inbox table, message_type != exec.gate)",
      "level": 3,
      "body": "Action area shows a reply textarea:\n\n```\nReply to <agent>\n[textarea — placeholder: \"Type your reply…\"]\nsends as justin → <agent> via HCS         [Dismiss] [Send]\n```\n\n**Send**\n- Looks up the sender agent's `inbound_topic_id` from `agent_definitions`\n- Posts `{ type: 'inbox.message', from: 'justin', to: senderAgent, subject: 'Re: ' + originalSubject, body: replyText, ref_id: originalRefId, ref_type: originalRefType }` via `POST /post-hcs-message`\n- Listener picks up the HCS message and writes it to the `inbox` table\n- On success: textarea clears, message item dims (marked read)\n\n**Dismiss**\n- Patches `inbox` row `processed = true` directly via Supabase REST (no HCS needed — just clears the unread state)\n- Message dims in the list; badge count decrements\n\n---"
    },
    {
      "title": "Compose (Ad Hoc Message)",
      "level": 2,
      "body": "Clicking `+ Compose` replaces the detail pane with a compose form. Cancelling returns to the previously selected message.\n\n**Fields:**\n\n| Field | Type | Options |\n|-------|------|---------|\n| To | Dropdown | All agents from `AGENT_DEPT` map |\n| Type | Dropdown | `question`, `feedback`, `fyi`, `decision.needed`, `review.request` |\n| Subject | Text input | One-line summary |\n| Message | Textarea | Full message body |\n\n**Send to HCS**\n- Looks up the selected agent's `inbound_topic_id` from `agent_definitions`\n- Posts `{ type: 'inbox.message', from: 'justin', to: selectedAgent, message_type: selectedType, subject, body }` via `POST /post-hcs-message`\n- Listener writes to `inbox` table on receipt\n- On success: compose form clears, drawer shows empty detail pane with \"Message sent\" confirmation\n\n**Cancel** — returns to previously selected message (or empty detail if none was selected)\n\n---"
    },
    {
      "title": "Loading inbox items",
      "level": 3,
      "body": "```js\n// Inbox messages\nsb.from('inbox')\n  .select('id, from_agent, message_type, subject, payload, ref_id, ref_type, priority, processed, created_at')\n  .eq('to_agent', 'justin')\n  .order('created_at', { ascending: false })\n  .limit(50)\n\n// Exec gate tasks\nsb.from('agent_tasks')\n  .select('topic_id, brief, project_id, created_at')\n  .eq('role', 'exec')\n  .eq('status', 'assigned')\n  .order('created_at', { ascending: true })\n```"
    },
    {
      "title": "Loading agent inbound topics (for reply/compose)",
      "level": 3,
      "body": "```js\nsb.from('agent_definitions')\n  .select('agent_id, inbound_topic_id')\n```\n\nFetched once on drawer open and cached for the session."
    },
    {
      "title": "Badge count",
      "level": 3,
      "body": "```js\nunreadCount = inbox.filter(m => !m.processed).length + execTasks.length\n```\n\n---"
    },
    {
      "title": "Realtime",
      "level": 2,
      "body": "Existing Supabase Realtime subscriptions are extended:\n\n- `agent_tasks` changes where `role = 'exec'` → refresh exec task list + badge count (already exists, keep)\n- Add: `inbox` table changes where `to_agent = 'justin'` → refresh inbox list + badge count\n\n---"
    },
    {
      "title": "Removed",
      "level": 2,
      "body": "- `Exec Inbox` `<span class=\"cockpit-label\">` and `#exec-inbox-container` div from the right sidebar\n- `renderExecInbox()` function (replaced by drawer)\n- Realtime subscription for `agent_tasks` filter `role=eq.exec` in the old right-panel path (re-wired to drawer)\n\n---"
    },
    {
      "title": "HCS Message Format",
      "level": 2,
      "body": "All outbound messages post to `POST http://87.99.154.64:3001/post-hcs-message`:\n\n```json\n{\n  \"topic_id\": \"<target_topic_id>\",\n  \"message\": {\n    \"type\": \"inbox.message | task.complete | task.blocked\",\n    ...fields\n  }\n}\n```\n\nThe `hcs-post-server` is already live and tested. No changes needed to the server.\n\n---"
    },
    {
      "title": "Listener Dependency",
      "level": 2,
      "body": "Compose and reply post `inbox.message` to an agent's `inbound_topic_id` on HCS. The listener (`vps/hedera-listener.py`) must handle this message type on agent inbound topics and write it to the `inbox` table.\n\nThis is **not yet implemented** in the listener. The implementation plan must include adding `handle_inbox_message()` to the listener:\n\n```python"
    }
  ],
  "html_path": "artifacts/2026-04-27-cockpit-inbox-design-5d5847e98c.html",
  "json_path": "artifacts/2026-04-27-cockpit-inbox-design-5d5847e98c.json"
}