aaif-goose / aaif-goose/goose

Scheduler unusable for unattended recipes on 1.45.0: headless runs die at first tool call, recipe-level goose_mode is silently ignored, an interrupted run-now permanently wedges the job, and spawned sessions get no schedule_id

Abierto
#11,164 10 comentarios 0 reacciones 2 asignados Reclamado por @lifeizhou-ap Ver en GitHub
Lenguaje dominante
Rust
Estrellas
54.2k
Forks
6.2k
Merge medio
3 d 2 h
PR fusionados (30 d)
262

Descripción

Version: goose 1.45.0 (Homebrew CLI and Desktop cask, both), macOS 15 (Intel)

Provenance: Bugs 2 and 4 were re-confirmed against current on-disk artifacts while writing this report. Bugs 1 and 3 are from the original live observations and were not re-run, since reproducing them means re-registering the scheduled job that these defects broke.

Summary

Setting up one recurring, unattended recipe surfaced four defects. Together they make the built-in scheduler unusable for its main purpose on this version: the run cannot complete, the documented-looking way to fix that has no effect, one interrupted invocation stops all future runs, and the sessions it did create cannot be listed back.

This machine ended up abandoning goose schedule entirely and driving the same recipe from a launchd agent instead.

Bug 1 — Headless scheduled sessions inherit the global approval mode and die at the first tool call
Reproduction

In ~/.config/goose/config.yaml, set the global mode to require approval: GOOSE_MODE: approve
Add a scheduled job for a recipe that calls at least one tool: goose schedule add --schedule-id checkin --cron "0 */6 * * *" --recipe-source /path/to/checkin.yaml
Let the cron trigger fire, or force it with goose schedule run-now --schedule-id checkin.
The session starts, reaches its first tool call, and terminates instead of completing.
Expected

A scheduled job has no terminal and no user attached, so there is nothing to approve. It should either run non-interactively by default, or schedule add should refuse up front with a clear message when the effective mode requires approval — rather than accepting the job and failing at run time.

Actual

Every scheduled run dies at its first tool call. Because the failure happens inside an unattended run, nothing surfaces it until the expected output never arrives.

Bug 2 — A recipe's settings.goose_mode is accepted by validation but has no effect
This is the natural fix for Bug 1, and it does not work.

Reproduction

Add a mode setting to the recipe itself:
settings:
goose_provider: ollama
goose_model: qwen2.5:3b
goose_mode: auto
goose recipe validate recipe.yaml → ✓ recipe file is valid (re-confirmed on 1.45.0 while writing this report).
Schedule that recipe and let it run.
The session still behaves according to the global GOOSE_MODE, not the recipe's. With GOOSE_MODE: approve globally, the run still dies exactly as in Bug 1.
Expected

Either the recipe-level setting is honoured, or validation rejects the key so the author knows it does nothing. Note that sibling keys in the same settings: block — goose_provider and goose_model — are honoured, so there is no visible signal that this one key behaves differently.

Actual

Silently ignored. It validates, it looks correct, it is inert.

Bug 3 — An interrupted run-now leaves a stale lock that hangs every future trigger
Reproduction

With a scheduled job registered, start it manually: goose schedule run-now --schedule-id checkin
Interrupt it before it finishes (Ctrl-C, or kill the process).
Inspect ~/.local/share/goose/schedule.json. The job's "currently_running" field is left true, with current_session_id and process_start_time still populated from the dead run.
All subsequent cron triggers are skipped, and further run-now invocations refuse, because the job still looks like it is running.
schedule.json holds a JSON array of job objects. Each carries the job's id, source, cron and last_run, alongside the three fields that matter here: currently_running (boolean), current_session_id and process_start_time. It is currently_running that is left set after an interrupted run.

Expected

A crashed or interrupted run should not disable the schedule permanently. process_start_time is already recorded — a liveness check against it, or clearing the flag on startup, would recover automatically.

Actual

The job is wedged until schedule.json is hand-edited to set currently_running back to false. There is no CLI command that clears it, and nothing reports why subsequent runs are being skipped.

Bug 4 — Scheduler-spawned sessions are stored with an empty schedule_id, orphaning them from goose schedule sessions
Reproduction

Register and run a scheduled job as above.
goose schedule sessions --schedule-id checkin → returns nothing.
Query the store directly:
SELECT id, created_at, schedule_id FROM sessions;
in ~/.local/share/goose/sessions/sessions.db. Sessions that the scheduler created have schedule_id = NULL.
Evidence from this machine

0 of 200 sessions in the database carry a non-empty schedule_id — not one, ever.
8 sessions titled Scheduled Check-in exist from the period when the job was active (2026-08-02 → 2026-08-04). All 8 have schedule_id = NULL.
These were unambiguously scheduler-spawned, not manual: schedule.json records last_run: 2026-08-04T14:46:33.427626Z, and session 20260804_32 was created at 2026-08-04 14:46:34 — matching to the second. Another, 20260803_11, was created at exactly 06:30:00, on the 0 */6 * * * boundary.
Expected

Sessions created by a schedule should record that schedule's ID, which is the only thing making goose schedule sessions meaningful.

Actual

The link is never written, so goose schedule sessions reports nothing for a job that has demonstrably run, and there is no supported way to find a scheduled job's output after the fact.

Combined effect
Bug 1 stops unattended runs. Bug 2 removes the fix a user would reach for first, without telling them it did nothing. Bug 3 means the first interrupted attempt at debugging leaves the schedule permanently wedged, with no message explaining why. Bug 4 means the sessions that did run cannot be listed back, so there is little to inspect afterwards.

Workaround adopted here

Abandoned goose schedule and drove the recipe from a macOS launchd agent calling goose run --recipe directly, with the mode set per-invocation in the runner script rather than in the recipe. That sidesteps all four defects, at the cost of not using the built-in feature at all.

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.