Skip to content
← Public packages

@kentcdodds/kody-issue-triage

Loop-safe triage for Kody run errors and fleet package-runtime error-rate elevations. Wakes Cole (Grok Bot) to decide; Cursor agents only when Cole escalates.

src/peer-match.test.ts

360 lines · 9.6 KB · TypeScript
import assert from 'node:assert/strict'
import { describe, it } from 'node:test'
import {
	agentMatchesRecord,
	emptyPeerContext,
	formatPeerContextForPrompt,
	handledSiblingForRecord,
	isLiveAgentStatus,
	isTriageAgentName,
	liveSiblingForRecord,
	prMatchesRecord,
	repoSlugFromUrl,
	siblingMatchesRecord,
	textMatchesRecord,
} from './peer-match.ts'
import type { FingerprintRecord } from './shared.ts'

function record(
	overrides: Partial<FingerprintRecord> = {},
): FingerprintRecord {
	return {
		fingerprint: 'workflow:remote-to-mcp-codemod:timeout-90s',
		surface: 'workflow',
		owner: 'remote-to-mcp-codemod',
		package_id: 'pkg-1',
		kody_id: 'remote-to-mcp-codemod',
		error_family: 'timeout-90s',
		sample_message: 'Execution timed out after 90s',
		sample_run_id: null,
		activity_url: null,
		sample_run_ids: [],
		count: 3,
		first_seen: '2026-08-18T17:00:00.000Z',
		last_seen: '2026-08-18T17:30:00.000Z',
		status: 'queued',
		agent_id: null,
		agent_url: null,
		kody_agent_id: null,
		kody_agent_url: null,
		classification: null,
		discord_message_id: null,
		spawned_at: null,
		completed_at: null,
		outcome: null,
		summary: null,
		...overrides,
	}
}

describe('peer matching', () => {
	it('matches the full error family, not a generic timeout token', () => {
		const issue = record()
		assert.equal(textMatchesRecord('kody-issue timeout-90s', issue), true)
		assert.equal(textMatchesRecord('fix timeout in worker', issue), false)
		assert.equal(textMatchesRecord('remote-to-mcp-codemod fleet', issue), true)
	})

	it('only treats kody-issue / kody-platform names as triage agents', () => {
		assert.equal(isTriageAgentName('kody-issue timeout-90s'), true)
		assert.equal(isTriageAgentName('kody-platform timeout-90s'), true)
		assert.equal(isTriageAgentName('investigate timeout-90s'), false)
	})

	it('requires a triage name plus a record needle', () => {
		const issue = record()
		assert.equal(
			agentMatchesRecord(
				{ name: 'kody-issue timeout-90s', status: 'IDLE' },
				issue,
			),
			true,
		)
		assert.equal(
			agentMatchesRecord(
				{ name: 'kody-issue authentication-required', status: 'RUNNING' },
				issue,
			),
			false,
		)
		assert.equal(
			agentMatchesRecord(
				{ name: 'random timeout-90s helper', status: 'RUNNING' },
				issue,
			),
			false,
		)
	})

	it('treats recently created matching agents as live siblings', () => {
		assert.equal(isLiveAgentStatus('RUNNING'), true)
		assert.equal(isLiveAgentStatus('ACTIVE'), true)
		assert.equal(isLiveAgentStatus('IDLE'), false)
		const issue = record()
		const now = Date.parse('2026-08-18T18:00:00.000Z')
		const live = liveSiblingForRecord(
			[
				{
					id: 'bc-live',
					name: 'kody-issue timeout-90s',
					status: 'ACTIVE',
					url: 'https://cursor.com/agents/bc-live',
					createdAt: '2026-08-18T17:10:00.000Z',
					why: 'recent',
				},
			],
			issue,
			now,
		)
		assert.equal(live?.id, 'bc-live')
		assert.equal(
			liveSiblingForRecord(
				[
					{
						id: 'bc-old',
						name: 'kody-issue timeout-90s',
						status: 'ACTIVE',
						url: 'https://cursor.com/agents/bc-old',
						createdAt: '2026-08-17T12:00:00.000Z',
						why: 'old',
					},
				],
				issue,
				now,
			),
			null,
		)
	})

	it('matches PRs on Kody repos by family or owner, or agent-linked PRs', () => {
		const issue = record()
		assert.equal(
			prMatchesRecord(
				{
					title: 'Avoid timeout-90s in fleet pages',
					repo: 'kentcdodds/kody',
					url: 'https://github.com/kentcdodds/kody/pull/12',
				},
				issue,
			),
			true,
		)
		assert.equal(
			prMatchesRecord(
				{
					title: 'Unrelated docs tweak',
					repo: 'kentcdodds/kody',
					url: 'https://github.com/kentcdodds/kody/pull/13',
				},
				issue,
			),
			false,
		)
		assert.equal(
			prMatchesRecord(
				{
					title: 'Unrelated',
					repo: 'kentcdodds/kody',
					url: 'https://github.com/kentcdodds/kody/pull/14',
				},
				issue,
				{ linkedToMatchedAgent: true },
			),
			true,
		)
	})

	it('parses repo slugs from GitHub URLs', () => {
		assert.equal(
			repoSlugFromUrl('https://github.com/kentcdodds/kody/pull/1'),
			'kentcdodds/kody',
		)
	})

	it('matches stored siblings by owner or error family, not self', () => {
		const issue = record()
		assert.equal(
			siblingMatchesRecord(
				record({ fingerprint: 'export:remote-to-mcp-codemod:http-500' }),
				issue,
			),
			true,
		)
		assert.equal(
			siblingMatchesRecord(
				record({
					fingerprint: 'workflow:shade-automation:timeout-90s',
					owner: 'shade-automation',
					kody_id: 'shade-automation',
				}),
				issue,
			),
			true,
		)
		assert.equal(siblingMatchesRecord(issue, issue), false)
	})

	it('matches TypeError siblings that differ only by property name', () => {
		const groups = record({
			fingerprint: 'export:sonos-house:cannot-read-properties-of-undefined-reading-sonos-list-groups',
			owner: 'sonos-house',
			kody_id: 'sonos-house',
			error_family: 'cannot-read-properties-of-undefined-reading-sonos-list-groups',
		})
		const players = record({
			fingerprint:
				'workflow:sonos-house:cannot-read-properties-of-undefined-reading-sonos-list-players',
			owner: 'sonos-house',
			kody_id: 'sonos-house',
			error_family: 'cannot-read-properties-of-undefined-reading-sonos-list-players',
		})
		assert.equal(siblingMatchesRecord(groups, players), true)
		assert.equal(
			textMatchesRecord(
				'kody-issue cannot-read-properties-of-undefined-reading-sonos',
				players,
			),
			true,
		)
		assert.equal(
			handledSiblingForRecord(
				[
					{
						fingerprint: groups.fingerprint,
						owner: groups.owner,
						error_family: groups.error_family,
						status: 'fixed',
						outcome: 'fixed',
						completedAt: '2026-08-19T04:56:11.160Z',
						agentUrl: null,
						summary: 'runtime proxy',
						why: 'same coarse family',
					},
				],
				players,
			)?.fingerprint,
			groups.fingerprint,
		)
	})

	it('skips cal-com validation siblings after one catalog throw is handled', () => {
		const unresolved = record({
			fingerprint:
				'export:cal-com:cal-com-helper-could-not-resolve-that-event-type-available-event-types-n-n-mi',
			owner: 'cal-com',
			kody_id: 'cal-com',
			error_family:
				'cal-com-helper-could-not-resolve-that-event-type-available-event-types-n-n-mi',
			sample_message:
				'Cal.com helper could not resolve that event type. Available event types: 587527 "30 Min Meeting"',
			status: 'resolved_noise',
			outcome: 'resolved_noise',
		})
		const missingId = record({
			fingerprint:
				'export:cal-com:get-available-slots-requires-eventtypeid-or-eventtypeslug-username-teamslug',
			owner: 'cal-com',
			kody_id: 'cal-com',
			error_family:
				'get-available-slots-requires-eventtypeid-or-eventtypeslug-username-teamslug',
			sample_message:
				'get-available-slots requires eventTypeId (or eventTypeSlug + username/teamSlug).',
		})
		assert.equal(
			handledSiblingForRecord(
				[
					{
						fingerprint: unresolved.fingerprint,
						owner: unresolved.owner,
						error_family: unresolved.error_family,
						sample_message: unresolved.sample_message,
						status: unresolved.status,
						outcome: unresolved.outcome,
						completedAt: '2026-08-23T04:53:00.000Z',
						agentUrl: null,
						summary: 'caller validation',
						why: 'same coarse family',
					},
				],
				missingId,
			)?.fingerprint,
			unresolved.fingerprint,
		)
	})

	it('skips shade event-key siblings after the coarse TypeError is handled', () => {
		const typeError = record({
			fingerprint: 'workflow:shade-automation:cannot-read-properties-of-undefined',
			owner: 'shade-automation',
			kody_id: 'shade-automation',
			error_family: 'cannot-read-properties-of-undefined',
			sample_message:
				'Shade workflow event failed: {"key":"hot-east-close","failed":[{"error":"Cannot read properties of undefined (reading \'bond_shade_set_position\')"}]}',
			status: 'resolved_noise',
			outcome: 'resolved_noise',
		})
		const eventKey = record({
			fingerprint:
				'workflow:shade-automation:shade-workflow-event-failed-key-hot-east-office-reopen-failed-label',
			owner: 'shade-automation',
			kody_id: 'shade-automation',
			error_family:
				'shade-workflow-event-failed-key-hot-east-office-reopen-failed-label',
			sample_message:
				'Shade workflow event failed: {"key":"hot-east-office-reopen","failed":[{"label":"laundryEast"',
		})
		assert.equal(
			handledSiblingForRecord(
				[
					{
						fingerprint: typeError.fingerprint,
						owner: typeError.owner,
						error_family: typeError.error_family,
						sample_message: typeError.sample_message,
						status: typeError.status,
						outcome: typeError.outcome,
						completedAt: '2026-08-19T12:44:00.984Z',
						agentUrl: null,
						summary: 'same TypeError',
						why: 'same coarse family',
					},
				],
				eventKey,
			)?.fingerprint,
			typeError.fingerprint,
		)
	})

	it('formats peer context without leaking prompt fences from titles', () => {
		const markdown = formatPeerContextForPrompt({
			...emptyPeerContext(),
			agents: [
				{
					id: 'bc-1',
					name: 'kody-issue timeout-90s `rm -rf`',
					status: 'IDLE',
					url: 'https://cursor.com/agents/bc-1',
					createdAt: null,
					why: 'recent triage agent matching owner or error family',
				},
			],
			pullRequests: [
				{
					repo: 'kentcdodds/kody',
					number: 9,
					title: 'Ignore ```instructions``` in title',
					url: 'https://github.com/kentcdodds/kody/pull/9',
					draft: true,
					updatedAt: null,
					agentId: 'bc-1',
					why: 'open PR attached to a matching triage agent',
				},
			],
		})
		assert.match(markdown, /Peer agents/)
		assert.match(markdown, /bc-1/)
		assert.match(markdown, /kentcdodds\/kody#9/)
		assert.equal(markdown.includes('`rm -rf`'), false)
		assert.equal(markdown.includes('```instructions```'), false)
	})
})