2026 03 12 S211 Generation Fixes And Observability
Documentation for Plans
S211.3 — Generation Fixes, Cancel+Diagnostic Flow, Process Observability
Session: 211.3 | Date: 2026-03-12 | Status: Approved
Problem Statement
Three interconnected issues in Vitto's platform generation pipeline:
- Wrong GitLab repo name (P0 bug): Vitto creates repos with the space slug as both subgroup AND project name (e.g.,
chrome-space-1/chrome-space-1instead ofchrome-space-1/vitto-uni-test-1) - No stop generation button (P1 UX): Users cannot cancel a running generation from chat, and when they do cancel there's no intelligent recovery
- Invisible internal processes (P2 observability): Vitto's prompt sequences, tool calls, and reasoning are ephemeral — executives/devs/admins cannot inspect, debug, or improve them
P0: Wrong Repo Name
Root Cause
Frontend — vitto-chat-engine.service.ts:400:
universeSlug: universeId, // BUG: sends universeId as universeSlug
UniverseContextService has no universeSlug field. The code reuses universeId() which can return a route segment (slug), MongoDB ObjectId, or stale autoFlashState value. When it returns the space slug, both subgroup and repo get the same name.
Backend defense gap — module-gen-phase.executor.ts:162-166:
The looksLikeRawId() check only catches ObjectIds, hex strings, and timestamps. A valid-looking but WRONG slug (like the space slug) passes through unchecked.
Fix
Layer 1 — Frontend (UniverseContextService):
- Add
universeSlug: string | nulltoUniverseContextinterface andDEFAULT_CONTEXT - Add
readonly universeSlug = computed(() => this._context().universeSlug)signal - Add
universeSlugtosetContext()and route sync - Update
vitto-chat-engine.service.ts:400to usethis.universeContext?.universeSlug()
Layer 2 — Backend defense-in-depth (module-gen-phase.executor.ts):
- ALWAYS resolve universe slug from backend-api when
spaceId+universeIdare available - Compare resolved slug against frontend-provided slug
- Log warning on mismatch, use backend-resolved value as canonical
- This catches future frontend bugs and stale state
Files Changed
| File | Change |
|---|---|
libs/libs-foundational/core-angular/src/lib/services/universe-context.service.ts |
Add universeSlug to interface, state, computed signal |
libs/libs-ai/vitto-chat/src/lib/services/vitto-chat-engine.service.ts |
Fix line 400: universeSlug: this.universeContext?.universeSlug() |
APIs/backend-vitto-api/src/app/module-generation/phases/module-gen-phase.executor.ts |
Always resolve + compare universe slug from backend-api |
P1: Cancel Generation + Vitto Diagnostic Flow
Design
When the user cancels, Vitto doesn't just stop — it investigates and offers recovery options.
Flow:
- User clicks cancel in chat →
POST /vitto-api/engine/cancel/:taskId - Backend aborts generation pipeline (cancels module-gen session, kills pending LLM calls)
- Backend injects a diagnostic system message into conversation history
- This triggers a new agent loop iteration where Vitto uses its tools to check GitLab state
- Vitto reports findings and offers concrete options (delete wrong repo, rename, retry, keep partial)
Diagnostic system message (injected on cancel):
The user cancelled the generation. IMMEDIATELY investigate what happened:
1. Use http_request to check the GitLab repo state — was it created? What name? Any commits?
2. Check the module generation session status via check_module_generation
3. Report your findings to the user clearly
4. Offer concrete recovery options: delete the wrong repo, rename it, retry with correct settings, or keep partial work
Do NOT ask the user what happened — YOU investigate first, then present what you found.
Backend Changes
New: POST /vitto-api/engine/cancel/:taskId endpoint in vitto-engine.service.ts:
- Finds the active agent loop for
taskId - Sets abort signal (AbortController pattern)
- Cancels the module-gen session if one is active
- Injects diagnostic system message
- Triggers new agent loop iteration with diagnostic context
Modified: agent-loop.service.ts:
- Add
AbortControllersupport — check signal before each iteration and tool execution - On abort, emit
task.cancelledSSE event with partial results
Modified: tool-registry.service.ts (start_module_generation tool):
- Accept abort signal — if aborted mid-wait, return cancellation result instead of timeout
Frontend Changes
Modified: Vitto chat panel — wire cancel button to POST /engine/cancel/:taskId
taskIdis returned by the initialPOST /engine/messageresponse (already available)- On cancel, the SSE stream naturally delivers Vitto's diagnostic response
- Show a "Cancelled — Vitto is investigating..." interim state
Files Changed
| File | Change |
|---|---|
APIs/backend-vitto-api/src/app/engine/vitto-engine.service.ts |
Add cancel endpoint + diagnostic injection |
APIs/backend-vitto-api/src/app/engine/agent-loop.service.ts |
AbortController support, abort check per iteration |
APIs/backend-vitto-api/src/app/tools/tool-registry.service.ts |
Abort signal in start_module_generation |
libs/libs-ai/vitto-chat/src/lib/services/vitto-chat-engine.service.ts |
Add cancelTask(taskId) method |
libs/libs-ai/vitto-chat/src/lib/components/ (chat panel) |
Wire cancel button to cancelTask |
Edge Cases
- Cancel during GitLab commit: Check if a commit is in-flight. If so, let it complete before aborting (partial commits are worse than no cancel).
- Cancel after generation complete: If the pipeline already finished, the cancel is a no-op. Return the result normally.
- Rapid cancel/retry: Debounce cancel button (300ms). Prevent starting new generation while cancel is processing.
P2: Process Observability (Vitto Internal Process Persistence)
Design
Persist every step of Vitto's reasoning to MongoDB so executives, devs, and admins can inspect, debug, and improve prompt sequences.
Schema: vitto_process_runs collection
interface VittoProcessRun {
_id: ObjectId;
taskId: string; // Links to SSE stream + conversation
userId: string;
spaceId?: string;
universeId?: string;
channel: 'web' | 'whatsapp' | 'slack' | 'api';
status: 'running' | 'completed' | 'failed' | 'cancelled';
// The audit trail — ordered sequence of all steps
steps: VittoProcessStep[];
// Aggregated metrics
totalTokensUsed: number;
totalCostUsd: number;
durationMs: number;
modelUsed: string;
iterationCount: number;
// Categorization
tags: string[]; // e.g., ['module-generation', 'chrome-space-1']
parentRunId?: string; // For nested processes
createdAt: Date;
completedAt?: Date;
}
interface VittoProcessStep {
index: number;
type: 'system_prompt' | 'user_message' | 'llm_response' | 'tool_call' | 'tool_result' | 'error' | 'cancel';
// Content
content?: string; // Prompt text, LLM response, or error message
// Tool execution details
toolName?: string;
toolInput?: Record<string, unknown>;
toolOutput?: string;
toolDurationMs?: number;
// LLM call details
model?: string;
tokensIn?: number;
tokensOut?: number;
stopReason?: string;
timestamp: Date;
}
Integration Point
Subscriber pattern in agent-loop.service.ts — NOT inline in the hot path:
// After each step, fire-and-forget to MongoDB
this.processRunService.appendStep(runId, step).catch(err =>
this.logger.warn(`Process persistence failed (non-blocking): ${err.message}`)
);
Uses the same fire-and-forget + retry queue pattern as ChatBackendSyncService. If MongoDB is slow/down, generation continues unaffected.
Future UI (not in this session)
A new page at /vitto/processes showing:
- List of recent process runs (filterable by status, user, space, tags)
- Drill-down: full step-by-step timeline with expandable content
- Prompt viewer: see exactly what system prompt was assembled
- Prompt improvement suggestions: Flag a step, propose a change, test it
Files Changed
| File | Change |
|---|---|
APIs/backend-vitto-api/src/app/engine/process-run.service.ts |
NEW — CRUD for vitto_process_runs |
APIs/backend-vitto-api/src/app/engine/process-run.schema.ts |
NEW — Mongoose schema |
APIs/backend-vitto-api/src/app/engine/agent-loop.service.ts |
Inject ProcessRunService, append steps |
APIs/backend-vitto-api/src/app/engine/engine.module.ts |
Register new service + schema |
TTL & Retention
- MongoDB TTL index: auto-delete runs older than 90 days
- Runs tagged
importantorflaggedexempt from TTL - Estimated storage: ~50KB per run × 100 runs/day = ~5MB/day = ~450MB/90 days
Implementation Order
- P0: Repo naming fix — 2 files frontend, 1 file backend. Small, high-impact.
- P1: Cancel + diagnostic — 3 files backend, 2 files frontend. Medium effort.
- P2: Process persistence — 4 new/modified files backend. Schema + service only (no UI this session).
Risks
| Risk | Mitigation |
|---|---|
autoFlashState may not have universeSlug for existing users |
Backend defense-in-depth always resolves from API; frontend handles null gracefully |
| Cancel during GitLab commit creates partial repo | Check in-flight status before aborting; let current commit finish |
| Process persistence slows agent loop | Fire-and-forget writes, never block the hot path |
| MongoDB TTL deletes important runs | flagged tag exempts from TTL; default 90-day retention is generous |