Schedules: store + schedule/list/cancel verbs + timer (delete old scheduler) #60
Labels
No labels
epic
in-review
ready-for-agent
ready-for-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
weiwen/evie#60
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Parent
#55
What to build
Dynamic, persisted Schedules — a Schedule is an instruction to run a delivery Verb (
send/prompt/run) against a chat at a future time, replacing the old static[[schedule]]config entirely.Grammar (argv-wrap):
evie schedule (--in <dur> | --at <ts> | --cron <expr>) [--name <name>] [--update] [--chat <id>] -- <verb> [args…]. Timing flags are a required, mutually-exclusive group. At creation the Daemon eagerly parses and validates the trailing verb, rejects anything butsend|prompt|run, resolves and freezes the target chat, then stores the normalized argv. At fire time the Daemon re-dispatches the stored argv in-process (no re-exec), so fire-time behavior — notably prompt-script resolution viaPromptSource— is identical to an immediate run.Identity & upsert: primary key
(Chat ID, name).--nameomitted → generate a short opaque handle and print it. Name exists without--update→ error.--update→ replace if present, create if absent (upsert). Names scoped per chat.Store & timer: persist to
~/.config/evie/schedules.json(atomic temp+rename, reloaded on Daemon startup — same idiom aschat_sessions.json). A timer fires due Schedules by re-dispatching. Firing decisions ("is this one-shot due / missed?", "next occurrence") are pure functions over an injectednow, extracted from thetokio::sleeploop. A one-shot whose time elapsed during downtime fires late and flagged ("⏰ due at T (evie was offline)"); a recurring Schedule fires at its next occurrence.--cronand--atevaluated inchrono::Local.Management verbs:
list(human table by default;--jsonemits raw entries{name, chat, schedule, argv, created_at, next_fire}, scoped to the resolved chat) andcancel --name <name>(prints confirmation; non-zero exit + not-found message if absent; no--all).Fired-
runenv: when a Schedule fires arun, additionally injectEVIE_SCHEDULE_NAMEandEVIE_SCHEDULE_TIME(local, ISO 8601).Deletions: remove
[[schedule]],ScheduleConfig, andscheduler_task. No migration — a stale[[schedule]]table is simply ignored (serde unknown field).Demoable:
evie schedule --in 10s -- send "ping"fires ~10s later;evie schedule --cron "0 8 * * *" -- prompt "morning digest"recurs;evie listshows both;evie cancel --name <n>removes one; a Schedule survives a daemon restart.Acceptance criteria
evie schedulecreates a Schedule wrappingsend/prompt/run; wrapping any other verb is rejected at creation.--in/--at/--cronare mutually exclusive and exactly one is required;--at/--croninterpreted in local time.--namecollision errors without--update;--updateupserts; omitted--namegenerates and prints a handle; names are unique per chat.schedules.json(atomic temp+rename) and reload on startup — verified by a restart surviving a pending Schedule.evie listshows the resolved chat's Schedules;--jsonemits parseable entries.evie cancel --nameremoves one and errors on not-found.runreceivesEVIE_SCHEDULE_NAME+EVIE_SCHEDULE_TIMEin addition toEVIE_CHAT_ID+EVIE_SOCKET.[[schedule]],ScheduleConfig, andscheduler_taskare removed; a config still containing[[schedule]]starts without error.send|prompt|runwhitelist,--name/--update); dispatch-core CRUD (collision→error, update upsert, per-chat scoping, list, cancel not-found); pure firing-decision functions (due / missed-late / next-occurrence / local time, notokio::sleep); store round-trip incl. frozen chat + argv.Blocked by