# SYNTAX.md ## Scope This document defines the user-facing schedule syntax accepted by Kron. The same syntax is used by: * Kubernetes CRDs (`KronJob.spec.schedule`, `KronJob.spec.window`, `KronJob.spec.distribution`, `KronJob.spec.constraints`) * `krontab` entries for `krond` The syntax is designed to be: * Cron-compatible for the baseline schedule * Explicit for probabilistic extensions * Deterministic and fully parseable --- ## Grammar overview A Kron schedule is composed of: 1. A baseline cron expression 2. Optional modifiers Canonical form: ``` [@tz()] [@win(,)] [@dist([,...])] [@seed([,...])] [@policy(...)] [@avoid()] [@only()] ``` Order of modifiers is not significant. Whitespace separates tokens. Parentheses do not contain unescaped whitespace. --- ## Cron expression Kron accepts standard 5-field cron: ``` ``` Ranges, lists, and steps are supported: * `*` * `n` * `n-m` * `n,m,k` * `*/s` * `n-m/s` Aliases are supported where common: * months: `JAN..DEC` * weekdays: `SUN..SAT` Day-of-week values: * `0-6` with `0=SUN` * `SUN..SAT` Examples: ``` 0 0 * * * # daily at midnight 15 9 * * MON-FRI # weekdays at 09:15 */5 * * * * # every 5 minutes 0 3 1 * * # monthly on the 1st at 03:00 ``` --- ## Timezone modifier ``` @tz() ``` `` is an IANA timezone name. Examples: ``` 0 10 * * * @tz(Europe/Paris) 0 18 * * * @tz(America/Los_Angeles) ``` If omitted, timezone is `UTC`. --- ## Window modifier ``` @win(,) ``` ``: * `after` * `around` `` is a Go-style duration: * `ns`, `us`, `ms`, `s`, `m`, `h` * composite forms are allowed: `1h30m` Semantics: * `after`: window is `[nominal, nominal + duration]` * `around`: window is `[nominal - duration/2, nominal + duration/2]` Examples: ``` 0 0 * * * @win(after,2h) 0 10 * * * @win(around,45m) ``` If omitted, window is `after,0s`. --- ## Distribution modifier ``` @dist([,...]) ``` ``: * `uniform` * `normal` * `skewEarly` * `skewLate` * `exponential` Parameters are `k=v` pairs. Unknown keys are invalid. MVP runtime status: * `krontab explain` and `krontab next` execute: `uniform`, `skewEarly`, `skewLate` * `normal` and `exponential` are syntax-valid and lint-validated, but runtime execution is not part of current MVP ### uniform ``` @dist(uniform) ``` No parameters. ### normal ``` @dist(normal[,mu=][,sigma=]) ``` MVP runtime note: syntax-valid and lint-validated, but not executed by `krontab explain`/`krontab next`. * `mu` sets the mean anchor point. * `sigma` sets standard deviation. `mu` anchors: * `nominal` * `start` * `mid` * `end` Defaults: * `mu=nominal` for `around` * `mu=mid` for `after` * `sigma=window/6` Examples: ``` 0 10 * * * @win(around,2h) @dist(normal,sigma=20m) 0 10 * * * @win(after,2h) @dist(normal,mu=start,sigma=15m) ``` ### skewEarly / skewLate ``` @dist(skewEarly[,shape=]) @dist(skewLate[,shape=]) ``` * `shape` controls skew intensity. * `shape` is a positive float. * default `shape=2.0` Examples: ``` 0 10 * * * @win(after,2h) @dist(skewEarly,shape=3) 0 10 * * * @win(after,2h) @dist(skewLate,shape=1.5) ``` ### exponential ``` @dist(exponential[,lambda=][,dir=]) ``` MVP runtime note: syntax-valid and lint-validated, but not executed by `krontab explain`/`krontab next`. * `lambda` is a positive float controlling decay rate. * `dir`: * `early` biases toward window start * `late` biases toward window end * defaults: `lambda=1.0`, `dir=early` Examples: ``` 0 10 * * * @win(after,2h) @dist(exponential,dir=early,lambda=1.2) 0 10 * * * @win(after,2h) @dist(exponential,dir=late,lambda=0.8) ``` If omitted, distribution is `uniform`. --- ## Seed modifier ``` @seed([,...]) ``` ``: * `stable` * `daily` * `weekly` Parameters: * `salt=` `` is a UTF-8 string without unescaped whitespace. Quoted strings are supported: * `"..."` with `\"` escapes Examples: ``` 0 0 * * * @seed(stable,salt="team-a") 0 10 * * * @seed(daily) 0 10 * * MON @seed(weekly,salt=prod) ``` If omitted, seed strategy is `stable` with empty salt. --- ## Policy modifier ``` @policy([,...]) ``` Keys: * `concurrency=allow|forbid|replace` * `deadline=` * `suspend=true|false` Defaults: * `concurrency=forbid` * `deadline=0s` (skip missed periods) * `suspend=false` Examples: ``` 0 0 * * * @policy(concurrency=forbid,deadline=10m) 0 10 * * * @policy(concurrency=replace) ``` --- ## Constraints modifiers Constraints restrict when a chosen time may be selected. Constraints apply after the window is computed and before a final time is accepted. If a candidate time violates constraints, a new candidate is drawn. If no valid time can be found, the period is unschedulable. ### Avoid modifier ``` @avoid() ``` ### Only modifier ``` @only() ``` A constraint spec is a semicolon-separated list of clauses: ``` ; ; ... ``` Clause forms: * `hours=` * `dow=` * `dom=` * `months=` * `between=-` * `date=` * `dates=..` Ranges: * numeric ranges with lists and hyphens, e.g. `1-5,7,9-12` * weekdays: `SUN..SAT` * months: `JAN..DEC` Time values are interpreted in the schedule timezone. Examples: ``` 0 10 * * * @win(after,2h) @avoid(hours=0-6) 0 18 * * * @win(around,1h) @only(dow=MON-FRI;between=08:00-20:00) 0 19 * * * @avoid(date=2026-12-25) 0 19 * * * @avoid(dates=2026-12-24..2026-12-31) ``` --- ## Validation rules * Cron expression must be valid and resolvable. * Timezone must be a valid IANA name. * Window duration must be non-negative. * For `around` mode, `duration/2` is computed with nanosecond precision. * Distributions must be recognized; parameters must be valid and within allowed ranges. * In current MVP runtime, only `uniform`, `skewEarly`, and `skewLate` are executable in `explain`/`next`. * Seed strategy must be recognized. * Policy keys must be recognized; values must be valid. * Constraint specs must be parseable; unknown clause keys are invalid. * `@only(...)` and `@avoid(...)` may both be present. * If constraints eliminate the entire window, the period is unschedulable. --- ## Examples (MVP executable with `explain`/`next`) ### Load smoothing (spread backups) ``` 0 0 * * * @tz(UTC) @win(after,3h) @dist(uniform) @seed(stable,salt=backup) @policy(concurrency=forbid,deadline=30m) ``` ### Human-like messaging (late-biased) ``` 0 10 * * * @tz(Europe/Paris) @win(around,90m) @dist(skewLate,shape=2.5) @seed(daily,salt=msgs) @only(dow=MON-FRI;between=08:00-20:00) ``` ### Home automation (gentle unpredictability) ``` 0 18 * * * @tz(America/Los_Angeles) @win(around,20m) @dist(skewLate,shape=1.4) @seed(daily,salt=lights) ``` ### Avoid nights and weekends ``` 0 * * * * @tz(UTC) @win(after,30m) @dist(skewEarly) @only(dow=MON-FRI;hours=8-18) ``` ### Monthly with wide window, strict deadline ``` 0 2 1 * * @tz(UTC) @win(after,8h) @dist(uniform) @policy(concurrency=forbid,deadline=15m) ``` ## Lint-only Examples (planned runtime distributions) These pass `krontab lint` but are not executable by MVP `krontab explain`/`krontab next`. ``` 0 10 * * * @win(around,2h) @dist(normal,sigma=20m) 0 10 * * * @win(after,2h) @dist(exponential,dir=early,lambda=1.2) ```