feat(workflow): orchestrate resumable dataflow handoffs

This commit is contained in:
2026-07-30 03:11:56 +02:00
parent a1654e70cf
commit a6e0e89829
18 changed files with 3938 additions and 27 deletions
@@ -10,6 +10,7 @@ import {
CheckCircle2,
CopyPlus,
GitFork,
ListChecks,
Plus,
RefreshCw,
RotateCcw,
@@ -57,6 +58,7 @@ import WorkflowCanvas, {
updateWorkflowGraphNode
} from "./WorkflowCanvas";
import WorkflowInspector from "./WorkflowInspector";
import WorkflowRunsDialog from "./WorkflowRunsDialog";
import {
FALLBACK_WORKFLOW_LIBRARY,
draftFromDefinition,
@@ -103,6 +105,7 @@ export default function WorkflowPage({
const [deleteOpen, setDeleteOpen] = useState(false);
const [definitionSettingsOpen, setDefinitionSettingsOpen] = useState(false);
const [deriveOpen, setDeriveOpen] = useState(false);
const [runsOpen, setRunsOpen] = useState(false);
const canWrite = hasScope(auth, "workflow:definition:write")
|| hasScope(auth, "workflow:instance:admin");
@@ -114,6 +117,18 @@ export default function WorkflowPage({
&& canWrite
&& draft.governance?.actions.derive?.allowed
);
const canStart = Boolean(
draft?.id
&& (hasScope(auth, "workflow:instance:start")
|| hasScope(auth, "workflow:instance:admin"))
&& draft.governance?.actions.start?.allowed !== false
);
const canTransition = hasScope(auth, "workflow:instance:transition")
|| hasScope(auth, "workflow:instance:admin");
const selectedDefinition = useMemo(
() => definitions.find((item) => item.id === draft?.id) ?? null,
[definitions, draft?.id]
);
const dirty = Boolean(draft)
&& workflowFingerprint(draft) !== workflowFingerprint(savedDraft);
const displayedGraph = historicalRevision?.graph ?? draft?.graph ?? null;
@@ -531,6 +546,14 @@ export default function WorkflowPage({
<Button onClick={() => void validate()} disabled={working}>
<CheckCircle2 size={16} /> Validate
</Button>
{draft.id ? (
<Button
onClick={() => setRunsOpen(true)}
disabled={dirty || historicalRevision !== null}
>
<ListChecks size={16} /> Runs
</Button>
) : null}
<IconButton
label="Definition settings"
icon={<Settings2 size={16} />}
@@ -748,6 +771,14 @@ export default function WorkflowPage({
setSuccess("Created a pinned scoped copy.");
}}
/>
<WorkflowRunsDialog
open={runsOpen}
settings={settings}
definition={selectedDefinition}
canStart={canStart}
canTransition={canTransition}
onClose={() => setRunsOpen(false)}
/>
</main>
);
}