fix(harness): recover schema table name typos
This commit is contained in:
@@ -367,6 +367,50 @@ function currentPhase(ctx, session) {
|
||||
return m ? parseInt(m[1], 10) : 1;
|
||||
}
|
||||
|
||||
// --- fuzzy table-name matching (prevents multi-turn debug loops on typos) ---
|
||||
|
||||
function levenshtein(a, b) {
|
||||
if (a === b) return 0;
|
||||
const la = a.length, lb = b.length;
|
||||
if (!la) return lb;
|
||||
if (!lb) return la;
|
||||
let prev = Array.from({ length: lb + 1 }, (_, i) => i);
|
||||
for (let i = 1; i <= la; i++) {
|
||||
const cur = [i];
|
||||
for (let j = 1; j <= lb; j++) {
|
||||
cur[j] = Math.min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + (a[i - 1] !== b[j - 1] ? 1 : 0));
|
||||
}
|
||||
prev = cur;
|
||||
}
|
||||
return prev[lb];
|
||||
}
|
||||
|
||||
const _catalogNamesCache = new Map();
|
||||
function catalogTableNames(ctx) {
|
||||
const key = ctx.cwd ?? "";
|
||||
if (_catalogNamesCache.has(key)) return _catalogNamesCache.get(key);
|
||||
let names;
|
||||
try {
|
||||
const raw = tht(ctx, ["schema", "render", "--format", "mschema-text"]);
|
||||
names = [...raw.matchAll(/^CREATE TABLE (\S+)/gm)].map((m) => m[1]);
|
||||
} catch {
|
||||
names = [];
|
||||
}
|
||||
_catalogNamesCache.set(key, names);
|
||||
return names;
|
||||
}
|
||||
|
||||
function closestTableName(ctx, name, maxDist = 3) {
|
||||
const names = catalogTableNames(ctx);
|
||||
let best = null, bestDist = maxDist + 1;
|
||||
for (const n of names) {
|
||||
if (Math.abs(n.length - name.length) > maxDist) continue;
|
||||
const d = levenshtein(n, name);
|
||||
if (d < bestDist) { bestDist = d; best = n; }
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
// tht phase advance --auto: exit 6 = not ready / needs human (silent no-op), others propagated.
|
||||
// Used for the fire-and-forget auto-advance of the auto phases (F2 memory, F6 cte) after a
|
||||
// reviewer_decide: it only advances when the phase is auto-eligible (zero substantive
|
||||
@@ -978,10 +1022,23 @@ export default function (pi) {
|
||||
try {
|
||||
cat = JSON.parse(tht(ctx, ["schema", "columns", t.name, "--json"]));
|
||||
} catch (e) {
|
||||
const msg = (e.stderr || e.message || String(e)).toString().trim();
|
||||
return textResult(
|
||||
`Tabella '${t.name}' non caricabile dal catalogo (${msg}). Proponi solo tabelle presenti nel catalogo (usa 'tht schema render' / 'tht search' per verificarne i nomi).`,
|
||||
);
|
||||
// Fuzzy recovery: the model often misspells Italian table names
|
||||
// (e.g. "abellazione" vs "ablazione"). Auto-correct if a close
|
||||
// catalog match exists, preventing a multi-turn debug spiral.
|
||||
const fix = closestTableName(ctx, t.name);
|
||||
if (fix) {
|
||||
try {
|
||||
cat = JSON.parse(tht(ctx, ["schema", "columns", fix, "--json"]));
|
||||
t.id = fix;
|
||||
t.name = fix;
|
||||
} catch { /* fall through to error */ }
|
||||
}
|
||||
if (!cat) {
|
||||
const msg = (e.stderr || e.message || String(e)).toString().trim();
|
||||
return textResult(
|
||||
`Tabella '${t.name}' non caricabile dal catalogo (${msg}). Proponi solo tabelle presenti nel catalogo (usa 'tht schema render' / 'tht search' per verificarne i nomi).`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!Array.isArray(cat.columns)) {
|
||||
return textResult(`Catalogo per '${t.name}' non contiene colonne valide. Verifica con 'tht schema columns ${t.name} --json'.`);
|
||||
|
||||
Reference in New Issue
Block a user