From d0cba4e03ebbbc0993571a32cdadb3eb8a27807c Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Sun, 25 Oct 2026 22:53:55 +0530 Subject: [PATCH 1/4] Keep self-reported drill solves out of mastery --- src/lib/contentQuality.ts | 2 +- src/pages/BuildLab.persistence.test.tsx | 29 +++++++++++++++++++++---- src/pages/BuildLab.test.ts | 4 ++-- src/pages/BuildLab.tsx | 23 +++++++++++--------- 4 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/lib/contentQuality.ts b/src/lib/contentQuality.ts index 9a9bfaaf..616305d1 100644 --- a/src/lib/contentQuality.ts +++ b/src/lib/contentQuality.ts @@ -42,7 +42,7 @@ export const DRILL_VERIFICATION_HINT: Record = { 'outline-check': 'Only the shape of your outline is checked — that every required point is answered in your own words. Nobody grades whether it is right, so mastery moves less than an auto-graded drill.', 'self-reported': - 'Nothing is verified in-app. Marking this solved is your own claim, and it moves mastery less than an auto-graded drill.', + 'Nothing is verified in-app. Marking this solved records your claim only; pass the explain-back to update mastery.', }; export function isEditorialDrill(drill: Drill): boolean { diff --git a/src/pages/BuildLab.persistence.test.tsx b/src/pages/BuildLab.persistence.test.tsx index 975cb243..438de326 100644 --- a/src/pages/BuildLab.persistence.test.tsx +++ b/src/pages/BuildLab.persistence.test.tsx @@ -9,6 +9,8 @@ import { DRILL_BY_ID } from '../data/learning-os'; import { STORE_KEYS } from '../lib/userStore'; const auth = vi.hoisted(() => ({ user: null as null | { id: string } })); +const mastery = vi.hoisted(() => ({ review: vi.fn(), refresh: vi.fn() })); +const reviewMastery = vi.hoisted(() => ({ review: vi.fn(), mastery: {} })); vi.mock('../contexts/AuthContext', () => ({ useAuth: () => ({ user: auth.user }) })); vi.mock('../components/CodeEditor', () => ({ default: ({ code, onChange }: { code: string; onChange: (value: string) => void }) => ( @@ -22,10 +24,10 @@ vi.mock('../components/CodeEditor', () => ({ vi.mock('../components/FeynmanGate', () => ({ default: () => null })); vi.mock('../components/FeynmanNudge', () => ({ default: () => null })); vi.mock('../hooks/useConcepts', () => ({ - useConceptMastery: () => ({ mastery: {}, refresh: vi.fn(), review: vi.fn() }), + useConceptMastery: () => ({ mastery: {}, refresh: mastery.refresh, review: mastery.review }), })); vi.mock('../hooks/useReviewMastery', () => ({ - useReviewMastery: () => ({ mastery: {}, review: vi.fn() }), + useReviewMastery: () => reviewMastery, })); vi.mock('../hooks/useActivity', () => ({ useActivityLogger: () => vi.fn() })); vi.mock('../hooks/useCodeExecution', () => ({ @@ -47,6 +49,7 @@ Object.defineProperty(globalThis, 'localStorage', { }); const drill = DRILL_BY_ID['build-tokenizer']; +const unverifiedDrill = DRILL_BY_ID['lc-two-sum']; const nextDrill = Object.values(DRILL_BY_ID).find( (item) => item.id !== drill.id && item.testCases?.length )!; @@ -72,10 +75,10 @@ describe('drill grading and saved exercise resume', () => { button.textContent?.includes('Mark solved') )!; - async function render() { + async function render(drillId = drill.id) { await act(async () => root.render( - + Next exercise } /> @@ -89,6 +92,9 @@ describe('drill grading and saved exercise resume', () => { vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true); storage.clear(); auth.user = null; + mastery.review.mockClear(); + mastery.refresh.mockClear(); + reviewMastery.review.mockClear(); container = document.createElement('div'); document.body.append(container); root = createRoot(container); @@ -107,6 +113,8 @@ describe('drill grading and saved exercise resume', () => { expect(container.textContent).not.toContain('All tests passed.'); await act(async () => typeCode(editor(), drill.referenceSolution!)); await act(async () => click(solve())); + expect(mastery.review).toHaveBeenCalledWith(drill.conceptId, 'easy'); + expect(reviewMastery.review).toHaveBeenCalled(); const saved = JSON.parse(storage.get(STORE_KEYS.drills)!)[drill.id]; expect(saved).toEqual({ status: 'solved', lastCode: drill.referenceSolution, attempts: 1 }); expect(container.textContent).toContain('All tests passed.'); @@ -129,6 +137,19 @@ describe('drill grading and saved exercise resume', () => { expect(container.textContent).toContain(nextDrill.title); }); + it('keeps self-reported completion out of concept mastery and review scheduling', async () => { + await render(unverifiedDrill.id); + expect(container.textContent).toContain('Honor system'); + + await act(async () => click(solve())); + + expect(JSON.parse(storage.get(STORE_KEYS.drills)!)[unverifiedDrill.id]).toMatchObject({ + status: 'solved', + }); + expect(mastery.review).not.toHaveBeenCalled(); + expect(reviewMastery.review).not.toHaveBeenCalled(); + }); + it('hydrates account code received after mount without overwriting a learner edit', async () => { auth.user = { id: 'test-learner' }; let resolveFetch!: (value: Response) => void; diff --git a/src/pages/BuildLab.test.ts b/src/pages/BuildLab.test.ts index 09a8f383..f62ecde0 100644 --- a/src/pages/BuildLab.test.ts +++ b/src/pages/BuildLab.test.ts @@ -26,8 +26,8 @@ describe('ratingForSolve', () => { expect(ratingForSolve('automated', 5)).toBe('hard'); }); - it('never treats an unverified claim as a clean pass', () => { - expect(ratingForSolve('self-reported', 0)).toBe('hard'); + it('does not grant mastery credit for an unverified claim', () => { + expect(ratingForSolve('self-reported', 0)).toBeNull(); expect(ratingForSolve('outline-check', 0)).toBe('good'); }); }); diff --git a/src/pages/BuildLab.tsx b/src/pages/BuildLab.tsx index 2bf62632..a0a8bed1 100644 --- a/src/pages/BuildLab.tsx +++ b/src/pages/BuildLab.tsx @@ -309,8 +309,8 @@ const LANGUAGES: Language[] = ['typescript']; export function ratingForSolve( verification: DrillVerification, attemptsBefore: number -): MasteryRating { - if (verification === 'self-reported') return 'hard'; +): MasteryRating | null { + if (verification === 'self-reported') return null; if (verification === 'outline-check') return attemptsBefore >= 3 ? 'hard' : 'good'; if (attemptsBefore === 0) return 'easy'; return attemptsBefore <= 2 ? 'good' : 'hard'; @@ -379,11 +379,17 @@ function DrillWorkspace({ drillId }: { drillId: string }) { } const wasSolved = entry.status === 'solved'; setDrill(drillId, { status, lastCode: code, attempts: entry.attempts }); - // Solving a drill closes the loop — it nudges the concept toward mastery, - // then asks for a 30-second explain-back (Feynman Gate) to prove it. + // Verified solves can seed mastery/review state. A self-reported external + // solve remains an activity record until the explain-back provides evidence. if (status === 'solved') { setShowExplainNudge(true); - void review(drill?.conceptId, ratingForSolve(verification, entry.attempts)); + const masteryRating = ratingForSolve(verification, entry.attempts); + if (masteryRating) { + void review(drill?.conceptId, masteryRating); + for (const seed of reviewsToSeedForConcept(drill?.conceptId, REVIEW_QUESTIONS, rqMastery)) { + void reviewRq(seed.questionId, seed.rating); + } + } void logActivity({ kind: 'drill_solve', conceptIds: [drill?.conceptId], @@ -391,9 +397,6 @@ function DrillWorkspace({ drillId }: { drillId: string }) { payload: { difficulty: drill?.difficulty }, }); recordSessionActivity('drill_solve'); - for (const seed of reviewsToSeedForConcept(drill?.conceptId, REVIEW_QUESTIONS, rqMastery)) { - void reviewRq(seed.questionId, seed.rating); - } for (const q of REVIEW_QUESTIONS.filter((rq) => rq.conceptId === drill?.conceptId)) { if (loadSuspendedRqs().has(q.id)) unsuspendReviewQuestion(q.id); } @@ -546,8 +549,8 @@ function DrillWorkspace({ drillId }: { drillId: string }) { {external ? (

- This drill links to LeetCode — solve there first, then mark solved here to update - mastery. + This drill links to LeetCode — mark your completion here as self-reported. Pass the + explain-back to update mastery.

{DRILL_VERIFICATION_HINT['self-reported']} From e858c08096c3a21a99ab34443528c22fd488ab63 Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Sun, 25 Oct 2026 22:58:58 +0530 Subject: [PATCH 2/4] Gate self-reported solve side effects --- src/pages/BuildLab.persistence.test.tsx | 15 ++++++++++++++- src/pages/BuildLab.tsx | 10 +++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/pages/BuildLab.persistence.test.tsx b/src/pages/BuildLab.persistence.test.tsx index 438de326..69c87964 100644 --- a/src/pages/BuildLab.persistence.test.tsx +++ b/src/pages/BuildLab.persistence.test.tsx @@ -11,6 +11,8 @@ import { STORE_KEYS } from '../lib/userStore'; const auth = vi.hoisted(() => ({ user: null as null | { id: string } })); const mastery = vi.hoisted(() => ({ review: vi.fn(), refresh: vi.fn() })); const reviewMastery = vi.hoisted(() => ({ review: vi.fn(), mastery: {} })); +const elo = vi.hoisted(() => ({ recordResult: vi.fn() })); +const suspendedQuestions = vi.hoisted(() => ({ unsuspend: vi.fn() })); vi.mock('../contexts/AuthContext', () => ({ useAuth: () => ({ user: auth.user }) })); vi.mock('../components/CodeEditor', () => ({ default: ({ code, onChange }: { code: string; onChange: (value: string) => void }) => ( @@ -29,13 +31,18 @@ vi.mock('../hooks/useConcepts', () => ({ vi.mock('../hooks/useReviewMastery', () => ({ useReviewMastery: () => reviewMastery, })); +vi.mock('../lib/reviewMastery', async (importOriginal) => ({ + ...(await importOriginal()), + loadSuspendedRqs: () => new Set(['rq-array-hashing-core', 'rq-tokenization-symmetry']), + unsuspendReviewQuestion: suspendedQuestions.unsuspend, +})); vi.mock('../hooks/useActivity', () => ({ useActivityLogger: () => vi.fn() })); vi.mock('../hooks/useCodeExecution', () => ({ useCodeExecution: () => ({ output: '', errors: '', isRunning: false, execute: vi.fn() }), })); vi.mock('../hooks/useUserStore', async (importOriginal) => ({ ...(await importOriginal()), - useUserElo: () => ({ recordResult: vi.fn() }), + useUserElo: () => elo, })); const storage = new Map(); @@ -95,6 +102,8 @@ describe('drill grading and saved exercise resume', () => { mastery.review.mockClear(); mastery.refresh.mockClear(); reviewMastery.review.mockClear(); + elo.recordResult.mockClear(); + suspendedQuestions.unsuspend.mockClear(); container = document.createElement('div'); document.body.append(container); root = createRoot(container); @@ -115,6 +124,8 @@ describe('drill grading and saved exercise resume', () => { await act(async () => click(solve())); expect(mastery.review).toHaveBeenCalledWith(drill.conceptId, 'easy'); expect(reviewMastery.review).toHaveBeenCalled(); + expect(elo.recordResult).toHaveBeenCalledOnce(); + expect(suspendedQuestions.unsuspend).toHaveBeenCalledWith('rq-tokenization-symmetry'); const saved = JSON.parse(storage.get(STORE_KEYS.drills)!)[drill.id]; expect(saved).toEqual({ status: 'solved', lastCode: drill.referenceSolution, attempts: 1 }); expect(container.textContent).toContain('All tests passed.'); @@ -148,6 +159,8 @@ describe('drill grading and saved exercise resume', () => { }); expect(mastery.review).not.toHaveBeenCalled(); expect(reviewMastery.review).not.toHaveBeenCalled(); + expect(elo.recordResult).not.toHaveBeenCalled(); + expect(suspendedQuestions.unsuspend).not.toHaveBeenCalled(); }); it('hydrates account code received after mount without overwriting a learner edit', async () => { diff --git a/src/pages/BuildLab.tsx b/src/pages/BuildLab.tsx index a0a8bed1..bfd58c2e 100644 --- a/src/pages/BuildLab.tsx +++ b/src/pages/BuildLab.tsx @@ -378,17 +378,20 @@ function DrillWorkspace({ drillId }: { drillId: string }) { if (!result.passed) return; } const wasSolved = entry.status === 'solved'; + const masteryRating = status === 'solved' ? ratingForSolve(verification, entry.attempts) : null; setDrill(drillId, { status, lastCode: code, attempts: entry.attempts }); // Verified solves can seed mastery/review state. A self-reported external // solve remains an activity record until the explain-back provides evidence. if (status === 'solved') { setShowExplainNudge(true); - const masteryRating = ratingForSolve(verification, entry.attempts); if (masteryRating) { void review(drill?.conceptId, masteryRating); for (const seed of reviewsToSeedForConcept(drill?.conceptId, REVIEW_QUESTIONS, rqMastery)) { void reviewRq(seed.questionId, seed.rating); } + for (const q of REVIEW_QUESTIONS.filter((rq) => rq.conceptId === drill?.conceptId)) { + if (loadSuspendedRqs().has(q.id)) unsuspendReviewQuestion(q.id); + } } void logActivity({ kind: 'drill_solve', @@ -397,9 +400,6 @@ function DrillWorkspace({ drillId }: { drillId: string }) { payload: { difficulty: drill?.difficulty }, }); recordSessionActivity('drill_solve'); - for (const q of REVIEW_QUESTIONS.filter((rq) => rq.conceptId === drill?.conceptId)) { - if (loadSuspendedRqs().has(q.id)) unsuspendReviewQuestion(q.id); - } } else if (status === 'attempted') { void logActivity({ kind: 'drill_fail', @@ -412,7 +412,7 @@ function DrillWorkspace({ drillId }: { drillId: string }) { // First-time solve bumps ELO for every roadmap this concept belongs to. // v1 only counts the success signal; an explicit "couldn't solve" path // would be needed to push ELO down. - if (status === 'solved' && !wasSolved && concept) { + if (status === 'solved' && !wasSolved && concept && masteryRating) { recordResult(concept.roadmaps, difficultyToElo(drill?.difficulty), 1); } } From 9ff3357d1298cc0ed56cb1f0ba8f9d8898cbfcd6 Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Sun, 25 Oct 2026 23:12:50 +0530 Subject: [PATCH 3/4] Patch audited dependency advisories --- package.json | 7 +++-- pnpm-lock.yaml | 77 +++++++++++++++++++++++++------------------------- 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/package.json b/package.json index 035bfe9f..073d4740 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "@vitejs/plugin-react": "6.0.4", "@vitest/coverage-v8": "4.1.10", "@vitest/ui": "4.1.10", - "happy-dom": "20.11.1", + "happy-dom": "21.0.4", "husky": "^9.1.7", "jscpd": "5.0.14", "knip": "^6.29.0", @@ -133,12 +133,13 @@ "nanoid@>=4.0.0 <5.1.16": "5.1.16", "postcss@<=8.5.22": "8.5.23", "undici@>=5.0.0 <6.27.0": "6.29.0", - "undici@>=7.0.0 <7.29.1": "7.29.1", + "undici@>=7.0.0 <7.30.1": "7.30.1", "sharp@<0.35.5": "0.35.5", "js-yaml@>=4.0.0 <4.3.2": "4.3.2", "smol-toml@<1.7.1": "1.7.1", "source-map-js@<1.2.2": "1.2.2", - "dompurify@<3.4.17": "3.4.17" + "dompurify@<3.4.17": "3.4.17", + "zod@<4.6.6": "4.6.6" }, "peerDependencyRules": { "allowAny": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eb996cc2..00b150a6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,12 +19,13 @@ overrides: nanoid@>=4.0.0 <5.1.16: 5.1.16 postcss@<=8.5.22: 8.5.23 undici@>=5.0.0 <6.27.0: 6.29.0 - undici@>=7.0.0 <7.29.1: 7.29.1 + undici@>=7.0.0 <7.30.1: 7.30.1 sharp@<0.35.5: 0.35.5 js-yaml@>=4.0.0 <4.3.2: 4.3.2 smol-toml@<1.7.1: 1.7.1 source-map-js@<1.2.2: 1.2.2 dompurify@<3.4.17: 3.4.17 + zod@<4.6.6: 4.6.6 patchedDependencies: es6-promise-pool@2.5.0: @@ -37,7 +38,7 @@ importers: dependencies: '@ai-sdk/openai-compatible': specifier: 2.0.41 - version: 2.0.41(zod@4.4.3) + version: 2.0.41(zod@4.6.6) '@cloudflare/realtimekit': specifier: 2.0.1 version: 2.0.1 @@ -61,7 +62,7 @@ importers: version: https://codeload.github.com/sass-maker/ui-library/tar.gz/af95cd6b4aa4bb2004343655bb495255e9f51c8b#path:/packages/ui(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.3.3) ai: specifier: 6.0.168 - version: 6.0.168(zod@4.4.3) + version: 6.0.168(zod@4.6.6) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -160,8 +161,8 @@ importers: specifier: 4.1.10 version: 4.1.10(vitest@4.1.10) happy-dom: - specifier: 20.11.1 - version: 20.11.1 + specifier: 21.0.4 + version: 21.0.4 husky: specifier: ^9.1.7 version: 9.1.7 @@ -194,7 +195,7 @@ importers: version: 8.1.6(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.79.6)(tsx@4.21.0)(yaml@2.9.0) vitest: specifier: 4.1.10 - version: 4.1.10(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.0)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(@vitest/ui@4.1.10)(happy-dom@20.11.1)(vite@8.1.6(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.79.6)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.1.10(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.0)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(@vitest/ui@4.1.10)(happy-dom@21.0.4)(vite@8.1.6(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.79.6)(tsx@4.21.0)(yaml@2.9.0)) wrangler: specifier: 4.114.0 version: 4.114.0(@types/node@26.1.1) @@ -205,19 +206,19 @@ packages: resolution: {integrity: sha512-ZKX5n74io8VIRlhIMSLWVlvT3sXC8Z7cZ9GHuWBWZDVi96+62AIsWuLGvMfcBA1STYuSoDrp6rIziZmvrTq0TA==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: 4.6.6 '@ai-sdk/openai-compatible@2.0.41': resolution: {integrity: sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: 4.6.6 '@ai-sdk/provider-utils@4.0.23': resolution: {integrity: sha512-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: 4.6.6 '@ai-sdk/provider@3.0.8': resolution: {integrity: sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==} @@ -2906,7 +2907,7 @@ packages: resolution: {integrity: sha512-2HqCJuO+1V2aV7vfYs5LFEUfxbkGX+5oa54q/gCCTL7KLTdbxcCu5D7TdLA5kwsrs3Szgjah9q6D9tpjHM3hUQ==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: 4.6.6 any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} @@ -3434,8 +3435,8 @@ packages: hachure-fill@0.5.2: resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} - happy-dom@20.11.1: - resolution: {integrity: sha512-XSt8tMzbW9ymE7687xztkO1ckR7qJNQ3LywY9vlYGhGi3zXrGBHuUo2Cl1ztZaICW+1eAGdkLbj6iwVqDT33kg==} + happy-dom@21.0.4: + resolution: {integrity: sha512-tFm+GaLBgo6o0awHnvHaNoraVo27qJ+2tZEtZ+cIa08hXbeitrxAJajLI05hsUy/2/YTKcUjQYzHe5mCcraLBw==} engines: {node: '>=20.0.0'} has-flag@4.0.0: @@ -4555,8 +4556,8 @@ packages: undici-types@8.3.0: resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} - undici@7.29.1: - resolution: {integrity: sha512-RYONW2MeafgYlkVOKYKkA/Ag7BmXqgIWCa8t1m0JcxrQg9pI9lEqRhAOruOBCbAohOa/gkCF+iPi9hrgvTzu6Q==} + undici@7.30.1: + resolution: {integrity: sha512-03KrMbjIFDWQR5RNPrSw3bd1r12JpyFQl9/yxjUbJtqkjmllqC5lFjZh6ujeuJCKr8EOQvRW872Oy/az+nLYxg==} engines: {node: '>=20.18.1'} unenv@2.0.0-rc.24: @@ -4820,8 +4821,8 @@ packages: youch@4.1.0-beta.10: resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==} - zod@4.4.3: - resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + zod@4.6.6: + resolution: {integrity: sha512-4UmzQUF5fxvfcYHlbjBmejU+w+hbDnWFx4aS8kBx7nPBHcO2SqPbpAG8snbdg+Xse4pRefR98hgjFgIMCI/UhA==} zustand@4.5.7: resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} @@ -4843,25 +4844,25 @@ packages: snapshots: - '@ai-sdk/gateway@3.0.104(zod@4.4.3)': + '@ai-sdk/gateway@3.0.104(zod@4.6.6)': dependencies: '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.23(zod@4.4.3) + '@ai-sdk/provider-utils': 4.0.23(zod@4.6.6) '@vercel/oidc': 3.2.0 - zod: 4.4.3 + zod: 4.6.6 - '@ai-sdk/openai-compatible@2.0.41(zod@4.4.3)': + '@ai-sdk/openai-compatible@2.0.41(zod@4.6.6)': dependencies: '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.23(zod@4.4.3) - zod: 4.4.3 + '@ai-sdk/provider-utils': 4.0.23(zod@4.6.6) + zod: 4.6.6 - '@ai-sdk/provider-utils@4.0.23(zod@4.4.3)': + '@ai-sdk/provider-utils@4.0.23(zod@4.6.6)': dependencies: '@ai-sdk/provider': 3.0.8 '@standard-schema/spec': 1.1.0 eventsource-parser: 3.0.8 - zod: 4.4.3 + zod: 4.6.6 '@ai-sdk/provider@3.0.8': dependencies: @@ -7183,7 +7184,7 @@ snapshots: obug: 2.1.4 std-env: 4.2.0 tinyrainbow: 3.1.0 - vitest: 4.1.10(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.0)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(@vitest/ui@4.1.10)(happy-dom@20.11.1)(vite@8.1.6(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.79.6)(tsx@4.21.0)(yaml@2.9.0)) + vitest: 4.1.10(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.0)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(@vitest/ui@4.1.10)(happy-dom@21.0.4)(vite@8.1.6(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.79.6)(tsx@4.21.0)(yaml@2.9.0)) '@vitest/expect@4.1.10': dependencies: @@ -7229,7 +7230,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vitest: 4.1.10(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.0)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(@vitest/ui@4.1.10)(happy-dom@20.11.1)(vite@8.1.6(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.79.6)(tsx@4.21.0)(yaml@2.9.0)) + vitest: 4.1.10(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.0)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(@vitest/ui@4.1.10)(happy-dom@21.0.4)(vite@8.1.6(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.79.6)(tsx@4.21.0)(yaml@2.9.0)) '@vitest/utils@4.1.10': dependencies: @@ -7241,13 +7242,13 @@ snapshots: agent-base@7.1.4: {} - ai@6.0.168(zod@4.4.3): + ai@6.0.168(zod@4.6.6): dependencies: - '@ai-sdk/gateway': 3.0.104(zod@4.4.3) + '@ai-sdk/gateway': 3.0.104(zod@4.6.6) '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.23(zod@4.4.3) + '@ai-sdk/provider-utils': 4.0.23(zod@4.6.6) '@opentelemetry/api': 1.9.0 - zod: 4.4.3 + zod: 4.6.6 any-promise@1.3.0: {} @@ -7808,7 +7809,7 @@ snapshots: hachure-fill@0.5.2: {} - happy-dom@20.11.1: + happy-dom@21.0.4: dependencies: '@types/node': 26.1.1 '@types/whatwg-mimetype': 3.0.2 @@ -8024,7 +8025,7 @@ snapshots: tinyglobby: 0.2.17 unbash: 4.0.3 yaml: 2.9.0 - zod: 4.4.3 + zod: 4.6.6 langium@3.3.1: dependencies: @@ -8581,7 +8582,7 @@ snapshots: dependencies: '@cspotcode/source-map-support': 0.8.1 sharp: 0.35.5(@types/node@26.1.1) - undici: 7.29.1 + undici: 7.30.1 workerd: 1.20260722.1 ws: 8.21.0 youch: 4.1.0-beta.10 @@ -9242,13 +9243,13 @@ snapshots: jsonc-parser: 3.3.1 nypm: 0.6.9 yaml: 2.9.0 - zod: 4.4.3 + zod: 4.6.6 unbash@4.0.3: {} undici-types@8.3.0: {} - undici@7.29.1: {} + undici@7.30.1: {} unenv@2.0.0-rc.24: dependencies: @@ -9341,7 +9342,7 @@ snapshots: tsx: 4.21.0 yaml: 2.9.0 - vitest@4.1.10(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.0)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(@vitest/ui@4.1.10)(happy-dom@20.11.1)(vite@8.1.6(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.79.6)(tsx@4.21.0)(yaml@2.9.0)): + vitest@4.1.10(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.0)(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(@vitest/ui@4.1.10)(happy-dom@21.0.4)(vite@8.1.6(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.79.6)(tsx@4.21.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.10 '@vitest/mocker': 4.1.10(vite@8.1.6(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.79.6)(tsx@4.21.0)(yaml@2.9.0)) @@ -9369,7 +9370,7 @@ snapshots: '@types/node': 26.1.1 '@vitest/coverage-v8': 4.1.10(vitest@4.1.10) '@vitest/ui': 4.1.10(vitest@4.1.10) - happy-dom: 20.11.1 + happy-dom: 21.0.4 transitivePeerDependencies: - msw @@ -9476,7 +9477,7 @@ snapshots: cookie: 1.1.1 youch-core: 0.3.3 - zod@4.4.3: {} + zod@4.6.6: {} zustand@4.5.7(@types/react@19.2.17)(react@19.2.8): dependencies: From edcfa862983572f4eb85260ff6ec529a8b499310 Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Sun, 25 Oct 2026 23:18:24 +0530 Subject: [PATCH 4/4] Bound zod override to v4 --- package.json | 2 +- pnpm-lock.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 073d4740..3ba157fc 100644 --- a/package.json +++ b/package.json @@ -139,7 +139,7 @@ "smol-toml@<1.7.1": "1.7.1", "source-map-js@<1.2.2": "1.2.2", "dompurify@<3.4.17": "3.4.17", - "zod@<4.6.6": "4.6.6" + "zod@>=4.0.0 <4.6.6": "4.6.6" }, "peerDependencyRules": { "allowAny": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 00b150a6..083d7385 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,7 +25,7 @@ overrides: smol-toml@<1.7.1: 1.7.1 source-map-js@<1.2.2: 1.2.2 dompurify@<3.4.17: 3.4.17 - zod@<4.6.6: 4.6.6 + zod@>=4.0.0 <4.6.6: 4.6.6 patchedDependencies: es6-promise-pool@2.5.0: