Skip to content

Commit d174d6d

Browse files
committed
chore(scheduled-tasks): drop the leftovers the removal stranded
An independent audit of the branch turned up dead code and stale docs that the compiler cannot see — nothing behavioural, but all of it rots silently. - README still sold the feature: the "Scheduled tasks" tile, the prose listing it as a workspace surface, and the now-unreferenced screenshot. The landing surface went in c61770a; this tile was missed. - `resource-content.tsx`: `SCHEDULE_STATUS_LABEL`, `formatScheduleInstant` and `ScheduledTaskField` were orphaned when the schedule render branch went. - `computeNextRunAt`: zero callers, including tests — its only consumer was the removed agent-job runner. - `applyScheduleUpdate`'s `allowCompleted` option: no call site passes it, and its comment described self-completion, which no longer exists. The guard stays (legacy `sourceType='job'` rows still carry `status='completed'` until the DB follow-up); it is simply unconditional now. - Three TSDoc blocks still described a create-job route and "opening a scheduled-task artifact". Type-check re-run with --force, since a cached turbo replay is not a check.
1 parent 1d4f4a0 commit d174d6d

7 files changed

Lines changed: 12 additions & 91 deletions

File tree

README.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Open [http://localhost:3000](http://localhost:3000)
4747

4848
## One workspace, every surface
4949

50-
<p align="center">Chat and workflows are just the start — tables, files, knowledge, and scheduled tasks all live in the same workspace.</p>
50+
<p align="center">Chat and workflows are just the start — tables, files, and knowledge all live in the same workspace.</p>
5151

5252
<table>
5353
<tr>
@@ -65,10 +65,7 @@ Open [http://localhost:3000](http://localhost:3000)
6565
<img src="apps/sim/public/static/readme-knowledge.png" alt="Knowledge bases in Sim — synced docs your agents can search" width="100%"/>
6666
<p align="center"><b>Knowledge</b> — your agents' memory</p>
6767
</td>
68-
<td width="50%" valign="top">
69-
<img src="apps/sim/public/static/readme-scheduled-tasks.png" alt="Scheduled tasks in Sim — recurring agent runs on a calendar" width="100%"/>
70-
<p align="center"><b>Scheduled tasks</b> — runs on your schedule</p>
71-
</td>
68+
<td width="50%" valign="top"></td>
7269
</tr>
7370
</table>
7471

apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313
WorkflowX,
1414
} from '@sim/emcn/icons'
1515
import { createLogger } from '@sim/logger'
16-
import { format } from 'date-fns'
1716
import { useRouter } from 'next/navigation'
1817
import { isApiClientError } from '@/lib/api/client/errors'
1918
import { useSession } from '@/lib/auth/auth-client'
@@ -781,32 +780,6 @@ function EmbeddedFolder({ workspaceId, folderId }: EmbeddedFolderProps) {
781780
)
782781
}
783782

784-
const SCHEDULE_STATUS_LABEL: Record<string, string> = {
785-
active: 'Active',
786-
disabled: 'Paused',
787-
completed: 'Completed',
788-
}
789-
790-
function formatScheduleInstant(iso: string | null): string {
791-
if (!iso) return '—'
792-
const date = new Date(iso)
793-
return Number.isNaN(date.getTime()) ? '—' : format(date, "EEE, MMM d 'at' h:mm a")
794-
}
795-
796-
interface ScheduledTaskFieldProps {
797-
title: string
798-
value: string
799-
}
800-
801-
function ScheduledTaskField({ title, value }: ScheduledTaskFieldProps) {
802-
return (
803-
<div className='flex flex-col gap-1'>
804-
<span className='text-[var(--text-muted)] text-caption'>{title}</span>
805-
<span className='text-[var(--text-body)] text-small'>{value}</span>
806-
</div>
807-
)
808-
}
809-
810783
interface EmbeddedLogProps {
811784
workspaceId: string
812785
logId: string

apps/sim/background/schedule-execution.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ async function applyScheduleUpdate(
136136
context: string,
137137
options: {
138138
expectedLastQueuedAt?: Date | null
139-
allowCompleted?: boolean
140139
/**
141140
* Set at call sites that can transition the row to `disabled`. Presence both
142141
* opts the site into the auto-disable email and adds a `status <> 'disabled'`
@@ -159,15 +158,10 @@ async function applyScheduleUpdate(
159158
? isNull(workflowSchedule.lastQueuedAt)
160159
: eq(workflowSchedule.lastQueuedAt, options.expectedLastQueuedAt)
161160

162-
// A run that completes itself mid-execution sets status='completed'. The post-run
163-
// bookkeeping that follows would otherwise write status='active' and a
164-
// fresh nextRunAt straight back over it — the claim guard does not catch
165-
// this, because completing the job does not touch lastQueuedAt. Terminal
166-
// means terminal: only callers that explicitly opt in may move a completed
167-
// row.
168-
const notCompletedGuard = options.allowCompleted
169-
? undefined
170-
: ne(workflowSchedule.status, 'completed')
161+
// Terminal means terminal: a completed row is never moved back to active
162+
// with a fresh nextRunAt. The claim guard does not cover this on its own,
163+
// because reaching 'completed' does not touch lastQueuedAt.
164+
const notCompletedGuard = ne(workflowSchedule.status, 'completed')
171165

172166
/**
173167
* `RETURNING` yields the NEW row, so `status === 'disabled'` alone only means

apps/sim/hooks/queries/schedules.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,8 @@ export function useWorkspaceSchedules(workspaceId?: string, options?: { enabled?
9090
}
9191

9292
/**
93-
* Fetch a single schedule (job) by id. Used by the mothership resource viewer so
94-
* opening a scheduled-task artifact does a lightweight by-id read instead of the
95-
* whole-workspace `useWorkspaceSchedules` fetch (which contended with the chat
96-
* stream connection and stalled start/resume).
93+
* Fetch a single workflow schedule by id — a lightweight by-id read instead of
94+
* the whole-workspace `useWorkspaceSchedules` fetch.
9795
*/
9896
export function useScheduleById(scheduleId?: string) {
9997
return useQuery({

apps/sim/lib/api/contracts/schedules.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,8 @@ export const listWorkspaceSchedulesContract = defineRouteContract({
163163
})
164164

165165
/**
166-
* Single-schedule read by id. Used by the mothership resource viewer so opening
167-
* a scheduled-task artifact does a lightweight by-id fetch instead of pulling
168-
* the entire workspace schedule list (which contended with the chat stream).
166+
* Single-schedule read by id: a lightweight fetch for one workflow schedule
167+
* instead of pulling the whole workspace list.
169168
*/
170169
export const getScheduleByIdContract = defineRouteContract({
171170
method: 'GET',
@@ -180,9 +179,8 @@ export const getScheduleByIdContract = defineRouteContract({
180179
})
181180

182181
/**
183-
* Newly-created job schedules emit a partial summary with the canonical fields
184-
* the route synthesizes server-side; everything else is filled in on
185-
* subsequent reads.
182+
* Re-arms a disabled schedule: the route recomputes `nextRunAt` from the stored
183+
* cron expression and clears the failure counters.
186184
*/
187185
export const reactivateScheduleContract = defineRouteContract({
188186
method: 'PUT',

apps/sim/lib/workflows/schedules/utils.ts

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -53,45 +53,6 @@ export function validateCronExpression(
5353
/** Upper bound on how many excluded occurrences the next-run search skips before giving up. */
5454
const MAX_OCCURRENCE_SKIP = 1000
5555

56-
/**
57-
* Computes the next run instant for a recurring schedule, skipping occurrences
58-
* the user deleted individually and stopping at the recurrence end boundary.
59-
* Returns `null` when the recurrence has no remaining run (past `endsAt`, or
60-
* every candidate within the search bound is excluded).
61-
*
62-
* Excluded occurrences are matched by exact instant, so callers must record the
63-
* cron-produced occurrence time (not a rounded value) when excluding.
64-
*/
65-
export function computeNextRunAt(params: {
66-
cronExpression: string
67-
timezone?: string
68-
from?: Date
69-
excludedDates?: string[] | null
70-
endsAt?: Date | null
71-
}): Date | null {
72-
const { cronExpression, timezone, from, excludedDates, endsAt } = params
73-
let cron: Cron
74-
try {
75-
cron = new Cron(cronExpression, timezone ? { timezone } : undefined)
76-
} catch {
77-
return null
78-
}
79-
80-
const excluded = new Set(
81-
(excludedDates ?? []).map((iso) => new Date(iso).getTime()).filter((ms) => !Number.isNaN(ms))
82-
)
83-
84-
let cursor = from ?? new Date()
85-
for (let i = 0; i < MAX_OCCURRENCE_SKIP; i++) {
86-
const next = cron.nextRun(cursor)
87-
if (!next) return null
88-
if (endsAt && next.getTime() > endsAt.getTime()) return null
89-
if (!excluded.has(next.getTime())) return next
90-
cursor = next
91-
}
92-
return null
93-
}
94-
9556
interface SubBlockValue {
9657
value: string
9758
}
-199 KB
Binary file not shown.

0 commit comments

Comments
 (0)