avelino / avelino/dotagent

Parse cron expressions in `Schedule::Expression` so the adaptive scheduler can use them

Open
#5 0 comments 0 reactions 0 assignees View on GitHub
area: scheduler type: feature
Dominant language
Rust
Stars
3
Forks
1
Avg merge
4h 50m
Merged PRs (30d)
1

Description

`agent.toml` supports three schedule types (see [`docs/reference/agent-spec.md`](docs/reference/agent-spec.md#schedule-types)):

| Type | Example |
|--------------|----------------------------------------|
| `interval` | every N minutes |
| `cron` | `weekday + hour:minute` (structured) |
| `expression` | free-form cron string (e.g. `*/15 * * * *`) |

The `expression` variant is documented as "Linux-only, via systemd OnCalendar" — the OS handles it for installed units. But the **adaptive scheduler** inside the daemon does not know how to compute the next fire time for `Schedule::Expression`, so it returns `None`. Two consequences:

- `dotagent tick` and the daemon loop silently skip these schedules when computing `next_event`.
- Cross-platform parity is broken: macOS users can't use `expression` at all (no systemd to delegate to).

This is flagged with `TODO: parse cron expression` in `crates/dotagent-scheduler/src/lib.rs` (two call sites).

## Proposal

Teach the scheduler to compute `next_after(expr, now)` and `prev_before(expr, now)` for the `expression` variant, using a standard 5-field cron string (minute hour day-of-month month day-of-week).

Once that lands:

- `expression` works on macOS (daemon computes the next fire time itself instead of relying on systemd).
- `dotagent next` (sibling issue) can show upcoming fires for `expression` schedules.
- `dotagent status` / `dotagent why` give correct answers for `expression` schedules.

## Acceptance criteria

- [ ] `Schedule::Expression { cron: "*/15 * * * *" }` resolves to the next quarter-hour from any `now`.
- [ ] Edge cases covered with unit tests: month boundary, year boundary, DST transitions, day-of-month vs day-of-week with `OR` semantics (POSIX).
- [ ] Invalid expressions are rejected at `validate()` time with a clear error pointing at the bad field.
- [ ] No new IO in `dotagent-scheduler` — the pure-function invariant from [`CLAUDE.md`](CLAUDE.md) is preserved.
- [ ] `agent-spec.md` no longer says "Linux only" for `expression`.

## Where to start

- `crates/dotagent-scheduler/src/lib.rs` — the two `TODO` sites.
- `crates/dotagent-core/src/manifest.rs` — the `Schedule` enum and its `Expression` variant.
- Existing cron-string crates in the Rust ecosystem are fine to use (workspace dep); evaluate maintenance status and license before picking.

## Non-goals

- Quartz extensions (seconds, years, `L`, `W`, `#`). POSIX 5-field is enough for v1.
- Changing the manifest schema.

Contributor guide

Open the contributing guide

Research direction

Start with the two TODO sites in crates/dotagent-scheduler/src/lib.rs and the Schedule::Expression definition in crates/dotagent-core/src/manifest.rs. Read CLAUDE.md for the pure-function constraint, review the existing schedule tests, and evaluate workspace cron crates for maintenance and licensing. Done means valid POSIX five-field expressions resolve next and previous times across the listed edge cases, invalid fields fail validation clearly, and agent-spec.md no longer says Linux-only.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
backend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.