Phase 0 foundation is verified live. The codebase contains all prerequisite infrastructure for the intern sprint: feature registry with canUse() gates, circuit breaker with budget enforcement, formal handoff contract, spend tracking, and feature metrics logging. The gap analysis reveals exactly four missing API routes and two missing feature registrations that must be created before or during the intern sprints.
Key finding: Week 1 is safe for both interns — pure frontend work against existing data, no new API routes needed. Week 2 is where dependencies get real. Charlie's Quick Classify needs a new PATCH route. The promote button coordination between Charlie and Bear requires them to agree on HandoffPayload, which already has a formal contract in src/lib/handoff.ts.
Verified Foundation (Phase 0 complete)
ComponentFileStatus Feature typessrc/lib/features/types.tsLIVE Feature registrysrc/lib/features/registry.tsLIVE Permission gatessrc/lib/features/permissions.tsLIVE — canUse(), tierOf(), gateReason() Metrics loggingsrc/lib/features/metrics.tsLIVE — recordMetric(), featureUsageCount() Meeting feature defs (6)src/lib/features/defs/meetings.tsLIVE Deal feature defs (6)src/lib/features/defs/deals.tsLIVE Campaign feature defs (5)src/lib/features/defs/campaigns.tsDESIGN status Handoff contractsrc/lib/handoff.tsLIVE — PromotionCandidate, HandoffPayload, validateHandoff(), executeHandoff() Circuit breakersrc/lib/circuit-breaker.tsLIVE — checkBudget(), recordSpend() Promote routesrc/app/api/deals/promote-from-meeting/route.tsLIVE — uses canUseKey, recordMetric, executeHandoff Auto-promote routesrc/app/api/meetings/auto-promote/route.tsLIVE Foundation tables migrationsupabase/migrations/2026_05_06__foundation_tables.sqlLIVE — feature_metrics, outreach_campaigns, campaign_targets, agent_tickets, spend_log
Existing API Routes (relevant to interns)
RouteMethodUsed By /api/deals/promote-from-meetingPOSTCharlie (promote button) — operator tier /api/deals/[slug]GET/PATCHBear (deal detail, inline edit) /api/deals/[slug]/stagePATCHBear (view only — admin tier) /api/deals/[slug]/contactsGET/POSTBear (contacts list) /api/deals/[slug]/playsGET/POSTBear (deal plays) /api/meetings/[id]/draft-emailPOSTCharlie (follow-up drafts) /api/meetings/[id]/extractPOSTCharlie (extraction) /api/meetings/auto-promotePOSTSystem (cron) /api/meetings/approve-learningPOSTEwing only /api/webhooks/salesfinityPOSTSystem (webhook)
Missing Routes (must build)
RouteNeeded BySprintWhy PATCH /api/meetings/[id]/classifyCharlieWeek 2Quick Classify dropdown writes classification to meeting_notes GET /api/campaignsCharlieWeek 2Campaign target resolution view needs to list campaigns GET /api/campaigns/[slug]/targetsCharlieWeek 2Show which campaign a call came from GET /api/deals/recently-promotedBearWeek 2Recently Promoted section needs deals where flex_data has promoted_from_meeting
Supabase Schema (columns relevant to intern features)
TableKey ColumnsNotes meeting_notesmeeting_id, title, entity_id, classification, call_id, promoted_to_deal_id, updated_atclassification added in 2026_05_05 migration. No opportunity_score column — it is computed in derive.ts dealsslug, name, stage, updated_at, created_at, flex_dataNo stage_changed_at column. Must compute days-in-stage from deal_stage_history.at deal_stage_historydeal_id, from_stage, to_stage, actor, atLatest row per deal_id gives stage entry date deal_activity_logdeal_id, kind, headline, body, actor, atlast_activity_at is max(at) from this table outreach_campaignsid, slug, name, vertical, statusCampaign to link calls back campaign_targetscampaign_id, company_name, meeting_id, deal_id, outreach_statusLinks meetings to campaigns
Shared — Both Interns (Day 1)
Read CLAUDE.md intern rules section completely Read src/lib/features/types.ts — understand FeatureDef, FeatureTier, FeatureStatus Read src/lib/features/permissions.ts — understand canUse(), tierOf() Run through the feature registry: src/lib/features/registry.ts — see what is live vs design Understand: all writes go through API routes, never direct Supabase queries
Charlie — Week 1 (Meetings)
#TaskTypeDependenciesDetails
C1.1 Browse /meetings, click 5 meetings, map every field Exploration None Read src/app/meetings/page.tsx (630 lines). Trace data from Fireflies API through src/lib/fireflies.ts to the page.
C1.2 Read the Salesfinity webhook Exploration None Read src/app/api/webhooks/salesfinity/route.ts. Understand how calls arrive and get stored in call_log.
C1.3 Read signal detection code Exploration C1.1 Read src/components/meetings/derive.ts — detectRevenueSignal(), detectMotivatedSeller(), opportunity scoring.
C1.4 Build: "Time since last activity" column Frontend only C1.1
Feature def: meetings.view-list already registered (tier: intern)
API route: Not needed — updated_at already in meeting_notes, format as "3d ago"
Table reads: meeting_notes.updated_at
Table writes: None
Permission tier: intern
Blocked by: Nothing — pure frontend date formatting
Bear — Week 1 (Deals)
#TaskTypeDependenciesDetails
B1.1 Browse /deals, click each deal, understand stages/fees/probability Exploration None Read src/app/deals/page.tsx + DealsDashboard.tsx (739 lines). Read src/lib/deals.ts — DEAL_STAGES array, stage transitions, activity log.
B1.2 Read deal-awareness-protocol skill Exploration None Read skills/deal-awareness-protocol/SKILL.md. Understand deal lifecycle: worth_chasing through closed_won/closed_lost.
B1.3 Read the promote-from-meeting route Exploration B1.1 Read src/app/api/deals/promote-from-meeting/route.ts. Understand how canUseKey gates operator-tier actions. Read src/lib/handoff.ts for the full contract.
B1.4 Build: "Days in stage" badge Frontend only B1.1
Feature def: deals.view-pipeline already registered (tier: intern)
API route: Not needed — compute from deal_stage_history or deals.updated_at
Table reads: deal_stage_history (latest row per deal_id gives stage entry timestamp). If no history row, fall back to deals.created_at.
Table writes: None
Permission tier: intern
Blocked by: Nothing — pure frontend. Note: must query deal_stage_history alongside deals. If this join is not in the existing page query, extend it.
Gotcha: The deals table has no stage_changed_at column. Days-in-stage = now() minus max(deal_stage_history.at) WHERE deal_id matches. If no history rows exist, use deals.created_at.
R1: Missing classify route blocks Charlie's Week 2 entirely Impact: High — Quick Classify is Charlie's first write operation, the cornerstone of Week 2.
Mitigation: Ewing creates PATCH /api/meetings/[id]/classify before Week 2 starts. Estimated effort: 30 minutes. Pattern is identical to the promote route — canUseKey check, Supabase update, recordMetric.
Evidence: No route exists at src/app/api/meetings/[id]/classify/. The meeting_notes.classification column exists (added in 2026_05_05__meeting_to_deal_promotion.sql).
R2: deal_stage_history may have zero rows for existing deals Impact: Medium — Bear's "Days in stage" badge would show NaN or crash if no history rows exist.
Mitigation: Bear must include a fallback: if no deal_stage_history row for a deal, use deals.created_at as the stage entry date. Document this in code comments.
Evidence: deal_stage_history only gets rows when stage transitions occur via the stage API. Deals seeded before this migration have no history rows.
R3: Campaign data may not exist yet when Charlie builds campaign resolution view Impact: Medium — Campaign target resolution view will be empty if no campaigns/targets are in outreach_campaigns table.
Mitigation: Ewing seeds at least one campaign with targets before Week 2. Alternatively, Charlie builds the UI to gracefully show "No campaign linked" when campaign_targets has no matching meeting_id.
R4: Permission tier confusion — Charlie tries to test promote button Impact: Low (but frustrating) — Charlie builds promote UI, cannot test it because meetings.promote-to-deal is tier: operator. She will get a 403.
Mitigation: Charlie builds the UI and modal. Ewing tests the full promote flow end-to-end using his admin tier. Or: create a test-mode flag that overrides tier checks in development only.
Evidence: src/lib/features/permissions.ts line 21 — canUse() blocks any actor below the feature's tier.
R5: HandoffPayload coordination between Charlie and Bear Impact: Low — The contract already exists in src/lib/handoff.ts lines 17-32. Both interns read the same type.
Mitigation: Schedule a 15-minute sync at the start of Week 2 where both interns read HandoffPayload together and confirm they understand what executeHandoff() creates.
Evidence: HandoffPayload is a TypeScript type — compiler will catch mismatches.
R6: Feature def registration gap — 3 features need registration before interns can build Impact: Medium — If feature defs are not registered, canUseKey() will return false for those features, and metrics will not log correctly.
Mitigation: Ewing registers these before Week 2:
1. meetings.campaign-resolution in meetings.ts
2. deals.health-indicator in deals.ts
3. deals.recently-promoted in deals.ts
All three should be status: 'build', tier: 'intern'.
R7: $25/day budget limit confusion Impact: Low for Week 1-2 (no expensive API calls). Higher in Week 3+.
Mitigation: Both interns read CLAUDE.md intern rules on Day 1. Week 1-2 features are cost_category: 'free'. The circuit breaker at src/lib/circuit-breaker.ts will block anything that exceeds campaign budgets. No enrichment calls planned for these sprints.
Generated from 018__quarterback.md — do not edit this HTML directly.