Skip to content

resolve some issues with the task scheduler#532

Closed
Giordano10 wants to merge 0 commit intosipeed:mainfrom
Giordano10:main
Closed

resolve some issues with the task scheduler#532
Giordano10 wants to merge 0 commit intosipeed:mainfrom
Giordano10:main

Conversation

@Giordano10
Copy link
Copy Markdown

@Giordano10 Giordano10 commented Feb 20, 2026

📝 Description

The goal of this PR is to resolve some issues with the task scheduler. When requesting picoclaw to schedule reminders at specific times, cron saves the task, but for some reason it is being triggered at different times than scheduled, and sometimes repeats the same schedule without being requested.

🗣️ Type of Change

  • 🐞 Bug fix (non-breaking change which fixes an issue)

🤖 AI Code Generation

  • 🛠️ Mostly AI-generated (AI draft, Human verified/modified)

🔗 Related Issue

<(#528) (Fixes #528 ) -->

📚 Technical Context (Skip for Docs)

  • Reference URL:
  • Reasoning:

🧪 Test Environment

  • Hardware:
  • OS:
  • Model/Provider:
  • Channels:

📸 Evidence (Optional)

Click to view Logs/Screenshots

photo_4956724308260621193_y jpg

☑️ Checklist

  • My code/docs follow the style of this project.
  • I have performed a self-review of my own changes.
  • I have updated the documentation accordingly.

Copilot AI review requested due to automatic review settings February 20, 2026 13:55
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses critical issues with the cron scheduler where tasks were triggered at incorrect times, repeated unexpectedly, and the list command exposed all users' jobs instead of filtering by chat context. The changes introduce timezone support for cron expressions and implement proper job filtering based on channel and chat ID.

Changes:

  • Added timezone parameter support for cron expressions, allowing users to specify IANA timezone names
  • Implemented job list filtering by channel and chat ID to prevent exposing other users' scheduled tasks
  • Added test coverage for both the filtering logic and timezone-aware schedule computation

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
pkg/tools/cron_tool_test.go New test file validating that job listing correctly filters by chat context
pkg/tools/cron.go Added timezone parameter to tool schema, timezone validation, and filtering logic in listJobs()
pkg/cron/service_test.go New test verifying timezone-aware next run computation for cron expressions
pkg/cron/service.go Modified computeNextRun() to respect timezone for cron expressions
README.pt-br.md Added documentation about cron scheduling via Telegram and fixed "Preferências" spelling

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pkg/tools/cron_tool_test.go Outdated
t.Fatalf("filtered list leaked other chat jobs: %s", res.ForLLM)
}
}

Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test only covers the case where context is set and filtering works correctly. Consider adding a test case for when SetContext is not called or called with empty values to verify the behavior when session context is absent. This would document whether listing all jobs without context is intentional or should return an error.

Suggested change
func TestCronToolListWithoutContextListsAllJobs(t *testing.T) {
storePath := t.TempDir() + "/jobs.json"
cs := cron.NewCronService(storePath, nil)
schedule := cron.CronSchedule{Kind: "every", EveryMS: int64Ptr(60000)}
if _, err := cs.AddJob("mine", schedule, "hello", true, "telegram", "chat-a"); err != nil {
t.Fatalf("failed to add job: %v", err)
}
if _, err := cs.AddJob("other", schedule, "hi", true, "telegram", "chat-b"); err != nil {
t.Fatalf("failed to add other job: %v", err)
}
cfg := &config.Config{}
msgBus := bus.NewMessageBus()
tool := NewCronTool(cs, mockJobExecutor{}, msgBus, t.TempDir(), false, 0, cfg)
// Intentionally do not set context to verify behavior without session context.
res := tool.Execute(context.Background(), map[string]interface{}{
"action": "list",
})
if res.IsError {
t.Fatalf("unexpected error when listing without context: %s", res.ForLLM)
}
if !strings.Contains(res.ForLLM, "mine") {
t.Fatalf("unfiltered list missing job 'mine': %s", res.ForLLM)
}
if !strings.Contains(res.ForLLM, "other") {
t.Fatalf("unfiltered list missing job 'other': %s", res.ForLLM)
}
}

Copilot uses AI. Check for mistakes.
Comment thread README.pt-br.md Outdated
> 1. Abra a conversa com o bot já configurado.
> 2. Envie pedidos naturais, ex.: `Me lembre em 10 minutos de reiniciar o Raspberry` ou `Todo dia às 9h, me lembre de verificar os backups`.
> 3. O bot cria o job com o seu chat automaticamente; confirme enviando `Liste os agendamentos` (ou via CLI: `picoclaw cron list`).
> 4. O horário segue o fuso configurado no sistema onde o gateway roda (ex.: timezone do seu Raspberry).
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation states that scheduling follows the timezone configured on the system where the gateway runs. However, with the new timezone parameter added in this PR, users can now specify a timezone for cron expressions. Consider updating this documentation to mention that users can optionally provide a timezone parameter for cron-based schedules, while at_seconds-based reminders will still use the server's timezone. This would help users understand when and how to use the new timezone feature.

Suggested change
> 4. O horário segue o fuso configurado no sistema onde o gateway roda (ex.: timezone do seu Raspberry).
> 4. Por padrão, o horário segue o fuso configurado no sistema onde o gateway roda (ex.: timezone do seu Raspberry), mas para agendamentos baseados em expressões `cron` você pode informar um parâmetro `timezone`; lembretes do tipo `at_seconds` continuam usando o fuso do servidor.

Copilot uses AI. Check for mistakes.
Comment thread pkg/tools/cron.go Outdated
tz = strings.TrimSpace(tz)
if _, err := time.LoadLocation(tz); err != nil {
return ErrorResult(fmt.Sprintf("invalid timezone: %v", err))
}
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The timezone parameter is accepted but only respected for "cron" schedule kind (lines 261-286 in service.go). When using "at_seconds" or "every_seconds", the timezone is stored in the schedule but never used. This means that one-time reminders created with "at_seconds" (e.g., "remind me in 10 minutes") will always use the server's local time, not the user's timezone. Consider either:

  1. Documenting that timezone only applies to cron expressions, or
  2. Handling timezone for "at" schedules by computing the offset and adjusting the atMS value accordingly
Suggested change
}
}
// Timezone is only meaningful for cron expressions; for "at" and "every" schedules,
// the delay is relative and independent of timezone, so we reject timezone usage there
if schedule.Kind != "cron" {
return ErrorResult("timezone parameter is only supported for cron_expr schedules")
}

Copilot uses AI. Check for mistakes.
Comment thread pkg/cron/service_test.go Outdated
t.Fatalf("next run = %s, want %s", got, want)
}
}

Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test validates timezone behavior for cron expressions but doesn't cover timezone handling for "at_seconds" or "every_seconds" schedule types. Since the timezone parameter can be provided for any schedule type but is only used for cron expressions, add test cases to verify that timezone is correctly ignored (or document this limitation) for "at" and "every" schedules. This would help prevent confusion about the expected behavior when users specify timezones with at_seconds parameters.

Suggested change
func TestComputeNextRunIgnoresTimezoneForEvery(t *testing.T) {
storePath := filepath.Join(t.TempDir(), "cron", "jobs.json")
cs := NewCronService(storePath, nil)
// For non-cron schedules (e.g., "every", "at"/"*_seconds"), the TZ field
// is ignored and next-run computation is based solely on the schedule.
now := time.Date(2024, 1, 1, 10, 0, 0, 0, time.UTC)
everyMs := int64(60_000) // every 60 seconds
withTZ := CronSchedule{
Kind: "every",
EveryMS: &everyMs,
TZ: "America/New_York",
}
withoutTZ := CronSchedule{
Kind: "every",
EveryMS: &everyMs,
}
nextWithTZ := cs.computeNextRun(&withTZ, now.UnixMilli())
if nextWithTZ == nil {
t.Fatalf("expected next run time for schedule with TZ, got nil")
}
nextWithoutTZ := cs.computeNextRun(&withoutTZ, now.UnixMilli())
if nextWithoutTZ == nil {
t.Fatalf("expected next run time for schedule without TZ, got nil")
}
if *nextWithTZ != *nextWithoutTZ {
t.Fatalf("expected timezone to be ignored for 'every' schedules; got nextWithTZ=%d, nextWithoutTZ=%d", *nextWithTZ, *nextWithoutTZ)
}
}

Copilot uses AI. Check for mistakes.
Comment thread pkg/tools/cron.go Outdated
Comment on lines +225 to +240
jobs := t.cronService.ListJobs(false)

t.mu.RLock()
channel := t.channel
chatID := t.chatID
t.mu.RUnlock()

if channel != "" && chatID != "" {
filtered := make([]cron.CronJob, 0, len(jobs))
for _, j := range jobs {
if j.Payload.Channel == channel && j.Payload.To == chatID {
filtered = append(filtered, j)
}
}
jobs = filtered
}
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When listing jobs without a session context (channel and chatID are empty), the filtering is skipped and all jobs are returned. This could expose jobs from other users/chats when the list command is executed from a CLI or another context where session info isn't set. Consider whether this is the intended behavior, or if the list command should require a session context (similar to how add requires it on line 139-141). If returning all jobs in CLI context is intentional, this is acceptable but should be documented.

Suggested change
jobs := t.cronService.ListJobs(false)
t.mu.RLock()
channel := t.channel
chatID := t.chatID
t.mu.RUnlock()
if channel != "" && chatID != "" {
filtered := make([]cron.CronJob, 0, len(jobs))
for _, j := range jobs {
if j.Payload.Channel == channel && j.Payload.To == chatID {
filtered = append(filtered, j)
}
}
jobs = filtered
}
t.mu.RLock()
channel := t.channel
chatID := t.chatID
t.mu.RUnlock()
// Require a session context when listing jobs to avoid exposing jobs
// from other users or chats when called from contexts without session info.
if channel == "" || chatID == "" {
return SilentResult("Listing scheduled jobs requires an active chat session")
}
jobs := t.cronService.ListJobs(false)
filtered := make([]cron.CronJob, 0, len(jobs))
for _, j := range jobs {
if j.Payload.Channel == channel && j.Payload.To == chatID {
filtered = append(filtered, j)
}
}
jobs = filtered

Copilot uses AI. Check for mistakes.
@xiaket
Copy link
Copy Markdown
Collaborator

xiaket commented Feb 22, 2026

Hi there,

Can you please write the PR description in English so the community is at a better place to understand your contribution?

Also would love to have the doc update translated to other languages as well, even if the translation is done via AI.

@Giordano10
Copy link
Copy Markdown
Author

Oh, excuse me, I will translate the description of this PR for the community.

📝 Description
The goal of this PR is to resolve some issues with the task scheduler. When requesting picoclaw to schedule reminders at specific times, cron saves the task, but for some reason it is being triggered at different times than scheduled, and sometimes repeats the same schedule without being requested.

🗣️ Type of Change
🐞 Bug fix (non-breaking change which fixes an issue)
🤖 AI Code Generation
🛠️ Mostly AI-generated (AI draft, Human verified/modified)
🔗 Related Issue
<(#528) (Fixes #528 ) -->

📚 Technical Context (Skip for Docs)
Reference URL:
Reasoning:

🧪 Test Environment
Hardware:
OS:
Model/Provider:
Channels:
📸 Evidence (Optional)
Click to view Logs/Screenshots
☑️ Checklist

My code/docs follow the style of this project.

I have performed a self-review of my own changes.

I have updated the documentation accordingly.

Regarding the README file documentation for other languages, I can do this in English and French, but I will have difficulties doing it for Eastern languages ​​because I don't have the knowledge base to check if the translation is correct with what I described. I hope you understand.

@xiaket xiaket changed the title Resolver problemas com o agendador de tarefas cron resolve some issues with the task scheduler Feb 22, 2026
@xiaket
Copy link
Copy Markdown
Collaborator

xiaket commented Feb 22, 2026

Thanks @Giordano10 , do you mind also updating the message in the issue as well?

@Giordano10
Copy link
Copy Markdown
Author

updated @xiaket thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Problemas com agendamento pelo chat usando cron

3 participants