improvement(workflows): stop writing placeholder workflow descriptions (#5729)

* improvement(workflows): stop writing placeholder workflow descriptions

* fix(workflows): scrub placeholder descriptions at the import boundary

* test(workflows): pin import-boundary description scrubbing behavior
This commit is contained in:
Waleed
2026-07-17 00:25:04 -07:00
committed by GitHub
parent f6bb8e6d3e
commit 42b9d5f81d
5 changed files with 79 additions and 13 deletions
@@ -144,7 +144,7 @@ export function Home({ chatId, userName, userId }: HomeProps) {
filename: `${seed.workflowName}.json`,
workspaceId,
nameOverride: seed.workflowName,
descriptionOverride: seed.workflowDescription || 'Imported from landing template',
descriptionOverride: seed.workflowDescription || undefined,
createWorkflow: async ({ name, description, workspaceId }) => {
return requestJson(createWorkflowContract, {
body: {
@@ -17,6 +17,7 @@ import {
} from '@sim/emcn'
import { getErrorMessage } from '@sim/utils/errors'
import { useParams } from 'next/navigation'
import { getMeaningfulWorkflowDescription } from '@/lib/mcp/workflow-tool-schema'
import { normalizeInputFormatValue } from '@/lib/workflows/input-format'
import { isInputDefinitionTrigger } from '@/lib/workflows/triggers/input-definition-triggers'
import type { InputFormatField } from '@/lib/workflows/types'
@@ -89,14 +90,9 @@ export function ApiInfoModal({ open, onOpenChange, workflowId }: ApiInfoModalPro
useEffect(() => {
if (open) {
const normalizedDesc = workflowMetadata?.description?.toLowerCase().trim()
const isDefaultDescription =
!workflowMetadata?.description ||
workflowMetadata.description === workflowMetadata.name ||
normalizedDesc === 'new workflow' ||
normalizedDesc === 'your first workflow - start building here!'
const initialDescription = isDefaultDescription ? '' : workflowMetadata?.description || ''
const initialDescription =
getMeaningfulWorkflowDescription(workflowMetadata?.description, workflowMetadata?.name) ??
''
setDescription(initialDescription)
initialDescriptionRef.current = initialDescription
@@ -181,7 +177,7 @@ export function ApiInfoModal({ open, onOpenChange, workflowId }: ApiInfoModalPro
await updateWorkflowMutation.mutateAsync({
workspaceId,
workflowId,
metadata: { description: description.trim() || 'New workflow' },
metadata: { description: description.trim() },
})
}
+2 -2
View File
@@ -175,7 +175,7 @@ export function useCreateWorkflow() {
body: {
id,
name: name || generateCreativeWorkflowName(),
description: description || 'New workflow',
description,
workspaceId,
folderId: folderId || null,
sortOrder,
@@ -226,7 +226,7 @@ export function useCreateWorkflow() {
name: variables.name || generateCreativeWorkflowName(),
lastModified: new Date(),
createdAt: new Date(),
description: variables.description || 'New workflow',
description: variables.description ?? '',
workspaceId: variables.workspaceId,
folderId: variables.folderId || null,
sortOrder,
@@ -2,9 +2,14 @@ import { describe, expect, it, vi } from 'vitest'
vi.unmock('@/blocks/registry')
vi.mock('@/lib/api/client/request', () => ({
requestJson: vi.fn().mockResolvedValue({}),
}))
import {
extractWorkflowName,
parseWorkflowJson,
persistImportedWorkflow,
sanitizePathSegment,
} from '@/lib/workflows/operations/import-export'
@@ -134,6 +139,67 @@ describe('workflow import/export parsing', () => {
})
})
describe('persistImportedWorkflow description handling', () => {
function buildContent(description?: string) {
const state = createLegacyState()
return JSON.stringify({
data: {
version: '1.0',
workflow: { name: 'Imported Workflow' },
state: {
...state,
metadata: { name: 'Imported Workflow', description },
},
},
})
}
async function importWithContent(content: string, descriptionOverride?: string) {
const createWorkflow = vi.fn().mockResolvedValue({ id: 'wf-1' })
await persistImportedWorkflow({
content,
filename: 'imported-workflow.json',
workspaceId: 'ws-1',
descriptionOverride,
createWorkflow,
})
return createWorkflow.mock.calls[0][0].description as string
}
it('scrubs placeholder metadata descriptions to an empty string', async () => {
expect(await importWithContent(buildContent('New workflow'))).toBe('')
expect(
await importWithContent(buildContent('Your first workflow - start building here!'))
).toBe('')
})
it('scrubs name-equal metadata descriptions to an empty string', async () => {
expect(await importWithContent(buildContent('Imported Workflow'))).toBe('')
})
it('preserves meaningful metadata descriptions', async () => {
expect(await importWithContent(buildContent('Syncs leads from HubSpot to Slack'))).toBe(
'Syncs leads from HubSpot to Slack'
)
})
it('uses an empty string when no description is present', async () => {
expect(await importWithContent(buildContent(undefined))).toBe('')
})
it('prefers a meaningful override over metadata', async () => {
expect(
await importWithContent(buildContent('Metadata description'), 'Override description')
).toBe('Override description')
})
it('falls back to meaningful metadata when the override is a placeholder', async () => {
expect(await importWithContent(buildContent('Metadata description'), 'New workflow')).toBe(
'Metadata description'
)
})
})
describe('sanitizePathSegment', () => {
it('should preserve ASCII alphanumeric characters', () => {
expect(sanitizePathSegment('workflow-123_abc')).toBe('workflow-123_abc')
@@ -11,6 +11,7 @@ import {
type WorkflowStateContractInput,
workflowVariablesContract,
} from '@/lib/api/contracts/workflows'
import { getMeaningfulWorkflowDescription } from '@/lib/mcp/workflow-tool-schema'
import { migrateSubblockIds } from '@/lib/workflows/migrations/subblock-migrations'
import {
type ExportWorkflowState,
@@ -677,7 +678,10 @@ export async function persistImportedWorkflow({
const createdWorkflow = await createWorkflow({
name: workflowName,
description: descriptionOverride || workflowData.metadata?.description || 'Imported from JSON',
description:
getMeaningfulWorkflowDescription(descriptionOverride, workflowName) ??
getMeaningfulWorkflowDescription(workflowData.metadata?.description, workflowName) ??
'',
workspaceId,
folderId,
sortOrder,