#1082 Implementation Summary
What Was Implementedโ
Backend (api/)โ
-
Database Migration (
1780300000002-UpdateExpertApplicationForAI.ts)- Added new columns for #1082 spec:
current_role,years_experience,impact_statement,ai_score_breakdown(JSONB),qualification_threshold,reapply_eligible_at - Changed
ai_scorefrom int to float (0-1 range) - Added unique index on
emailfor duplicate detection - Added index for rejected applications (90-day cooldown queries)
- Added new columns for #1082 spec:
-
Entity Update (
entities/expert-application.entity.ts)- Extended
ExpertApplicationStatusenum with new states:qualified,invited,interview_completed - Added types:
YearsExperience,PrimaryDomain,AIScoreBreakdown - Added fields:
currentRole,yearsExperience,impactStatement,aiScoreBreakdown,qualificationThreshold,reapplyEligibleAt
- Extended
-
DTO Update (
dto/expert-application.dto.ts)CreateExpertApplicationDto: 8 required fields with validation per #1082 spec- LinkedIn OR resume URL validation (at least one required)
- Impact statement: 300 char max with MinLength(10)
CheckApplicationStatusDto: new DTO for status lookup by email- Response types defined
-
Service Update (
marketing.service.ts)checkApplicationStatus(): duplicate detection + re-application gating (90-day cooldown)createExpertApplication(): creates application with new fields, checks duplicatestriggerAIScreeningStub(): STUB - logs intent, BLOCKED pending Eusden/ops confirmationrunAIScreening(): admin method to manually trigger AI scoringupdateQualificationThreshold(): admin-configurable threshold- Constants for AI weights (40% seniority, 40% domain, 20% impact) and default threshold (0.70)
-
Controller Update (
marketing.controller.ts)POST /marketing/experts/application: create application (5 req/min limit)GET /marketing/experts/application/status: check status by email (30 req/min)GET /marketing/experts/application/:id: get public-safe application details (60 req/min)
Frontend (frontend/)โ
-
Application Form Component (
experts/apply/_components/expert-application-form.tsx)- Single-page form with all 8 fields
- Client-side validation matching DTO rules
- LinkedIn OR resume URL validation
- Impact statement: 300 char counter
- Duplicate detection via pre-flight check to status endpoint
- Re-application gating: shows reapply date if rejected < 90 days
- Confirmation screen with immediate feedback (no spinner wait)
- Mobile-responsive design
-
Page Component (
experts/apply/page.tsx)- Metadata for SEO
- Dark gradient background
- Public route (no auth required)
What Is BLOCKED / Pendingโ
1. AI Scoring Criteria (Waiting for Eusden/ops)โ
Location: marketing.service.ts:triggerAIScreeningStub()
The AI scoring implementation is stubbed. Before enabling:
- Confirm weights: seniority 40%, domain 40%, impact 20%
- Confirm threshold: 0.70 default
- Implement actual LLM call with structured output
- Calculate composite score:
seniority*0.4 + domain*0.4 + impact*0.2 - Store breakdown in
aiScoreBreakdown(JSONB) - Generate reasoning text in
aiReasoning
2. Resume File Upload (Needs S3/R2 Integration)โ
Location: Frontend form, resumeUrl field
Currently using URL input. For full implementation:
- Add S3/R2 bucket for resume storage
- Implement presigned URL upload flow
- Add virus scanning (ClamAV or similar)
- Store
resumeUrlas CDN URL
3. Email Flows (Depends on #1083 #1084)โ
Location: marketing.service.ts
-
Qualifiedโ Tavus interview email with link (#1083) -
Waitlistedโ Waitlist confirmation email (#1084) - Email templates and sending logic
4. Tavus Integration (#1083)โ
Location: marketing.service.ts:promoteExpertApplication(), state transitions
When AI score >= threshold:
- Trigger Tavus conversation creation
- Update status to
invited - Store
tavusConversationId,tavusConversationUrl - Handle webhook
conversation.endedโinterview_completed
5. Async Job Queue (Optional)โ
Current: AI stub logs intent synchronously Future: Move AI scoring to background job (BullMQ/SQS)
Testing Checklistโ
- Submit form with all 8 fields โ creates application with status
pending - Submit duplicate email โ shows status check message
- Submit with rejected status < 90 days โ shows reapply date
- LinkedIn OR resume validation โ must have at least one
- Character limits โ currentRole 80, impactStatement 300
- Mobile layout โ works at 375px width
- API rate limiting โ 5 req/min for submit
Database Queriesโ
-- Check for duplicate
SELECT * FROM expert_applications WHERE LOWER(email) = LOWER($1) ORDER BY created_at DESC LIMIT 1;
-- Re-application gating (rejected within 90 days)
SELECT * FROM expert_applications
WHERE email = $1 AND status = 'rejected'
AND updated_at > NOW() - INTERVAL '90 days';
-- List pending applications for AI processing
SELECT * FROM expert_applications
WHERE status = 'pending' AND ai_score IS NULL;
State Machineโ
[pending]
โ AI screening runs (BLOCKED: stub only)
โโ score >= 0.70 โ [qualified]
โ โ Tavus invite (BLOCKED: #1083)
โ โ [invited]
โ โ Interview complete
โ โ [interview_completed]
โ โ Admin approval
โ โ [approved]
โโ score < 0.70 โ [waitlisted]
โ Admin decline
โ [rejected]
โ 90-day cooldown
โ reapply_eligible_at set
Related Issuesโ
- #1081: Expert Landing Page โ (already implemented, links to /experts/apply)
- #1083: Tavus Video Interview โ BLOCKS qualified flow
- #1084: Waitlist Flow โ BLOCKS waitlisted emails