diff --git a/frontend/src/viewers/ArtifactView.test.tsx b/frontend/src/viewers/ArtifactView.test.tsx
index 03342f4c..ccf02393 100644
--- a/frontend/src/viewers/ArtifactView.test.tsx
+++ b/frontend/src/viewers/ArtifactView.test.tsx
@@ -10,6 +10,11 @@ test("sql artifact renders the SQL text", () => {
expect(screen.getByText("SELECT 1")).toBeInTheDocument();
});
+test("sql artifact accepts a { content } wrapper", () => {
+ render();
+ expect(screen.getByText("SELECT 7")).toBeInTheDocument();
+});
+
test("cte_plan renders an ordered list of names", () => {
render();
expect(screen.getByText("a_cte")).toBeInTheDocument();
diff --git a/frontend/src/viewers/ArtifactView.tsx b/frontend/src/viewers/ArtifactView.tsx
index d1b00643..14903191 100644
--- a/frontend/src/viewers/ArtifactView.tsx
+++ b/frontend/src/viewers/ArtifactView.tsx
@@ -38,6 +38,7 @@ function toSqlBlocks(raw: unknown): SqlBlock[] | null {
const rec = asRecord(raw);
if (!rec) return null;
if (typeof rec.sql === "string") return [{ name: "SQL", sql: rec.sql }];
+ if (typeof rec.content === "string") return [{ name: "SQL", sql: rec.content }];
if (Array.isArray(rec.ctes)) {
const blocks = rec.ctes
.map((c) => asRecord(c))
diff --git a/frontend/src/viewers/SchemaLinkingViewer.test.tsx b/frontend/src/viewers/SchemaLinkingViewer.test.tsx
index 8a706196..a5e1a699 100644
--- a/frontend/src/viewers/SchemaLinkingViewer.test.tsx
+++ b/frontend/src/viewers/SchemaLinkingViewer.test.tsx
@@ -97,3 +97,16 @@ test("(e) buildErDiagram emits entities, attributes and a relation", () => {
expect(def).toContain("col id");
expect(def).toContain("orders }o--o{ customers : join");
});
+
+test("(f) buildErDiagram uses a column's real type when the data carries one", () => {
+ const def = buildErDiagram(
+ [
+ { kind: "table", name: "orders", decision: "promoted" },
+ { kind: "column", name: "orders.total", decision: "promoted", signals: { data_type: "numeric" } },
+ { kind: "column", name: "orders.id", decision: "promoted" }, // no type -> generic "col"
+ ],
+ [],
+ );
+ expect(def).toContain("numeric total");
+ expect(def).toContain("col id");
+});
diff --git a/frontend/src/viewers/SchemaLinkingViewer.tsx b/frontend/src/viewers/SchemaLinkingViewer.tsx
index cc9c6b1c..b8f18074 100644
--- a/frontend/src/viewers/SchemaLinkingViewer.tsx
+++ b/frontend/src/viewers/SchemaLinkingViewer.tsx
@@ -33,6 +33,19 @@ function reasonFor(c: Candidate): string {
return "";
}
+// The real DB type for a promoted column, when the schema-linking data carries it
+// (`data_type`/`type`, at top level or under `signals`). Today's schema_linking.json
+// has no column types, so this falls back to the generic `col` token that Mermaid
+// still needs as the attribute type; a real type shows as soon as the pipeline
+// provides one. The token is sanitized to keep the erDiagram valid.
+function attrType(c: Candidate): string {
+ const sig = (c.signals ?? {}) as Record;
+ const rec = c as unknown as Record;
+ const raw = sig["data_type"] ?? sig["type"] ?? rec["data_type"] ?? rec["type"];
+ const t = typeof raw === "string" && raw.trim() ? raw.trim() : "col";
+ return t.replace(/[^a-zA-Z0-9]/g, "_");
+}
+
export function buildErDiagram(promoted: Candidate[], joins: Join[]): string {
const sanitize = (name: string) => name.replace(/[^a-zA-Z0-9]/g, "_");
const tables = promoted.filter((c) => c.kind === "table");
@@ -46,7 +59,7 @@ export function buildErDiagram(promoted: Candidate[], joins: Join[]): string {
const cols = columns.filter((col) => col.name.startsWith(t.name + "."));
lines.push(` ${id} {`);
for (const col of cols) {
- lines.push(` col ${sanitize(col.name.slice(t.name.length + 1))}`);
+ lines.push(` ${attrType(col)} ${sanitize(col.name.slice(t.name.length + 1))}`);
}
lines.push(` }`);
}