chore(telemetry): add path-confusion loop breaker and strict blank-preview diagnostic protocol

This commit is contained in:
2026-06-09 16:10:45 -07:00
parent dd510fe81f
commit 7a9c2575f0

View File

@@ -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 previewUrl host, no protocol>' } }\`. 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<string, string>();
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<string>();
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;
}