From 1dd9a47dd1c53ac09b0c22780b382aa1de26ceaa Mon Sep 17 00:00:00 2001 From: Tirth Kanani Date: Sun, 14 Jun 2026 18:01:28 +0100 Subject: [PATCH 1/2] fix(schema): preserve graph `kind` field through validateGraph MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `KnowledgeGraphSchema` declares an optional `kind` enum ("codebase" | "knowledge") and the `KnowledgeGraph` type mirrors it, but `validateGraph` rebuilds its return object from scratch and never copied `kind`. A valid input graph with `kind: "knowledge"` therefore came back with `kind === undefined` — silent data loss on the declared API surface (loadGraph round-trips strip it, and the dashboard had to read `kind` from the raw pre-validation data to work around it). Carry the validated `kind` through, only when it is a valid enum value (mirroring how `version` is already preserved). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../packages/core/src/__tests__/schema.test.ts | 15 +++++++++++++++ .../packages/core/src/schema.ts | 3 +++ 2 files changed, 18 insertions(+) diff --git a/understand-anything-plugin/packages/core/src/__tests__/schema.test.ts b/understand-anything-plugin/packages/core/src/__tests__/schema.test.ts index d81759bdf..280322119 100644 --- a/understand-anything-plugin/packages/core/src/__tests__/schema.test.ts +++ b/understand-anything-plugin/packages/core/src/__tests__/schema.test.ts @@ -66,6 +66,21 @@ describe("schema validation", () => { expect(result.issues).toEqual([]); }); + it("preserves the kind field for knowledge graphs", () => { + const graph = structuredClone(validGraph); + (graph as any).kind = "knowledge"; + + const result = validateGraph(graph); + expect(result.success).toBe(true); + expect(result.data!.kind).toBe("knowledge"); + }); + + it("leaves kind undefined when the input omits it", () => { + const result = validateGraph(validGraph); + expect(result.success).toBe(true); + expect(result.data!.kind).toBeUndefined(); + }); + it("rejects graph with missing required fields", () => { const incomplete = { version: "1.0.0" }; const result = validateGraph(incomplete); diff --git a/understand-anything-plugin/packages/core/src/schema.ts b/understand-anything-plugin/packages/core/src/schema.ts index 71b9cd8ab..0d694e110 100644 --- a/understand-anything-plugin/packages/core/src/schema.ts +++ b/understand-anything-plugin/packages/core/src/schema.ts @@ -652,6 +652,9 @@ export function validateGraph(data: unknown): ValidationResult { const graph = { version: typeof fixed.version === "string" ? fixed.version : "1.0.0", + ...(fixed.kind === "codebase" || fixed.kind === "knowledge" + ? { kind: fixed.kind as "codebase" | "knowledge" } + : {}), project: projectResult.data, nodes: validNodes, edges: validEdges, From 190bf0120a4ea5a879efb3c76a6e33060e1aae1b Mon Sep 17 00:00:00 2001 From: Tirth Kanani Date: Tue, 16 Jun 2026 23:30:34 +0100 Subject: [PATCH 2/2] fix(schema): emit issue for out-of-enum kind, read kind from validated data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Concern 1: validateGraph now pushes an auto-corrected GraphIssue when `kind` is present but not "codebase"/"knowledge", instead of silently narrowing it to undefined — surfacing typos and stale extractor values like every other invalid field. Concern 2: App.tsx reads `result.data.kind` (the validated source of truth) rather than the raw pre-validation payload now that kind is preserved. Concern 3: replace the two kind tests with an it.each over ["codebase","knowledge","bogus",undefined] locking all four arms of the branch, plus issue-emission coverage for the out-of-enum path. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../core/src/__tests__/schema.test.ts | 31 ++++++++++++++++--- .../packages/core/src/schema.ts | 19 ++++++++++-- .../packages/dashboard/src/App.tsx | 2 +- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/understand-anything-plugin/packages/core/src/__tests__/schema.test.ts b/understand-anything-plugin/packages/core/src/__tests__/schema.test.ts index 280322119..2c29f24c5 100644 --- a/understand-anything-plugin/packages/core/src/__tests__/schema.test.ts +++ b/understand-anything-plugin/packages/core/src/__tests__/schema.test.ts @@ -66,19 +66,40 @@ describe("schema validation", () => { expect(result.issues).toEqual([]); }); - it("preserves the kind field for knowledge graphs", () => { + it.each([ + ["codebase", "codebase"], + ["knowledge", "knowledge"], + ["bogus", undefined], + [undefined, undefined], + ] as const)("carries kind=%j through validation as %j", (input, expected) => { + const graph = structuredClone(validGraph); + if (input === undefined) { + delete (graph as any).kind; + } else { + (graph as any).kind = input; + } + + const result = validateGraph(graph); + expect(result.success).toBe(true); + expect(result.data!.kind).toBe(expected); + }); + + it("emits an auto-corrected issue when kind is out of enum", () => { const graph = structuredClone(validGraph); - (graph as any).kind = "knowledge"; + (graph as any).kind = "bogus"; const result = validateGraph(graph); expect(result.success).toBe(true); - expect(result.data!.kind).toBe("knowledge"); + expect(result.data!.kind).toBeUndefined(); + expect(result.issues).toContainEqual( + expect.objectContaining({ level: "auto-corrected", category: "out-of-range", path: "kind" }) + ); }); - it("leaves kind undefined when the input omits it", () => { + it("does not emit a kind issue when kind is omitted", () => { const result = validateGraph(validGraph); expect(result.success).toBe(true); - expect(result.data!.kind).toBeUndefined(); + expect(result.issues).toEqual([]); }); it("rejects graph with missing required fields", () => { diff --git a/understand-anything-plugin/packages/core/src/schema.ts b/understand-anything-plugin/packages/core/src/schema.ts index 0d694e110..8134bf693 100644 --- a/understand-anything-plugin/packages/core/src/schema.ts +++ b/understand-anything-plugin/packages/core/src/schema.ts @@ -650,11 +650,24 @@ export function validateGraph(data: unknown): ValidationResult { } } + // Preserve a valid `kind` enum value; an out-of-enum value is dropped with + // an auto-corrected issue so a typo / stale extractor value is surfaced + // rather than silently narrowed to undefined. + const validKind = fixed.kind === "codebase" || fixed.kind === "knowledge" + ? (fixed.kind as "codebase" | "knowledge") + : undefined; + if (fixed.kind !== undefined && validKind === undefined) { + issues.push({ + level: "auto-corrected", + category: "out-of-range", + message: `"kind" ${JSON.stringify(fixed.kind)} is not one of "codebase" | "knowledge" — dropped`, + path: "kind", + }); + } + const graph = { version: typeof fixed.version === "string" ? fixed.version : "1.0.0", - ...(fixed.kind === "codebase" || fixed.kind === "knowledge" - ? { kind: fixed.kind as "codebase" | "knowledge" } - : {}), + ...(validKind !== undefined ? { kind: validKind } : {}), project: projectResult.data, nodes: validNodes, edges: validEdges, diff --git a/understand-anything-plugin/packages/dashboard/src/App.tsx b/understand-anything-plugin/packages/dashboard/src/App.tsx index cd1b33c79..359917b3c 100644 --- a/understand-anything-plugin/packages/dashboard/src/App.tsx +++ b/understand-anything-plugin/packages/dashboard/src/App.tsx @@ -139,7 +139,7 @@ function Dashboard({ accessToken }: { accessToken: string }) { if (result.success && result.data) { setGraph(result.data); setGraphIssues(result.issues); - if ((data as Record).kind === "knowledge") { + if (result.data.kind === "knowledge") { useDashboardStore.getState().setViewMode("knowledge"); useDashboardStore.getState().setIsKnowledgeGraph(true); }