● refactoringbuildingsamridhlimbu.com/projects/kairos · v0.1
Kairos
● live · next.js monorepoupdatingScheduling that reads intent. Paste unstructured notes — Kairos extracts tasks and places them into Google Calendar automatically. Rewrote a 1,051-line Python prototype into a clean Next.js monorepo: pure-function scheduler pipeline, plugin host with zero LLM imports in core, HTTP plugin runtime, theme marketplace enforced by CI, and a session-scoped LLM chat that can read and write your entire task graph.
Context
Solo, 2025–ongoing. I own design, scheduling logic, plugin system, API surface, and all five shipped phases. The core problem: a scheduling tool that understands context — deadlines, dependencies, preferred working windows — instead of just storing a list. Started as a Python/FastAPI prototype, rewrote everything once the file hit 1,051 lines. Phase 5d (Collections) shipped May 2026; v1.0.0 tag pending.
Timeline
Jul 2025
First commit
Sketch of a cron alternative. Just cron with retries, honestly.
Aug 2025
State machine
Replaced the "run and hope" loop with an explicit FSM. Everything downstream cleaner.
Oct 2025
Postgres over Redis
Migrated off Redis queues. Advisory locks, single source of truth.
Jan 2026
The off-by-one
1 in ~120 jobs expired quietly. Wrote the boundary tests I should have written first.
Feb 2026
Full rewrite decision
Largest file hit 1,051 lines. Declared prototype done, started clean Next.js monorepo. Backend logic ported as pure functions.
Mar 2026
Plugin system · Phase 3
Zod-validated plugin I/O contract. PluginContext abstraction. Core has zero LLM dependencies — all provider calls route through lib/llm/.
Apr 2026
Marketplaces · Phase 4
Theme + plugin marketplaces shipped. HTTP plugin runtime: HMAC-signed requests, 5 s timeout, circuit breaker. Install-without-redeploy on the hosted instance.
Apr 2026
Chat · Phase 5c
Session-scoped chat with streaming tokens. LLM can read and write tasks, query the schedule, manage collections. Tool calls render inline with collapsible output blocks.
May 2026
Collections · Phase 5d
Coordination layer above tags: ordered phases, bulk-schedule, progress endpoint. Added backlog + blocked task states. v1.0.0 tag pending.
Key technical decisions
01plugin host over hardcoded extraction › LLM call in core
PluginContext gives every plugin complete/completeStructured, config, memory, and logging — without core importing any provider SDK. Adding a new input source is a PR to the public registry, not a core change. 10 plugins shipped at launch.
02schedule-on-write › poll-and-schedule
Creating a task enqueues a Postgres job immediately — placement completes in 1–3 s within the same Vercel function call. No separate trigger, no user action. Idempotency keys prevent duplicates under concurrent saves.
03no-raw-colors ESLint rule › style guide doc
A custom ESLint rule bans hex literals and raw Tailwind colour utilities in component files. All components reference semantic tokens (--color-accent, --color-surface, etc.). Theme marketplace themes compile to CSS at install time — safe without code review.
04pure-function pipeline › side-effectful scheduler
runner.ts is the only file with DB or Google Calendar side effects. Every other module in lib/scheduler/ is a pure function — full unit coverage with no mocks and no database connection required.
The off-by-one
kairos/scheduler.pypy
1def within_retry_window(jobs, high):
2 # bug: j < high - 1 drops the last slot
3 for j in jobs:
4- if j < high - 1:
5+ if j < high:
6 yield j
One in ~120 scheduled jobs quietly expired instead of running. Lesson: write boundary tests before happy-path tests. The FSM made this findable; without it the bug would've read as noise.
What I'd do differently
Write the pure-function pipeline before the HTTP layer, not alongside it. I pinned myself into endpoint shapes early that the scheduler had to work around. The rewrite enforces the discipline: runner.ts is the only file with side effects — everything else is a pure function you can test without a database.