john mark lowry
§ ai tools · 2026

intake bot

Product owners were the intake queue — every DM and hallway ask landed on them, and the work entered the backlog already shaped wrong.

built at team one. internal identifiers, names, and screenshots sanitized or pending clearance.

01 · the moment

New work reached the product team through every door at once: Slack DMs, hallway conversations, one-off meetings — from account, strategy, production, creative, and clients. No single intake door, no consistent capture of business context, no routing layer between “someone wants something” and “the work is in a backlog.”

The symptoms compounded. POs spent real time triaging vague requests over DM. Quarterly priorities destabilized the moment post-planning requests arrived. Engineers discovered requirements mid-sprint. And the twice-weekly triage meetings were quietly doing discovery — reconstructing what a request even was — instead of making decisions.

02 · the reframe

It read like a staffing problem: too many requests, not enough people to triage them. It was an information-structuring problem — requests arrived without the structure a good decision needs, so every downstream step paid to reconstruct it. And the answer was not “make a form.” A form is a Google Form with extra steps; people route around it or type “make it better” and move on. The goal was to move the discovery conversation itself to the front door. The thing that probes a vague request until it's real shouldn't be a PO's afternoon — it should be a tool with infinite patience.

03 · the routes

A persona with standards, not a form with fields. The bot operates as a director-level product owner: direct, curious, unwilling to accept vague answers. “Make it better” gets “what specifically would change — what would a user notice?” “ASAP” gets “what's the hard date, and what breaks if it slips two weeks?” There's no retry cap; it probes until the answer is substantive or the user takes an explicit exit. For net-new work it forces the conversation no one wants to have at intake: if this becomes a priority, something has to move — what's this worth relative to what's already committed?

Deterministic ticket creation — the most important architectural decision. The LLM does not decide when to create a ticket. A side-effecting action should never be hostage to a probabilistic system. A state machine checks field completeness after every turn; when the gate is satisfied, the conversation transitions to pending confirmation; the draft preview is built by the system from the database, not by the model; and the yes/no is detected deterministically, without an LLM call.

Sequence diagram: the LLM captures fields, but the state machine checks completeness, builds the preview from the database, and creates the ticket deterministically
the creation decision — note where the llm isn't

Three honest exit paths. Real intake is rarely finished in one sitting. Complete intake produces a structured ticket; blocked-but-wants-visibility produces a draft ticket with the missing fields enumerated, which the bot later updates in place rather than duplicating; save-and-resume issues a magic link with no ticket at all. One conversation creates at most one ticket — ever.

And a lesson paid for in production: the first trigger system used keyword phrasing, and it silently ignored a real colleague's real request. The fix was an LLM intent-classification pass on incoming messages — the model normalizes what humans actually type; brittle string matching doesn't get a second chance at the front door.

State diagram of the three exit paths: completed ticket, draft ticket updated in place on resume, and save-for-later
three exits, no duplicates
04 · the architecture

Five entry points — web form, two slash commands, @mention, DM — feed one backend. Slack traffic is HMAC-verified and de-duplicated, then a router splits intake from work-ticket flows into two engines sharing one LLM layer: a prompt builder assembles the system prompt from the knowledge spine plus live synced data, the model probes and emits captured fields in tagged blocks, the system validates and upserts them per field, and the deterministic confirmation step gates creation. The Jira client resolves reporter and assignee from a synced roster, links epics, and retries with backoff. Everything persists to Postgres; every action writes to an append-only audit log.

System architecture: five entry points through Slack verification into a conversation router, two engines, a shared LLM layer fed by the knowledge spine and sync services, deterministic confirmation, and the Jira client
one backend, five doors, two engines — and the llm behind a provider interface, swappable by config

The quiet decision that pays compound interest: the bot's domain knowledge — platform systems, org structure, ticket conventions, ranked priorities — lives in a version-controlled markdown repo the product team already maintains for its other AI tools. The bot is a consumer, never a producer: no write-back, no duplicated copy, fresh on a refresh cadence plus a push webhook. Its knowledge stays current because the humans keeping it accurate are doing so anyway.

The shared knowledge spine: one version-controlled markdown repo feeding multiple AI tools, with the bot as consumer, never producer
the shared knowledge spine — one source, many tools

And the same conversation core powers a second product: a slash command and @mention that read an existing Slack thread, infer the issue type, draft a properly formatted ticket, and route it to the right team. Same spine — probe, capture, confirm deterministically, create — pointed at a different job.

05 · the outcome

In production and in active daily use, with real-user feedback driving iteration — and built against a 200+ item acceptance criteria document written before any code. The design targets, stated honestly as targets: 90% of new product and engineering requests through intake within 60 days of launch, zero PO DMs for new work within 90 days, every non-draft ticket carrying substantive answers to every required field — and triage meetings shifted measurably from discovery to decision.

The way to scale a senior person's judgment isn't to clone the person — it's to encode the questions they'd ask and the trade-offs they'd force. The LLM runs the conversation; the state machine owns the consequences.

the insight
ran onTypeScript + Node (Fastify) · React + Vite · PostgreSQL · LLM behind a provider interface · Slack HTTP events + HMAC verification · Jira REST v3 (ADF) · production on AWS

← all work