Foundation Scheduler — Agentic Cron

  • Area: Foundation
  • Path: services/foundation/scheduler
  • Kind: Agentic cron — runs prompts on a schedule, executes inside engines/sandbox, dispatches results via Kode Relay
  • Status: v0.1.0 — foundation shipped (2026-05-24); follow-ups #013–#017 (real sandbox executor, real relay dispatch, active/standby leader election, Koder ID JWKS, OpenAPI emission)

Role in the stack

scheduler is the Koder Stack's agentic cron. It owns the question "run prompt P on schedule S as agent A, deliver to channel C" — the missing layer that turns one-shot agent invocations into a durable, audited, time-aware workflow. Without it, every product re-implements its own cron + retry + result delivery (and most don't, so periodic AI tasks live as ad-hoc shell scripts on developer laptops). It pairs the engines/sandbox runtime (per-job isolation) with engines/kode-relay (multi-channel delivery) into one operator surface: HTTP API + kjob CLI.

The seven done/ tickets that predate v0.1.0 are research bench: feature-parity specs against Google Cloud Tasks, Cloud Scheduler, Cloud Functions, Cloud Batch, Composer, Workflows, and WorkflowExecutions. v0.1.0 is the first executable layer; #013–#017 unlock the production path.

Boundary vs neighbors

  • engines/sandbox is the per-job execution sandbox (v0 uses deterministic Stub; #013 wires the real backend).
  • engines/kode-relay is the result dispatch channel (chatemailwebhook; v0 stub; #014 wires).
  • services/foundation/id is the auth boundary (v0 static Bearer; #016 swaps to Koder ID JWKS).
  • services/foundation/billing/engine will receive UsageEvent for each run when wired (planned follow-up beyond #017).
  • services/ai/agents is the natural agent driver — many scheduled prompts will be agent-loop invocations rather than raw LLM calls.

Features (v0.1.0)

  • HTTP daemon koder-scheduler on :9445
  • Operator CLI kjob (list / create / get / delete / run-now / runs)
  • 5-field cron + @every <duration> shorthand
  • In-memory Job + Run store with per-tenant slug uniqueness
  • Tick loop (1s) that advances NextFire and dispatches via Executor
  • Pluggable Executor interface: deterministic Stub (v0), typed sandbox + relay stubs returning SCHED-BACKEND-UNAVAILABLE-001 referencing #013/#014
  • Bearer middleware with Identity{TenantID, UserID, Admin} + dev-mode short-circuit
  • Per-(tenant, op) quota counter with daily window (default 100 jobsday, 200 runsday)
  • Cross-tenant isolation: reads from a different tenant return 404 (not 403), per policies/multi-tenant-by-default.kmd
  • Go SDK engines/sdk/go/scheduler/

Primary couplings

Producer Relationship
engines/sandbox Per-job execution sandbox (#013)
engines/kode-relay Multi-channel result dispatch (#014)
services/foundation/id Bearer / JWKS auth (#016)
infra/data/kdb-next Persistence + leader-election keyspace (#015)
Consumer Relationship
services/ai/agents Schedules periodic agent runs
services/ai/eval Periodic regression suites
services/foundation/reporter Scheduled report generation
Product backends Tenant-scoped cron (daily summaries, weekly digests, periodic data sync)

RFC and bootstrap

  • RFC: services/foundation/scheduler/docs/rfcs/RFC-001-scheduler-cron-agentic.md (scaffold, ratified)
  • Foundation tickets shipped: #008 (cron + JobRun store + tick loop), #009 (Executor interface + Stub + typed sandboxrelay stubs), #010 (HTTP API + kjob CLI + Go SDK), #011 (multi-tenant Bearer + per-tenant quota), #012 (SCHED-* error map + TOML config)
  • Follow-up tickets: services/foundation/scheduler/backlog/pending/{013..017}

Self-hosted-first analysis (5 gates)

Gate Status Notes
G1 — Feature parity Partial v0.1.0 covers cron + run history + manual run-now. Per-tenant CRUD covered. Distributed scheduling (active/standby) + real sandbox execution + result dispatch are the gap vs Cloud Scheduler / Cloud Tasks / Apache Airflow.
G2 — Performance Not measured v0 is in-memory single-process — bench-worthy after #015 lands.
G3 — Operational maturity Pre-prod Single-node; no leader election; no persistence beyond process lifetime. #015 closes leader gap.
G4 — Self-host security OK Bearer + per-tenant isolation (404, not 403). JWKS via Koder ID in #016.
G5 — Ecosystem depth Bootstrapping Go SDK shipped; DartPythonCLI clients deferred until consumer demand.

Promotion to "shadow-active" (vs Google Cloud Scheduler / Airflow) requires #013 + #014 + #015 closed.

Variants (per specs/variants/taxonomy.kmd)

  • scheduler-backend-linux — koder-scheduler daemon (Go, Linux amd64 + arm64)
  • scheduler-cli-linux — kjob (Go, Linux amd64 + arm64)