fix(Schedule Node): Cap day-of-month jitter at 28 (#29614)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
mfsiega
2026-05-01 10:34:37 +00:00
committed by GitHub
co-authored by Claude Opus 4.7
parent bdf06fa8dd
commit 86f47ee6dc
2 changed files with 6 additions and 4 deletions
@@ -126,7 +126,9 @@ export const toCronExpression = (interval: ScheduleInterval, nodeKey: string): C
return `${second} ${minute} ${hour} * * ${daysOfWeek}` as CronExpression;
}
const dayOfMonth = interval.triggerAtDayOfMonth ?? stableInt(nodeKey, 'dayOfMonth', 1, 31);
// Cap at 29 (exclusive) so jitter yields 1-28: any higher day would silently
// skip months that don't contain it (e.g. day 30 skips February every year).
const dayOfMonth = interval.triggerAtDayOfMonth ?? stableInt(nodeKey, 'dayOfMonth', 1, 29);
return `${second} ${minute} ${hour} ${dayOfMonth} */${interval.monthsInterval} *`;
};
@@ -33,7 +33,7 @@ function mockMomentTz(values: {
// from the input interval.
//
// For `seed = 'test-key'`, `stableInt` produces:
// second=56, minute=19, hour=14, dayOfMonth=4
// second=56, minute=19, hour=14, dayOfMonth=22
const TEST_SEED = 'test-key';
describe('toCronExpression', () => {
@@ -158,7 +158,7 @@ describe('toCronExpression', () => {
TEST_SEED,
);
expect(result).toEqual('56 0 0 1 */3 *');
// Nothing pinned, so sec=56 / min=19 / hr=14 / dom=4 are all filler.
// Nothing pinned, so sec=56 / min=19 / hr=14 / dom=22 are all filler.
const result1 = toCronExpression(
{
field: 'months',
@@ -166,7 +166,7 @@ describe('toCronExpression', () => {
},
TEST_SEED,
);
expect(result1).toEqual('56 19 14 4 */3 *');
expect(result1).toEqual('56 19 14 22 */3 *');
});
});