Issue #3605811: Drain a synchronous instance inline when a timer is fired on demand
Problem
The "fire timer now" operator action ran its resume inside the engine batch context (WorkflowEngine::batch()), which holds the advance depth above zero. The inline drain that start() and signal() perform once committed is gated to depth zero (maybeDrainInline()), so it was suppressed for the fire. That is correct for the cron sweep, which fires many due timers and lets the queue worker drain, but wrong for an on-demand fire: a synchronous instance was left with its resumed work merely enqueued, so the run appeared stalled until cron.
Fix
WorkflowEngine::drainSynchronous(): a thin public wrapper over the existing inline-drain guard (only at depth zero, only when the instance runs synchronously).TimeoutSweep::fireNow()resolves the instance up front and, once the batch has unwound, callsdrainSynchronous($instance)so the run reaches its next stable state in the same request. A queued instance is unchanged: it stays enqueued for cron.
Tests
Two new TimeoutTest cases:
testFireTimeoutNowDrainsSynchronousInstance: a synchronous fire reaches the next stable state with no manual drain.testFireTimeoutNowLeavesQueuedInstanceForCron: a queued fire resumes the token but leaves the advance for cron.