@@ -54,22 +54,45 @@ export function isGroupEligible(
5454) : boolean {
5555 const isManualRun = opts ?. isManualRun ?? false
5656 const mode = opts ?. mode ?? 'all'
57- if ( group . autoRun === false && ! isManualRun ) return false
57+ const tag = `[Eligibility] row=${ row . id } group=${ group . id } manual=${ isManualRun } mode=${ mode } `
58+
59+ if ( group . autoRun === false && ! isManualRun ) {
60+ logger . debug ( `${ tag } → skip: autoRun=false on auto-fire` )
61+ return false
62+ }
5863
5964 const exec = row . executions ?. [ group . id ]
60- if ( isExecInFlight ( exec ) ) return false
65+ if ( isExecInFlight ( exec ) ) {
66+ logger . debug ( `${ tag } → skip: in-flight (status=${ exec ?. status } )` )
67+ return false
68+ }
6169 const status = exec ?. status
6270
6371 const completedAndFilled = status === 'completed' && areOutputsFilled ( group , row )
64- if ( ! isManualRun && completedAndFilled ) return false
65- if ( ! isManualRun && ( status === 'error' || status === 'cancelled' ) ) return false
66- // `mode: 'incomplete'` (manual): re-runs only rows whose outputs are missing
67- // or whose last run failed. A genuinely completed cell (status + filled
68- // outputs) is skipped; error / cancelled remain runnable.
69- if ( mode === 'incomplete' && completedAndFilled ) return false
70-
71- if ( isManualRun && group . autoRun === false ) return true
72- return areGroupDepsSatisfied ( group , row )
72+ if ( ! isManualRun && completedAndFilled ) {
73+ logger . debug ( `${ tag } → skip: completed+filled on auto-fire` )
74+ return false
75+ }
76+ // Auto-fire skips `error` to avoid infinite-retry loops on a deterministic
77+ // failure. `cancelled` doesn't get the same treatment — cancellation is
78+ // user-initiated, and once an upstream dep re-fires the cascade, the user
79+ // almost certainly wants the chain to continue.
80+ if ( ! isManualRun && status === 'error' ) {
81+ logger . debug ( `${ tag } → skip: terminal status=error on auto-fire` )
82+ return false
83+ }
84+ if ( mode === 'incomplete' && completedAndFilled ) {
85+ logger . debug ( `${ tag } → skip: completed+filled on mode=incomplete` )
86+ return false
87+ }
88+
89+ if ( isManualRun && group . autoRun === false ) {
90+ logger . debug ( `${ tag } → eligible: manual on autoRun=false group (deps bypassed)` )
91+ return true
92+ }
93+ const depsOk = areGroupDepsSatisfied ( group , row )
94+ logger . debug ( `${ tag } → ${ depsOk ? 'eligible: deps satisfied' : 'skip: deps unmet' } ` )
95+ return depsOk
7396}
7497
7598/**
@@ -107,6 +130,10 @@ export async function scheduleRunsForRows(
107130 const groups = groupIdFilter ? allGroups . filter ( ( g ) => groupIdFilter . has ( g . id ) ) : allGroups
108131 if ( groups . length === 0 ) return { triggered : 0 }
109132
133+ logger . debug (
134+ `[Cascade] scheduleRunsForRows table=${ table . id } rows=${ rows . length } groups=${ groups . length } manual=${ opts ?. isManualRun ?? false } mode=${ opts ?. mode ?? 'all' } scoped=${ groupIdFilter ? 'yes' : 'no' } `
135+ )
136+
110137 const orderedRows = rows . length <= 1 ? rows : [ ...rows ] . sort ( ( a , b ) => a . position - b . position )
111138
112139 const pendingRuns : RunGroupCellOptions [ ] = [ ]
@@ -410,6 +437,10 @@ async function runWorkflowGroupsInternal(opts: {
410437 const targetGroups = groupIds ? allGroups . filter ( ( g ) => groupIds . includes ( g . id ) ) : allGroups
411438 if ( targetGroups . length === 0 ) return { triggered : 0 }
412439
440+ logger . info (
441+ `[Cascade] [${ requestId } ] runWorkflowColumn table=${ tableId } groups=[${ targetGroups . map ( ( g ) => g . id ) . join ( ',' ) } ] rows=${ rowIds ? `[${ rowIds . join ( ',' ) } ]` : 'all' } mode=${ mode } `
442+ )
443+
413444 const filters = [ eq ( userTableRows . tableId , tableId ) , eq ( userTableRows . workspaceId , workspaceId ) ]
414445 if ( rowIds && rowIds . length > 0 ) {
415446 filters . push ( inArray ( userTableRows . id , rowIds ) )
0 commit comments