This commit is contained in:
Siddharth Ganesan
2026-01-27 18:16:46 -08:00
parent 07dfedd5f1
commit 79857e1a04
3 changed files with 34 additions and 11 deletions

View File

@@ -1661,11 +1661,16 @@ export function useWorkflowExecution() {
},
onExecutionError: (data) => {
if (data.error?.includes('Block not found in workflow')) {
const isWorkflowModified =
data.error?.includes('Block not found in workflow') ||
data.error?.includes('Upstream dependency not executed')
if (isWorkflowModified) {
clearLastExecutionSnapshot(workflowId)
addNotification({
level: 'info',
message: 'Workflow was modified. Run the workflow again to refresh.',
level: 'error',
message:
'Workflow was modified. Run the workflow again to enable running from block.',
workflowId,
})
} else {

View File

@@ -331,6 +331,24 @@ describe('validateRunFromBlock', () => {
expect(result.error).toContain('Upstream dependency not executed')
})
it('rejects blocks with unexecuted transitive upstream dependencies', () => {
// A → X → B → C, where X is new (not executed)
// Running from C should fail because X in upstream chain wasn't executed
const dag = createDAG([
createNode('A', [{ target: 'X' }]),
createNode('X', [{ target: 'B' }]),
createNode('B', [{ target: 'C' }]),
createNode('C'),
])
const executedBlocks = new Set(['A', 'B', 'C']) // X was not executed (new block)
const result = validateRunFromBlock('C', dag, executedBlocks)
expect(result.valid).toBe(false)
expect(result.error).toContain('Upstream dependency not executed')
expect(result.error).toContain('X')
})
it('allows blocks with no dependencies even if not previously executed', () => {
// A and B are independent (no edges)
const dag = createDAG([createNode('A'), createNode('B')])

View File

@@ -169,15 +169,15 @@ export function validateRunFromBlock(
if (node.metadata.isSentinel) {
return { valid: false, error: 'Cannot run from sentinel node' }
}
}
if (node.incomingEdges.size > 0) {
for (const sourceId of node.incomingEdges.keys()) {
if (!executedBlocks.has(sourceId)) {
return {
valid: false,
error: `Upstream dependency not executed: ${sourceId}`,
}
}
// Check that ALL upstream blocks were executed (transitive check)
const { upstreamSet } = computeExecutionSets(dag, blockId)
for (const upstreamId of upstreamSet) {
if (!executedBlocks.has(upstreamId)) {
return {
valid: false,
error: `Upstream dependency not executed: ${upstreamId}`,
}
}
}