9.5 KiB
✅ Collector Handoff Persistence - Complete
Overview
The Collector AI now persists its checklist state to Firestore on every chat turn, ensuring the checklist survives across sessions and page refreshes.
What Was Added
1. Structured Output from AI
The Collector AI now returns both:
- Conversational reply (user-facing message)
- Collector handoff data (structured checklist state)
{
"reply": "✅ I see you've uploaded 2 documents. Anything else?",
"collectorHandoff": {
"hasDocuments": true,
"documentCount": 2,
"githubConnected": false,
"extensionLinked": false,
"readyForExtraction": false
}
}
2. Persistence to Firestore
Location: projects/{projectId}/phaseData.phaseHandoffs.collector
Structure:
interface CollectorPhaseHandoff {
phase: 'collector';
readyForNextPhase: boolean; // Ready for extraction?
confidence: number; // 0.5 or 0.9
confirmed: {
hasDocuments?: boolean; // Docs uploaded?
documentCount?: number; // How many?
githubConnected?: boolean; // GitHub connected?
githubRepo?: string; // Repo name
extensionLinked?: boolean; // Extension connected?
};
uncertain: {
extensionDeclined?: boolean; // User said no to extension?
noGithubYet?: boolean; // User doesn't have GitHub?
};
missing: string[]; // What's still needed
questionsForUser: string[]; // Follow-up questions
sourceEvidence: string[]; // Source references
version: string; // "1.0"
timestamp: string; // ISO timestamp
}
3. Code Changes
app/api/ai/chat/route.ts
Added structured output schema:
const ChatReplySchema = z.object({
reply: z.string(),
collectorHandoff: z.object({
hasDocuments: z.boolean().optional(),
documentCount: z.number().optional(),
githubConnected: z.boolean().optional(),
githubRepo: z.string().optional(),
extensionLinked: z.boolean().optional(),
extensionDeclined: z.boolean().optional(),
noGithubYet: z.boolean().optional(),
readyForExtraction: z.boolean().optional(),
}).optional(),
});
Added persistence logic:
// If in collector mode and AI provided handoff data, persist it
if (resolvedMode === 'collector_mode' && reply.collectorHandoff) {
const handoff: CollectorPhaseHandoff = {
phase: 'collector',
readyForNextPhase: reply.collectorHandoff.readyForExtraction ?? false,
confidence: reply.collectorHandoff.readyForExtraction ? 0.9 : 0.5,
confirmed: {
hasDocuments: reply.collectorHandoff.hasDocuments,
documentCount: reply.collectorHandoff.documentCount,
githubConnected: reply.collectorHandoff.githubConnected,
githubRepo: reply.collectorHandoff.githubRepo,
extensionLinked: reply.collectorHandoff.extensionLinked,
},
uncertain: {
extensionDeclined: reply.collectorHandoff.extensionDeclined,
noGithubYet: reply.collectorHandoff.noGithubYet,
},
missing: [],
questionsForUser: [],
sourceEvidence: [],
version: '1.0',
timestamp: new Date().toISOString(),
};
// Persist to Firestore
await adminDb.collection('projects').doc(projectId).set(
{
'phaseData.phaseHandoffs.collector': handoff,
},
{ merge: true }
);
}
Added console logging:
console.log(`[AI Chat] Collector handoff persisted:`, {
hasDocuments: handoff.confirmed.hasDocuments,
githubConnected: handoff.confirmed.githubConnected,
extensionLinked: handoff.confirmed.extensionLinked,
readyForExtraction: handoff.readyForNextPhase,
});
lib/ai/prompts/collector.ts
Added structured output instructions:
**STRUCTURED OUTPUT:**
In addition to your conversational reply, you MUST also return a collectorHandoff object tracking the checklist state:
```json
{
"reply": "Your conversational response here",
"collectorHandoff": {
"hasDocuments": true, // Are documents uploaded?
"documentCount": 5, // How many?
"githubConnected": true, // Is GitHub connected?
"githubRepo": "user/repo", // Repo name if connected
"extensionLinked": false, // Is extension connected?
"extensionDeclined": false, // Did user say no to extension?
"noGithubYet": false, // Did user say they don't have GitHub yet?
"readyForExtraction": false // Is user ready to move to extraction? (true when they say "yes" to "Is that everything?")
}
}
Update this object on EVERY response based on the current state of:
- What you see in projectContext (documents, GitHub, extension)
- What the user explicitly confirms or declines
This data will be persisted to Firestore so the checklist state survives across sessions.
---
## How It Works
### **Flow:**
1. **User sends message** → "I uploaded some docs"
2. **Collector AI analyzes** `projectContext`:
- Sees `knowledgeSummary.bySourceType.imported_document = 3`
- Sees `project.githubRepo = null`
- Sees no extension data
3. **AI responds with structured output**:
```json
{
"reply": "✅ I see you've uploaded 3 documents. Do you have a GitHub repo?",
"collectorHandoff": {
"hasDocuments": true,
"documentCount": 3,
"githubConnected": false,
"extensionLinked": false,
"readyForExtraction": false
}
}
-
Backend persists handoff to Firestore:
- Writes to
projects/{projectId}/phaseData.phaseHandoffs.collector - Logs checklist state to console
- Writes to
-
On next page load/refresh:
- Checklist state is still there
- AI can see previous state and continue from where it left off
Benefits
✅ Checklist Survives Sessions
- User can close browser and come back
- Progress is never lost
✅ Debugging & Analytics
- Can see exact checklist state at any point
- Helps debug "why did AI ask that?" questions
✅ Smart Handoff Protocol
- When
readyForExtraction = true, system knows to transition - Can build automatic phase transitions later
✅ Historical Tracking
- Timestamp on every update
- Can see how checklist evolved over time
Example Firestore Document
{
"projects": {
"abc123": {
"name": "My SaaS",
"currentPhase": "collector",
"phaseData": {
"phaseHandoffs": {
"collector": {
"phase": "collector",
"readyForNextPhase": false,
"confidence": 0.5,
"confirmed": {
"hasDocuments": true,
"documentCount": 3,
"githubConnected": true,
"githubRepo": "user/my-saas",
"extensionLinked": false
},
"uncertain": {
"extensionDeclined": false,
"noGithubYet": false
},
"missing": [],
"questionsForUser": [],
"sourceEvidence": [],
"version": "1.0",
"timestamp": "2025-11-17T22:30:00.000Z"
}
}
}
}
}
}
Testing
Manual Test:
- Start a new project chat
- Say: "I uploaded some documents"
- Check Firestore:
# In Firebase Console → Firestore projects/{projectId}/phaseData.phaseHandoffs.collector - Verify
confirmed.hasDocuments = true - Refresh page
- Send another message
- Verify handoff updates with latest state
Console Output:
Watch for this log on each collector message:
[AI Chat] Collector handoff persisted: {
hasDocuments: true,
githubConnected: false,
extensionLinked: false,
readyForExtraction: false
}
Future Enhancements
1. Auto-Transition to Extraction
When readyForExtraction = true, automatically switch mode:
if (handoff.readyForNextPhase) {
await adminDb.collection('projects').doc(projectId).update({
currentPhase: 'analyzed',
});
// Next message will be in extraction_review_mode
}
2. Visual Checklist UI
Display the checklist state in the UI:
<ChecklistCard>
<ChecklistItem
checked={handoff.confirmed.hasDocuments}
label="Documents uploaded"
/>
<ChecklistItem
checked={handoff.confirmed.githubConnected}
label="GitHub connected"
/>
<ChecklistItem
checked={handoff.confirmed.extensionLinked}
label="Extension linked"
/>
</ChecklistCard>
3. Analytics Dashboard
Track average time to complete collector phase:
- Time from first message to
readyForExtraction = true - Most common blockers (missing docs? no GitHub?)
- Drop-off points
4. Smart Reminders
If user hasn't interacted in 24 hours and checklist incomplete:
- Send email: "Hey! You're 2/3 done setting up your project..."
- Show prompt on next login
Files Changed
- ✅
app/api/ai/chat/route.ts- Added handoff persistence - ✅
lib/ai/prompts/collector.ts- Added structured output instructions - ✅
lib/types/phase-handoff.ts- Type already existed (no changes needed)
Status
✅ Complete and deployed
- Collector AI returns structured handoff data
- Handoff data persists to Firestore on every message
- Console logging for debugging
- No linting errors
- Dev server restarted with changes
Summary
The Collector AI now maintains persistent checklist state in Firestore, ensuring users never lose progress and enabling future features like:
- Auto-transitions between phases
- Visual checklist UI
- Analytics and reminders
Status: 🚀 Ready for testing!