# Jobs — Scheduled Recurring Tasks

Jobs are file-based cron tasks. Each `.md` file in `active/` is a job
definition with YAML frontmatter. Each has a `.state.json` sidecar
tracking execution history.

## Anatomy

```
jobs/
├── active/
│   ├── daily-report.md          # Job definition (cron + instructions)
│   ├── daily-report.state.json  # Runtime state (last_run, result)
│   └── ...
└── archived/                    # Cancelled/completed jobs
```

### Job File (`<id>.md`)

```yaml
---
cron: "@every 2h"
notify: stdio:default
owner_session: stdio:default
cwd_rel: projects/reports
created_at: 2026-02-17T10:00:00Z
---
# The actual instruction markdown for the job session...
```

### State File (`<id>.state.json`)

```json
{
  "last_scheduled_at": "2026-02-17T12:00:00Z",
  "last_run_at": "2026-02-17T12:01:30Z",
  "last_result": "success",
  "run_count": 42
}
```

## Job Lifecycle

1. **Created** via `ManageJob` tool (action: create)
2. **Scanned** by job scheduler every 60 seconds
3. **Spawned** as a session when `isJobDue(cron, last_scheduled_at)` is true
4. **Completed/Failed** — state updated, results routed to `notify` targets
5. **Archived** via `ManageJob` tool (action: archive)

## How to Trigger a Job Immediately

The job scheduler determines "due" by comparing `cron` against
`last_scheduled_at` in the state file. To force immediate execution:

**Option A — Via cadence queue (recommended):**

Drop a `.pending` file in `~/.aladuo/var/cadence/inbox/`:

```
- [ ] (cadence:fast) trigger job:<job-id> — reset last_scheduled_at so scheduler treats it as due
```

**Option B — Direct state edit (if you understand the implications):**

Set `last_scheduled_at` to `null` in `<job-id>.state.json`. The scheduler
will see it as never-scheduled and spawn it within 60 seconds.

Note: Option A is preferred because the cadence-executor can handle
edge cases (check if job exists, verify it's not already running).

## Cron Syntax

- `"once"` — run once immediately, then auto-archive
- `"@in 5m"` — run once after 5 minutes
- `"@every 2h"` — run every 2 hours
- `"0 9 * * *"` — standard cron (9 AM daily)

## Managing Jobs

Use the `ManageJob` tool:

- `action: "list"` — see all active jobs
- `action: "read", id: "..."` — inspect a specific job + state
- `action: "create"` — create a new job
- `action: "archive"` — cancel and archive
