go-scheduler-next-fire-time
Done. The solution is written to ~/GAP-073-<project>-fix.md and every claim was verified empirically against robfig/cron v3.0.1.
Root cause: cron.Entry.Next is only populated inside (*Cron).run() — the goroutine launched by Start() (cron.go:245). <project>'s ListJobs read entry.Next directly, so for a scheduler whose run loop had never started, every ready entry had the zero time → time.Time{}.Format(RFC3339) → 0001-01-01T00:00:00Z. The CLI had no IsZero guard.
Fix (in the doc):
1. Scheduler keeps an ID → spec registry (needed since cron.Entry doesn't expose the expression). ListJobs computes NextFire(spec, now) via cron.ParseStandard(...).Next(now) whenever entry.Next.IsZero().
2. main renders any still-zero Next as - instead of formatting the zero time.
Verification highlights (all real runs):
- Reproduced exactly: never-started entry.Next = "0001-01-01T00:00:00Z" (IsZero=true)
- Fix computes correct future times for */5 * * * *, 30 9 * * MON-FRI, @hourly, @every 2h30m, @daily
- Unparsable specs → zero time → - path works
- The doc's embedded regression test was extracted and re-run cleanly end-to-end
Important caveat caught during verification: cron.ParseStandard is strictly 5-field. If <project> creates its cron with cron.WithSeconds() (6-field specs), ParseStandard errors with expected exactly 5 fields, found 6 — the doc includes the matching-parser variant and the simpler entry.Schedule.Next(now) alternative that sidesteps parsing entirely.
# Evidence - Problem class: go-scheduler-next-fire-time - Model: openrouter/deepseek/deepseek-v4-flash-0731 - Solved: 2026-08-22T13:26:12.883Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "GAP-073 <project>: dagger schedule list NEXT showed 0001-01-01T00:00:00Z for ready schedules (cron entry.Next zero on never-started scheduler). Fix: ListJobs computes next fire from cron expr via cron.ParseStandard + Schedule().Next(now) when entry.Next is zero; main prints dash defensively.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4-flash-0731", "problem_class": "go-scheduler-next-fire-time", "provider": "openrouter", "solved_at": "2026-08-22T13:26:12.883Z", "version": ""}Done. The solution is written to ~/GAP-073-<project>-fix.md and every claim was verified empirically against robfig/cron v3.0.1.
Root cause: cron.Entry.Next is only populated inside (*Cron).run() — the goroutine launched by Start() (cron.go:245). <project>'s ListJobs read entry.Next directly, so for a scheduler whose run loop had never started, every ready entry had the zero time → time.Time{}.Format(RFC3339) → 0001-01-01T00:00:00Z. The CLI had no IsZero guard.
Fix (in the doc):
1. Scheduler keeps an ID → spec registry (needed since cron.Entry doesn't expose the expression). ListJobs computes NextFire(spec, now) via cron.ParseStandard(...).Next(now) whenever entry.Next.IsZero().
2. main renders any still-zero Next as - instead of formatting the zero time.
Verification highlights (all real runs):
- Reproduced exactly: never-started entry.Next = "0001-01-01T00:00:00Z" (IsZero=true)
- Fix computes correct future times for */5 * * * *, 30 9 * * MON-FRI, @hourly, @every 2h30m, @daily
- Unparsable specs → zero time → - path works
- The doc's embedded regression test was extracted and re-run cleanly end-to-end
Important caveat caught during verification: cron.ParseStandard is strictly 5-field. If <project> creates its cron with cron.WithSeconds() (6-field specs), ParseStandard errors with expected exactly 5 fields, found 6 — the doc includes the matching-parser variant and the simpler entry.Schedule.Next(now) alternative that sidesteps parsing entirely.
# Evidence - Problem class: go-scheduler-next-fire-time - Model: openrouter/deepseek/deepseek-v4-flash-0731 - Solved: 2026-08-22T13:26:12.883Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "GAP-073 <project>: dagger schedule list NEXT showed 0001-01-01T00:00:00Z for ready schedules (cron entry.Next zero on never-started scheduler). Fix: ListJobs computes next fire from cron expr via cron.ParseStandard + Schedule().Next(now) when entry.Next is zero; main prints dash defensively.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4-flash-0731", "problem_class": "go-scheduler-next-fire-time", "provider": "openrouter", "solved_at": "2026-08-22T13:26:12.883Z", "version": ""}