Task 06 opened the whole implementer log (~360 kB) after reading supporting modules; OpenCode returned "Context size has been exceeded" and made no commit. AGENTS.md and tasks 06-10 now say to read the first 12 lines only. Co-Authored-By: Grok 4.6 <noreply@x.ai>
5.4 KiB
M5d-2 task 07: cron tick
Branch: m5d2. Check it: git branch --show-current prints m5d2 and git status --short
is empty; otherwise stop. Never switch branch.
Commit subject: loopd: cron tick, new session per run
Log row: the Task cell is M5d2/07-loopd-scheduler-cron.
Goal
After the heartbeat work, tick loads {home}/jobs/*.toml and runs each job whose schedule
matches now (spec 4.2). A new session job-<name>-<yyyymmdd>t<hhmm> on the background slot.
Skip the tick when a previous run of that name is still live. Leftover started from a dead
process becomes failed on the first tick (already started in task 06: only when that session
is not busy). Speaker None. Same time-line prefix as heartbeat.
Every place that changes
| Place | What happens there |
|---|---|
crates/loopd/src/scheduler.rs |
after heartbeat, iterate jobs (edit T1) |
crates/loopd/tests/scheduler_cron.rs |
new, given |
| everything else | no change: not TickWork, not hold, not heartbeat |
Files
- Copy, from
docs/plans/M5d2/files/(the same path under it): the new testcrates/loopd/tests/scheduler_cron.rs. - Modify:
crates/loopd/src/scheduler.rs,docs/implementer-log.md. - Do not touch: any other file.
Read only what the step names. Step 1 is the cp. At the log step, read only the first 12
lines of docs/implementer-log.md. Do not load the rest of that file.
Facts you need
- Load.
jobs::load(&ctx.cfg.paths.home.join("jobs")). OnErr, logloopd: {e}and skip cron this tick (the process stays up: README known limits). OnOk, for eachJobin order:cron::due(&job.schedule, now).Err→ log and skip that job.Ok(false)→ skip.- Ledger path
{home}/jobs/runs.jsonl. Iflive_sessionisSome(_), pushSkippedJob { name }and continue. (Task 06 has already closed leftovers that were not busy.) session_id(&job.name, now).Err→ log and skip.ctx.hold(session.clone()).Err→SkippedJob.Ok(held)→ create the session (Session::createwithassembleandslots.background; ifExists,openinstead),jobs::appendStarted, runrun_turnthenfinish_turnwith user textformat!("It is {}.\n\n{}", now.to_rfc3339(), job.prompt), speakerNone, thenappendFinishedorFailed(turnErr→Failed).drop(held). PushTickWork::Job { name, session }.
- Two due minutes. Two ticks at 06:59 and 07:00 with
* * * * *make two session ids. - Not due.
0 7 * * *at 06:59 pushes nothing. scheduler.rsstays ≤ 500 lines. After this task it is about 200.- The items the edits use. This table is complete.
Item What it does jobs::load/session_id/append/live_sessiontask 05 cron::duetask 04 Context::holdtask 06 run_turn/finish_turnsame as heartbeat
The edits, exactly
There is no quoted "before" for a unique heartbeat-only block: you add a loop after the
heartbeat section of tick, before work is returned. If tick currently ends with
work after heartbeat, insert the job loop there. If you cannot find a single place to
insert without touching heartbeat, stop.
Do not change heartbeat behaviour. Do not add a sleep.
Steps
-
1. Copy the given test.
cp docs/plans/M5d2/files/crates/loopd/tests/scheduler_cron.rs crates/loopd/tests/ -
2. See it fail.
cargo test -p loopd --test scheduler_cron, timeout 600000 ms: the tests compile (TickWork::Jobalready exists) anda_due_job_starts_a_new_session_on_the_background_slotfails:workis empty, not aJob.a_job_not_due_does_not_runmay pass. -
3. The edit. Add the job loop.
cargo check -p loopdprints no error and no warning.wc -l crates/loopd/src/scheduler.rsprints a number ≤ 500. -
4. The tests.
cargo test -p loopd --test scheduler_cron --test scheduler_heartbeat --test jobs --test cron: every test passes, 6 inscheduler_cron. -
5. Check.
git diff --statlists exactlycrates/loopd/src/scheduler.rs. -
6. Run the gate.
cargo fmt --all, thenmake gate, timeout 600000 ms. Last linegate: ok. Test count 1557 (1551 before, plus 6). Aftergate: ok, do not add uptest result:lines. -
7. Log and commit. Task cell
M5d2/07-loopd-scheduler-cron. Read only the first 12 lines ofdocs/implementer-log.md; insert the row under the header. Stage withgit add crates/loopd docs/implementer-log.md Cargo.lock, then commit asAGENTS.mdsays.
Done when
make gateprintsgate: okwith 1557 tests.git diff HEAD~1 --statlists onlycrates/loopd/src/scheduler.rs,crates/loopd/tests/scheduler_cron.rsand the log.
Stop and report if
- The branch check fails; step 2 differs (tests do not compile, or a different assertion
fails);
cargo checkwarns; a test fails; the gate's last line is notgate: ok; you reuse a session across runs; you sendslots.main; you set a speaker; you skip leftovers that are not busy; a heartbeat test fails.
What the given tests pin
A due job creates job-mail-… on slot 1 with the time line and the prompt; a job not due
does nothing; a live previous run (Started + hold) is SkippedJob with no request and the
ledger still says started (not failed); leftover started is failed on the first tick
(the due job may then run); speaker is None; two due minutes make two sessions.