chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
+207
View File
@@ -0,0 +1,207 @@
/**
* Shared cell-write primitives for workflow-group execution paths.
*
* Both the scheduler (`runWorkflowGroupCell`) and the cell task body
* (`executeWorkflowGroupCellJob`) need to write `data` patches + `executions`
* patches together while honoring the `cancelled` state written by
* `cancelWorkflowGroupRuns` — without the guard, a stop click that lands
* mid-enqueue or mid-run would get clobbered by the in-flight code path's
* next write.
*/
import { createLogger } from '@sim/logger'
import { isExecCancelled } from '@/lib/table/deps'
import { appendTableEvent } from '@/lib/table/events'
import type { RowData, RowExecutionMetadata, RowExecutions, WorkflowGroup } from '@/lib/table/types'
const logger = createLogger('WorkflowCellWrite')
export interface WriteWorkflowGroupContext {
tableId: string
rowId: string
workspaceId: string
groupId: string
executionId: string
/** Used as the `requestId` passed to `updateRow` for log correlation. */
requestId?: string
}
export interface WriteWorkflowGroupStatePayload {
/** Plain primitives to merge into `row.data`. Empty patch is fine. */
dataPatch?: RowData
/** New execution state for `executions[groupId]`. */
executionState: RowExecutionMetadata
}
/**
* Writes the row unless `cancelWorkflowGroupRuns` has already authoritatively
* written `cancelled` for this run. Returns `'skipped'` so the caller can
* short-circuit any follow-up writes / job dispatch.
*/
export async function writeWorkflowGroupState(
ctx: WriteWorkflowGroupContext,
payload: WriteWorkflowGroupStatePayload
): Promise<'wrote' | 'skipped'> {
const { tableId, rowId, workspaceId, groupId, executionId } = ctx
const requestId = ctx.requestId ?? `wfgrp-${executionId}`
const { getTableById } = await import('@/lib/table/service')
const { getRowById, updateRow } = await import('@/lib/table/rows/service')
const table = await getTableById(tableId)
if (!table) {
logger.warn(`Table ${tableId} vanished before group state write`)
return 'wrote'
}
const row = await getRowById(tableId, rowId, workspaceId)
if (!row) {
logger.warn(`Row ${rowId} vanished before group state write`)
return 'wrote'
}
const current = row.executions?.[groupId] as RowExecutionMetadata | undefined
// Stale-worker guard: only blocks writes FROM an old worker (status =
// running / completed / error / pending). A `queued` stamp from the
// scheduler can claim the cell for a brand-new run — that's the new
// authority. Same for `cancelled` (always authoritative, written by stop).
const isCancelStamp = payload.executionState.status === 'cancelled'
const isQueuedStamp = payload.executionState.status === 'queued'
const isNewQueuedStamp = isQueuedStamp && current?.executionId !== executionId
const bypassStaleWorker = isNewQueuedStamp || isCancelStamp
if (!bypassStaleWorker && current && current.executionId && current.executionId !== executionId) {
logger.info(
`Skipping group write — stale worker (table=${tableId} row=${rowId} group=${groupId} mine=${executionId} active=${current.executionId})`
)
return 'skipped'
}
// A late `queued` stamp for the SAME run that's already moved past queued
// (worker called markWorkflowGroupPickedUp before our parallel stamp landed)
// must NOT overwrite the further-along state. Without this, a cell can show
// "queued" forever while the worker is actually running.
if (isQueuedStamp && current?.executionId === executionId && current.status !== 'pending') {
logger.info(
`Skipping queued stamp — same run already at status=${current.status} (table=${tableId} row=${rowId} group=${groupId} executionId=${executionId})`
)
return 'skipped'
}
// A `cancelled` cell rejects any worker write regardless of executionId — a
// stop click can only stamp the dispatcher pre-stamp's executionId (often
// null), so an executionId-matched guard would let the worker that later
// claims the cell with its real id resurrect it. `bypassStaleWorker` (a fresh
// `queued` claim from a new dispatch, or the authoritative cancel write
// itself) still passes; manual re-runs clear the tombstone before stamping.
if (!bypassStaleWorker && isExecCancelled(current)) {
logger.info(
`Skipping group write — cancelled (table=${tableId} row=${rowId} group=${groupId} executionId=${executionId})`
)
return 'skipped'
}
// Skip writing `cancelled` state with the guard — that's an authoritative
// write from `cancelWorkflowGroupRuns` and must always land. New `queued`
// stamps from the scheduler also bypass — they ARE the new authority. Cell-
// task writes (running/completed/error) get the SQL guard so an in-flight
// partial can't clobber a stop click or a newer run that already committed.
const cancellationGuard = bypassStaleWorker ? undefined : { groupId, executionId }
const result = await updateRow(
{
tableId,
rowId,
data: payload.dataPatch ?? {},
workspaceId,
executionsPatch: { [groupId]: payload.executionState },
cancellationGuard,
},
table,
requestId
)
if (result === null) {
logger.info(
`Skipping group write — SQL guard saw cancelled (table=${tableId} row=${rowId} group=${groupId} executionId=${executionId})`
)
return 'skipped'
}
const dataPatch = payload.dataPatch
const hasOutputs = dataPatch && Object.keys(dataPatch).length > 0
const runningBlockIds = payload.executionState.runningBlockIds
const blockErrors = payload.executionState.blockErrors
void appendTableEvent({
kind: 'cell',
tableId,
rowId,
groupId,
status: payload.executionState.status,
executionId: payload.executionState.executionId ?? null,
jobId: payload.executionState.jobId ?? null,
error: payload.executionState.error ?? null,
...(hasOutputs ? { outputs: dataPatch } : {}),
...(runningBlockIds && runningBlockIds.length > 0 ? { runningBlockIds } : {}),
...(blockErrors && Object.keys(blockErrors).length > 0 ? { blockErrors } : {}),
})
return 'wrote'
}
/**
* Flips `queued` → `running` to signal the cell task body has actually been
* picked up by a worker. The renderer uses the `queued` vs `running` distinction
* to label cells "Queued" vs "Waiting" (worker started, this block hasn't run
* yet) — without this marker we couldn't tell if a row was sitting in the
* trigger.dev queue or actively executing.
*/
export async function markWorkflowGroupPickedUp(
ctx: WriteWorkflowGroupContext,
prev: Pick<RowExecutionMetadata, 'workflowId' | 'jobId'>
): Promise<'wrote' | 'skipped'> {
return writeWorkflowGroupState(ctx, {
executionState: {
status: 'running',
executionId: ctx.executionId,
jobId: prev.jobId,
workflowId: prev.workflowId,
error: null,
},
})
}
/** Builds the canonical `cancelled` execution state used by every cancel path.
* Preserves `blockErrors` from the prior state so errored cells keep
* rendering Error after a stop click — only cells that hadn't yet produced
* a value or an error should flip to "Cancelled". `cancelledAt` is the
* tombstone the dispatcher reads to skip re-runs of cells the user killed
* mid-cascade. */
export function buildCancelledExecution(
prev: Pick<RowExecutionMetadata, 'executionId' | 'workflowId' | 'blockErrors'>
): RowExecutionMetadata {
return {
status: 'cancelled',
executionId: prev.executionId ?? null,
jobId: null,
workflowId: prev.workflowId,
error: 'Cancelled',
cancelledAt: new Date().toISOString(),
...(prev.blockErrors ? { blockErrors: prev.blockErrors } : {}),
}
}
/**
* Maps a group's `outputs[]` to a `blockId → Array<{path, columnName}>` map.
* The cell task uses this to fan a single block-complete event into N column
* writes.
*/
export function buildOutputsByBlockId(
group: WorkflowGroup
): Map<string, Array<{ path: string; columnName: string }>> {
const map = new Map<string, Array<{ path: string; columnName: string }>>()
for (const out of group.outputs) {
const list = map.get(out.blockId) ?? []
list.push({ path: out.path, columnName: out.columnName })
map.set(out.blockId, list)
}
return map
}
/** Type-narrowing helper used by readers that can't assume `executions` is set. */
export function readExecutions(
row: { executions?: RowExecutions } | null | undefined
): RowExecutions {
return row?.executions ?? {}
}