DESIGN-detribalise-trent.md8.0 KBView on GitHub # De-tribalising knowledge — Trent's Playbook + Call Review
## Intent
A small pool of reps drives an outsized share of revenue. That talent is tribal —
it lives as pattern-matched intuition in one person's head, and it walks out the
door if they leave. The goal of these two documents is to **extract that intuition
into explicit heuristics an AI can apply to coach everyone else**.
- **Trent's Playbook** — the *codified output*: what the standout rep does, distilled
into AI-usable rules.
- **Call Review (call comparison)** — the *evidence*: a side-by-side of Trent and one
named newer rep on an analogous call, showing exactly where behavior diverges.
The two cross-link: the playbook points to the review for proof; the review points
to the playbook for the rule each moment teaches.
Decisions (locked): comparison is **Trent vs one specific named rep** on a real
analogous call; data is **illustrative mock first** (swap to live queries later); the
review's centerpiece is a **dedicated `callComparison` dashboard block**.
## Current state
Both documents are Cedar Docs rendered through the existing dashboard system —
markdown with ` ```dashboard ` fences (see the multi-threading + top-rep docs).
- Spec schema + types: `apps/mail/modules/dashboards/types/dashboard.ts`
- Renderer + block dispatcher: `apps/mail/modules/dashboards/components/blocks.tsx`
- Fence → live dashboard: `apps/mail/modules/conversations/components/tiptap-extensions/DashboardFenceNode.tsx`
- Server markdown ↔ Y.Doc (backfill of `content_yjs` on first open):
`apps/server/src/services/document-saving/markdown-parser.ts` / `serialize.ts`
Existing blocks cover almost everything: `stat`, `chart`, `table`, `funnel`,
`comparison`, `heatmap`, `playbook` (stage cards), `repeater`, `entityCard`,
`profile`, `text`, `docLink`, `tabs`. The one genuinely missing widget is the
speaker-aligned **call comparison**.
## Proposed change
### New block: `callComparison`
A two-column, speaker-aligned transcript of two calls at analogous moments, each
moment annotated (good / missed / neutral) with a coaching note, under a header that
chips the two reps and (optionally) a delta summary.
Schema (added to `dashboard.ts`, mirroring the `heatmap` addition):
```ts
const callSpeakerSchema = z.object({
speaker: z.string().optional(), // "Trent" / "Rep" / "Prospect"
text: z.string(), // the quote
tag: z.enum(['good', 'miss', 'neutral']).optional(),
note: z.string().optional(), // the coaching annotation
});
const callMomentSchema = z.object({
at: z.string().optional(), // timestamp label, e.g. "10:02"
topic: z.string().optional(), // what's happening, e.g. "Pricing objection"
left: callSpeakerSchema,
right: callSpeakerSchema,
});
const callComparisonBlock = z.object({
type: z.literal('callComparison'),
title: z.string().optional(),
left: z.object({ name: z.string(), label: z.string().optional(), initials: z.string().optional() }),
right: z.object({ name: z.string(), label: z.string().optional(), initials: z.string().optional() }),
source: z.string().optional(), // rows are moments…
rowField: z.string().optional(), // …or an array on the repeater row…
moments: z.array(callMomentSchema).optional(), // …or inline.
});
```
Renderer `CallComparisonView` (in `blocks.tsx`): a header with two `profile`-style
chips (left highlighted as top rep), then per-moment rows. Each moment renders an
optional `topic`/`at` label spanning both columns, then two cells. Each cell tints
by `tag` — emerald (`good`), amber/red (`miss`), muted (`neutral`) — shows
`speaker: text`, and renders `note` as a small italic coaching line. Reuses the
existing emerald/neutral palette and `text-[12px]` aesthetic.
No server change — `createDashboardTool` only does loose top-level validation; full
block validation is client-side via `parseDashboardSpec`.
### Document 1 — Trent's Playbook
Path `user/files/trent-playbook`, sibling of the existing reports. Sections, each a
` ```dashboard ` fence:
1. **The concentration risk** — stat grid (Trent's share of closed-won, win rate vs
team, # deals influenced) + a bar chart of revenue (or win rate) by rep with Trent
highlighted. Opens with the one-line verdict: *your best outcomes live in one
person's head.*
2. **What Trent does differently — the signatures** — the behaviors, as `playbook`
stage cards mapped: `title` = behavior, `objection` = the trigger/situation,
`reply` = Trent's move, `tactic` = why it works, `deliverable` = the codified
heuristic. ~5 cards (discovery / cost-of-inaction, multi-threading to the economic
buyer, reframing price as risk, tailored same-day follow-up, mutual close plan).
3. **The heuristic library** — a `table` of the extracted **if-X-then-Y rules**: columns
`Trigger | Heuristic | Source behavior`. This is the AI-usable payload — the rows
are written to read like rules that could drop straight into a coaching-framework /
AOP subagent.
4. **How Trent reads a deal** — pattern-matching tells: a two-column / table of
good-deal signals vs bad-deal signals (his qualification intuition).
5. **Where newer reps diverge** — a `comparison` (Trent vs team) on the key behaviors,
plus a `docLink` to the Call Review for the evidence.
### Document 2 — Call Review (call comparison)
Path `user/files/call-review-trent`. Sections:
1. **The two calls** — context (deal, stage, situation), two `profile` chips, and a
`table`/`comparison` scorecard: talk ratio, discovery questions asked, objections
handled, next step set, outcome.
2. **Moment-by-moment** — the new `callComparison` block. 6–10 aligned moments across
discovery, an objection, pricing, and the close — each annotated so the divergence
is unmissable.
3. **The deltas that mattered** — a short `table`/repeater of the 3–4 pivotal
divergences, each `docLink`-ing to the matching heuristic in Trent's Playbook.
4. **What to coach** — takeaways framed as the rules the newer rep should adopt
(mirrors the heuristic library).
### Critical files
- `apps/mail/modules/dashboards/types/dashboard.ts` — add `callComparison` schema +
type, wire into `layoutNodeSchema` union and `LayoutNode`.
- `apps/mail/modules/dashboards/components/blocks.tsx` — add `CallComparisonView` +
dispatcher case.
- Seed scripts (build markdown + validate specs + insert rows) — temp build scripts,
same approach as the multi-threading seed.
## Seeding safety
The multi-threading doc was emptied earlier because its row was rewritten with raw
SQL **while open in the editor** — the live `CedarYjsProvider` then flushed its local
(empty) Y.Doc back over the change. These two docs are **brand-new rows not open in
any editor**, so a plain insert with `content_yjs = NULL` (backfilled on first open) is
safe. Rule of thumb encoded here: insert new docs freely; never hand-edit an
already-open doc's row — close it first or go through the document-write path.
## Phased plan
- [ ] **Phase 1 — component.** Add `callComparison` schema/type + `CallComparisonView`
renderer + dispatcher case. Typecheck `dashboard.ts` and `blocks.tsx` clean.
- [ ] **Phase 2 — Trent's Playbook.** Author the 5-section spec (mock data), validate
all fences against the real zod schema, insert the row.
- [ ] **Phase 3 — Call Review.** Author the 4-section spec incl. the `callComparison`
block, validate, insert the row.
- [ ] **Phase 4 — cross-link + verify.** Add the `docLink`s both directions; open both
docs in the app and confirm every block (esp. `callComparison`) renders.
## Verification
- Each fence passes `parseDashboardSpec` (Node `--experimental-strip-types` against
`dashboard.ts`, as used for the heatmap).
- `pnpm --filter @zero/mail types` shows no new errors in the two touched files.
- Both rows insert with `content_yjs = NULL`; first open backfills live blocks.
- Manual: open each doc from the Files folder; the call-comparison moments render
speaker-aligned with good/miss tinting and notes.