Scheduled tasks
A schedule is just a prompt sent automatically. Same queue, same agent, same chat — the only difference is that a clock sent it instead of you. Everything below is clickable, with realistic data.
Yours
Created by the agent Self-scheduled
You describe it. The agent writes the cron.
You said you're not typing cron expressions. Type it how you'd say it, and it shows exactly what it understood before anything is armed. Edit the sentence below — the parse updates live.
0 9, so the whole planet's jobs land on the same instant. The agent nudges to an odd minute unless you name an exact time — you never notice, the infrastructure does.Relative timers, not just calendars
Setting one up, in the chat you already use
No new surface to learn. You type it in the composer like anything else.
sch_7f2a.Every other Saturday at 09:07 SGT, running
/seo-followup against pestco.reputifly.cloud, focused on service pages. 8 runs, then it deletes itself.Next five: 6 Sep · 20 Sep · 4 Oct · 18 Oct · 1 Nov.
I used 09:07 rather than 09:00 — everyone's "9am" jobs land on the same instant, so an odd minute is kinder to the API. Say the word if you want it exactly on the hour.
It's read-only: reads Search Console, writes a report. Nothing reaches the client, so I've left the approval gate off. Turn it on if you'd rather see each one first.
One thing worth deciding: the run count is still 8, so it now stretches to 8 months instead of 4. Want me to drop it to 4 so it still ends this year?
Saturday, 09:07 — it fires on its own
Same chat, three days later. Nobody typed anything.
sch_7f2a run 3 of 8The three pages I flagged last run all moved:
/termite-control 14→7, /pest-inspection 22→11, /commercial unchanged at 31.Not repeating myself: last run I recommended rewriting
/commercial. It shipped 28 Aug and hasn't moved, so it's a different problem than I thought — the content is fine, it has no internal links from the service pages. That's the actual fix.The depth pass found something the API hides: 41 queries about "bed bugs" with impressions and zero clicks. There's no bed bug page at all. That's the biggest single gap on the site.
Two recommendations in the report, both new.
That run writes to the live site, unlike the read-only ones so far — so I've switched the approval gate on for run 4 only. You'll see the page before it goes live, then it reverts to ungated.
The agent waking itself up
This is the part that can't work today. "I'll check back in 45 minutes" is a promise the process can't keep — the timer lives in memory owned by a service set to Restart=always.
Item 4 needs the hero photo they mentioned, and it isn't uploaded — the portal shows it pending, not submitted.
I've scheduled myself to re-check rather than telling you I'll remember. Attempt 1 of 6, backing off 5m → 15m → 45m → 2h → 6h, hard stop 18:00. If it's still missing then, I escalate to you instead of continuing quietly.
You'll hear from me once — when it's done, or when I give up. Not five times in between.
sch_b41c attempt 4 of 6For the record: attempts 1–3 found nothing and stayed silent by design. Total wait 2h 41m across four checks, not 32 polls.
| Today | With durable schedules |
|---|---|
| "I'll check back in 45 min" — lost on the next restart, silently | Row in sqlite, survives restarts, re-fired on boot |
| You remember to ask "is that done yet?" | It comes back to you once, with the answer |
| Long tasks stall invisibly | Hard deadline forces an escalation you can see |
When it breaks — the case that matters most
A scheduled system you can't trust when it fails is worse than no scheduled system, because you stop checking. This is what a failure looks like.
sch_2d90 run 7What you still got: the API trend pass worked, so the report shipped — just thinner. Clicks 89 → 96, no position changes worth acting on.
What you didn't get: the query-level breakdown, which is the part the API can't give.
I retried once, got the same failure, and paused the routine rather than let it fail silently every Monday. Two options: I re-find the selector, or drop the depth pass for this client and keep the trend-only report. Your call — I haven't guessed.
Weekly digest — so nothing runs that you forgot about
The cheapest safety feature in the design. You'd spot a runaway loop in days instead of at the invoice.
Yours (4)
· SEO follow-up Pestco — Sat 09:07, run 3/8
· Social batch Karen Lim — 1 Oct, gated, waiting your approval
· Fortnightly fleet report — 17 Sep
· GSC export Elementary — failed Mon, self-paused
The agent's own (2)
· Re-check revision Bespoke Blooms — attempt 3/6, gives up 18:00
· Confirm DNS cci.sg — attempt 1/4, deadline Sat
Worth your eye: the Elementary export broke because Search Console moved its export button. The trend pass still worked, so the report shipped thinner rather than not at all. Fix the selector, or drop the depth pass for that client?
Commands
Same verb from the dashboard, Telegram, or inside the agent's own reasoning.
# you, in plain words — the agent writes the cron /schedule "every other saturday morning, SEO follow-up for Pestco, 8 times" /schedule "remind me to chase the cci.sg invoice in 3 days" /schedule "first monday monthly, social batch for Karen — gated" # managing /schedules list everything armed /schedules pestco just this client /schedule cancel sch_7f2a /schedule pause sch_7f2a /schedule edit sch_7f2a "make it weekly instead" /schedule runnow sch_7f2a fire once, doesn't shift the cadence # what the AGENT calls mid-task, instead of promising schedule_followup({ when: "+45m", skill: "/revisions check", args: { client: "bespokeblooms" }, max_attempts: 6, backoff: "exponential", deadline: "+9h", reason: "item 4 pending client photo" })
The row behind all of it
CREATE TABLE followups ( id TEXT PRIMARY KEY, -- sch_7f2a company_id TEXT NOT NULL, created_by TEXT NOT NULL, -- 'operator' | 'agent' ← the provenance badge skill TEXT NOT NULL, args TEXT NOT NULL, -- json cron TEXT, -- null for one-shots next_fire_at INTEGER NOT NULL, runs_done INTEGER DEFAULT 0, max_runs INTEGER, -- "4 times then stop" max_attempts INTEGER, -- self-check cap deadline_at INTEGER, -- hard stop, always set for agent rows approval_gate INTEGER DEFAULT 0, status TEXT DEFAULT 'armed', idem_key TEXT UNIQUE, -- never double-fire reason TEXT -- why the agent armed it );
messages and ingest_keys. Not the in-memory queue — that one is documented as "lost on backend restart", which is exactly the failure that once left a chat silent forever. A row only flips to done when the agent actually picks the work up, and boot re-fires anything left hanging.dashboard/frontend/src/index.css, ToolBubble.tsx, SessionStatusPill.tsxEvery client name, metric and timestamp on this page is invented for the demo.