john mark lowry
§ ai tools · 2024–present

T1 triage bot

Eight scrum teams, a rotating triage duty, and severity calls that depended on who was on shift.

built at team one. metrics are sanitized aggregates; sla rubric described at the shape level only.

01 · the moment

Bug triage across the digital org's scrum teams ran on a human rotation: someone watches the channel, someone eyeballs severity, someone chases the SLA. Manual, slow, and inconsistent — the same ticket got a different priority depending on who was on duty that week. The cost wasn't just time; severity, the thing the whole SLA hinges on, was a coin flip weighted by whoever was on shift.

02 · the reframe

It looked like a staffing problem. It was an information problem. Everything needed for a severity call already lives in the ticket system — the bot just has to read it the way the system intends: programmatically, through the API, with the ticket system as the source of truth. No pasting ticket text into a chat window.

03 · the routes

The tempting first build was a chat assistant: copy the ticket, paste it, ask for a severity. It would have demoed well and rotted fast — pasted blobs lose structure, drift out of sync with the ticket, and can't be a system of record. The route taken instead: structured API data is the only input the model ever sees.

Contrast diagram: the rejected paste-into-chat path versus the chosen programmatic API path
the rejected path vs. the one that shipped

The second choice gets missed in most AI-tool conversations: the model is used only where the judgment actually lives. Severity classification — reading a thread plus live ticket data against the SLA rubric and citing which criteria fired — is the LLM call. Everything else (the rotation schedule, notifications, user-group updates, channel topics) is a deterministic engine on cron. No model where a rule will do.

04 · the architecture

Mention the bot in a thread and it pulls the full conversation, extracts the ticket keys, fetches each ticket from Jira in parallel, and sends thread + structured tickets + the four-level SLA rubric to the model — which returns a severity level with the specific objective criteria cited, posted back in-thread. Conversation memory makes follow-ups work: ask “what if it only affects one region?” and it re-assesses in context. Postgres holds rotation state, Redis caches, cron drives the morning and evening rotation checks.

Sequence diagram: Slack mention, thread pull, parallel Jira fetches, model assessment with cited criteria, threaded reply
one assessment, end to end — the rotation engine never touches a model

Runtime is OpenAI; Claude's role in this system is the build chain — the repo is specced and maintained through Claude Code, spec-first, like everything that came after it.

05 · the outcome

About 90% of manual routing eliminated. Severity recommendations land within ~5 minutes — against up to a full day before — and get accepted roughly 99% of the time. The bot handles hundreds of tickets a month and prevents dozens of SLA breaches monthly.

And it became the canonical internal reference — “see: how the t1 triage bot pulls ticket information” is how we describe the pattern to every tool that came after.

The LLM reads structured API data, not pasted blobs. Every tool we built after this one inherited that rule.

the insight
ran onNode + Express · Slack Bolt · Jira API · OpenAI API (runtime) · PostgreSQL + Prisma · Redis · built with Claude Code · production on AWS

← all work