From e722edec2f429dcdc0393570972499a7435bf363 Mon Sep 17 00:00:00 2001 From: Dante Date: Sat, 30 May 2026 14:20:50 +0900 Subject: [PATCH] fix: Remove duplicate app workflow validation (#12208) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary `extra.linearData.inputs` validation rejected the entire workflow whenever a single entry didn't match the strict `z.union([3-tuple, 2-tuple])`, surfacing as `Failed to load shared workflow: invalid workflow data` for some published cloud shares (e.g. `share=21e32125c692`). ## Changes - **What**: Shared workflow load now matches the regular load policy at `scripts/app.ts:1191-1198` — if Zod validation fails, fall back to the raw `workflow_json` instead of throwing. Share service no longer runs schema validation directly; `app.loadGraphData()` continues to validate and apply the same raw fallback under `Comfy.Validation.Workflows`. - **Breaking**: None — workflows that previously failed to load through the share path now load with the same permissive behavior as workflows opened through any other entry point. ## Review Focus The original approach added a `tolerantArray` combinator to drop bad `linearData.inputs` entries inside the schema. After review, the cleaner direction is to keep schemas strict as the canonical spec and apply tolerance at the consumer boundary — which `scripts/app.ts:1191-1198` already does for the regular load path (`graphData = validatedGraphData ?? graphData` with the comment "Ideally we should not block users from loading the workflow"). This PR aligns the share path with that existing policy and removes a cross-path inconsistency rather than introducing a new schema-level concept. Consequences: - Schemas in `workflowSchema.ts` stay unchanged (canonical spec). - The `extra.*` shape drift problem is now solved generally, not just for `linearData.inputs` — future fields hitting the same class of issue will load instead of blocking. - `validateComfyWorkflow` still logs the Zod error via `console.warn` for debugging. ## Tests - Existing schema tests stay strict (3-tuple, 2-tuple unions reject bad shapes). - New regression test in `workflowShareService.test.ts`: a `workflow_json` that passes the share response envelope (`record`) but fails `ComfyWorkflowJSON` is returned raw, not thrown. ### current prod Screenshot 2026-05-13 at 1 29 00 PM ### test `/?share=21e32125c692` Screenshot 2026-05-13 at 1 27 40 PM ## Follow-up Structural issue separately: cloud OpenAPI defines `workflow_json` as opaque `z.record(z.unknown())` (`packages/ingest-types/src/zod.gen.ts:137,325,375,397,457`), so ingest schema CI cannot enforce inner shape. Matt Miller is driving the lift-schema-to-core-OpenAPI direction; tracked separately, not in this PR. Fixes FE-690. --- .../services/workflowShareService.test.ts | 38 +++++++++++++++---- .../sharing/services/workflowShareService.ts | 7 ---- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/platform/workflow/sharing/services/workflowShareService.test.ts b/src/platform/workflow/sharing/services/workflowShareService.test.ts index f8ab1d02a3..4183d03969 100644 --- a/src/platform/workflow/sharing/services/workflowShareService.test.ts +++ b/src/platform/workflow/sharing/services/workflowShareService.test.ts @@ -16,14 +16,6 @@ const mockGetShareableAssets = vi.fn() const mockFetchApi = vi.fn() const mockInvalidateInputAssetsIncludingPublic = vi.hoisted(() => vi.fn()) -vi.mock( - '@/platform/workflow/validation/schemas/workflowSchema', - async (importOriginal) => ({ - ...(await importOriginal()), - validateComfyWorkflow: vi.fn(async (json: unknown) => json) - }) -) - vi.mock('@/scripts/api', () => ({ api: { getShareableAssets: (...args: unknown[]) => mockGetShareableAssets(...args), @@ -408,6 +400,36 @@ describe(useWorkflowShareService, () => { ) }) + it('returns raw workflow_json when it does not match ComfyWorkflowJSON schema', async () => { + const rawWorkflowJson = { + extra: { + linearData: { + inputs: [ + [1, 'prompt'], + [2, 'seed', 'invalid-third-element'] + ], + outputs: [] + } + } + } + mockFetchApi.mockResolvedValue( + mockJsonResponse({ + share_id: 'share-raw', + workflow_id: 'wf-raw', + name: 'Raw', + listed: false, + publish_time: null, + workflow_json: rawWorkflowJson, + assets: [] + }) + ) + + const service = useWorkflowShareService() + const shared = await service.getSharedWorkflow('share-raw') + + expect(shared.workflowJson).toEqual(rawWorkflowJson) + }) + it('treats malformed publish-status payload as unpublished', async () => { mockFetchApi.mockResolvedValue(mockJsonResponse({ is_published: true })) diff --git a/src/platform/workflow/sharing/services/workflowShareService.ts b/src/platform/workflow/sharing/services/workflowShareService.ts index 4de4670c5d..c9c310a6b6 100644 --- a/src/platform/workflow/sharing/services/workflowShareService.ts +++ b/src/platform/workflow/sharing/services/workflowShareService.ts @@ -9,7 +9,6 @@ import type { import { assetService } from '@/platform/assets/services/assetService' import type { ThumbnailType } from '@/platform/workflow/sharing/types/comfyHubTypes' import type { ComfyWorkflowJSON } from '@/platform/workflow/validation/schemas/workflowSchema' -import { validateComfyWorkflow } from '@/platform/workflow/validation/schemas/workflowSchema' import type { AssetInfo } from '@/schemas/apiSchema' import { zHubWorkflowPrefillResponse, @@ -249,12 +248,6 @@ export function useWorkflowShareService() { throw new Error('Failed to load shared workflow: invalid response') } - const validated = await validateComfyWorkflow(workflow.workflowJson) - if (!validated) { - throw new Error('Failed to load shared workflow: invalid workflow data') - } - workflow.workflowJson = validated - return workflow }