150 lines
14 KiB
Markdown
150 lines
14 KiB
Markdown
# Prospect detail · screen
|
||
|
||
Implementation breakdown of [brief B](./B-drawers.brief.md) §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 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`](./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](./_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 unresolved** — `prospect-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).
|
||
9. **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."
|
||
10. **Chain-confidence hedge** — `prospect-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.
|
||
8. **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](./T-analytics-dashboard.brief.md)).
|
||
- **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`](./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](./coop-intel-detail.screen.md) §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.
|
||
|
||
## Related
|
||
|
||
- [brief B](./B-drawers.brief.md) §B3 — drawer parent.
|
||
- [brief P](./P-inboxes.brief.md) — unified inbox routes here on row tap; channel posture from P drives the composer's default `via:`.
|
||
- [brief N](./N-provider-coop.brief.md) — coop intel callout source; opens [`coop-intel-detail.screen.md`](./coop-intel-detail.screen.md).
|
||
- [brief Q](./Q-vigil-journal-auto-conversations.brief.md) §Q3 — auto-conversation live indicator.
|
||
- [brief U](./U-global-search.brief.md) — search routes land here.
|
||
- [brief I](./I-audit-trust-replay.brief.md) — counter-action target; row-level retract / revert / follow-up.
|
||
- [brief W](./W-org-overlay.brief.md) — share-with-org cross-context handoff.
|
||
- [brief K](./K-safety-blocklist.brief.md) §K1 (blocked-in-drawer), §K3c-1 (govt-name hard rule), §K3h (cross-channel identifier guard).
|
||
- [voice](./00-system-voice.md) §V2b working register · §V2c plain on coop callout + safety stubs.
|
||
- [T-analytics-dashboard.brief.md §T-attribution](./T-analytics-dashboard.brief.md) — per-prospect chain is the drilldown surface from T2 `Top paths`.
|
||
- [T-analytics-dashboard.brief.md §T-attribution-model](./T-analytics-dashboard.brief.md) — model picker shared with this screen.
|
||
- [specialist-prospect-resolver.contract.md](./specialist-prospect-resolver.contract.md) — writes the `prospect_touchpoints` rows this screen renders; handles state-10 hedged links.
|
||
- [_engineering-surface-metrics.md](./_engineering-surface-metrics.md) §4 (touchpoints schema) + §9 (privacy: anonymous-aggregate-only invariant).
|