SPEC.md
Scope
This document specifies Kron’s scheduling and execution semantics as a stable contract.
It applies to all Kron deployments and adapters:
kron-core(engine)krond(host daemon)kron-operator(Kubernetes controller)
Where an adapter cannot enforce a behavior directly, it must preserve the observable contract.
Terminology
Schedule: A cron expression interpreted in a timezone.
Timezone: IANA timezone used to interpret schedule and calendar constraints.
Nominal time: The scheduled timestamp produced by resolving a schedule for a period.
Period: The discrete scheduling opportunity anchored at one nominal time.
Period ID: Canonical identifier for a period.
Window: The allowed time interval in which an execution may be chosen.
Chosen time: The specific timestamp selected within the window for a period.
Decision: The computed record containing period, window, distribution, seed, and chosen time.
Constraint: A rule restricting which timestamps are allowed.
Candidate: A sampled timestamp within the window prior to constraint validation.
Deadline: Maximum allowed lateness relative to chosen time to still execute.
Execution: One realized run of a job (process spawn or Kubernetes
Jobcreation).Handled period: A period for which Kron has reached a terminal outcome.
Terminal outcome:
executed,skipped,missed, orunschedulable.Active execution: An execution that has started but not completed.
Identity: Stable identifier of a schedule entry.
Time Model
All internal comparisons use UTC instants.
Input schedules and constraints are interpreted in the configured timezone.
All persisted timestamps are stored as RFC3339 UTC.
Kron’s decisions are defined at second-level granularity for chosen timestamps unless a platform supports higher precision; implementations may use higher precision but must not violate determinism for the same inputs.
Inputs
A job definition provides:
identity: stable identifierschedule: cron expressiontimezone: optional, defaultUTCwindow_mode:afteroraroundwindow_duration: non-negative durationdistribution: name + parametersseed_strategy: name + optional parameterssalt: optional stringconstraints: optionalonlyand/oravoidpolicy:concurrency:allow|forbid|replacedeadline: duration (default0s)suspend: boolean (defaultfalse)
Adapters also provide:
now: current time instant
Outputs
For each job, Kron produces:
A
Decisionfor a specificPeriod:period_idnominal_timewindow_startwindow_endchosen_timetimezonedistribution+ parametersseed_strategy+ parametersseed_hashconstraints_appliedsummary
A terminal outcome per period:
executedskippedmissedunschedulable
Periods
Period definition
A period is the interval of responsibility anchored at one resolved nominal time.
For a schedule S, timezone TZ, and an evaluation time t, define:
prev_nominal(t): greatest nominal time ≤tnext_nominal(t): smallest nominal time >t
A period is identified by its nominal time.
Period ID
period_id is the RFC3339 UTC representation of the nominal time.
Canonical form:
period_id = nominal_time_utc_rfc3339
Implementations must treat the same nominal instant as the same period even if represented in different timezones.
Window Semantics
Given nominal time N and window duration D:
If
window_mode=after:window_start = Nwindow_end = N + D
If
window_mode=around:window_start = N - (D / 2)window_end = N + (D / 2)
window_start and window_end are inclusive bounds for candidate generation, with the constraint that the chosen time must satisfy:
window_start ≤ chosen_time ≤ window_end
If D=0, then:
chosen_time = N
Distribution Semantics
A distribution maps a seeded pseudorandom stream to a time within the window.
Distributions must be bounded: no value outside the window may be chosen.
Distributions may require parameters; missing parameters imply deterministic defaults.
Distribution evaluation must be deterministic for identical inputs.
The chosen time must be stable for:
the same
identitythe same
period_idthe same schedule configuration
the same seed strategy and salt
Seed Semantics
Determinism requirement
Kron must produce the same chosen_time for the same job definition and period, independent of:
process restarts
leader changes
reconciliation frequency
Seed inputs
Seed derivation uses:
identityperiod_key(derived fromperiod_idand seed strategy)salt(string, possibly empty)
Seed strategies
Seed strategies define the period_key:
stable:period_key = period_iddaily:period_key = YYYY-MM-DD in TZ corresponding to nominal timeweekly:period_key = ISO week (YYYY-Www) in TZ corresponding to nominal time
Seed hash
Kron computes:
seed_hash = HASH(identity || "\n" || period_key || "\n" || salt)
Where:
HASHis a stable algorithm selected by Kron (cryptographic hash required)identity,period_key, andsaltare UTF-8 strings||indicates concatenation
seed_hash is the canonical seed representation exposed in logs and status.
The pseudorandom generator uses seed_hash as seed material in a stable, specified transformation.
Constraint Semantics
Constraints restrict allowable chosen times.
onlydefines the allowed set.avoiddefines the disallowed set.If both are present, a time is valid only if it satisfies
onlyand does not satisfyavoid.
Constraints are evaluated in the schedule timezone.
Constraints apply to candidate timestamps during decision computation.
Candidate selection with constraints
To compute chosen_time:
Sample a candidate within the window using the distribution.
If candidate violates constraints, reject and sample a new candidate.
Continue until a valid candidate is found or the sampling budget is exhausted.
Sampling budget is deterministic and fixed by Kron.
Unschedulable periods
If no valid candidate is found within the sampling budget, the period outcome is unschedulable and no execution occurs.
Decision Computation
For each job and period:
Resolve
nominal_timefor the period based on schedule and timezone.Compute window bounds.
Compute
period_keybased on seed strategy.Compute
seed_hash.Initialize deterministic pseudorandom generator from
seed_hash.Sample candidate(s) according to distribution within window.
Apply constraints.
Produce
Decisionincluding chosen time and metadata.
A Decision is specific to one period.
If the job definition changes, decisions for future periods may change. Decisions for already-handled periods must not cause additional execution.
Handling and Outcomes
A period is handled exactly once by reaching one terminal outcome.
Terminal outcomes:
executed: an execution was started for the periodskipped: execution was intentionally not started due to policymissed: execution was not started because the deadline expiredunschedulable: no valid time could be selected within the window and constraints
Once a period is handled, Kron must not start another execution for that period.
Execution Trigger Semantics
A period becomes eligible for trigger when:
now ≥ chosen_time
If policy.suspend=true, no trigger occurs and no period is handled while suspended.
When eligible, Kron attempts to reach a terminal outcome by evaluating policies and system state.
Deadline Semantics
Let:
Cbechosen_timeDLbepolicy.deadlinenowcurrent time at trigger evaluation
If DL=0s, then the period is handled as missed if now > C.
If DL>0s, then:
If
now ≤ C + DL, execution may proceedIf
now > C + DL, the period is handled asmissed
Concurrency Semantics
Concurrency policy is evaluated at trigger time.
Let active indicate whether there is an active execution for the job identity.
allow: proceed to execute regardless ofactive.forbid: ifactive=true, handle period asskipped.replace: ifactive=true, terminate the active execution and proceed to execute.
Termination semantics for replace are adapter-defined but must be best-effort and observable.
Idempotency Semantics
Kron must guarantee at most one execution per (identity, period_id).
Adapters must implement an idempotency check before starting execution.
In Kubernetes mode: by checking for an existing owned Job with the period identifier.
In daemon mode: by consulting persisted state and verifying active/existing executions.
If an execution for the period is already recorded as started or completed, the period is handled and must not be executed again.
State Machine
Per job identity, each period transitions:
Planned: decision computed, chosen time knownEligible: now ≥ chosen_timeTerminal: one ofexecuted|skipped|missed|unschedulable
Execution sub-states for executed:
Running: started, not yet completedCompleted: finished with an exit result (adapter-specific)
State transitions are monotonic.
A period cannot transition from a terminal outcome to a different terminal outcome.
Spec Change Semantics
A job definition change is any change to:
schedule
timezone
window mode/duration
distribution or parameters
constraints
seed strategy or salt
policy settings
Effects:
Future decisions may change starting from the first unhandled period.
Already-handled periods must remain handled and must not execute again.
Active executions are governed by concurrency policy and adapter behavior.
Clock Change Semantics
Kron treats now as authoritative input.
If
nowjumps forward, some periods may become immediately eligible and will be handled subject to deadline and idempotency.If
nowjumps backward, Kron must not re-handle already handled periods.
Adapters must persist handled period outcomes to preserve idempotency across clock changes.
Observability Contract
For each period and identity, Kron must expose:
period_idnominal_timechosen_timeterminal outcome
reason for non-execution (
skipped,missed,unschedulable) when applicableseed hash and distribution metadata sufficient to reproduce the decision
Logs must contain enough information to reproduce the decision off-line.
Invariants
Kron guarantees:
chosen_timeis within[window_start, window_end].At most one execution per
(identity, period_id).Deterministic
chosen_timefor the same inputs and period.No unbounded historical replay.
Terminal outcomes are monotonic and permanent for a period.
Suspension prevents executions without advancing handled periods.
If constraints make selection impossible, the period becomes
unschedulableand no execution occurs.
Compatibility
The meaning of
period_id, window computation, seed hashing inputs, and determinism rules are stable contracts.New distributions, parameters, and constraint types may be added.
Any behavior change that affects decisions for the same inputs requires a major version bump of the relevant API surface.