Charlie's Package: First Build, First Three Sessions
Read the shared briefing first. This page assumes you already have. Two halves: your first AI build, and prep packs for your three expert Zooms.
01The mission
The big goal. Then the entry point.
Mark, Ewing, and John each cover a slice of the funnel today. The ceiling is their time. The mission is to take the Meetings page from a working tool to a B2B CRM automation engine that lets each of them cover 5x as many leads at the same quality. Same headcount. Same close rate. Five times the deal flow.
That goal is months of feature work. Your first build is the entry point. It is small on purpose. It teaches you the data, the signals, the API patterns, and the people. Once you understand those, you stop being the new person and start being the operator who knows the funnel better than anyone else.
02What you're building (concrete)
A morning view that sorts unclassified meetings by signal strength, lets you classify them in batches, and shows you how fast you got through the queue.
Page name: Daily Triage. Lives at /meetings/triage. Permission tier: intern. You can ship this without operator approval.
What's on the page
1. Daily stats card (top of page)
Four numbers, computed live from feature_metrics:
- Meetings classified today: count of
meetings.classifyevents where actor is you and the timestamp is today. - Median time per classification: median delta between consecutive classify events in your session today. If you only have one event, show "first one of the day".
- Promotion candidates surfaced: count of meetings in today's queue where the promote criteria are met (defined below).
- Leads qualified: count of meetings classified as a deal today by you.
2. Prioritized queue (main body)
List of unclassified meetings sorted by a single composite priority score:
priority = (revenue_signal_count * 30) + opportunity_score + recency_bonus
recency_bonus = 20 if call was today
= 10 if call was yesterday
= 0 otherwise
Each row shows: contact name, company, when the call happened, the auto detected signals (badges), the Quick Classify dropdown, and the promote indicator.
3. Auto detected signals (badges per row)
Read straight from src/components/meetings/derive.ts. Three badges:
- Revenue mentioned (green):
signals.revenue_mentionedis truthy. - Motivated seller (orange):
signals.sell_urgencyis truthy. - Buyer signal (blue): the transcript matched the buyer phrase set in derive.ts.
4. Quick Classify dropdown (per row)
Inline select with the canonical classifications from src/lib/classifications.ts: deal, voicemail, no_answer, gatekeeper, not_interested, bad_number, callback_requested. On change, the dropdown calls PATCH /api/meetings/[id]/classify and the row fades out of the queue.
5. Promote to deal indicator (per row)
Lights up green when all four conditions are true:
- revenue signal flag is set on the meeting
- opportunity score is 30 or higher
- classification is
deal - entity (company) is linked
Charlie cannot promote. Promotion is operator tier. The indicator just tells you "this one is ready for Mark to promote." Clicking it creates a task in the /review/tasks queue.
03The data you'll touch
Two tables and one component. Read each before you write a line of code.
| Source | What it has | What you read |
|---|---|---|
meeting_notes (Supabase) |
The canonical row per meeting: id, contact, company, transcript, classification, signal flags, opportunity score, entity link. | Filter where classification is null. Order by the priority formula above. |
feature_metrics (Supabase) |
Every feature invocation. Actor, feature key, timestamp, metadata. | Filter actor='charlie' and feature_key='meetings.classify' and timestamp is today. Compute the stats card from this. |
src/components/meetings/derive.ts |
Signal detection logic. Functions deriveSignals(), deriveOpportunityScore(), the buyer phrase set. |
Import and call. Don't reimplement. |
Read derive.ts before writing the priority sort. The signal field names matter. revenue_mentioned is a DealSignal object with .found, not a boolean. Look at the type before you sort by it.
04The feature def you'll register
Add this entry to src/lib/features/defs/meetings.ts before you write any UI code.
{
key: 'meetings.daily-triage',
page: 'meetings',
label: 'Daily Triage',
description: 'Morning queue of unclassified meetings sorted by signal strength, with inline Quick Classify and live stats',
status: 'design',
tier: 'intern',
owner: 'charlie',
created_at: '2026-05-06',
metrics_key: 'meetings.daily-triage',
requires_review: false,
cost_category: 'free',
}
Status starts at design. Move it to build when you start writing the page. Move it to test when you can render the queue. Move it to live only after Mark has classified at least 5 meetings using your page without a bug.
05The API route already exists
You don't need to build a route for the classify dropdown. It's done.
The handler at src/app/api/meetings/[id]/classify/route.ts accepts PATCH with body { classification: string }. It validates against the canonical list, writes the update, and records a meetings.classify event in feature_metrics.
The pattern from your dropdown
async function classify(meetingId: string, classification: string) {
const res = await fetch(`/api/meetings/${meetingId}/classify`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ classification }),
})
if (!res.ok) throw new Error(await res.text())
return res.json()
}
Two rules:
- Never write to
meeting_notesfrom the browser. Always go through this route. - If the call fails, surface the error in the row, not as a toast that disappears. Mark needs to see what went wrong.
06The page you'll build
One server file for the initial render. One client file for the dropdowns.
File layout
src/app/meetings/triage/page.tsx |
Server component. Fetches the queue and the stats. Renders the static parts. |
src/app/meetings/triage/QuickClassifyRow.tsx |
Client component. One row in the queue. Owns the dropdown state, calls the API on change, fades the row on success. |
src/app/meetings/triage/StatsCard.tsx |
Server component. Renders the four numbers from a stats query. |
src/app/meetings/triage/queue.ts |
Server only helper. Builds the prioritized queue from meeting_notes. This is where the priority formula lives. |
Server pattern (page.tsx)
import { canUse } from '@/lib/features/permissions'
import { getCurrentActor } from '@/lib/auth'
import { getTriageQueue, getTodayStats } from './queue'
import { StatsCard } from './StatsCard'
import { QuickClassifyRow } from './QuickClassifyRow'
import { FeedbackBox } from '@/components/FeedbackBox'
export default async function TriagePage() {
const actor = await getCurrentActor()
if (!canUse('meetings.daily-triage', actor)) return <NotAllowed />
const [queue, stats] = await Promise.all([
getTriageQueue(),
getTodayStats(actor),
])
return (
<main>
<StatsCard stats={stats} />
<ul>
{queue.map((m) => <QuickClassifyRow key={m.id} meeting={m} />)}
</ul>
<FeedbackBox feature="meetings.daily-triage" />
</main>
)
}
Two things this pattern enforces. The canUse() check gates the page. The FeedbackBox guarantees Mark can leave a sentence the first time he uses it.
07Definition of done
The checklist. Don't say "done" until every box is ticked.
npx tsc --noEmitpasses with zero errors.npx next buildpasses.- The page renders at
/meetings/triagewith no console errors. - The queue is sorted by the priority formula. Verify by reading three rows from top, bottom, and middle and walking through the math.
- The Quick Classify dropdown writes to
meeting_notesvia the API route end to end. feature_metricsrecords ameetings.daily-triageview event when the page loads.feature_metricsrecords ameetings.classifyevent when you classify a row.- The stats card numbers match what you can count by hand in the database.
- The promote indicator only lights up when all four conditions are true. Verify with at least one positive case and one near miss.
- The empty state renders without crashing (filter to a date with no unclassified meetings).
- The
FeedbackBoxis on the page and submits successfully. - Feature def status is at
testindefs/meetings.ts.
08How to demo it
Show, don't tell. Let Mark drive.
Set up a 15 minute slot with Mark. Pull up /meetings/triage on his screen. Hand him the keyboard. Say one sentence: "Triage the morning queue." Then shut up.
Watch where he hesitates. The hesitations are the bugs. After 10 minutes of him classifying, ask three questions:
- "What was missing?"
- "What was in the way?"
- "What would you do tomorrow morning that this doesn't help with yet?"
Write his answers down. Within 24 hours, file each answer as a feature idea or a ticket. Don't argue any of them in the room.
09Recommended skills for this build
Four skills that will make this faster.
| Skill | When to invoke |
|---|---|
/cold-call-workflow |
Your domain skill. Read it once before you start. It explains how calls flow from Salesfinity into meeting_notes, what each classification means, and what Mark looks at first thing in the morning. |
/listener |
When a transcript reads strange and you can't articulate why. Listener surfaces unstated intent, missed signals, and tonal mismatches between the contact and the caller. |
/audit-quality |
Run before you say "done." It checks your output against a 10 point quality bar and returns PASS, CONCERNS, REWORK, or FAIL. If it returns anything other than PASS, fix the findings and run it again. |
/architect |
When you're not sure where a file belongs. The repo has rules about what goes in src/app, what goes in src/lib, and what goes in src/components. Ask architect before guessing. |
10What to do AFTER this ships
The next five builds. Each one builds on the Daily Triage.
Don't pick the next one yet. Ship the Triage first. Demo it. Take feedback. Then pick whichever of these Mark or John tells you matters most.
- Auto classification suggestions. Use the existing transcript and signal data to suggest a classification on each row. The dropdown still lets Charlie or Mark override. Track agreement rate. Once agreement passes 85%, suggest defaulting.
- Voicemail callback queue. A second view at
/meetings/callbacks. Surfaces voicemail classified meetings ordered by callback freshness, contact responsiveness history, and time of day patterns. The job: tell Mark which callback to make next. - Gatekeeper detection signals. When the transcript suggests the caller hit a gatekeeper, surface what the gatekeeper said and what John would say next. Track gatekeeper bypass rate per company.
- Campaign aware prioritization. The queue today treats every meeting equal. Layer in
outreach_campaignsso meetings tied to active campaigns float up and meetings tied to closed campaigns sink. - Auto enrichment trigger. When a contact answers a phone for the first time and gets classified as a deal, fire an enrichment job (linkedin, exa, web) and put the results on the meeting detail page within an hour. Bound by the circuit breaker so it never runs more than 25 times a day on intern tier.
Each one is its own feature def, its own loop, its own demo. Don't try to ship two at once.
11Your three expert Zooms
30 minutes each. Product input, not training. The point is for you to learn what to build, not for them to teach you M&A.
The shared briefing covers the universal rules. Read those again before each call. Below are the three prep packs tuned to the specific person you're talking to. Each pack has a goal, what to verify before the call, the question bank, what NOT to ask, and the writeup template you fill out within 24 hours.
Charlie : John (founder, built the calling discipline)
A. Goal of this session
Understand what calling discipline looks like across years of cold calling. John has made more cold calls than anyone in the company. You want the rhythms, the tells, and the judgment calls that no playbook captures.
B. What to verify before the call
- Read
/cold-call-workflowSKILL.md end to end. - Pull 5 of John's recent meetings from
meeting_notes. Read each transcript. Note the open, the pivot, the close. - Look at
feature_metricsfor John's classification pattern over the last 30 days. What does he classify most? What does he never classify? - Read the buyer phrase set in
src/components/meetings/derive.ts. Note what triggers a buyer signal.
C. Question bank
- "When you look at a call list first thing in the morning, what makes a name jump to the top?"
- "How do you decide which call to make NEXT in a sequence?"
- "What does a great cold call sound like in the first 10 seconds?"
- "What's the difference between a good 'no' and a bad 'no'?"
- "How do you read voicemails. Same script every time, or tuned to the contact?"
- "How do you handle gatekeepers without burning the relationship?"
- "What's the worst classification mistake a caller can make and what did it cost?"
- "Tell me about a call that turned into a closed deal. What signal did you catch?"
- "Tell me about a call that wasted time. What signal did you miss?"
- "Where would you never trust software to make the classification call?"
- "What's a tell in someone's voice that you've learned to trust over the years?"
- "If I gave you a screen that ranked every uncalled name by signal, what's the first thing you'd want it to get right?"
D. Anti questions
- Don't ask "what's a meeting" or "what's a deal." Read the docs first.
- Don't ask about dialer specifics. That's Mark's territory.
- Don't ask technical implementation questions. John is not the engineer.
- Don't ask him to summarize his own approach in one sentence. Let him tell stories. The stories are the data.
- Don't ask "what should I build." You tell him what you're building. He reacts.
Charlie : Mark (operational, makes calls and triages daily)
A. Goal of this session
Understand what slows Mark down during a day of dialing. Mark is at the keyboard most of the day. He knows what wastes seconds and what wastes minutes.
B. What to verify before the call
- Pull Mark's last 7 days of
feature_metricsevents. Look at what he clicks, when he clicks it, and how long between actions. - Read the meetings page (
src/app/meetings/page.tsx) so you know what's in front of him today. - Look at the
spend_logfor any calls Mark made yesterday that touched a paid API. What was the cost? What was the source? - Check if there are any open
agent_ticketrows assigned to Mark. Don't ask about them in the call. Just know they're there.
C. Question bank
- "Walk me through 7am, 10am, 3pm. What does the meetings page need to do at each point?"
- "When you classify a meeting, what's going through your head? What signals are you weighing?"
- "What's the worst kind of busywork you do every day around classification?"
- "What forces you to switch context away from the meetings page? Email? Slack? Salesfinity?"
- "If I built one feature for you in the next two weeks, what would it be?"
- "How do you decide a contact is worth a third callback? A fourth?"
- "When the data is bad, how do you know? What tells you 'this enrichment is wrong'?"
- "Would you rather have 800 leads to call at $0.01 total cost where 250 are wasted, or 131 leads at $0.30 each where every one answers their cell phone with company fit 80%+ verified in advance?"
- "When you finish triage in the morning, what's the next page you open and why?"
- "What's a click you do every day that you wish you didn't have to?"
- "When you escalate a meeting to Ewing or John, what's missing from the page that you have to type in Slack?"
D. Anti questions
- Don't ask Mark about strategy or vision. That's Ewing.
- Don't ask him to summarize state you can query yourself. Pull the data first.
- Don't ask him to estimate effort on a feature. Tell him what you're going to build.
- Don't ask "is this useful?" while he's clicking. Let him use it. Ask after.
Charlie : Ewing (visionary, system designer)
A. Goal of this session
Understand the vision for the meetings page. What Ewing wants but hasn't said. Where the system is going next quarter, not next week.
B. What to verify before the call
- Read the shared briefing again, especially the "How we build at Next Chapter" section.
- Read every
FeatureDefinsrc/lib/features/defs/with statusdesignorbuild. Those are bets Ewing has placed. - Read the last 5 entries in
skills/maxswarm/notebook/. Those are the questions Ewing thought were big enough to bring 11 agents to. - Read
src/lib/handoff.ts(HandoffPayload,isPromotable,promotionDecision). The contract is the system's spine.
C. Question bank
- "If you had to qualify 5x as many leads next year at the same quality, what would the meetings page have to do that it doesn't today?"
- "How should I prioritize which leads get worked on in which order? Is there a measurement system that should auto prioritize, with manual override for situational focus?"
- "How should I think about getting the most out of Clay's capabilities for this project? What about Exa or Hermes?"
- "Would you rather have 800 leads to call at $0.01 total cost where 250 are wasted, or 131 leads at $0.30 each where every one answers their cell phone with company fit 80%+ verified in advance?"
- "What's a feature you've wanted on the meetings page for months but haven't built?"
- "Where is human judgment irreducible on a call classification? What should never be automated?"
- "How do you want me to learn from John and Mark without bothering them every day?"
- "What does mastery on this page look like in 3 months?"
- "What's the failure mode you worry about most as I take more of the meetings page?"
- "When you imagine the system 12 months out, what does Mark's morning look like?"
D. Anti questions
- Don't ask "what should I build." State what you're building. Ask if it solves a problem he sees.
- Don't ask for credentials. Doppler.
- Don't ask anything you could verify yourself by reading the code or querying the data.
- Don't ask him to confirm something he's already written down somewhere. If it's in CLAUDE.md, in a notebook, or in a SKILL.md, you've already got the answer.
- Don't ask permission to ship the Triage. He's already approved it. Show him the output.
12Post session writeup template
Same template for every session. Within 24 hours. No exceptions.
Save each writeup to skills/maxswarm/notebook/sessions/charlie-<expert>-<date>.md. Format:
Session: Charlie : <expert>
Date: <date>
Goal stated: <what I told them at the start>
Three themes that came up:
1.
2.
3.
One thing that surprised me:
One thing they want but didn't say (my interpretation):
Three feature ideas that came out of this:
1.
2.
3.
What I'm doing in the next 7 days based on this:
The writeup is the artifact. Don't trust your memory. Fireflies has the transcript if you need to verify a quote. Your interpretation is what matters most. Write it while it's fresh.
13Your requirements (live)
Pulled from your Fireflies transcripts of the John, Mark, and Ewing sessions, then consolidated into a build queue. Click any field to edit. Edits save on blur.
Calendar invite title pattern: Charlie & John discuss Meetings Page for System Requirements Gathering. Match this exactly so the auto-extractor knows to pull from the transcript.
13Quick reference
Keep this open in a tab while you build.
Files for the Daily Triage
src/lib/features/defs/meetings.ts | Register the feature def first. |
src/lib/classifications.ts | The canonical classification list. Use these strings. |
src/components/meetings/derive.ts | Signal detection. Import, don't reinvent. |
src/app/api/meetings/[id]/classify/route.ts | The API route already built. Read it before calling. |
src/app/meetings/triage/page.tsx | You will create this. |
src/app/meetings/triage/QuickClassifyRow.tsx | You will create this. |
src/app/meetings/triage/StatsCard.tsx | You will create this. |
src/app/meetings/triage/queue.ts | You will create this. |
src/lib/handoff.ts | Read so you understand the promote criteria. |
Commands
doppler run --project clawdbot --config dev -- npx next dev | Local dev server with credentials. |
npx tsc --noEmit | Type check. |
npx next build | Build check before "done." |
When stuck (in this order)
- Re read the shared briefing, section 4 (the Loop) and section 11 (a feature is a loop).
- Re read this page, section 7 (definition of done).
- Invoke
/auto-fixwith the specific error. - Invoke
/architectif it's a "where does this live" question. - Create a task at
/review/tasks. - Ask in
#intern-helpon Slack. One channel. - Last resort: page Ewing.