feat: recurring/scheduled task support#73
Open
marktoda wants to merge 12 commits into
Open
Conversation
Database-level recurring task support via per-queue s_{queue} tables,
PL/pgSQL cron parser, and tick_schedules called inside claim_task.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10 bite-sized tasks covering cron parser, schedule table, CRUD procedures, tick engine, claim_task integration, TypeScript SDK methods, migration, and integration tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Expands a single cron field (wildcards, ranges, steps, lists, named days/months) into a sorted integer array. Foundation for the cron parser needed by recurring schedules. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement database-level schedule management with PL/pgSQL cron engine.
Schedules are per-queue (s_{queue} table), ticked automatically when
workers poll via claim_task, with skip/all catchup policies.
New stored procedures:
- parse_cron_field: expands cron field to integer array
- next_cron_time: 5-field cron + shorthands + @every N
- create/get/list/delete/update_schedule: full CRUD
- tick_schedules: spawns tasks from due schedules
Modified: claim_task (ticks before claiming), ensure_queue_tables and
drop_queue (include s_ table lifecycle).
Also adds TypeScript SDK methods, migration file (0.0.7-0.0.8), and
35 schedule-specific tests (68 total passing).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two fixes from code review: 1. Idempotency keys in tick_schedules used timestamptz::text which renders differently depending on session TimeZone, allowing duplicate task spawns across workers with different TZ settings. Changed to extract(epoch from ...)::bigint::text for a timezone-independent key. 2. Migration file used bare "create function" for new functions, causing failure if re-run after partial application. Changed all 8 new functions to "create or replace function" to match the pattern already used for modified functions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three fixes from code review: 1. parse_cron_field now handles wrap-around ranges (e.g. FRI-MON / 5-1) by expanding both ends of the range around the boundary. Previously this produced an empty array and a cryptic "no match found within 4 years" error. 2. next_cron_time now validates that @every is followed by a plain integer. Previously @every 300s or @every 5m hit a raw Postgres cast error. 3. Added index on s_{queue}(enabled, next_run_at) so tick_schedules uses an index scan instead of a sequential scan on every claim_task poll. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Nanoclaw-inspired personal AI assistant built on Absurd as the durable task management layer. Key design decisions: Telegram via grammY, Claude API for reasoning, containerized plugin execution, markdown agent notes, Postgres for history/state, no slash commands. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add schedule management commands to absurdctl (create-schedule, update-schedule, delete-schedule, list-schedules), a schedules API endpoint and view to the habitat web UI, and schedule CRUD methods to the Python SDK (both sync and async clients). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ipts The original Docker-per-plugin architecture was too rigid for self-extending behaviors. Replace with a nanoclaw-inspired model where the agent runs entirely in a Docker container and extends itself by writing markdown skill files, executable tool scripts, and creating Absurd schedules. Add the full implementation plan (14 tasks across 6 phases). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
# Conflicts: # absurdctl # habitat/ui/src/App.tsx # sdks/typescript/package-lock.json # sql/absurd.sql
Member
|
I'm definitely open to it, I'm not sure if I am happy with that approach but I will review it. |
|
Hey @mitsuhiko (apologies for over-pinging per the other PR) - could you unpack what you'd prefer for this approach? Happy to take it over / work with Mark (we've worked together before) on getting this over the line. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds cron and interval-based recurring tasks
Schedules are ticked lazily inside claim_task(). When a worker polls for work, tick_schedules checks for due schedules and spawns tasks before returning. Epoch-based idempotency keys (sched::) prevent double-firing across concurrent workers.
Two catchup policies handle downtime recovery:
What changed
SQL — s_{queue} table, parse_cron_field (full 5-field cron with wrap-around ranges, named days/months, @daily/@every shorthands), next_cron_time, tick_schedules, and CRUD functions (create_schedule, get_schedule, list_schedules, update_schedule, delete_schedule).
TypeScript & Python SDKs — createSchedule, getSchedule, listSchedules, updateSchedule, deleteSchedule. Spawned tasks inherit all schedule metadata
(params, headers, retry strategy, cancellation, max attempts).
absurdctl — create-schedule, list-schedules, update-schedule, delete-schedule commands.
Habitat — GET /api/queues/{queue}/schedules endpoint and a new Schedules view with queue filtering, status badges, and relative timestamps.
Test plan
538-line test suite (tests/test_schedules.py) covering cron parsing, next-time computation, full CRUD lifecycle, tick behavior for both catchup policies, and idempotency.
AI Disclosure
This feature was developed with the use of Claude Code
Contribution Agreement
By submitting this pull request, I confirm the following:
I understand that the entity Earendil Inc. (incorporated in the state of Delaware in 2025) needs some rights from me in order to utilize my contributions in this PR. As a contributor I retain all rights, title and interest in and to my contributions, and by keeping this boilerplate intact I confirm that Earendil Inc. can use, modify, copy, and redistribute my contributions, under Earendil Inc.'s choice of terms.
Fixes #72