Holding the due follow-up instead of adding business days to the date is a choice I agree with, and comparing now.weekday as a number is a good catch. One case gets past the 'a wrong setting sends nothing' rule, though. The code only waits when waited < days[sent] is true. If follow_up_days is typed as '3;4;7' or '3 4 7', the entry becomes NaN, the filter removes it, and days is empty. days[0] is then undefined, waited < undefined is false, and the row returns follow_up on the first run inside working hours. The same thing happens once sent reaches the length of the list: the last follow-up goes out again on every hourly run unless a separate limit check runs first. One guard before the comparison closes both cases: if days is empty or sent >= days.length, return review instead of follow_up. Then a malformed setting fails as safely as a misspelled timezone already does.
StackShortlist
Proof Over Promises.
Recipient time zone is the detail most sequences get wrong. Storing it on the contact instead of computing it at send time also makes holidays easier to handle later. Good writeup.