feat(replay): extract reviewer_schema_linking gates + de-slug question fallback
extract.mjs now emits a schema-linking ui_request descriptor (columns rendered from the model's suggested_columns, since the live catalog enrichment needs the DWH) and parses the reviewer's "N tabelle" outcome; when neither the manifest nor question.md is reachable, the NL question is approximated by de-slugging the session id. replay.json regenerated from the 2026-07-06-175012 session (18 answered gates, includes the F4 schema-linking gate). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -215,6 +215,33 @@ function buildDescriptor(name, args, idx) {
|
||||
reserved: RESERVED,
|
||||
};
|
||||
}
|
||||
// F4 schema-linking gate (column curation, v2). The model supplies tables with
|
||||
// kind/rationale/suggested_columns; the live gate enriches columns from the
|
||||
// catalog via `tht schema columns` (needs DWH), so here we render only what the
|
||||
// model provided — columns come from suggested_columns as suggested=true.
|
||||
if (name === "reviewer_schema_linking") {
|
||||
const tables = parseOptions(args.tables).map((t) => ({
|
||||
id: t.id,
|
||||
name: t.name,
|
||||
kind: t.kind ?? "promote",
|
||||
recommended: !!t.recommended,
|
||||
description: t.description ?? null,
|
||||
rationale: t.rationale ?? null,
|
||||
columns: (parseOptions(t.suggested_columns).map((c) =>
|
||||
typeof c === "string" ? { name: c, description: null, suggested: true } : c,
|
||||
)),
|
||||
}));
|
||||
return {
|
||||
type: "ui_request",
|
||||
id,
|
||||
phase,
|
||||
schema_version: SCHEMA_VERSION,
|
||||
widget: "schema-linking",
|
||||
title: args.title,
|
||||
tables,
|
||||
reserved: RESERVED,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -263,6 +290,11 @@ function parseRealChoice(name, descriptor, resultText) {
|
||||
if (/Rifiutato/i.test(t)) return { labels: ["Rifiuta"], note: t };
|
||||
return { labels: [], note: t };
|
||||
}
|
||||
if (name === "reviewer_schema_linking") {
|
||||
// "Schema linking registrato dal reviewer (6 tabelle + colonne curate)."
|
||||
const m = t.match(/registrato dal reviewer \((\d+)\s+tabelle/);
|
||||
return { labels: m ? [`${m[1]} tabelle`] : [], note: t };
|
||||
}
|
||||
return { labels: [], note: t };
|
||||
}
|
||||
|
||||
@@ -303,6 +335,17 @@ function readSessionQuestion(sid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Last-resort fallback when neither the manifest nor question.md can be found
|
||||
// (e.g. the workspace repo is not checked out here). `tht session new` derives
|
||||
// the session id slug from the question text, so de-slugging it recovers a
|
||||
// readable approximation of the original NL question.
|
||||
function questionFromSessionId(sid) {
|
||||
if (typeof sid !== "string") return null;
|
||||
const slug = sid.replace(/^\d{4}-\d{2}-\d{2}-\d{6}-/, "");
|
||||
if (!slug) return null;
|
||||
return slug.replace(/-/g, " ");
|
||||
}
|
||||
|
||||
// Resolve a session id to its on-disk directory. We check every workspace yaml
|
||||
// in harness/workspaces/ for a `paths.sessions` entry (relative OR absolute),
|
||||
// then look for <sessions>/<session-id>/ in each. Workspaces are symlinks to
|
||||
@@ -350,7 +393,7 @@ const replay = {
|
||||
},
|
||||
session: {
|
||||
id: sessionId,
|
||||
question: readSessionQuestion(sessionId),
|
||||
question: readSessionQuestion(sessionId) ?? questionFromSessionId(sessionId),
|
||||
created_at: createdAt,
|
||||
},
|
||||
stats: {
|
||||
|
||||
+537
-4016
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user