The Loom
Overview
Melian's job system handles scheduled and recurring tasks. From morning briefings to nightly integration tests, jobs run autonomously on cron schedules or human-readable time expressions. Each run is recorded, output is routed to the channel you configure, and the self-healing pipeline keeps everything working.
Job Interface
export interface Job {
id: string;
name: string;
prompt: string;
schedule: string;
enabled: boolean;
created_at: number;
created_by: string;
last_run_at: number | null;
last_run_status: string | null;
model: string | null;
tools: string | null;
output_routing: string; // "log" | "imessage" | "push" | "imessage+push"
max_concurrent: number;
category: string;
}
export interface JobRun {
id: string;
job_id: string;
started_at: number;
completed_at: number | null;
status: string; // "success" | "error" | "running"
result: string | null;
error: string | null;
conversation_id: string;
}Categories
Categories are seeded at startup with a fixed display order:
| Category | Purpose |
|---|---|
core |
System maintenance: integration tests, self-healing, learning review |
home |
Smart home automation: night-time lights, sensor checks |
tasks |
Task management: Remarkable sync, follow-ups |
reminders |
User-created reminders and notifications |
uncategorized |
Default for jobs created without a category |
Seeded Jobs
These jobs are created on first boot if they don't already exist. Some are always seeded; others depend on their integration being configured.
Always seeded:
| Job | Schedule | Output | Category |
|---|---|---|---|
learning-review |
daily at 2:00am |
log |
core |
integration-tests |
daily at 2:00am |
log |
core |
self-healing |
daily at 2:30am |
log |
core |
daily-morning-briefing |
daily at 7:00am |
imessage+push |
core |
night-time-lights |
daily at 9pm |
log |
home |
Conditional (seeded when integration is configured):
| Job | Condition | Schedule | Output |
|---|---|---|---|
email-summary |
email.yaml present | */30 6-22 * * * |
log |
calendar-briefing |
calendar.yaml present | daily at {morning_briefing} |
imessage+push |
remarkable-task-sync |
tasks.yaml sync |
per config | log |
task-followup |
tasks.yaml followup |
per config | imessage+push |
daily-standup |
tasks.yaml standup |
per config | imessage+push |
evening-checkin |
tasks.yaml evening_checkin |
per config | imessage+push |
Self-Healing
The self-healing pipeline runs every night at 2:30am, after integration tests complete at 2:00am.
Pipeline steps:
- Phase 0: Learn from recently merged fix PRs
- Phase 1: Collect diagnostics via DiagnosticCollector
- Phase 2: Triage each issue via agent router, classifying as
meliantier (simple, agent can fix) orclaudetier (complex, needs Claude Code CLI) - Phase 3a: Melian-tier fixes via agent router with tool use (creates PRs through tools)
- Phase 3b: Claude-tier escalation spawns Claude Code CLI subprocess (30-minute timeout per fix)
- Runner handles notification (iMessage/push) with PR links
If no fixable errors are found, the job exits cleanly with a success status and no notification.
Schedule Formats
Jobs accept standard cron expressions and a small set of human-readable shorthands:
"0 9 * * *" → every day at 9am
"*/30 * * * *" → every 30 minutes
"0 2 * * *" → daily at 2am
"0 9 * * 1-5" → weekdays at 9amThe human-readable forms resolve to cron before storage:
"every 30 minutes" → */30 * * * *
"daily at 2:30am" → 30 2 * * *
"weekdays at 9am" → 0 9 * * 1-5API
| Method | Path | Description |
|---|---|---|
| GET | /jobs |
List all jobs with category grouping |
| POST | /jobs |
Create a new job |
| GET | /jobs/:id |
Get a job plus its recent runs |
| PUT | /jobs/:id |
Update job fields |
| DELETE | /jobs/:id |
Delete a job |
| POST | /jobs/:id/run |
Trigger a job immediately |
| GET | /jobs/:id/runs |
List all runs for a job |
| GET | /jobs/:id/runs/:runId |
Get a specific run |
| GET | /jobs/categories |
List all categories |
| POST | /jobs/categories |
Create a new category |
| DELETE | /jobs/categories/:name |
Delete a category |
| POST | /jobs/preview-schedule |
Preview next occurrences of a schedule expression |
Output Routing
| Value | Behavior |
|---|---|
log |
Result stored in the job_runs table only (no external notification) |
imessage |
Result sent to your iMessage self-chat |
push |
Web push notification with a summary |
imessage+push |
Both iMessage and web push |