cocottetech/@platform/codebase/@features/ai-copilot/docs/prospect-detail.screen.md

14 KiB
Raw Blame History

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
├────────────────────────────────────────┤
│  Touchpoint chain · [ Time-decay ▾ ]   │  ← attribution model picker (per T-attr-model)
│  ┌──────────────────────────────────┐  │
│  │ Tryst (Apr 8 14:02)              │  │
│  │   ↓ 19h                          │  │
│  │ iMessage (Apr 9 09:14)           │  │
│  │   ↓ 13h                          │  │
│  │ OF subscription (Apr 9 22:11)    │  │
│  │   ↓ ongoing · 3 tips $47         │  │
│  │                                  │  │
│  │ Credit:  Tryst 60% · X 0% · OF40%│  │
│  └──────────────────────────────────┘  │
├────────────────────────────────────────┤
│  ⚠ 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 37 — 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.
TouchpointChain new (B3 + T §T-attribution) Reads prospect_touchpoints for this prospect_id, oldest-first. Each touchpoint shows surface + kind + occurred_at; arrows show the elapsed time between consecutive touchpoints. Anonymous touchpoints never appear here (per _engineering-surface-metrics.md §9 invariant).
AttributionModelPicker shared (T §T-attribution-model) Dropdown: First-touch / Last-touch / Time-decay (default) / Position-based. Same component used in T2 funnel Top paths. Persists to users.attribution_model_pref.
AttributionCreditStrip new (B3 + T §T-attribution-model) Per-surface credit weights for this prospect under the selected model. Shows (unmeasured) for surfaces with tier-gated analytics off, never 0% (which would falsely imply "no influence" rather than "we can't tell").

States to render

  1. Typical — known prospect, mixed-surface history, funnel-stage PPV, no coop reports.
  2. 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."
  3. 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."
  4. 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.
  5. Peer-coop reports present (per brief N) — CoopIntelCallout rendered above the timeline in plain register. Reply composer soft-warns but is not disabled; Quinn judges.
  6. 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."
  7. Cross-surface unresolvedprospect-resolver hasn'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. Touchpoint chain stays empty until merge resolves — anonymous touchpoints never render per-prospect (surface-metrics §9 invariant).
  8. Single-touch prospect — only one resolved touchpoint exists. TouchpointChain renders the single node without arrows; AttributionCreditStrip shows 100% credit to that surface under any model. Strategist insight chip (working): "Single-touch prospect. Worth nudging onto a second surface — typical Felix-types convert at 3× after the second touchpoint."
  9. Chain-confidence hedgeprospect-resolver linked touchpoints via identifier_hash match but didn't confirm. Chain renders with dotted arrows + plain-register hint: "Linked by identifier hash — Cocotte's 78% confident this is the same person." Quinn can confirm/break the link from a long-press menu on any arrow.
  10. Share-with-org pending (per brief W) — header share button 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 attribution-model picker: switches model; credit strip + insight chip recompute instantly (all 4 models pre-computed per T §T-attribution-model).
  • Long-press a chain arrow: opens "confirm / break link" sheet for hedged identifier-hash links (state 10).
  • 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_id view, 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; once prospect-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.