@@ -644,6 +644,40 @@ assert(catchup_scheduled_times[2] < 45.2, "Second fire shows synced schedule (~4
644644-- Clean up
645645LLTimers :off (catchup_handler )
646646
647+ -- Test minimum interval guarantee after catchup
648+ -- After waking from a long sleep near a catchup boundary, the SECOND tick should
649+ -- never fire faster than the specified interval. Regression test for "overcorrection" bug.
650+ setclock (0.0 )
651+ local min_interval_fires = 0
652+ local min_interval_times = {}
653+ local min_interval_handler = LLTimers :every (1.0 , function (scheduled_time )
654+ min_interval_fires += 1
655+ table.insert (min_interval_times , getclock ())
656+ end )
657+
658+ -- Timer created at T=0, first scheduled for T=1.0
659+ -- Simulate script disable/re-enable: jump forward 5.5s (past 2s catchup threshold)
660+ setclock (5.5 )
661+ LLEvents :_handleEvent (' timer' )
662+ assert (min_interval_fires == 1 , " First tick should fire immediately after wake" )
663+
664+ -- The second tick should be at least 1.0s from now, not at T=6.0
665+ setclock (6.0 ) -- Only 0.5s later
666+ LLEvents :_handleEvent (' timer' )
667+ assert (min_interval_fires == 1 , " Second tick should NOT fire at T=6.0 (only 0.5s after first)" )
668+
669+ -- Should fire at T=6.5 or later (1.0s after T=5.5)
670+ setclock (6.51 )
671+ LLEvents :_handleEvent (' timer' )
672+ assert (min_interval_fires == 2 , " Second tick should fire at ~T=6.5 (1.0s after first)" )
673+
674+ -- Verify the gap between fires was at least the interval
675+ local gap = min_interval_times [2 ] - min_interval_times [1 ]
676+ assert (gap >= 1.0 , ` Gap between ticks should be >= 1.0s, got {gap}` )
677+
678+ -- Clean up
679+ LLTimers :off (min_interval_handler )
680+
647681
648682-- Verify that user code can't get a reference to the timers tick function through `debug`
649683local expected = {
0 commit comments