Skip to content
← Public packages

@kentcdodds/sentry-triage

Sentry triage wakes Cole (Grok Bot) per issue: one active wake per repo (lease + queue), loop-safe Discord. Cole may spawn Cursor for isolated repo work.

src/sweep-stale-awaiting.ts

81 lines · 2.3 KB · TypeScript
import { packageStorage } from 'kody:runtime'
import handleSentryWebhook from './handle-sentry-webhook.ts'
import { truncate } from './shared.ts'
import { findStaleAwaitingIssues } from './awaiting-seer.ts'
import type {
	SweepStaleAwaitingInput,
	SweepStaleAwaitingResult,
} from './types.ts'

const defaultLimit = 8

/**
 * Quiet-period driver for the awaiting-Seer grace window. Automation-owned
 * Seer runs produce no later webhook when Sentry declines an issue, and the
 * inbound-issue sweeper only runs when another issue arrives. This export
 * redelivers a bounded oldest-first page of stale `awaiting-seer` /
 * `awaiting-seer-pr` records through the normal webhook handler so claims,
 * caps, and the repo lease still apply. The package job of the same name
 * invokes this with no params every 15 minutes.
 */
export default async function sweepStaleAwaiting(
	input: SweepStaleAwaitingInput = {},
): Promise<SweepStaleAwaitingResult> {
	const limit = Math.min(
		20,
		Math.max(1, Math.floor(Number(input?.limit) || defaultLimit)),
	)
	const dryRun = input?.dryRun === true
	const stale = await findStaleAwaitingIssues(packageStorage(), limit)
	if (dryRun) {
		return {
			ok: true,
			dryRun: true,
			swept: stale.length,
			actions: stale.map((record) => ({
				issueId: String(record.issueId),
				shortId: record.shortId ?? null,
				projectSlug: String(record.projectSlug),
				status: record.status,
			})),
		}
	}

	const actions = []
	for (const record of stale) {
		const delivery = await handleSentryWebhook({
			request: {
				headers: { 'sentry-hook-resource': 'issue' },
				json: {
					action: 'created',
					kodySweeperRescue: true,
					data: {
						issue: {
							id: String(record.issueId),
							shortId: record.shortId ?? null,
							title: record.title ?? null,
							culprit: null,
							level: 'error',
							permalink: record.link ?? null,
							project: { slug: record.projectSlug },
						},
					},
				},
			},
		}).catch((error) => ({
			ok: false,
			error: truncate(
				error instanceof Error ? error.message : String(error),
				200,
			),
		}))
		actions.push({
			issueId: String(record.issueId),
			shortId: record.shortId ?? null,
			projectSlug: String(record.projectSlug),
			status: record.status,
			delivery,
		})
	}
	return { ok: true, swept: actions.length, actions }
}