@@ -773,6 +773,176 @@ describe('EdgeManager', () => {
773773 } )
774774 } )
775775
776+ describe ( 'Multiple error ports to same target' , ( ) => {
777+ it ( 'should mark target ready when one source errors and another succeeds' , ( ) => {
778+ // This tests the case where a node has multiple incoming error edges
779+ // from different sources. When one source errors (activating its error edge)
780+ // and another source succeeds (deactivating its error edge), the target
781+ // should become ready after both sources complete.
782+ //
783+ // Workflow 1 (errors) ─── error ───┐
784+ // ├──→ Error Handler
785+ // Workflow 7 (succeeds) ─ error ───┘
786+
787+ const workflow1Id = 'workflow-1'
788+ const workflow7Id = 'workflow-7'
789+ const errorHandlerId = 'error-handler'
790+
791+ const workflow1Node = createMockNode ( workflow1Id , [
792+ { target : errorHandlerId , sourceHandle : 'error' } ,
793+ ] )
794+
795+ const workflow7Node = createMockNode ( workflow7Id , [
796+ { target : errorHandlerId , sourceHandle : 'error' } ,
797+ ] )
798+
799+ const errorHandlerNode = createMockNode ( errorHandlerId , [ ] , [ workflow1Id , workflow7Id ] )
800+
801+ const nodes = new Map < string , DAGNode > ( [
802+ [ workflow1Id , workflow1Node ] ,
803+ [ workflow7Id , workflow7Node ] ,
804+ [ errorHandlerId , errorHandlerNode ] ,
805+ ] )
806+
807+ const dag = createMockDAG ( nodes )
808+ const edgeManager = new EdgeManager ( dag )
809+
810+ // Workflow 1 errors first - error edge activates
811+ const readyAfterWorkflow1 = edgeManager . processOutgoingEdges ( workflow1Node , {
812+ error : 'Something went wrong' ,
813+ } )
814+ // Error handler should NOT be ready yet (waiting for workflow 7)
815+ expect ( readyAfterWorkflow1 ) . not . toContain ( errorHandlerId )
816+
817+ // Workflow 7 succeeds - error edge deactivates
818+ const readyAfterWorkflow7 = edgeManager . processOutgoingEdges ( workflow7Node , {
819+ result : 'success' ,
820+ } )
821+ // Error handler SHOULD be ready now (workflow 1's error edge activated)
822+ expect ( readyAfterWorkflow7 ) . toContain ( errorHandlerId )
823+ } )
824+
825+ it ( 'should mark target ready when first source succeeds then second errors' , ( ) => {
826+ // Opposite order: first source succeeds, then second errors
827+
828+ const workflow1Id = 'workflow-1'
829+ const workflow7Id = 'workflow-7'
830+ const errorHandlerId = 'error-handler'
831+
832+ const workflow1Node = createMockNode ( workflow1Id , [
833+ { target : errorHandlerId , sourceHandle : 'error' } ,
834+ ] )
835+
836+ const workflow7Node = createMockNode ( workflow7Id , [
837+ { target : errorHandlerId , sourceHandle : 'error' } ,
838+ ] )
839+
840+ const errorHandlerNode = createMockNode ( errorHandlerId , [ ] , [ workflow1Id , workflow7Id ] )
841+
842+ const nodes = new Map < string , DAGNode > ( [
843+ [ workflow1Id , workflow1Node ] ,
844+ [ workflow7Id , workflow7Node ] ,
845+ [ errorHandlerId , errorHandlerNode ] ,
846+ ] )
847+
848+ const dag = createMockDAG ( nodes )
849+ const edgeManager = new EdgeManager ( dag )
850+
851+ // Workflow 1 succeeds first - error edge deactivates
852+ const readyAfterWorkflow1 = edgeManager . processOutgoingEdges ( workflow1Node , {
853+ result : 'success' ,
854+ } )
855+ // Error handler should NOT be ready yet (waiting for workflow 7)
856+ expect ( readyAfterWorkflow1 ) . not . toContain ( errorHandlerId )
857+
858+ // Workflow 7 errors - error edge activates
859+ const readyAfterWorkflow7 = edgeManager . processOutgoingEdges ( workflow7Node , {
860+ error : 'Something went wrong' ,
861+ } )
862+ // Error handler SHOULD be ready now (workflow 7's error edge activated)
863+ expect ( readyAfterWorkflow7 ) . toContain ( errorHandlerId )
864+ } )
865+
866+ it ( 'should NOT mark target ready when all sources succeed (no errors)' , ( ) => {
867+ // When neither source errors, the error handler should NOT run
868+
869+ const workflow1Id = 'workflow-1'
870+ const workflow7Id = 'workflow-7'
871+ const errorHandlerId = 'error-handler'
872+
873+ const workflow1Node = createMockNode ( workflow1Id , [
874+ { target : errorHandlerId , sourceHandle : 'error' } ,
875+ ] )
876+
877+ const workflow7Node = createMockNode ( workflow7Id , [
878+ { target : errorHandlerId , sourceHandle : 'error' } ,
879+ ] )
880+
881+ const errorHandlerNode = createMockNode ( errorHandlerId , [ ] , [ workflow1Id , workflow7Id ] )
882+
883+ const nodes = new Map < string , DAGNode > ( [
884+ [ workflow1Id , workflow1Node ] ,
885+ [ workflow7Id , workflow7Node ] ,
886+ [ errorHandlerId , errorHandlerNode ] ,
887+ ] )
888+
889+ const dag = createMockDAG ( nodes )
890+ const edgeManager = new EdgeManager ( dag )
891+
892+ // Both workflows succeed - both error edges deactivate
893+ const readyAfterWorkflow1 = edgeManager . processOutgoingEdges ( workflow1Node , {
894+ result : 'success' ,
895+ } )
896+ expect ( readyAfterWorkflow1 ) . not . toContain ( errorHandlerId )
897+
898+ const readyAfterWorkflow7 = edgeManager . processOutgoingEdges ( workflow7Node , {
899+ result : 'success' ,
900+ } )
901+ // Error handler should NOT be ready (no errors occurred)
902+ expect ( readyAfterWorkflow7 ) . not . toContain ( errorHandlerId )
903+ } )
904+
905+ it ( 'should mark target ready when both sources error' , ( ) => {
906+ // When both sources error, the error handler should run
907+
908+ const workflow1Id = 'workflow-1'
909+ const workflow7Id = 'workflow-7'
910+ const errorHandlerId = 'error-handler'
911+
912+ const workflow1Node = createMockNode ( workflow1Id , [
913+ { target : errorHandlerId , sourceHandle : 'error' } ,
914+ ] )
915+
916+ const workflow7Node = createMockNode ( workflow7Id , [
917+ { target : errorHandlerId , sourceHandle : 'error' } ,
918+ ] )
919+
920+ const errorHandlerNode = createMockNode ( errorHandlerId , [ ] , [ workflow1Id , workflow7Id ] )
921+
922+ const nodes = new Map < string , DAGNode > ( [
923+ [ workflow1Id , workflow1Node ] ,
924+ [ workflow7Id , workflow7Node ] ,
925+ [ errorHandlerId , errorHandlerNode ] ,
926+ ] )
927+
928+ const dag = createMockDAG ( nodes )
929+ const edgeManager = new EdgeManager ( dag )
930+
931+ // Workflow 1 errors
932+ const readyAfterWorkflow1 = edgeManager . processOutgoingEdges ( workflow1Node , {
933+ error : 'Error 1' ,
934+ } )
935+ expect ( readyAfterWorkflow1 ) . not . toContain ( errorHandlerId )
936+
937+ // Workflow 7 errors
938+ const readyAfterWorkflow7 = edgeManager . processOutgoingEdges ( workflow7Node , {
939+ error : 'Error 2' ,
940+ } )
941+ // Error handler SHOULD be ready (both edges activated)
942+ expect ( readyAfterWorkflow7 ) . toContain ( errorHandlerId )
943+ } )
944+ } )
945+
776946 describe ( 'Chained conditions' , ( ) => {
777947 it ( 'should handle sequential conditions (condition1 → condition2)' , ( ) => {
778948 const condition1Id = 'condition-1'
0 commit comments