Scheduled Tasks
Schedule recurring agent prompts that the Signet Daemon executes automatically via Claude Code or OpenCode CLI.
Overview
Section titled “Overview”Scheduled tasks let you automate recurring agent workflows — PR reviews, code linting, status summaries, dependency checks, etc. The daemon evaluates cron expressions and spawns CLI processes on schedule.
Source code: platform/daemon/src/scheduler/
Creating Tasks
Section titled “Creating Tasks”Via Dashboard
Section titled “Via Dashboard”- Open the Signet Dashboard (http://localhost:3850)
- Navigate to the Tasks tab
- Click + New Task
- Fill in the form:
- Name: descriptive label (e.g. “Review open PRs”)
- Prompt: what the agent should do
- Harness: Claude Code or OpenCode
- Schedule: pick a preset or enter a custom cron expression
- Working Directory: optional project path for context
- Click Create Task
Via API
Section titled “Via API”curl -X POST http://localhost:3850/api/tasks \ -H "Content-Type: application/json" \ -d '{ "name": "Daily PR review", "prompt": "Review all open pull requests and summarize findings", "cronExpression": "0 9 * * *", "harness": "claude-code", "workingDirectory": "/home/user/my-project" }'Required fields: name, prompt, cronExpression, harness.
Optional fields: workingDirectory.
Supported harness values: claude-code, opencode.
Cron Expressions
Section titled “Cron Expressions”Standard 5-field cron syntax: minute hour day-of-month month day-of-week
Presets
Section titled “Presets”The dashboard offers these built-in presets (defined in
platform/daemon/src/scheduler/cron.ts):
| Preset | Expression |
|---|---|
| Every 15 min | */15 * * * * |
| Hourly | 0 * * * * |
| Daily 9am | 0 9 * * * |
| Weekly Mon 9am | 0 9 * * 1 |
Custom expressions are validated before saving using cron-parser.
The validateCron() function returns true for valid expressions
and false otherwise — invalid expressions are rejected at creation
time.
Execution Model
Section titled “Execution Model”- The daemon polls every 15 seconds for due tasks
- Maximum 3 concurrent task processes
- Each run gets a unique UUID and captures stdout/stderr
- Output is capped at 1 MB per stream (1,048,576 characters)
- Default timeout: 10 minutes per task
- Tasks that are already running are skipped (no double-execution)
- On daemon restart, any in-progress runs are marked as failed
Process Commands
Section titled “Process Commands”The spawnTask() function in platform/daemon/src/scheduler/spawn.ts
builds the CLI command based on the harness:
- Claude Code:
claude --dangerously-skip-permissions -p "<prompt>" - OpenCode:
opencode run --format json "<prompt>"
Before spawning, the function checks that the CLI binary exists on
PATH via Bun.which(). If the binary isn’t found, the run fails
immediately with a "CLI binary not found on PATH" error.
Environment Isolation
Section titled “Environment Isolation”Spawned processes inherit the daemon’s environment with two modifications:
CLAUDECODEis stripped to avoid nested-session detectionSIGNET_NO_HOOKSis set to"1"to prevent hook loops (Signet harness integrations treat the spawned agent as a sterile background process)
Timeout Behavior
Section titled “Timeout Behavior”When a task exceeds its timeout:
SIGTERMis sent to the process- After 5 additional seconds,
SIGKILLis sent if still alive - The run is recorded with
timedOut: trueand an error message
Startup Recovery
Section titled “Startup Recovery”When the daemon starts, the scheduler marks all pending and running
task runs as failed with error "daemon_restart". This prevents
orphaned runs from blocking future executions.
Task Streaming
Section titled “Task Streaming”The daemon provides real-time output streaming for running tasks via Server-Sent Events (SSE).
Endpoint
Section titled “Endpoint”GET /api/tasks/:id/streamReturns an SSE stream with the following event types:
Event Types
Section titled “Event Types”connected — Sent immediately on connection.
{ "type": "connected", "taskId": "abc-123", "timestamp": "2026-03-01T10:00:00.000Z"}run-started — A new run has begun. Also sent as a replay if a
run is already in progress when the client connects.
{ "type": "run-started", "taskId": "abc-123", "runId": "def-456", "startedAt": "2026-03-01T10:00:01.000Z", "timestamp": "2026-03-01T10:00:01.000Z"}run-output — A chunk of stdout or stderr from the running process.
{ "type": "run-output", "taskId": "abc-123", "runId": "def-456", "stream": "stdout", "chunk": "Analyzing pull requests...\n", "timestamp": "2026-03-01T10:00:05.000Z"}run-completed — The run has finished.
{ "type": "run-completed", "taskId": "abc-123", "runId": "def-456", "status": "completed", "completedAt": "2026-03-01T10:05:00.000Z", "exitCode": 0, "error": null, "timestamp": "2026-03-01T10:05:00.000Z"}Replay on Connect
Section titled “Replay on Connect”When a client connects while a task is already running, the stream
replays the current run state: a run-started event followed by
buffered stdout/stderr chunks (up to 200,000 characters per stream).
This lets late-joining clients catch up without missing output.
Buffer Management
Section titled “Buffer Management”The in-memory buffer retains the most recent 200,000 characters per stream (stdout and stderr independently). When the buffer exceeds this limit, the oldest chunks are trimmed from the front. The buffer is cleared when a run completes.
API Endpoints
Section titled “API Endpoints”| Endpoint | Method | Description |
|---|---|---|
/api/tasks |
GET | List all tasks with last run status |
/api/tasks |
POST | Create a new task |
/api/tasks/:id |
GET | Get task details + recent runs |
/api/tasks/:id |
PATCH | Update task fields (name, prompt, cron, enabled, etc.) |
/api/tasks/:id |
DELETE | Delete a task and its run history |
/api/tasks/:id/run |
POST | Trigger immediate execution |
/api/tasks/:id/runs |
GET | Paginated run history (?limit=&offset=) |
/api/tasks/:id/stream |
GET | SSE stream of real-time task output |
Managing Tasks
Section titled “Managing Tasks”Enable/Disable
Section titled “Enable/Disable”Toggle the switch on any task card in the dashboard, or via API:
curl -X PATCH http://localhost:3850/api/tasks/<id> \ -H "Content-Type: application/json" \ -d '{"enabled": false}'Manual Run
Section titled “Manual Run”Trigger a task immediately without waiting for the next scheduled time. Click “Run Now” in the task detail panel or:
curl -X POST http://localhost:3850/api/tasks/<id>/runViewing Run History
Section titled “Viewing Run History”Click any task card in the dashboard to see its run history with stdout/stderr output. Or via API:
curl http://localhost:3850/api/tasks/<id>/runs?limit=20&offset=0Error Handling and Retry
Section titled “Error Handling and Retry”Tasks do not automatically retry on failure. Each run is a one-shot execution. If a run fails:
- The run status is set to
"failed" - The
errorfield captures the reason (timeout, non-zero exit, spawn error) - The task’s
next_run_atis still advanced to the next cron tick - The task remains enabled and will execute again at the next scheduled time
A run is considered failed when:
- The process exits with a non-zero exit code
- The
errorfield on theSpawnResultis non-null (binary not found, spawn error) - The process times out
Successful runs have status: "completed" and exitCode: 0.
Security
Section titled “Security”Claude Code runs with --dangerously-skip-permissions, meaning
tasks execute without user approval gates. The dashboard displays
a warning when creating Claude Code tasks.
Only schedule tasks you trust. The daemon runs them with the same permissions as the daemon process itself.
Troubleshooting
Section titled “Troubleshooting”Task not running?
- Check that the daemon is running (
signet status) - Verify the CLI binary is on PATH (
which claudeorwhich opencode) - Check the task is enabled in the dashboard
Task failing?
- Open the task detail to view stdout/stderr from the last run
- Check for timeout issues (default 10 minutes)
- Verify the working directory exists and is accessible
Daemon restart clears running tasks?
- This is expected — in-progress runs are marked as failed on restart
- The task will be picked up again at the next scheduled time