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>
10 KiB
Prospect detail · screen
Implementation breakdown of brief B §B3 — the cross-surface message-history drawer for one resolved prospect_id, reached from chat citations, the unified-inbox row tap, audit-row links, and global-search prospect hits.
Layout (iPhone)
┌────────────────────────────────────────┐
│ ← @felix ⋮ share │ ← header: back, overflow, share-with-org (W)
├────────────────────────────────────────┤
│ @felix · OF + Tryst + SMS │ ← identity line (working register)
│ first DM Mar 12 · last contact 2h ago │
│ [ funnel: PPV ▾ ] value: $1,420 ytd │ ← funnel chip + value-to-date strip
├────────────────────────────────────────┤
│ ⚠ Coop intel · 2 peer reports │ ← coop callout (plain register)
│ Berlin Providers · UNSAFE │
│ [ open report ] │
├────────────────────────────────────────┤
│ Timeline · cross-surface │
│ ┌──────────────────────────────────┐ │
│ │ Tryst · 2h ago │ │
│ │ him: "thinking about Berlin..." │ │
│ │ Cocotte drafted: "Berlin Oct…" │ │
│ │ [ approve · edit · set aside ] │ │
│ ├──────────────────────────────────┤ │
│ │ OF DM · yesterday 9:14pm │ │
│ │ him: "subbing again" │ │
│ │ you: "welcome back —" · sent │ │
│ ├──────────────────────────────────┤ │
│ │ SMS · Mar 28 · tip $40 │ │
│ │ event · funnel: free → tip │ │
│ └──────────────────────────────────┘ │
├────────────────────────────────────────┤
│ Reply │
│ via: [ Tryst DM ▾ ] │
│ ┌──────────────────────────────────┐ │
│ │ Berlin Oct 3–7 — want a slot? │ │
│ └──────────────────────────────────┘ │
│ [ send ] [ ask Cocotte to draft ] │
└────────────────────────────────────────┘
Component table
| Component | Source | Notes |
|---|---|---|
ProspectHeader |
new (B3) | Identity line + surfaces list + first/last-contact stamps. Working register. |
FunnelChip |
new (B3 + T2) | Stage selector: free / DM / tip / PPV / subscribe / booking. Tap opens stage history sheet. |
ValueToDateStrip |
new (B3 + T2) | YTD revenue attributed to this prospect_id. Reads engagement_events rollup. |
CoopIntelCallout |
shared (coop-intel-detail) | Inline summary if peer reports match this subject. Plain register. Tap opens coop-intel-detail.screen.md. |
CrossSurfaceTimelineRow |
new (B3) | One row per engagement_events entry: source chip (per F5d) + body + Cocotte draft inline if pending. |
InlineApprovalStrip |
shared (A, approval-card) | Same swipe + button trio: approve / edit / set aside. |
ReplyComposer |
new (B3 + P) | Channel picker (defaults to last-contact channel) + text + "ask Cocotte to draft" — routes through mcp__quinn-prospector__draft_message. |
ShareWithOrgButton |
new (W) | Cross-context handoff affordance; gated on org membership + share scope. |
UnresolvedIdentifierBanner |
new (B3 + P §prospect-resolver) | Renders when prospect-resolver has unmerged candidate identifiers for this prospect. |
States to render
- Typical — known prospect, mixed-surface history, funnel-stage PPV, no coop reports.
- New prospect, no history — single inbound event. Timeline shows one row + hearth-leaning empty hint above the composer: "First contact. Cocotte hasn't drafted yet — ask for a draft, or write your own."
- Funnel-stage paying (PPV / subscribe / booking) — value strip foregrounded, accent-green; strategist insight chip appears: "Warm cohort — worth a follow-up before the heat fades."
- Funnel-stage cold — value strip muted, hearth chip suggests "On the back burner. Want strategist to draft a re-engagement?" Reply composer still available; no auto-draft pre-filled.
- Peer-coop reports present (per brief N) —
CoopIntelCalloutrendered above the timeline in plain register. Reply composer soft-warns but is not disabled; Quinn judges. - Blocked in K1 — drawer renders for replay/audit purposes only. Composer + approval strips are disabled with a plain-register notice: "This prospect is on your blocklist. Reply disabled. Manage in safety settings."
- Cross-surface unresolved —
prospect-resolverhasn't merged yet. Banner above the timeline: "2 unmerged identifiers — Cocotte is still confirming this is the same person. Showing what's matched so far." Identity line shows resolved channels only. - Share-with-org pending (per brief W) — header
sharebutton shows a confirmation sheet before any data crosses scopes. Org-context voice shift applies once confirmed.
Gestures
- Swipe right on a timeline row: approve a pending draft (when the row carries one).
- Swipe left on a timeline row: set aside the draft (same semantics as A's chat cards).
- Tap a timeline row: opens the source's raw thread (audit-shadow view).
- Long-press the funnel chip: opens funnel-stage history sheet (when did this prospect move from free → tip, etc.).
- Tap the coop callout: opens
coop-intel-detail.screen.md. - Swipe down: dismiss to the surface that opened this drawer (chat / inbox / search / audit).
In-the-wild copy
Header identity line (working):
@felix · OF + Tryst · first DM Mar 12 · last contact 2h ago
Funnel chip, paying (working):
PPV · $1,420 ytd · last buy 4 days ago
Coop intel callout (plain):
2 peer reports on this subject. Berlin Providers · UNSAFE. Read before you reply.
Unresolved-identifier banner (working):
2 unmerged identifiers — Cocotte is still confirming this is the same person. Showing what's matched so far.
Blocked-in-K1 notice (plain):
This prospect is on your blocklist. Reply disabled. Manage in safety settings.
Share-with-org confirmation (plain, per brief W):
Share this prospect with Demimonde org? Members with prospect-scope access will see this history and funnel state. Continue / cancel.
Auto-conversation live indicator (working, per Q3):
Cocotte is on a thread with @felix right now — last turn 12s ago.
Privacy invariants
- Subject phone / email / handle identifiers in the header and timeline are never copyable to clipboard (same rule as coop-intel-detail §privacy). Tap-and-hold opens a privacy reminder, not a copy menu.
- Cross-channel identifier guards (per brief K3h): the reply composer's channel picker warns when the chosen channel uses an identifier the prospect hasn't seen — "They messaged via Tryst. You're replying via SMS, which uses a number they may not have. Continue / switch back."
- No screenshot affordance. iOS screen-capture detection raises a banner per coop-intel-detail pattern.
- Coop intel callout obeys per-coop visibility rules — reports from coops Quinn isn't in never surface here.
- Share-with-org never echoes identifiers outside the org scope; the org receives the resolved
prospect_idview, not raw cross-channel identifiers from coops Quinn-only is in.
Edge cases
- Govt-name draft reply (K3c-1 hard rule) — if a Cocotte-proposed draft contains what the safety pass flags as a government / legal name, the draft is withheld; row shows plain-register stub: "Draft held — contains a name we don't put in writing. Ask Cocotte to redraft." Hard rule; no override here.
- Search lands here — brief U query routes to this screen with the matched timeline row scrolled into view and highlighted for ~2s; back-swipe returns to search results, not chat.
- Auto-conversation in progress (per brief Q3) — when triage is mid-thread with this prospect, a live indicator pulses next to the header and the composer is dimmed with copy: "Cocotte is replying — interrupt to take over?" Tap "interrupt" snapshots the conversation and hands control back.
- Two phone numbers, same prospect, not yet merged — timeline renders both as separate source chips with the
UnresolvedIdentifierBanner; onceprospect-resolver(P4) merges, banner clears and rows recompose without a reload. - Outbound failure on reply — failed row enters
pending retry(per brief M §M3). Approval strip becomes "retry / set aside" until reconciled. - Counter-action target (per brief I) — a row sent by mistake can be tapped → "Counter-action: retract / revert / follow-up" sheet opens; counter-actions land in audit.
Related
- brief B §B3 — drawer parent.
- brief P — unified inbox routes here on row tap; channel posture from P drives the composer's default
via:. - brief N — coop intel callout source; opens
coop-intel-detail.screen.md. - brief Q §Q3 — auto-conversation live indicator.
- brief U — search routes land here.
- brief I — counter-action target; row-level retract / revert / follow-up.
- brief W — share-with-org cross-context handoff.
- brief K §K1 (blocked-in-drawer), §K3c-1 (govt-name hard rule), §K3h (cross-channel identifier guard).
- voice §V2b working register · §V2c plain on coop callout + safety stubs.