From 7a9c2575f09d5b569350b7c6357ca10a44cb46d8 Mon Sep 17 00:00:00 2001 From: mawkone Date: Tue, 9 Jun 2026 16:10:45 -0700 Subject: [PATCH] chore(telemetry): add path-confusion loop breaker and strict blank-preview diagnostic protocol --- vibn-frontend/app/api/chat/route.ts | 60 +++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/vibn-frontend/app/api/chat/route.ts b/vibn-frontend/app/api/chat/route.ts index 7cc686e8..0913952e 100644 --- a/vibn-frontend/app/api/chat/route.ts +++ b/vibn-frontend/app/api/chat/route.ts @@ -310,6 +310,15 @@ Each project has a persistent \`vibn-dev\` container. Edit files via \`fs_*\` an - After \`dev_server_start\` returns a \`previewUrl\` AND \`healthCheck.status === 200\`, call \`browser_console { url: previewUrl }\` to capture frontend console errors. - **CRITICAL:** Next.js HMR overlay syntax errors do NOT crash the \`dev_server_start\` command. Even if \`dev_server_start\` returns \`Status: success\`, you MUST call \`browser_console\` to verify that there are no red syntax error overlays on the screen. If \`browser_console\` returns errors, fix them with \`fs_edit\` before declaring done. A green \`healthCheck\` plus a clean console is the real "done" signal for UI work. +**BLANK PREVIEW / NOT LOADING PROTOCOL:** +If the user tells you the preview is blank, not loading, or shows nothing: +1. **DO NOT GUESS OR EDIT CODE YET.** +2. Run \`dev_server_list\` to check if the server is actually running. +3. If it is not running, run \`dev_server_start\`. +4. If it is running, run \`dev_server_logs\` on its port to check for compilation hangs (e.g. Turbopack slow filesystem hangs) or fatal errors. +5. Run \`browser_console\` on the previewUrl. +6. ONLY edit code or configuration once the logs/console explicitly identify the source file or error. + **HMR through the proxy (apply when scaffolding):** - **Vite (verified working):** in \`vite.config\` set \`server: { host: '0.0.0.0', port: <3000-3009>, strictPort: true, hmr: { clientPort: 443, protocol: 'wss', host: '' } }\`. The \`hmr.host\` is REQUIRED — without it Vite's HMR client can guess the wrong host and the WS handshake fails through Traefik. Default localhost binding looks fine locally but breaks HMR through the proxy. - **Next dev:** \`next dev -p 3000 -H 0.0.0.0\` (WSS HMR works automatically through the proxy without extra config). @@ -822,6 +831,7 @@ export async function POST(request: Request) { let lastVerifySig: string | null = null; let lastRoundToolSig: string | null = null; + let lastRoundResults: any[] = []; let fileHashes = new Map(); let stallRounds = 0; @@ -1064,7 +1074,16 @@ export async function POST(request: Request) { "Stalled (Repeated the exact same tool calls twice without advancing)"; } + const pathConfusion = detectPathConfusion( + currentRoundResults, + lastRoundResults, + ); + if (pathConfusion) { + loopBreakReason = `PATH_CONFUSION: ${pathConfusion}`; + } + lastRoundToolSig = currentRoundToolSig; + lastRoundResults = currentRoundResults; if (loopBreakReason) break; } @@ -1546,3 +1565,44 @@ function checkRoundProgress( return { progressed, nextHashes }; } + +function detectPathConfusion(current: any[], last: any[]): string | null { + const missingFiles = new Set(); + + const extractMissingFiles = (results: any[]) => { + for (const tr of results) { + if (!tr.content) continue; + try { + const parsed = JSON.parse(tr.content); + const resultStr = + typeof parsed === "string" ? parsed : JSON.stringify(parsed); + const argsStr = typeof tr.toolName === "string" ? tr.toolName : ""; + const allStr = resultStr + argsStr; + + if ( + allStr.includes("not a file or missing") || + allStr.includes("No such file or directory") + ) { + const match = allStr.match(/([a-zA-Z0-9_\-\.]+\.[a-zA-Z0-9]+)/); + if (match && match[1]) { + missingFiles.add(match[1]); + } + } + } catch (e) {} + } + }; + + extractMissingFiles(current); + extractMissingFiles(last); + + // If the agent hit a "Not a file" error on the same basename across two different rounds + if (missingFiles.size > 0 && current.length > 0 && last.length > 0) { + for (const file of missingFiles) { + // Very basic heuristic: if it complains about the same file twice, break the loop + // and instruct it to use \`find\` + return `You are trying to read ${file} but the path is wrong. Stop guessing paths. Run 'shell_exec { command: "find . -name ${file}" }' to discover the exact path to this file, then use it exactly once.`; + } + } + + return null; +}