Clean successor to V3 (forge: lilith/atlilith). Seeded from local Mac working tree at ~/Code/@projects/@cocottetech/. node_modules and build artifacts excluded via .gitignore. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.4 KiB
Unified inbox · screen
Implementation breakdown of brief P's unified inbox view. The screen is reachable as a drawer from chat-home (a sibling to brief B's catalog — "inbox" added to the drawer roster) or as a deep-link from a notification.
Layout (iPhone)
┌────────────────────────────────────────┐
│ ← Inbox ⋮ ⚙ │ ← top bar: back, filter chip, settings
├────────────────────────────────────────┤
│ [all] [warm] [cold] [flagged] │ ← state filter row (horizontal scroll)
│ via: [any▾] sort: [most recent ▾] │ ← source filter + sort
├────────────────────────────────────────┤
│ • @felix · via Tryst · 11 min │ ← prospect-rolled row
│ "Hey, thinking about Berlin..." │
│ triage: warm · drafted reply │
├────────────────────────────────────────┤
│ • +1 415 555 0142 · via SMS · 47 min │ ← unknown sender; channel-explicit
│ "is this number still good?" │
│ triage: cold · queued for review │
├────────────────────────────────────────┤
│ • via Proton: myprivatedelights@… │ ← per-mailbox source label
│ PrivateDelights verification reply │
│ triage: system · auto-archived │
├────────────────────────────────────────┤
│ • @felix · via OF DM · 2h │ ← same prospect, different channel
│ "subbing again" │
│ threaded with Tryst row above ↑ │
└────────────────────────────────────────┘
Component table
| Component | Source | Notes |
|---|---|---|
SourceChip |
new (P + F F5d) | Rounded-square chip with channel-tinted glyph; per brief F F5d geometry distinguishes channel from surface. |
MailboxChip |
new (P) | Sub-variant of SourceChip showing per-mailbox label for email rows ("Proton: myprivatedelights@…"). |
TriageBadge |
new (P + L) | Inline tag: warm / cold / flagged / system / blocked. Plain register. |
ProspectThread |
sibling to B3 | Groups multiple channel rows by prospect_id once prospect-resolver (P4) catches up. Visual: thin vertical connector on the left edge. |
InboxRow |
new (P) | Row composition: SourceChip + sender + timestamp + body preview + TriageBadge. |
FilterRow |
shared (B4) | Horizontal scrollable chips. Same component as audit drawer filters. |
States to render
-
Empty — no inbound across any source. Hearth register:
Quiet. She's tending the queues and will surface anything worth your time.
-
Typical mix — 30–80 rows across 4–6 sources. Default sort: most recent.
-
All-source filtered — only one source active in
via:filter. Other rows hidden. -
Prospect-grouped — same prospect across iMessage + OF + Tryst. Thin connector groups; tap a row to expand the cluster inline.
-
Source-degraded — Tryst inbox stalled. Banner above the affected rows:
Tryst inbox stalled — last fetched 47 min ago. Showing what we have.
-
Per-source policy editor — long-press a source chip → bottom sheet showing per-source triage posture (auto-reply / draft-only / hands-off + cadence).
-
Reply-as-different-channel confirm — Quinn opens a fan's email row, taps "Reply via iMessage" instead of "Reply via email":
They emailed. You're replying via iMessage — that uses a different identifier they may not have. Continue / switch back to email.
-
Auto-replied row — triage already sent. Row shows
triage: auto-replied · 6 min agoand the body preview is the reply, not the inbound. -
Blocked sender — collapsed under a single card:
5 blocked-prospect messages today. Review.
-
Inbox loading initial fetch — skeleton rows.
Gestures
- Swipe right on a row: opens reply composer in the appropriate channel.
- Swipe left: archive (preserved in audit; triage knows you saw it).
- Tap a row: opens the prospect drawer (brief B3) scoped to this conversation.
- Long-press: multi-select for batch archive / batch route-to-Quinn.
- Pinch / pull on a
ProspectThread: expand to show every channel row in the cluster.
Per-source triage posture (settings sub-page)
Reached via top-bar settings gear. Per-source row:
iMessage [ Auto-reply ▾ ] cadence: instant
SMS [ Draft-only ▾ ] cadence: instant
Proton: hi@cocotte.maison
[ Draft-only ▾ ] poll: every 5min
Proton: myprivatedelights@…
[ Auto-archive ▾ ] poll: every 30min (transactional)
Tryst DM [ Hands-off ▾ ] poll: every 1min
OF DM [ Draft-only ▾ ] poll: every 10min
personas.facets[channel_id].triage_policy is the canonical store; this UI is a friendlier editor over it.
Edge cases
- Same prospect, two phone numbers —
prospect-resolvermay take a vigil to merge. UI shows two rows; once merged, both collapse into oneProspectThreadwith a small "merged" indicator. - A Proton mailbox temporarily disconnected — surfaces an inline degradation banner (per brief M M2a); rows from that source greyed.
- Outbound failure on reply — row flips to a
pending retrystate with the reply still visible; matches brief M M3 failure interrupt pattern. - A surface-DM that triage classified as blocked — never appears in the feed; subsumed under the "blocked-prospect messages today" card.