May 20, 20267 min read

The Expensive Part of a Ticket Is What Was Never Written Down

I built a Slack bot that asks the clarifying questions before a Linear ticket exists, so the ticket shows up ready to build.


The Expensive Part of a Ticket Is What Was Never Written Down

A ticket that says “we need a dashboard for campaign performance” is not a spec. It is a guess.

Leadership files it. Engineering picks it up. Then the real work starts: what metrics, for whom, compared to what, which campaigns, which environments, what does “done” look like?

None of that shows up in Linear at first. It lives in side conversations. And every minute spent chasing context is a minute not spent building.

I built this system because I lived in that loop.

Where it started

The first version was aimed at a specific pattern: people in leadership roles introducing new features without enough context.

Not malice. Not carelessness. They have the vision in their head. They are moving fast. The ticket is a pointer: “build this.”

For engineering, that pointer is not enough.

I did not have direct access to stakeholders. I could not walk over and ask five clarifying questions before lunch. I got a Linear issue, a Slack ping, maybe a one-liner in a meeting. Then I had to figure out what to build from that.

So I asked. A lot.

Who is the user? What is in scope for v1? What already exists? What breaks if we ship the wrong thing? What does success look like?

Every new feature request turned into an interview I was running alone, after the work was already “assigned.”

That is the bottleneck most teams do not measure. Implementation is cheap compared to discovering what was actually requested.

The real bottleneck

We treated ticket creation as the finish line.

Describe the idea. Hit submit. Move on.

But for engineers, submit was the starting gun for a second job: translate a vague request into something buildable. Expected behavior. Constraints. Acceptance criteria. Edge cases.

The ticket looked done. The work was not.

The pattern shows up everywhere. Bugs with no repro steps. Features with no boundaries. “Make it like the competitor” with no link to which flow.

And the cost compounds. One unclear ticket blocks a sprint slot. A developer context-switches into detective mode. PMs get pulled in. Sometimes the person who had the original intent is in back-to-back meetings when the questions finally land.

The interesting part is not that leaders or reporters are lazy. They are often the ones with the most context. They just do not put it on the page.

They assume engineering will “figure it out.”

We cannot. Not without guessing. And guessing is how you ship the wrong thing confidently.

That’s where things get uncomfortable. The queue is full of work that is not ready to be worked. It is placeholders for conversations we have not had yet.

What I wanted instead

I did not want another form with twelve required fields. Leadership will not fill out a spec template at 9pm before a board prep.

I wanted the clarification to feel like a normal Slack thread. Someone describes the feature or problem in their own words. The bot pushes back with specific questions. Only when there is enough signal does it open Linear with a structured description.

The goal was simple:

  • Ask the questions I used to ask before the ticket exists

  • Capture answers while the requester still has the details fresh

  • Give engineering a ticket you can actually start from, even without a stakeholder on speed dial

The bot is not there to replace product managers or engineers. It is there to front-load the discovery work that used to start after filing.

If I could not get stakeholders in the room, I could at least get their answers into the issue.

How it works

You @mention the bot in a channel or DM it. You describe the feature, change, or bug in plain language. You can attach screenshots or mocks. The bot keeps the thread as memory.

Behind that conversation, the system reads your GitHub repo (indexed in Supabase) and routes the request to a domain specialist: auth, billing, infra, campaigns, and so on. Each specialist has knowledge generated from the actual codebase, not a generic intake script.

That matters for feature requests. “Add export for campaigns” means something different depending on which module owns campaigns, how exports work today, and what already exists. The bot can ground questions in real code paths, not generic PM checklists.

End-to-end workflow: user message in Slack, bot clarifies or files, repo sync, and routing in the background

End-to-end workflow: user message in Slack, bot clarifies or files, repo sync, and routing in the background

The specialist does not file immediately. It scores its own confidence. Below the threshold, it returns one to three targeted questions: who needs this, what problem it solves, what is in and out of scope, what “done” looks like, what to verify.

You answer in the thread. It asks again if needed. When confidence is high enough, it creates the Linear issue with sections already filled in: description, expected behavior, technical context, acceptance criteria. When it has enough code context, it can even suggest implementation steps and affected files.

That loop is the product.

RAG and embeddings are underneath. They help the bot answer codebase questions and ground specialists in real files. They are not the point. The point is a ticket that arrives with the conversation already had, so the engineer picking it up is not the first person asking “what did you mean?”

Supabase documents table: embedded code chunks from the repo used to ground answers and context

Supabase documents table: embedded code chunks from the repo used to ground answers and context

Supabase agent_configs table: multiple specialists per repo, each with domain knowledge synced from code

Supabase agent_configs table: multiple specialists per repo, each with domain knowledge synced from code

What it looks like in Slack

For quick questions (”where is X handled?”, “why does this API return 403?”), the bot searches the repo and answers in the thread. That is a side path. Useful, but not why I built it.

The main bet is the ticket path: feature requests and changes that need shape before they land in the backlog.

Simple example describing a new feature, bot asks numbered clarifying questions before filing

Simple example describing a new feature, bot asks numbered clarifying questions before filing

When someone drops a screenshot or a mock with almost no text, the bot describes what it sees and folds that into the thread. Requesters do not have to write a full spec. The bot still pulls out enough to ask the right follow-ups.

Example of ticket creation using an uploaded image

Example of ticket creation using an uploaded image

What changed

The shift is behavioral, not magical.

Leadership still initiates from Slack. But “request a feature” now means finish the thread with the bot, not drop a headline into Linear and disappear.

Engineers get issues that read like someone already ran the first discovery pass. Less “can you hop on a call?” Less starting from a title and a prayer. More time building what was actually asked for.

It will not fix prioritization. It will not replace a PM when the problem needs real product judgment.

It does fix a failure mode I hit constantly: no access to stakeholders, but full responsibility to build the right thing.

The bot does not give you a seat at the strategy table. It forces the table’s questions into the ticket before engineering pays the price.

The principle

Ticket tools are optimized for tracking work, not for earning the right to be worked on.

When new features arrive thin on context, the problem is not developer speed. It is intake. Someone has to ask the questions. Better that happens before the issue is “in progress,” not after.

Ask before you file. Put the context in the ticket, not in the comments three days later.

That is the system I built: a gate between “we should build X” and “engineering knows what X means.”

Not because RAG is exciting. Because I was tired of being the only person asking the obvious questions, too late, without the people in the room.