SIGN IN SIGN UP

fix(workflow): exclude soft-deleted workspaces from the core-consistency cron (#24740)

## Context

The workflow core-consistency cron enumerated workspaces with raw SQL
filtering on `activationStatus IN ('ACTIVE', 'SUSPENDED')` only. Raw SQL
bypasses the ORM soft-delete filter, so the cron also scanned
soft-deleted workspaces awaiting hard deletion.

Those workspaces are skipped by the upgrade pipeline
(`WorkspaceIteratorService` excludes soft-deleted rows), so their
schemas can lag behind upgrade commands forever. On the dev cluster,
three workspaces soft-deleted on June 4 are stuck pre-2.23 and lack the
`coreWorkflowId` column, producing
[TWENTY-SERVER-JMH](https://twenty-v7.sentry.io/issues/7654702516/)
(`column wf.coreWorkflowId does not exist`) on every cron run since.
They can never converge: the cron checks them, the upgrade never touches
them.

## What this does

Replaces the hand-rolled raw-SQL enumeration and loop with
`WorkspaceIteratorService.iterate` — the same abstraction the upgrade
pipeline and app-install flows use. The iterator owns the workspace
scope (`PROVISIONED_WORKSPACE_ACTIVATION_STATUSES`, soft-delete-aware
through the entity's `@DeleteDateColumn`), the per-workspace error
isolation, and the workspace context setup; the cron just reports the
iterator's failures to Sentry as before. Workspaces without a
`databaseSchema` are skipped.

The cron's scope is now provably identical to what the upgrade covers
(CREATED, ACTIVE, SUSPENDED, not soft-deleted). CREATED workspaces are
newly included, which is consistent: they are provisioned and upgraded,
and the existing `shouldCheck` fast path skips them when they have no
workflows.

The per-workspace drift checks remain raw SQL since they join
workspace-schema tables against core tables.

## Query volume

The cron previously visited every provisioned workspace every 3 hours.
Instead of touching the whole fleet each run, the fleet is now split
into 8 deterministic shards (`mod(first uuid byte, 8)`) and each run
only checks the shard matching its fire hour, so every workspace is
still checked exactly once a day at an eighth of the per-run cost. The
shard filter is a generic `shard` option on `WorkspaceIteratorService`
applied in SQL, so out-of-shard workspaces cost nothing.

`WorkspaceIteratorContext` also exposes `databaseSchema` (the iterator
already fetches the row), so the callback doesn't re-fetch it. The drift
checks themselves keep their original one-query-per-concern shape for
readability.

Shard distribution verified against the dev database: 66 provisioned
workspaces spread 5-11 per shard.

Fixes TWENTY-SERVER-JMH
C
Charles Bochet committed
ebfbe8f24d316334ebcd8920fc8b5515fa5a3fd9
Parent: b275322
Committed by GitHub <noreply@github.com> on 8/25/2026, 11:39:22 AM