@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.
AGENTS.md
177 lines · 7.1 KB · Markdown@kentcdodds/kody-issue-triage — agents
Human overview: README.md. Secrets / wake URLs by name
only — never paste wake keys, tokens, or full webhook URLs with credentials.
Do not disable live subscriptions or the sweep job.
Secrets / wake storage
| Name / store | Notes |
|---|---|
grokBotWakeWebhookUrl | User-secret pattern for the minted @kentcdodds/grok-bot wake URL (document the name only) |
| packageStorage wake URL | Set via ./configure-wake-webhook after mint — never log the URL |
cursorApiKey | Via @kentcdodds/cursor when Patch escalates (Cursor / platform path) |
discordBotTokenKentPersonalAutomation | Via @kentcdodds/discord |
Import paths
import describe from 'kody:@kentcdodds/kody-issue-triage'
import onRunError from 'kody:@kentcdodds/kody-issue-triage/on-run-error'
import onFleetErrorRate from 'kody:@kentcdodds/kody-issue-triage/on-fleet-error-rate'
import sweep from 'kody:@kentcdodds/kody-issue-triage/sweep'
import getIssueState from 'kody:@kentcdodds/kody-issue-triage/get-issue-state'
import peerContext from 'kody:@kentcdodds/kody-issue-triage/peer-context'
import escalateToKody from 'kody:@kentcdodds/kody-issue-triage/escalate-to-kody'
import recordOutcome from 'kody:@kentcdodds/kody-issue-triage/record-outcome'
import updateCard from 'kody:@kentcdodds/kody-issue-triage/update-card'
import pause from 'kody:@kentcdodds/kody-issue-triage/pause'
import resume from 'kody:@kentcdodds/kody-issue-triage/resume'
import status from 'kody:@kentcdodds/kody-issue-triage/status'
import releaseLease from 'kody:@kentcdodds/kody-issue-triage/release-lease'
import configureWakeWebhook from 'kody:@kentcdodds/kody-issue-triage/configure-wake-webhook'
import probeWakeWebhook from 'kody:@kentcdodds/kody-issue-triage/probe-wake-webhook'Prefer static kody:@kentcdodds/kody-issue-triage/... imports from execute.
Do not lead examples with packages.invoke.
Smoke / dryRun
Operator snapshot (read-only):
import status from 'kody:@kentcdodds/kody-issue-triage/status'
export default async function main() {
return await status()
// => { enabled, hourBucket, spawnedThisHour, lease, recent, ... }
}Sweep without waking Patch / mutating Discord:
import sweep from 'kody:@kentcdodds/kody-issue-triage/sweep'
export default async function main() {
return await sweep({ dryRun: true, limit: 3 })
}Patch wake path probe (POSTs dryRun: true through stored wake URL; never
returns the URL):
import probeWakeWebhook from 'kody:@kentcdodds/kody-issue-triage/probe-wake-webhook'
export default async function main() {
return await probeWakeWebhook()
// => { ok, host, httpStatus, bodyPreview, live: false, ... }
}Lease release preview:
import releaseLease from 'kody:@kentcdodds/kody-issue-triage/release-lease'
export default async function main() {
return await releaseLease({ dryRun: true })
}Configure wake URL after mint (pass URL once; do not echo it back in chat):
import configureWakeWebhook from 'kody:@kentcdodds/kody-issue-triage/configure-wake-webhook'
export default async function main(input: { url: string }) {
return await configureWakeWebhook({ url: input.url })
}Progressive Discord card links
Keep the single Discord card updated as work progresses. Call ./update-card
at each stage (does not release the lease or close the fingerprint). Keep
Activity links; accumulate Agent → PR → Merge → Deploy.
import updateCard from 'kody:@kentcdodds/kody-issue-triage/update-card'
export default async function main() {
return await updateCard({
fingerprint: 'REPLACE_FINGERPRINT',
stage: 'started', // 'started' | 'pr' | 'merged' | 'deployed'
agentUrl: 'https://cursor.com/agents/REPLACE',
// prUrl: 'https://github.com/OWNER/REPO/pull/N',
// mergeCommitUrl: 'https://github.com/OWNER/REPO/commit/SHA',
// deployUrl: 'https://github.com/OWNER/REPO/actions/runs/ID',
})
}Finish with ./record-outcome (still accepts prUrl, plus optional
mergeCommitUrl / deployUrl).
record-outcome (agents / Patch finishing work):
import recordOutcome from 'kody:@kentcdodds/kody-issue-triage/record-outcome'
export default async function main() {
return await recordOutcome({
fingerprint: 'workflow:skills:timeout-90s',
outcome: 'recommendation',
classification: 'kody',
title: 'Raise the workflow timeout for skills?',
context: 'Standing 90s timeouts on skills repo.pushed.',
recommendation: 'Raise the step budget for this family.',
rightFix: 'Raise the step budget for this family.',
options: ['Raise the timeout', 'Treat as noise', 'Escalate to a platform ticket'],
})
}Edge cases (Patch wake / fingerprints / loops)
- Handlers only write
packageStorage(); they never spawn or post Discord. Sweep (job or event workflow) is the wake path. - One Patch wake per fingerprint (
surface+ owner + normalized family), not per run id. Coarse families collapse TypeError/timeout variants; JSON wrapper instance keys drop; catalog/missing-selector throws collapse topackage-caller-validation. - Skip: this package,
subscriptionsurface, agent MCPexecuteorigins (and package/inline-coderuns from execute), smoke-test fingerprints, sibling triagerecord-outcomevalidation throws. - Platform isolate-reset weather → standing
resolved_noise(skills retriever interrupts, 2500ms observer timeouts, dynamic-worker cap, system-email/api/statusinterrupts).ignored/resolved_noisedo not re-queue after 24h. - After
fixed/recommendationwith akody_agent_id, leftovers increment count but do not re-queue; pre-completed_atleftover runs stay closed. - Caps: max 3 agents/hour; anomaly at 15 new fingerprints or 80 raw events/hour. Global lease: one in-flight wake/agent.
- Dead agents without
record-outcome→failedafter ~2h stale-spawn window. - LIVE peer Cloud agents for the same family stay queued (no second wake).
- Discord spawn card: persist
discord_message_id(edit-existing-first for the same fingerprint) before waking Patch. On wake/spawn failure after the Discord post/edit, edit the card off pure "Waking Patch…" to a retryable queued status when practical — never leave an orphan Queued card with no stored id../record-outcomestill owns terminal Discord edits. - Primary wake target is Patch (
bot: 'patch'). Patch owns package/run triage and may escalate to Cole for platform issues.escalate-to-kody/ Cursor remains for platform work onkentcdodds/kodywhen Patch decides — triage agents must not edit Kody themselves. - Sentry issue triage stays on
@kentcdodds/sentry-triage→ Cole; this package covers Kody package/run errors only. - Verify helpers with
./smoke-testor{ smoke: true }when documented — never dummy ids that create new fingerprints. - Kill switch:
./pause/./resume../release-leaseonly for operator recovery when a spawn died withoutrecord-outcome.
Dependency note
Uses @kentcdodds/github, which authenticates via createAuthenticatedFetch on the github-bot / github-kent OAuth integrations (not mirrored github-*AccessToken user secrets).