diff --git a/docs/current-handoff.md b/docs/current-handoff.md index 8c2541f..c10adeb 100644 --- a/docs/current-handoff.md +++ b/docs/current-handoff.md @@ -78,6 +78,12 @@ - The 502 live run at `dabd9e2` was PATH NOT OBSERVABLE ON FAILURE: provider path survived provider errors but not successful-provider/later-validation failures. - Successful provider results now retain their factual attempted path through later reconstruction-validation failure diagnostics. This corrects only that observability seam; the next unknown remains one real `/api/cases/start` call establishing `/api/chat` versus `/api/generate`. +## Zod 4 migration + +- Zod upgraded from 3.25.76 to 4.5.4. Reconstruction and direct Zod-owner suites pass, and native `z.toJSONSchema()` converts the production reconstruction schema with required closed enum constraints. +- Zod-3-specific error-message assertions are version-independent while retaining semantic validation evidence. Production schema semantics are unchanged and no third-party JSON-schema conversion dependency was added. +- Next unknown: whether Ollama/Qwen accepts and obeys the generated schema through `/api/chat` format. + ## Current product architecture Three distinct routes, not a single page: diff --git a/package-lock.json b/package-lock.json index f089f7d..88cd085 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "next": "^14.2.0", "react": "^18.3.0", "react-dom": "^18.3.0", - "zod": "^3.23.0" + "zod": "^4.5.4" }, "devDependencies": { "@playwright/test": "^1.62.1", @@ -7646,9 +7646,9 @@ } }, "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.5.4.tgz", + "integrity": "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index 6dca8bf..30d5861 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "next": "^14.2.0", "react": "^18.3.0", "react-dom": "^18.3.0", - "zod": "^3.23.0" + "zod": "^4.5.4" }, "devDependencies": { "@playwright/test": "^1.62.1", diff --git a/tests/reconstruction.test.js b/tests/reconstruction.test.js index fb90fb4..6754afb 100644 --- a/tests/reconstruction.test.js +++ b/tests/reconstruction.test.js @@ -36,7 +36,8 @@ describe("reconstruction schema", () => { const result = reconstructionSchema.safeParse(input); expect(result.success).toBe(false); if (!result.success) { - expect(result.error.issues[0].message).toContain("Expected"); + expect(result.error.issues.length).toBeGreaterThan(0); + expect(result.error.issues[0].path).toContain("confidence"); } }); diff --git a/tests/reconstruction/compatibility.test.js b/tests/reconstruction/compatibility.test.js index 23ebbfe..e7454e8 100644 --- a/tests/reconstruction/compatibility.test.js +++ b/tests/reconstruction/compatibility.test.js @@ -254,10 +254,10 @@ describe("analyseScenario compatibility", () => { expect(result.validationIssues).toContainEqual(expect.objectContaining({ path: ["reconstruction", "observedStates", 0, "description"], code: "invalid_type", - message: "Required", + expected: "string", })); expect(result.providerApiPath).toBe("/api/chat"); - expect(result.errors).toContain("reconstruction: Required"); + expect(result.errors).toContainEqual(expect.stringMatching(/^reconstruction:/)); }); it("succeeds when reported_claim is the only evidenceType mismatch", async () => {