fix: digest real production plans (nesting depth, Object table names, character refs)

Measured against a 2.7 MB production plan that failed to import:
- maxNestedTags: real spaghettified plans nest 2729 tags deep and fast-xml-parser caps at 128, which surfaced as 'Maximum nested tags exceeded' and blamed the export. Raised to 20000; the 12 MB input cap bounds the worst case.
- table names live on <Object>, not on <IndexScan>/<TableScan>. The digest reported 0 tables for a plan with 51 objects; the analyst model never saw a table name. Now 37 distinct tables.
- SSMS writes StatementText with numeric character references (&#xD;&#xA; on every line, 90 in this plan). Attribute/tag processors decode them, so the model gets SQL instead of entity noise.
- 3 new digest tests, each from one of the three measurements above. 39 unit tests pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
ahmed mohammed
2026-09-22 11:16:18 +03:00
co-authored by Claude Opus 4.7
parent 4b5c19b5d2
commit 3fa436103a
4 changed files with 104 additions and 2 deletions
+40
View File
@@ -0,0 +1,40 @@
import { chromium } from "@playwright/test";
import { readFileSync } from "node:fs";
const BASE = "http://localhost:3000";
const b = await chromium.launch();
const p = await (await b.newContext({ baseURL: BASE, viewport: { width: 1400, height: 1000 } })).newPage();
await p.goto("/signup");
await p.fill("#email", `bigplan${Date.now()}@qai.test`);
await p.fill("#password", "0123456789ab");
await p.getByRole("button", { name: "Create account" }).click();
await p.waitForURL(/setup/, { timeout: 20000 });
await p.getByRole("link", { name: "Next" }).click();
await p.waitForURL(/step=2/, { timeout: 20000 });
await p.fill("#analyst_base_url", "http://192.168.88.131:8888/v1");
await p.fill("#analyst_key", "EMPTY");
await p.fill("#analyst_model", "qwen3.8-flash-next");
await p.fill("#analyst_extra", '{"chat_template_kwargs":{"enable_thinking":false}}');
await p.fill("#jev_key", process.env.JEV_API_KEY || "");
await p.getByRole("button", { name: "Save" }).click();
await p.waitForURL(/step=3/, { timeout: 20000 });
await p.goto("/app");
const t0 = Date.now();
await p.setInputFiles('input[type="file"]', "plan-cxpaket.sqlplan");
console.log("UPLIFT chars in box:", (await p.locator("#xml").inputValue()).length);
await p.getByRole("button", { name: "Analyse" }).click();
await p.waitForFunction(() => {
const btn = [...document.querySelectorAll("button")].find((x) => x.textContent === "Analyse");
return !!btn && !btn.disabled;
}, null, { timeout: 420000 });
const secs = Math.round((Date.now() - t0) / 1000);
const alert = await p.getByRole("alert").allInnerTexts();
console.log("RUN", secs + "s", "| alert:", JSON.stringify(alert).slice(0, 200));
console.log("REPORT headline:", JSON.stringify(await p.locator("#report h2").first().innerText().catch(() => "none")));
console.log("REPORT cards:", await p.locator("#report .card").count(),
"| sql blocks:", await p.locator("#report pre.sql").count(),
"| verdict:", JSON.stringify((await p.locator("[data-verdict] .label").first().innerText().catch(() => "none"))));
console.log("TABLES MENTIONED:", await p.getByText(/OSUSR_MSQ|dbo\./).count());
await b.close();