日本語 · English

Kairos

English 日本語版 README(ドキュメントは日本語が正)

The Japanese documentation is canonical; the spec, the descriptor reference, and the stdlib guides are fully mirrored in English under en/ (every page links its counterpart). llms.txt carries the machine-readable overview. Requests and issues in English are welcome — open an issue.

Kairos is a schedule definition language — a small, composable DSL that defines when things should happen. It goes beyond cron-style patterns: schedules like “3 business days before month-end”, “the first business day on or after February 1”, or dates derived from the lunisolar calendar are all first-class expressions.

premise JP { calendar-system: Gregorian; calendar: TSE; tz: "Asia/Tokyo"; wkst: Mon }

@JP
monthEnd |> roll(Preceding, on: bizDay) |> shift(-3, unit: bizDay)   # 3 business days before month-end

cron and iCalendar RRULE can express “N calendar days before month-end”, but no existing schedule language lets you write “the Nth business day” inside an expression. The root limitation is not a missing feature — their expressions do not compose: you cannot take the dates one rule derives and feed them into the next rule. Kairos is built around this closure property (every expression is a transformation from a time stream to a time stream), so substitute-holiday derivation, fiscal calendars, the Japanese lunisolar calendar, and the 24 solar terms are all written with the same small operator family.

The two-layer architecture: the premise layer defines calendars once (intension); evaluation is one-way into the body layer, which weaves them into a time stream whose points can be piped onward (closure).

Highlights

Comparison with cron, Quartz, and RRULE

✓ = expressible in the language/definition · △ = partial (hacks, add-ons, implementation-specific) · ✗ = not expressible. “BDC products” = business schedulers with a business-day-calendar object plus shift flags. Full version with section pointers: spec §1.2.

Why composition matters: cron stops at fixed patterns, RRULE at recurrence rules; Kairos expressions compose — every result is a stream that pipes into the next definition (closure).

Capability cron Quartz RRULE BDC products Kairos
Fixed-time recurrence (daily at 9:00)
Nth weekday (2nd Monday) △ (day/weekday OR trap) ✓ (#) ✓ (BYDAY + BYSETPOS) ✓ (nth)
Month-end / N calendar days before it ✗ (28–31 hack) ✓ (L) ✓ (BYMONTHDAY=-1) ✓ (month \|> last \|> shift)
Business days (holiday-aware) △ (exclusion = skip only) ✗ (static EXDATE) ✓ (calendar entity + derived bizDay)
Business-day arithmetic (Nth business day) △ (shift flags only) ✓ (roll / shift(unit: bizDay))
Deriving holidays by rule (substitute holidays) ✗ (enumeration only) ✓ (cascade)
User-defined calendars (fiscal, ISO week, lunisolar, solar terms) △ (RFC 7529, rarely implemented) ✓ (premise layer)
Composition / closure (derived dates feed the next rule) △ (RDATE/EXDATE only) ✓ (stream → stream)
Cross-timezone composition (Tokyo × NY joint business days) ✓ (rebase + alignment checks)
DST semantics △ (implementation-defined) ✓ (wall clock) ✓ (declared; gaps/overlaps are explicit errors)
Detecting stale calendar data ✓ (covering + out-of-coverage annotations)
Determinism / audit (definition = set of instants) ✗ (depends on current time) ✓ (missed fires enumerable)
Static checks against silent mistakes ✓ (alignment, granularity, tz, mandatory declarations)

What Kairos deliberately does not do: firing, retries, and execution management (the host runtime’s job — the language stops at defining the set of instants); feedback on execution state (“every 5 hours since the last completion” as one infinite stream — instead, computing the next fire from an injected instant is in scope and pure, see spec §7.7); count-based termination (RRULE COUNT); guaranteeing the authenticity of calendar data (provenance source: / asof: carries the evidence; the judgment is external); branching on runtime conditions.

Runtime integration — how a scheduler consumes Kairos

Kairos deliberately stops at defining the set of instants. Your scheduler (the “firing layer”) owns timers, retries, and state, and the division of labor is one simple loop — evaluate over a rolling horizon, register timers, repeat:

sequenceDiagram
    participant R as Runtime(firing layer / out of scope)
    participant K as Kairos(pure evaluation)
    loop Rolling horizon
        R->>K: evaluate definition over [from, to)
        K-->>R: list of instants(+ coverage annotations)
        R->>R: register timers → fire …(re-evaluate as "to" nears)
    end
    Note over R,K: every evaluation is a pure function<br/>overlapping ranges always agree(deterministic, auditable)

Feedback schedules (“every 5 hours since the last completion”) are not a single infinite stream — that would feed outputs back into the expression. Instead the runtime injects the last completion time as data (exactly like a holiday table), and Kairos computes the next fire as a pure function of it:

@JP
lastCompleted = [2026-07-09T14:23] covering: ..     # injected by the runtime (with source:/asof:)
lastCompleted |> snapTo(day) |> roll(Following, on: bizDay) |> shift(+3, unit: bizDay)
#=> 2026-07-14   ("3 business days after the last completion")

Full explanation with sequence diagrams and runnable doctests: spec §7.7–7.8; the full worked study is design/40-examples/07 (Japanese).

Quick start (reference implementation)

Runs TypeScript directly with Node.js 24+; zero runtime dependencies.

cd impl
npm install          # devDependencies only (typescript / vitest)
npm test             # spec examples, real-ephemeris cross-checks, doctests

node src/cli.ts examples/payday.kairos      --from 2026-01-01 --to 2027-01-01
node src/cli.ts examples/jp-holidays.kairos --from 2026-01-01 --to 2027-01-01
node src/cli.ts examples/rokuyo.kairos      --from 2026-01-01 --to 2027-01-01

jp-holidays.kairos derives Japan’s substitute holidays and citizens’ holidays from the statutory holiday table alone. rokuyo.kairos derives the rokuyō cycle (大安 and friends) from the lunisolar calendar, cut by the National Astronomical Observatory of Japan’s new-moon data.

Status and documentation

Release candidate (RC5, declared 2026-07-08; addenda through no. 11, 2026-07-26). Semantics, the operator family, grammar (EBNF), and lexis are frozen; naming is final for every word (the last placeholder shiftBoundary was settled as rephase on 2026-07-26). Expressiveness is validated against 20 well-known schedule families and by a reference implementation (466 tests), including cross-checks against the official ephemeris of the National Astronomical Observatory of Japan.

Directory Contents
spec/ Language specification (reviewable snapshot; start here — 日本語)
reference/ Descriptor reference — one page per operator; examples are doctested (日本語)
stdlib/ Standard premises: Gregorian, Fiscal, ISOWeek (日本語 — includes the Japanese-only Kyureki)
impl/ Reference implementation (TypeScript, zero runtime deps; prototype — Japanese)
design/ Design records: 46 ADRs, domain model, expressiveness studies (Japanese)

License

Apache-2.0 (attribution in NOTICE). Contributions are accepted under Apache-2.0 §5 with a DCO sign-off (Signed-off-by: line). The “Kairos” name and logo (assets/logo/) are excluded from the license (Apache-2.0 §6).