VIBN Frontend for Coolify deployment
This commit is contained in:
370
COLLECTOR_HANDOFF_PERSISTENCE.md
Normal file
370
COLLECTOR_HANDOFF_PERSISTENCE.md
Normal file
@@ -0,0 +1,370 @@
|
||||
# ✅ 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)
|
||||
|
||||
```typescript
|
||||
{
|
||||
"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:**
|
||||
```typescript
|
||||
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:
|
||||
```typescript
|
||||
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:
|
||||
```typescript
|
||||
// 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:
|
||||
```typescript
|
||||
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:
|
||||
|
||||
```markdown
|
||||
**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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
4. **Backend persists handoff to Firestore**:
|
||||
- Writes to `projects/{projectId}/phaseData.phaseHandoffs.collector`
|
||||
- Logs checklist state to console
|
||||
|
||||
5. **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
|
||||
|
||||
```json
|
||||
{
|
||||
"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:**
|
||||
|
||||
1. Start a new project chat
|
||||
2. Say: "I uploaded some documents"
|
||||
3. Check Firestore:
|
||||
```bash
|
||||
# In Firebase Console → Firestore
|
||||
projects/{projectId}/phaseData.phaseHandoffs.collector
|
||||
```
|
||||
4. Verify `confirmed.hasDocuments = true`
|
||||
5. Refresh page
|
||||
6. Send another message
|
||||
7. 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:
|
||||
|
||||
```typescript
|
||||
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:
|
||||
|
||||
```tsx
|
||||
<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
|
||||
|
||||
1. ✅ `app/api/ai/chat/route.ts` - Added handoff persistence
|
||||
2. ✅ `lib/ai/prompts/collector.ts` - Added structured output instructions
|
||||
3. ✅ `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!**
|
||||
|
||||
Reference in New Issue
Block a user