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-karabeg emir-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_schedule sourceType='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

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jun 13, 2026 10:59pm

Request Review

@emir-karabeg

Copy link
Copy Markdown
Collaborator Author

@greptile run

@cursor

cursor Bot 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 ChipConfirmModal text 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-apps Bot 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

Filename Overview
apps/sim/lib/workflows/schedules/utils.ts Adds 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.ts Adds 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.ts Migrates 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.ts Bridges 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.ts expandOccurrences 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.tsx Shared 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.ts today 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.ts Well-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.sql Additive 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 thread apps/sim/app/workspace/[workspaceId]/scheduled-tasks/hooks/use-calendar.ts Outdated
@greptile-apps

greptile-apps Bot 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

Filename Overview
apps/sim/app/workspace/[workspaceId]/scheduled-tasks/hooks/use-calendar.ts New 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.ts Pure 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.ts Event 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.tsx Calendar 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.tsx Calendar 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.tsx DropdownMenu 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.ts New 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.tsx Resource 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.tsx New 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.tsx Time 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)

    P2 Empty-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 thread apps/sim/app/workspace/[workspaceId]/scheduled-tasks/hooks/use-calendar.ts Outdated
@emir-karabeg

Copy link
Copy Markdown
Collaborator Author

@greptile run

emir-karabeg and 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 force-pushed the feat/calendar-tasks branch from cb9fb71 to 4469909 Compare June 13, 2026 21:52
@waleedlatif1 waleedlatif1 changed the base branch from main to staging June 13, 2026 21:52
@waleedlatif1 waleedlatif1 changed the title feat(scheduled-tasks): replace the schedules table with calendar views feat(scheduled-tasks): calendar views + persisted, runnable tasks Jun 13, 2026
@waleedlatif1

Copy link
Copy Markdown
Collaborator

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator

@cursor review

Comment thread apps/sim/app/workspace/[workspaceId]/files/files.tsx
Comment thread apps/sim/app/workspace/[workspaceId]/files/files.tsx
Comment thread apps/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

@cursor cursor Bot 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

@cursor cursor Bot 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 staging Jun 13, 2026
15 checks passed
@waleedlatif1 waleedlatif1 deleted the feat/calendar-tasks branch June 13, 2026 23:16
Sign up for free to 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