Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions packages/trpc/src/routes/workflows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,26 @@ export const workflowsRouter = router({
z.object({
workspaceId: z.uuid(),
workflowId: z.string().uuid(),
limit: z.number().min(1).max(1000).default(100),
limit: z.number().min(1).max(1000).default(500),
offset: z.number().min(0).default(0),
}),
)
.query(async ({ ctx, input }) => {
const newestRunIds = ctx.db
.select({ id: schema.workflowRun.id })
.from(schema.workflowRun)
.leftJoin(
schema.workflowJob,
eq(schema.workflowJob.workflowRunId, schema.workflowRun.id),
)
.leftJoin(schema.job, eq(schema.job.id, schema.workflowJob.jobId))
.where(eq(schema.workflowRun.workflowId, input.workflowId))
.groupBy(schema.workflowRun.id)
.orderBy(sql`min(${schema.job.createdAt}) desc nulls last`)
.limit(input.limit)
.offset(input.offset)
.as("newest_runs");
Comment on lines +165 to +178
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check schema.workflowRun for timestamp fields

# Search for workflowRun schema definition and look for timestamp fields
ast-grep --pattern $'workflowRun = $$$({
  $$$
})'

Repository: ctrlplanedev/ctrlplane

Length of output: 48


🏁 Script executed:

#!/bin/bash
# Description: Check if workflow runs can be created without jobs

# Look for workflowRun creation logic to see if jobs are always created
rg -nP --type=ts -C5 'workflowRun.*insert|insert.*workflowRun'

Repository: ctrlplanedev/ctrlplane

Length of output: 48


🏁 Script executed:

# Find schema-related files
fd -e ts -e js -e sql | grep -i schema | head -20

Repository: ctrlplanedev/ctrlplane

Length of output: 955


🏁 Script executed:

# Look for drizzle or database schema definitions
find . -type f \( -name "*schema*" -o -name "*migration*" \) | grep -E "\.(ts|js|sql)$" | head -20

Repository: ctrlplanedev/ctrlplane

Length of output: 248


🏁 Script executed:

# Search for workflowRun definition in the codebase
rg -l "workflowRun" --type=ts | head -10

Repository: ctrlplanedev/ctrlplane

Length of output: 325


🏁 Script executed:

# Check the workflow schema definition
cat -n packages/db/src/schema/workflow.ts

Repository: ctrlplanedev/ctrlplane

Length of output: 1761


🏁 Script executed:

# Look at the context around lines 165-178 in workflows.ts
sed -n '150,190p' packages/trpc/src/routes/workflows.ts

Repository: ctrlplanedev/ctrlplane

Length of output: 1453


🏁 Script executed:

# Check where workflowRun records are created
rg -n "workflowRun" packages/trpc/src/routes/workflows.ts -B5 -A5

Repository: ctrlplanedev/ctrlplane

Length of output: 3345


🏁 Script executed:

# Look for where workflowRun is inserted/created
rg -n "insert.*workflowRun|workflowRun.*insert" packages/trpc/src -B3 -A3

Repository: ctrlplanedev/ctrlplane

Length of output: 48


🏁 Script executed:

# Check if there's any creation logic that always creates jobs
rg -n "create.*workflow|workflow.*create" packages/trpc/src/routes/workflows.ts -B5 -A5

Repository: ctrlplanedev/ctrlplane

Length of output: 48


🏁 Script executed:

# Search for where workflowRun is created - look for .insert calls
rg -n "\.insert\(" packages/trpc/src/routes/workflows.ts -B3 -A3

Repository: ctrlplanedev/ctrlplane

Length of output: 48


🏁 Script executed:

# Look at the job schema to confirm it has createdAt
sed -n '1,100p' packages/db/src/schema/job.ts | head -50

Repository: ctrlplanedev/ctrlplane

Length of output: 1180


🏁 Script executed:

# Get more of the job schema to see the createdAt field
sed -n '45,150p' packages/db/src/schema/job.ts

Repository: ctrlplanedev/ctrlplane

Length of output: 3115


🏁 Script executed:

# Search for where workflowRun is created - broader search
rg -n "workflowRun" packages/trpc/src -B2 -A2 | grep -E "\.insert|\.values|create|add"

Repository: ctrlplanedev/ctrlplane

Length of output: 178


🏁 Script executed:

# Look for create run mutations
rg -n "createRun|create.*run|run.*create" packages/trpc/src/routes/workflows.ts -B3 -A10

Repository: ctrlplanedev/ctrlplane

Length of output: 48


🏁 Script executed:

# Check if there's any logic that always creates jobs when creating a run
rg -n "workflowRun.*insert|insert.*workflowRun" packages/trpc -B5 -A5

Repository: ctrlplanedev/ctrlplane

Length of output: 48


🏁 Script executed:

# Look at all procedures in workflows.ts to find run creation
sed -n '1,300p' packages/trpc/src/routes/workflows.ts | grep -E "\.mutation|\.insert|workflowRun" -B2 -A2

Repository: ctrlplanedev/ctrlplane

Length of output: 2329


Add createdAt field to workflowRun schema or reconsider ordering strategy.

The query orders by min(schema.job.createdAt) desc nulls last, which means workflow runs without any jobs will appear at the end of the list (because their min job creation time is NULL). Since the workflowRun table has no timestamp field (confirmed from schema), and runs can exist without jobs (indicated by the LEFT JOIN strategy), this ordering produces incorrect results for newly created runs that haven't spawned jobs yet.

Either add a createdAt timestamp to the workflowRun schema and order by that, or implement an alternative ordering strategy that correctly handles runs without jobs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/trpc/src/routes/workflows.ts` around lines 165 - 178, The ordering
uses min(schema.job.createdAt) so workflow runs without jobs sort last; add a
createdAt timestamp to the workflowRun schema and migration, then update the
newestRunIds query to order using COALESCE between job.createdAt and
workflowRun.createdAt (e.g. orderBy(sql`coalesce(min(${schema.job.createdAt}),
${schema.workflowRun.createdAt}) desc nulls last`)) or simply order by
schema.workflowRun.createdAt desc; update the workflowRun schema (createdAt),
migration, and replace the orderBy in the newestRunIds query accordingly.


const rows = await ctx.db
.select({
runId: schema.workflowRun.id,
Expand All @@ -170,15 +185,16 @@ export const workflowsRouter = router({
jobStatus: schema.job.status,
jobCreatedAt: schema.job.createdAt,
})
.from(schema.workflowRun)
.from(newestRunIds)
.innerJoin(
schema.workflowRun,
eq(schema.workflowRun.id, newestRunIds.id),
)
.leftJoin(
schema.workflowJob,
eq(schema.workflowJob.workflowRunId, schema.workflowRun.id),
)
.leftJoin(schema.job, eq(schema.job.id, schema.workflowJob.jobId))
.where(eq(schema.workflowRun.workflowId, input.workflowId))
.limit(input.limit)
.offset(input.offset);
.leftJoin(schema.job, eq(schema.job.id, schema.workflowJob.jobId));

return _.chain(rows)
.groupBy("runId")
Expand Down
Loading