π File detail
utils/cronJitterConfig.ts
π― Use case
This file lives under βutils/β, which covers cross-cutting helpers (shell, tempfiles, settings, messages, process input, β¦). On the API surface it exposes getCronJitterConfig β mainly functions, hooks, or classes. Dependencies touch schema validation. It composes internal code from services, cronTasks, and lazySchema (relative imports). What the file header says: GrowthBook-backed cron jitter configuration. Separated from cronScheduler.ts so the scheduler can be bundled in the Agent SDK public build without pulling in analytics/growthbook.ts and its large transitive dependency set (settings/hooks/config cycle). Usage: REPL (useScheduledTa.
Generated from folder role, exports, dependency roots, and inline comments β not hand-reviewed for every path.
π§ Inline summary
GrowthBook-backed cron jitter configuration. Separated from cronScheduler.ts so the scheduler can be bundled in the Agent SDK public build without pulling in analytics/growthbook.ts and its large transitive dependency set (settings/hooks/config cycle). Usage: REPL (useScheduledTasks.ts): pass `getJitterConfig: getCronJitterConfig` Daemon/SDK: omit getJitterConfig β DEFAULT_CRON_JITTER_CONFIG applies.
π€ Exports (heuristic)
getCronJitterConfig
π External import roots
Package roots from from "β¦" (relative paths omitted).
zod
π₯οΈ Source preview
// GrowthBook-backed cron jitter configuration.
//
// Separated from cronScheduler.ts so the scheduler can be bundled in the
// Agent SDK public build without pulling in analytics/growthbook.ts and
// its large transitive dependency set (settings/hooks/config cycle).
//
// Usage:
// REPL (useScheduledTasks.ts): pass `getJitterConfig: getCronJitterConfig`
// Daemon/SDK: omit getJitterConfig β DEFAULT_CRON_JITTER_CONFIG applies.
import { z } from 'zod/v4'
import { getFeatureValue_CACHED_WITH_REFRESH } from '../services/analytics/growthbook.js'
import {
type CronJitterConfig,
DEFAULT_CRON_JITTER_CONFIG,
} from './cronTasks.js'
import { lazySchema } from './lazySchema.js'
// How often to re-fetch tengu_kairos_cron_config from GrowthBook. Short because
// this is an incident lever β when we push a config change to shed :00 load,
// we want the fleet to converge within a minute, not on the next process
// restart. The underlying call is a synchronous cache read; the refresh just
// clears the memoized entry so the next read triggers a background fetch.
const JITTER_CONFIG_REFRESH_MS = 60 * 1000
// Upper bounds here are defense-in-depth against fat-fingered GrowthBook
// pushes. Like pollConfig.ts, Zod rejects the whole object on any violation
// rather than partially trusting it β a config with one bad field falls back
// to DEFAULT_CRON_JITTER_CONFIG entirely. oneShotFloorMs shares oneShotMaxMs's
// ceiling (floor > max would invert the jitter range) and is cross-checked in
// the refine; the shared ceiling keeps the individual bound explicit in the
// error path. recurringMaxAgeMs uses .default() so a pre-existing GB config
// without the field doesn't get wholesale-rejected β the other fields were
// added together at config inception and don't need this.
const HALF_HOUR_MS = 30 * 60 * 1000
const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000
const cronJitterConfigSchema = lazySchema(() =>
z
.object({
recurringFrac: z.number().min(0).max(1),
recurringCapMs: z.number().int().min(0).max(HALF_HOUR_MS),
oneShotMaxMs: z.number().int().min(0).max(HALF_HOUR_MS),
oneShotFloorMs: z.number().int().min(0).max(HALF_HOUR_MS),
oneShotMinuteMod: z.number().int().min(1).max(60),
recurringMaxAgeMs: z
.number()
.int()
.min(0)
.max(THIRTY_DAYS_MS)
.default(DEFAULT_CRON_JITTER_CONFIG.recurringMaxAgeMs),
})
.refine(c => c.oneShotFloorMs <= c.oneShotMaxMs),
)
/**
* Read `tengu_kairos_cron_config` from GrowthBook, validate, fall back to
* defaults on absent/malformed/out-of-bounds config. Called from check()
* every tick via the `getJitterConfig` callback β cheap (synchronous cache
* hit). Refresh window: JITTER_CONFIG_REFRESH_MS.
*
* Exported so ops runbooks can point at a single function when documenting
* the lever, and so tests can spy on it without mocking GrowthBook itself.
*
* Pass this as `getJitterConfig` when calling createCronScheduler in REPL
* contexts. Daemon/SDK callers omit getJitterConfig and get defaults.
*/
export function getCronJitterConfig(): CronJitterConfig {
const raw = getFeatureValue_CACHED_WITH_REFRESH<unknown>(
'tengu_kairos_cron_config',
DEFAULT_CRON_JITTER_CONFIG,
JITTER_CONFIG_REFRESH_MS,
)
const parsed = cronJitterConfigSchema().safeParse(raw)
return parsed.success ? parsed.data : DEFAULT_CRON_JITTER_CONFIG
}