Introducing the Truth Engine: Deterministic RRULE Expansion
Recurring calendar events follow the iCalendar RRULE specification (RFC 5545). It’s a deceptively complex format that breaks LLMs in subtle ways.
Why RRULE expansion is hard
Consider a weekly standup: RRULE:FREQ=WEEKLY;BYDAY=MO;UNTIL=20261231T235959Z. Simple enough. But real-world RRULEs get complex fast:
- DST transitions: A 9am meeting in
America/New_Yorkshifts its UTC offset twice a year. Naive implementations create phantom events or skip real ones. - BYSETPOS:
FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1means “last weekday of the month.” Most LLMs interpret this incorrectly. - EXDATE: Exception dates remove specific occurrences from the series. Missing one means showing a cancelled meeting as active.
- Leap years: February 29th events only recur every 4 years. Some implementations silently skip them.
- INTERVAL > 1:
FREQ=WEEKLY;INTERVAL=2means every other week. The anchor date matters — get it wrong and every occurrence shifts.
Why LLMs can’t do it
LLMs approach RRULE expansion as a text generation task. They predict what the next date “should be” based on the pattern. But RRULE expansion is a mathematical computation — the spec defines a deterministic algorithm.
When an LLM “expands” an RRULE, it’s guessing. When the Truth Engine expands an RRULE, it’s computing.
We tested this directly. We ran 5 real-world RRULEs through frontier LLMs and the Truth Engine. The results are stark — read the full side-by-side comparison. Here’s one example:
RRULE: FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,TH;COUNT=8 (biweekly on Tuesday and Thursday)
LLM: Generates occurrences every week, ignoring INTERVAL=2. Half the dates are wrong.
Truth Engine: Correctly skips alternate weeks. The 8 occurrences span 7 weeks, not 4.
The pattern repeats across all 5 tests. DST transitions, BYSETPOS, leap years, EXDATE — the LLM produces plausible-looking dates that fail RFC 5545 compliance. The Truth Engine produces correct dates every time.
The Truth Engine approach
The Truth Engine is a Rust library that implements RFC 5545 RRULE expansion with full spec compliance. It handles every edge case the spec defines:
- DST-aware UTC conversion: The same wall-clock time produces different UTC values before and after a DST transition. The Truth Engine preserves wall-clock time and recomputes the UTC offset.
- BYSETPOS with negative indices:
BYSETPOS=-1means “last in the set.” The set is computed per-period (per-month, per-year), then the index is applied. This requires computing all candidates first, then selecting. - EXDATE with timezone matching: Exception dates must match expanded instances exactly, accounting for timezone. A UTC EXDATE must be compared against timezone-converted instances, not raw local times.
- Leap year filtering:
BYMONTHDAY=29;BYMONTH=2produces instances only when February 29 exists. No substitution to Feb 28 or March 1. - INTERVAL across frequency units:
INTERVAL=2on a weekly frequency skips every other week. The interval applies to the frequency, not to individual BYDAY values.
Testing with 9,000+ property-based tests
Unit tests check specific cases. Property-based tests check invariants across randomly generated inputs. The Truth Engine uses both.
The property-based tests verify invariants like:
- Every expanded instance falls within the DTSTART-UNTIL range
- Instances are always in chronological order
- The count of instances never exceeds the RRULE’s COUNT parameter
- Every instance respects the BYDAY, BYMONTH, BYMONTHDAY constraints
- Given the same inputs, the Rust native, WASM, and Python bindings all produce identical output
9,000+ generated test cases means the engine has been tested against RRULE combinations that no human would think to write — unusual INTERVAL values, extreme BYSETPOS indices, RRULEs that span century boundaries. For a deeper look at how property-based testing catches calendar edge cases that unit tests miss, see Property-Based Testing for Calendar Engines.
Available on three platforms
# Rust (native)
cargo add truth-engine
# JavaScript/TypeScript (via WASM)
npm install @temporal-cortex/truth-engine
# Python (via PyO3 native bindings)
pip install truth-engine
The same Rust implementation powers all three packages. The JavaScript version compiles to WebAssembly, so it runs in browsers and Node.js with native-like performance. The Python version uses PyO3 for direct native bindings — no subprocess, no IPC overhead.
Every expansion is deterministic. Given the same RRULE and parameters, the Truth Engine always returns the same result — across platforms, across time.
Using it with Temporal Cortex
The Truth Engine powers the expand_rrule tool in the Temporal Cortex MCP server. Install the server and your AI agent gets deterministic RRULE expansion as one of 18 tools:
npx @temporal-cortex/cortex-mcp
The expand_rrule tool is a Layer 2 tool — it requires no network access and performs pure computation. You can also use the Truth Engine directly as a library in your own applications without the MCP server.
Open source
The Truth Engine is open source under MIT/Apache-2.0 dual license. It’s part of Temporal Cortex Core, available on crates.io, npm, and PyPI.
Browse the source on GitHub.