feat(workflow): route all version content readers through the flag-aware sources (#23583)

## What

Follow-up to #23499. Migrates every remaining reader of
`record.trigger`/`record.steps` so all version content flows through the
flag-aware sources, then removes `trigger`/`steps` from the record field
sets. The record CRUD path no longer carries version content anywhere in
the app.

Reading is still entirely behind `IS_WORKFLOW_VERSION_IN_CORE_ENABLED`:
this PR changes who asks, never where the answer comes from. Flag off
remains record reads (via the content hook's record branch), flag on the
core query.

## Per reader

| reader | now reads |
| --- | --- |
| `WorkflowDiagramCanvasEditable` (connect, drag-stop) | flow atom |
| `useDeleteStep` | flow atom |
| `WorkflowEditActionIfElseBody` (branch cleanup) | flow atom |
| `SidePanelWorkflowCreateStepContent` (parent-step lookup) | flow atom
|
| `SidePanelWorkflowStepInfo` | flow atom (explicit instance id),
falling back to `useWorkflowVersionContent` when the visualizer is not
mounted |
| `TestWorkflowSingleRecordCommand` | `useWorkflowVersionContent`;
`ready` gates on content being loaded |
| headless enrichment hook (imperative) | core content query when the
flag is on, record otherwise |
| `WorkflowRunVisualizerEffect` (step output schemas) | the run snapshot
(`state.flow`), which is what a run should show anyway |

## Field-set slimming

`useWorkflowVersion` and `useWorkflowWithCurrentVersion` stop fetching
`trigger`/`steps` (identity fields only). Three call sites lost their
only reason to call `useWorkflowWithCurrentVersion` and were dropped
entirely. Verified by grep that no `currentVersion.trigger/steps` reads
remain; the only remaining `.trigger`/`.steps` accesses are
argument-taking utils whose callers now pass flow/content-sourced
objects.

## Verification

- `nx typecheck twenty-front` green
- Front tests: 1058 green (the enrichment test gained mocks for the
apollo client and flag its hook now uses)
- Full-tree `oxfmt` + `oxlint --type-aware` green (3 remaining warnings
are pre-existing in unrelated record-field files)
- Live click-through pending, flag off and on

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/23583?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Thomas Trompette
2026-07-30 18:57:11 +02:00
committed by GitHub
parent 42de0482d0
commit 53a18d7528
11 changed files with 91 additions and 41 deletions
@@ -14,8 +14,6 @@ export const useWorkflowVersion = (workflowVersionId?: string) => {
createdAt: true,
updatedAt: true,
workflowId: true,
trigger: true,
steps: true,
status: true,
workflow: {
id: true,
@@ -55,6 +55,14 @@ export const useWorkflowWithCurrentVersion = (
{
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
objectRecordId: currentVersionId,
recordGqlFields: {
id: true,
name: true,
status: true,
workflowId: true,
createdAt: true,
updatedAt: true,
},
skip: !isDefined(currentVersionId),
},
);
@@ -1,5 +1,6 @@
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
import { flowComponentState } from '@/workflow/states/flowComponentState';
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState';
import { WorkflowDiagramCanvasBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase';
@@ -62,8 +63,10 @@ export const WorkflowDiagramCanvasEditable = () => {
const { startNodeCreation } = useStartNodeCreation();
const flow = useAtomComponentStateValue(flowComponentState);
const onConnect = async (edgeConnect: WorkflowConnection) => {
const steps = workflowWithCurrentVersion?.currentVersion?.steps;
const steps = flow?.steps;
const sourceStep = isDefined(steps)
? steps.find((step) => step.id === edgeConnect.source)
: undefined;
@@ -135,10 +138,7 @@ export const WorkflowDiagramCanvasEditable = () => {
};
const onNodeDragStop: OnNodeDrag<WorkflowDiagramNode> = async (_, node) => {
const stepToUpdate =
workflowWithCurrentVersion?.currentVersion?.steps?.find(
(step) => step.id === node.id,
);
const stepToUpdate = flow?.steps?.find((step) => step.id === node.id);
if (isDefined(stepToUpdate)) {
await updateStep({
@@ -149,7 +149,7 @@ export const WorkflowDiagramCanvasEditable = () => {
return;
}
const triggerToUpdate = workflowWithCurrentVersion?.currentVersion?.trigger;
const triggerToUpdate = flow?.trigger;
if (isDefined(triggerToUpdate)) {
await updateTrigger({
@@ -246,12 +246,16 @@ export const WorkflowRunVisualizerEffect = ({
]);
useEffect(() => {
if (!isDefined(workflowVersion)) {
if (!isDefined(workflowVersion) || !isDefined(workflowRun?.state)) {
return;
}
populateStepsOutputSchema(workflowVersion);
}, [populateStepsOutputSchema, workflowVersion]);
populateStepsOutputSchema({
...workflowVersion,
trigger: workflowRun.state.flow.trigger,
steps: workflowRun.state.flow.steps,
});
}, [populateStepsOutputSchema, workflowRun?.state, workflowVersion]);
return null;
};
@@ -1,7 +1,7 @@
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow';
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
import { flowComponentState } from '@/workflow/states/flowComponentState';
import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState';
import { useDeleteWorkflowVersionStep } from '@/workflow/workflow-steps/hooks/useDeleteWorkflowVersionStep';
import { useResetWorkflowAiAgentPermissionsStateOnSidePanelClose } from '@/workflow/workflow-steps/workflow-actions/ai-agent-action/hooks/useResetWorkflowAiAgentPermissionsStateOnSidePanelClose';
@@ -20,12 +20,12 @@ export const useDeleteStep = () => {
const workflowVisualizerWorkflowId = useAtomComponentStateValue(
workflowVisualizerWorkflowIdComponentState,
);
const workflow = useWorkflowWithCurrentVersion(workflowVisualizerWorkflowId);
const flow = useAtomComponentStateValue(flowComponentState);
const deleteStep = async (stepId: string) => {
const workflowVersionId = await getUpdatableWorkflowVersion();
const steps = workflow?.currentVersion?.steps;
const steps = flow?.steps;
const stepToDelete = isDefined(steps)
? steps.find((step) => step.id === stepId)
: undefined;
@@ -2,8 +2,7 @@ import { InputLabel } from '@/ui/input/components/InputLabel';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow';
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState';
import { flowComponentState } from '@/workflow/states/flowComponentState';
import {
type WorkflowIfElseAction,
type WorkflowStep,
@@ -66,10 +65,7 @@ export const WorkflowEditActionIfElseBody = ({
const { updateWorkflowVersionPosition } = useTidyUpWorkflowVersion();
const { deleteWorkflowVersionStep } = useDeleteWorkflowVersionStep();
const { deleteStepsOutputSchema } = useStepsOutputSchema();
const workflowVisualizerWorkflowId = useAtomComponentStateValue(
workflowVisualizerWorkflowIdComponentState,
);
const workflow = useWorkflowWithCurrentVersion(workflowVisualizerWorkflowId);
const flow = useAtomComponentStateValue(flowComponentState);
const currentStepFilters = useAtomComponentStateValue(
currentStepFiltersComponentState,
@@ -146,7 +142,7 @@ export const WorkflowEditActionIfElseBody = ({
await cleanupEmptyChildStepsFromDeletedBranches({
branchesToDelete,
allSteps: workflow?.currentVersion?.steps ?? undefined,
allSteps: flow?.steps ?? undefined,
});
setCurrentStepFilterGroups(updatedStepFilterGroups);