Skip to content

feat(scheduled-tasks): calendar views + persisted, runnable tasks - #4979

Merged
waleedlatif1 merged 14 commits into
stagingfrom
feat/calendar-tasks
Jun 13, 2026
Merged

feat(scheduled-tasks): calendar views + persisted, runnable tasks#4979
waleedlatif1 merged 14 commits into
stagingfrom
feat/calendar-tasks

Conversation

@emir-karabeg

@emir-karabegemir-karabeg commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

A scheduled-tasks calendar (month / week / day views) where you create, edit, and delete tasks that persist and actually run as Sim agent invocations.

Calendar UI

  • Month / week / day views with a Today control, scope dropdown, chevron navigation, and a current-time indicator on today's column. Week view is the default; Today scroll-centers the current time.
  • Click a day/hour slot (or the header action) to open the task modal: a prompt editor (@-mention resources, /-invoke skills like talking to Sim), a recurrence control, and chip date/time pickers.

Wired to the backend (binds to the existing workflow_schedulesourceType='job' rows — no new table)

  • Tasks persist via /api/schedules and fire the prompt as a real Sim agent run (/api/mothership/execute), with the captured @-mention contexts resolved into the run.
  • One-time and recurring (daily / weekly / weekday / monthly), with calendar-style end options: never, on a date, or after N runs.
  • Recurrences expand into per-occurrence calendar events client-side, bounded to the visible range.
  • Deleting a recurring task asks this occurrence vs the whole series (EXDATE-style per-occurrence exclusion).

Schema (migration 0235) — adds contexts, excludedDates, and endsAt to workflow_schedule.

Type of Change

  • New feature

Testing

  • bun run type-check, bun run check:api-validation, bun run check:react-query all pass
  • Vitest: new recurrence.test.ts (cron mapping, exclusions, end boundary, range bounds) plus existing schedules route/orchestration/execute suites — 194 tests green

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercelBot commented Jun 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
docsSkippedSkippedJun 13, 2026 10:59pm

Request Review

@emir-karabeg

Copy link
Copy Markdown
CollaboratorAuthor

@greptile run

@cursor

cursorBot commented Jun 11, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Schedule create/update/execute and context resolution affect when and how agent runs fire; branding and Resource table behavior changes are broad but mostly copy and presentation.

Overview
Scheduled tasks backend — Create/update schedule APIs now accept time, endsAt, and contexts so persisted jobs carry the same prompt and @-mentions as the calendar modal. A new exclude_occurrence action on PATCH /api/schedules/[id] drops a single recurrence instance (EXDATE-style) via performExcludeOccurrence.

Runnable prompts/api/mothership/execute accepts contexts and resolves them with processContextsServer before the headless agent run, matching interactive chat behavior for scheduled invocations.

Shared prompt editing — Chat input logic moves into usePromptEditor + PromptEditor (mentions, skills, chip clipboard, caret menus). UserInput becomes a thin shell (attachments, STT, drafts, send); the scheduled task modal reuses the same editor. The + resource menu is a toolbar button; PlusMenuDropdown only opens from explicit anchors.

Product language — Constitution, README, landing, SEO/FAQ, and in-app labels shift from Mothership/Copilot to Chat (module) and Sim (the agent). User-facing upload/execute errors say “Chat” where appropriate; internal mothership upload types remain.

UI components & workspace chrome — EMCN gains ChipCopyInput, ChipTimePicker, modal copy fields, and ChipConfirmModaltext slots; credential detail drops CopyableValueField. Resource.Table no longer owns sort/loading/empty states (callers sort, e.g. Files); headers fix title truncation and hydration flash on the location veil. Files views wrap in Resource and drop a redundant layout wrapper.

Reviewed by Cursor Bugbot for commit d38a99d. Configure here.

@greptile-apps

greptile-appsBot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a full calendar UI (month/week/day views) for scheduling persistent Sim agent tasks, backed by the existing workflow_schedule table extended with three new columns (contexts, excluded_dates, ends_at). The feature introduces create/edit/delete task flows through a prompt editor with @-mention support, recurrence control, and an EXDATE-style per-occurrence exclusion mechanism.

  • Backend: computeNextRunAt replaces raw validateCronExpression next-run logic throughout schedule execution and orchestration, correctly skipping individually-excluded occurrences and honoring endsAt boundaries. A new performExcludeOccurrence orchestration function handles single-occurrence deletion. The failure path for one-time tasks is fixed to mark them completed (instead of the previous zombie active state with a null nextRunAt).
  • Frontend: Calendar grid, recurrence expansion (expandOccurrences), and the task modal are new purpose-built utilities. Context-mention round-tripping through edit was addressed in a follow-up commit (4328701) by carrying contexts in the edit seed and re-registering via editor.setContexts on modal mount. The today highlight uses a minute-interval poll (per previous review feedback), and scroll behavior follows the learned smooth-only-for-Today rule.

Confidence Score: 5/5

The change is safe to merge — the new columns are additive nullables, all schema-breaking paths are guarded, and the known regressions called out in prior rounds have been addressed.

The backend execution paths (computeNextRunAt, performExcludeOccurrence, failure-path status transitions) are correct and well-tested (194 passing tests). The previously reported issues — stale today, smooth-scroll mis-application, and edit stripping @-mention contexts — are all addressed in the follow-up commits. No correctness issues were found in the latest revision.

No files require special attention. The migration is purely additive, and the core execution logic changes in schedule-execution.ts and orchestration.ts are well-guarded.

Important Files Changed

FilenameOverview
apps/sim/lib/workflows/schedules/utils.tsAdds computeNextRunAt — correctly skips excluded dates (Set-based millisecond matching) and stops at endsAt; the 1000-occurrence skip bound prevents infinite loops on pathological exclusion lists.
apps/sim/lib/workflows/schedules/orchestration.tsAdds performExcludeOccurrence and updates create/update flows with endsAt/contexts; the conditional nextRunAt advance in performExcludeOccurrence (only when the excluded instant equals the current nextRunAt) is correct and handles the race with the executor acceptably.
apps/sim/background/schedule-execution.tsMigrates next-run computation to computeNextRunAt in both success and failure paths; the isComplete flag now correctly marks one-time task failures as completed instead of leaving them as active (previous zombie state). nextRunAt is kept for disabled schedules to support reactivation.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/hooks/use-scheduled-tasks.tsBridges calendar UI to React Query mutations; editSeedFor now carries contexts (fixed in 4328701) so edit round-trips @-mentions correctly. Mutation objects are appropriately excluded from useCallback deps per project rules.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/utils/recurrence.tsexpandOccurrences correctly materialises future cron occurrences within the view range, starting 1ms before the lower bound to include on-boundary hits; the 500-occurrence cap prevents client-side perf issues on long ranges.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/task-modal.tsxShared create/edit modal; useEffect on mount re-registers edit contexts once per open (correct, since Radix unmounts content on close). isPastLaunch guard prevents scheduling one-time tasks in the past.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/hooks/use-calendar.tstoday is now state refreshed by a minute-interval poll guarded by isSameDay (addresses prior stale-date feedback); goToday sets anchor to new Date() which now agrees with the refreshed today highlight.
apps/sim/lib/api/contracts/schedules.tsWell-typed contract additions: scheduleContextSchema passthrough for open context shapes, excludeOccurrenceBodySchema with the required occurrence timestamp, and new optional endsAt/contexts on create/update schemas.
packages/db/migrations/0235_schedule_task_contexts_recurrence.sqlAdditive migration: three nullable columns (contexts jsonb, excluded_dates jsonb, ends_at timestamp) added to workflow_schedule. No destructive changes.

Sequence Diagram

sequenceDiagram
participant User as User (Browser)
participant TaskModal as TaskModal
participant Hook as useScheduledTasks
participant API as /api/schedules
participant Orch as Orchestration
participant Exec as schedule-execution
participant DB as workflow_schedule
User->>TaskModal: "Fill prompt + @mentions + recurrence"
TaskModal->>Hook: createTask(draft)
Hook->>API: POST /api/schedules (cronExpression/time, contexts, endsAt)
API->>Orch: performCreateJob(...)
Orch->>Orch: computeNextRunAt(cron, endsAt)
Orch->>DB: INSERT (contexts, excludedDates, endsAt, nextRunAt)
DB-->>API: schedule row
API-->>Hook: id, nextRunAt, status
Hook->>Hook: invalidate scheduleKeys.list(workspaceId)
Note over Exec,DB: At nextRunAt...
Exec->>DB: "SELECT job WHERE nextRunAt <= now AND status=active"
Exec->>API: POST /api/mothership/execute (contexts resolved server-side)
API-->>Exec: streamed response
Exec->>Exec: "computeNextRunAt(cron, from=now, excludedDates, endsAt)"
Exec->>DB: UPDATE nextRunAt / status
Note over User,DB: User deletes one occurrence
User->>Hook: deleteOccurrence(scheduleId, runAt)
Hook->>API: "PUT /api/schedules/:id action=exclude_occurrence"
API->>Orch: performExcludeOccurrence(occurrence)
Orch->>DB: "UPDATE excludedDates += occurrence"
Orch->>Orch: "if occurrence==nextRunAt then computeNextRunAt(from=occurrence, excludedDates)"
Orch->>DB: UPDATE nextRunAt (advance past excluded instant)
Loading

Reviews (6): Last reviewed commit: "simplify(scheduled-tasks): reuse date-fn..." | Re-trigger Greptile

Comment threadapps/sim/app/workspace/[workspaceId]/scheduled-tasks/hooks/use-calendar.ts Outdated
@greptile-apps

greptile-appsBot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces the scheduled-tasks table with a three-scope calendar (month/week/day), adds a ChipModal-based create-task form seeded from the clicked slot, simplifies the Resource shell (drops internal sort and empty-state handling from Resource.Table), fixes DropdownMenu scroll inside Radix dialogs via a new InsideModalContext, and renames "Mothership" to "Sim" across landing copy and API surfaces.

  • Calendar views: pure calendar-grid.ts/schedule-events.ts utilities with 12 unit tests power month/week/day grids; view state lives in useCalendar; a Google Calendar-style current-time indicator ticks every minute; event injection slots (eventsByDay/eventsByHour) are wired but intentionally empty until persistence ships.
  • Modal dropdown fix: DropdownMenu now reads InsideModalContext (set by ModalContent) and forces modal={true} inside dialogs, resolving the react-remove-scroll wheel-scroll incompatibility without affecting page-level menus.
  • Resource shell: Resource.Table drops emptyMessage, isLoading, defaultSort, and sortValues; the root gains position: relative as the overlay positioning context; consumers updated accordingly.

Confidence Score: 4/5

Safe to merge for the calendar UI and modal-dropdown fix; the today reference freezes at mount which breaks the today-highlight after midnight, and search/filter empty-state messages were removed without replacement.

The today value in useCalendar is computed once via useMemo(() => new Date(), []) and never refreshed. After midnight, goToday correctly advances anchor but today stays the old day, so buildCalendarGrid marks yesterday as isToday while the actual current day renders with no highlight. Separately, emptyMessage was removed from Resource.Table and consumers dropped the variable without adding replacement rendering, so users searching or filtering to zero results see a silent empty table.

apps/sim/app/workspace/[workspaceId]/scheduled-tasks/hooks/use-calendar.ts (stale today), apps/sim/app/workspace/[workspaceId]/files/files.tsx and apps/sim/app/workspace/[workspaceId]/knowledge/[id]/base.tsx (missing empty-state feedback)

Important Files Changed

FilenameOverview
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/hooks/use-calendar.tsNew hook for calendar view state; contains a stale today reference (frozen at mount via empty-dep useMemo) that will show the wrong day highlighted as "today" after midnight.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/utils/calendar-grid.tsPure calendar grid derivation utilities (build/advance/format); well-structured with no side effects — fully deterministic and unit-tested.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/utils/schedule-events.tsEvent adaptation and bucketing utilities; correct null/NaN guards on nextRunAt, consistent local-time bucketing aligned with the time grid display.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/scheduled-tasks.tsxCalendar page container; correctly wires useCalendar state to ScheduleCalendar and CreateTaskModal, slotKey re-seeds the modal on every new slot click.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/schedule-calendar/schedule-calendar.tsxCalendar body managing scroll and view dispatch; scroll-to-now logic may get a stale headerHeight on the first scope switch before the time-grid DOM is fully laid out.
apps/sim/components/emcn/components/dropdown-menu/dropdown-menu.tsxDropdownMenu now reads InsideModalContext and forces modal={true} inside Radix dialogs, fixing the wheel-scroll issue; page-level menus keep their consumer-set modality.
apps/sim/components/emcn/components/modal/modal-context.tsNew InsideModalContext and useInsideModal hook; minimal, correctly implemented with a false default so non-modal trees are unaffected.
apps/sim/app/workspace/[workspaceId]/components/resource/resource.tsxResource shell simplified: drops internal sort, emptyMessage, and isLoading from ResourceTable, making the root the overlay positioning context. Consumers that relied on emptyMessage for search/filter feedback now show silent empty tables.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/create-task-modal/create-task-modal.tsxNew create-task modal using ChipModal; correctly stubs persistence (logs only), seeds date/time from the clicked slot, and disables Submit until prompt is non-empty.
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/schedule-calendar/components/time-grid/time-grid.tsxTime grid for week/day scopes; CurrentTimeIndicator correctly defers to client-side only (avoiding hydration mismatch) and ticks every minute independently of the stale today reference.

Sequence Diagram

sequenceDiagram
participant User
participant ScheduledTasks
participant useCalendar
participant ScheduleCalendar
participant CreateTaskModal
User->>ScheduledTasks: mount
ScheduledTasks->>useCalendar: "init (scope=week, anchor=now, today=now frozen)"
useCalendar-->>ScheduledTasks: state
ScheduledTasks->>ScheduleCalendar: render(scope, anchor, today)
User->>ScheduleCalendar: click time slot
ScheduleCalendar->>ScheduledTasks: onSelectSlot(date, time)
ScheduledTasks->>useCalendar: selectSlot(date, time)
useCalendar-->>ScheduledTasks: "selectedSlot set, isCreateOpen=true"
ScheduledTasks->>CreateTaskModal: "open=true, slot"
User->>CreateTaskModal: fill prompt, click Schedule
CreateTaskModal->>CreateTaskModal: logger.info stub, no persistence
CreateTaskModal-->>ScheduledTasks: onOpenChange(false)
ScheduledTasks->>useCalendar: closeCreate()
User->>ScheduleCalendar: click Today
ScheduleCalendar->>ScheduledTasks: onToday()
ScheduledTasks->>useCalendar: "goToday sets anchor=new Date()"
ScheduleCalendar->>ScheduleCalendar: scrollSignal++ scrollTo(timeToOffset(now))
Loading

Comments Outside Diff (1)

  1. apps/sim/app/workspace/[workspaceId]/files/files.tsx, line 1890-1904 (link)

    P2Empty-state feedback lost after search/filter

    emptyMessage was removed from ResourceTable without a replacement in consumer code. Before this PR, searching for a file that doesn't exist (e.g. a typo in the search box) or applying a filter that matches nothing would display a centred message ("No files match…" / "Nothing matches your filter"). Now rows = [] produces a silent empty table body — users get no indication of why the list is blank. The same regression appears in the Knowledge Base (knowledge/[id]/base.tsx).

Reviews (2): Last reviewed commit: "fix(emcn): force dropdown menus modal in..." | Re-trigger Greptile

Comment threadapps/sim/app/workspace/[workspaceId]/scheduled-tasks/hooks/use-calendar.ts Outdated
@emir-karabeg

Copy link
Copy Markdown
CollaboratorAuthor

@greptile run

emir-karabegand others added 6 commits June 13, 2026 14:11
…dcrumbs
- Resource.Table: remove internal sorting (defaultSort/sortValues) and the
emptyMessage state — rows render in the order given, chrome always paints
- Resource: root is now the positioning context for overlays; consumers
(files, tables, knowledge, document) wrap detail views in <Resource>
instead of hand-rolled divs
- ResourceHeader: root titles no longer truncate during initial layout;
LocationFocusVeil gates the portal on mount to fix a hydration mismatch
- Toasts: drop the StackDismiss ring and stack countdown — each toast runs
its own timer; remove the Mod+E clear-notifications command; align toast
typography and icons with chip chrome
- Breadcrumbs: use the canonical '…' placeholder while names load
- incident.io: fix display name and catalog slug (with redirect)
- Add dev:capped / dev:full:capped scripts with a 4GB heap cap
Add month/time calendar views for scheduled tasks with toolbar, event
chips, and a create-task modal, backed by calendar-grid and
schedule-events utils (with tests) and a use-calendar hook. Replace the
old schedule-modal/context-menu flow.
Rename the "Mothership" agent to "Sim" and the chat surface to "Chat"
across landing copy, constitution, block metadata, API error messages,
and copilot/data-drain internals. Drop unused workspace route layouts.
A non-modal DropdownMenu portals outside an open dialog's
react-remove-scroll subtree, so its content cannot be wheel-scrolled
(e.g. the time picker in the scheduled-task create modal). ModalContent
now marks its subtree via an InsideModal context, and the emcn
DropdownMenu root upgrades itself to modal inside dialogs so it mounts
its own scroll lock and focus scope; page-level menus keep their
consumer-chosen modality.
Also stretch the create-task modal's date/time chip controls to full
width and drop the dead EDGE_GUTTER constant left behind by the
equal-tracks calendar layout.
…ck, smooth Today scroll
- useCalendar: today was frozen at mount, so after midnight the isToday
column highlight and the current-time indicator stayed on the previous
day. today is now state refreshed by a sleep-resilient minute poll
that only re-renders when the calendar day actually changes
- CreateTaskModal: the stub submit closed silently, reading as false
success; it now shows an info toast that the task was not created
- ScheduleCalendar: Today presses scroll smoothly as an orientation
cue; mount and scope switches keep instant positioning
- ChipCopyInput (canonical view-only copy field), ChipTimePicker,
ChipModalField type='copy', ChipTextarea viewOnly; new border chip
variant and shared chipPrimaryFillTokens
- migrate ~40 consumers off disabled inputs and the deleted
CopyableValueField; ChipConfirmModal description->text and
secondaryActions[] API sweep
- scheduled-tasks: rename create-task-modal to task-modal, add
task-details-modal + task-context-menu, useScheduledTasks hook
- home: extract prompt-editor (usePromptEditor) out of user-input
Wire the calendar UI to the existing sourceType='job' workflow_schedule
backend instead of local component state, so tasks persist and actually
run as Sim agent invocations.
- schema: add contexts (@-mentions resolved into the run), excludedDates
(per-occurrence deletes), and endsAt (recurrence end) to workflow_schedule
(migration 0235)
- contracts/schedules: expose one-time `time`, contexts, endsAt on create;
add the exclude_occurrence action; nullable cron in the create response
- orchestration: persist the new fields, honor exclusions + end boundary via
a shared computeNextRunAt, add performExcludeOccurrence
- execution: forward contexts to /api/mothership/execute and recompute the
next run through computeNextRunAt
- mothership/execute: accept + resolve contexts like the interactive chat path
- frontend: replace the local hook with React Query (create/update/delete +
exclude-occurrence), expand recurrences into calendar occurrences, add the
recurrence control (frequency + end) and the recurring this/all delete dialog
@waleedlatif1
waleedlatif1 changed the base branch from main to stagingJune 13, 2026 21:52
@waleedlatif1waleedlatif1 changed the title feat(scheduled-tasks): replace the schedules table with calendar viewsfeat(scheduled-tasks): calendar views + persisted, runnable tasksJun 13, 2026
@waleedlatif1

Copy link
Copy Markdown
Collaborator

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator

@cursor review

Comment threadapps/sim/app/workspace/[workspaceId]/files/files.tsx
Comment threadapps/sim/app/workspace/[workspaceId]/files/files.tsx
Comment threadapps/sim/app/api/mothership/execute/route.ts Outdated
- chip-modal: footer secondary cluster now wraps (min-w-0 flex-wrap) with a
non-shrinking action cluster, so scheduling controls can never clip Cancel/
primary; recurrence labels compacted so the common case stays one row
- schedule-execution: failure path now completes a recurring job when maxRuns/
endsAt/exclusions are exhausted (and a one-time/maxRuns job), mirroring the
success path instead of leaving it active with a stale nextRunAt
- prompt-editor: commitValue keeps valueRef in lockstep with state on the
mention-hook setter paths, completing the stale-ref fix
- task-modal: preserve @-mention contexts on edit (seed editor.setContexts);
single emptiness source of truth
- recurrence-control: preserve prior count when toggling end type; drop a
needless useMemo
- contracts: reuse scheduleContextSchema for the execute contexts shape
@waleedlatif1

Copy link
Copy Markdown
Collaborator

@greptile re-review the latest commits

@waleedlatif1

Copy link
Copy Markdown
Collaborator

@cursor review

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 6db24c8. Configure here.

@waleedlatif1

Copy link
Copy Markdown
Collaborator

@greptile re-review the latest commits

@waleedlatif1

Copy link
Copy Markdown
Collaborator

@cursor review

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit d38a99d. Configure here.

@waleedlatif1
waleedlatif1 merged commit bcedadf into stagingJun 13, 2026
15 checks passed
@waleedlatif1
waleedlatif1 deleted the feat/calendar-tasks branch June 13, 2026 23:16
waleedlatif1 added a commit that referenced this pull request Jul 1, 2026
…5311)
* fix(emcn): promote modal overlay + content wrapper to own compositing layers
The backdrop-blur overlay did not reliably paint above page content the
browser had GPU-promoted (position: sticky headers and z-indexed absolutes
inside a scroll container, e.g. the scheduled-tasks calendar), so those
composited layers bled through the overlay despite its higher z-index.
Force the overlay and the content wrapper onto their own compositing
layers via translateZ(0) so the compositor honors stacking order.
Inherent since the calendar shipped (#4979), not a refactor regression.
* Revert "fix(emcn): promote modal overlay + content wrapper to own compositing layers"
This reverts commit 52145ed.
* fix(ui): import calendar icon not calendar widget from emcn
* fix(ui): fix calendar icon emcn mismatch
---------
Co-authored-by: Theodore Li <theo@sim.ai>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@emir-karabeg@waleedlatif1