Forty-seven open tickets, one morning, four hours. I made about ten real decisions. The other 37 I touched without deciding. I read them, scrolled, opened a second tab for context, opened a third tab for scope, lost my place, and moved on. By the time I had enough loaded to make a call, I’d spent the attention the call was supposed to get.
The triage wasn’t slow. The tab-thrash was. And the reason it stung that morning is that the slow part of triage, the analysis, had just gotten nearly free, and I could suddenly see how much of my time had never been analysis at all.
The math that doesn’t add up
Run the numbers on a single actionable ticket. Someone files a three-sentence report: a page is broken for one of their events. To decide what to do with that, I need the ticket itself, the actual page in the admin to see the breakage, a linked parent ticket to understand scope, and sometimes a quick field-state lookup to confirm what the data says. Four surfaces. Each one click away. Each click breaks the scanning rhythm and forces a context rebuild when I come back.
Multiply by 47 and the morning is gone. Not to thinking. To navigation, to losing my cursor in a list of 47 rows, to re-reading the ticket I’d already read because the tab I opened in between flushed it out of my head.
I’d run triage this way for years and assumed the cost was the analysis. Read the report, work out whether it’s a two-hour fix or a two-week rewrite, decide. That judgment felt like the expensive part, so the navigation overhead hid behind it. The two were fused into one motion: read, think, decide.
What broke the fusion
Then I started handing the investigation pass to Claude Code. Point it at a ticket and it reads the report, traces the relevant code path, inspects what the linked page actually renders, and comes back in about 30 seconds with a verdict and a rough effort estimate. Two-hour fix or two-week rewrite, with the reasoning.
That collapsed the analysis cost to near zero. And the moment it did, the motion split in two. Reading and deciding were never actually one operation. They only looked like one because both were slow and they happened back to back. Speed up reading to 30 seconds and you’re left staring at the other half: deciding still meant the same five-tab dance, except now the dance was 95% of the time per ticket instead of half of it.
None of this is specific to JIRA or to me. AI doesn’t remove a bottleneck, it moves it. Whatever was second-slowest is now first. If your workflow has a fast-thinking step feeding a slow-acting step, accelerating the thinking step just shoves all the pressure onto the acting step, and you feel it immediately. The win you expected doesn’t show up, because the constraint relocated while you were celebrating.
Investigation got cheap. Decision didn’t. So decision became the bottleneck, and my decision UI, a backlog page that could show me a ticket existed but couldn’t let me do anything to it without leaving, was the choke point.
Build the verbs, not the display
The fix was to turn the backlog page into an action surface. I rebuilt the project-manager home page so that each row carries its own context inline: the ticket notes, the comment stream, the linked child tickets, and a RICE breakdown that the AI investigation pass pre-populates. So far, so ordinary. Pulling data onto a page is the easy 20%, and it is not where the leverage was.
The leverage was in the verbs. The page had to let me act without navigating away:
- Accept a ticket into the active queue.
- Comment without opening a new tab.
- Reroute to a different component.
- Park it.
One click per decision. The page stays intact underneath. No full reload, no route change, no confirmation dialog that scrolls me away from my place in a 47-row list. The write goes out to JIRA over the REST API and the row updates in place. Where JIRA demands metadata for a transition, a priority change, a status move that requires fields, the page surfaces a small inline form right there in the row: pick the transition, fill what’s required, confirm. Still no navigation.
The triage motion that came out the other side: read the pre-loaded verdict, decide in ten seconds, click one button, drop to the next row. No tabs. No lost place. The 37 tickets I used to touch-without-deciding became decisions, because deciding stopped costing four context switches.
When you build tooling on top of a fast AI analysis layer, the verbs are the product, not the display. A gorgeous read-only dashboard that still makes you leave to act is a slightly nicer version of the bottleneck. It shows you the verdict faster and then strands you.
The constraint that taught me something
The action endpoints hid one trap that anyone on the same stack will hit. My first version did the correct thing: on a bad request the endpoints returned a 4xx with a clean JSON error body. In the browser, the button clicked and nothing happened. No toast, no console error I could see, just a dead button. It ate an hour because nothing surfaced.
The cause: IIS, depending on how httpErrors is configured, replaces a non-200 response body with its own HTML error page. Fine for a human hitting a broken URL, poison for a JSON API on the same host. The browser was getting HTML, my JavaScript was trying to parse it as JSON, throwing silently, and the button did nothing with zero feedback.
The fix was to make every action endpoint return HTTP 200 and carry success or failure in the body: a status field of ok or error, with a message on the error case. The client reads status before treating the response as a win.
That is not the design I’d pick on a clean system. It’s the design that works on the system I have. And the constraint forced a discipline I’d have been sloppy about otherwise: every action endpoint now has an explicit success/failure contract in its payload, independent of the HTTP layer. When you’re building the verbs, the verbs have to report back reliably, and “the framework silently ate my error response” is exactly the failure that turns a one-click action surface back into a guess-and-refresh surface.
The cheap fix that beat the expensive one
The single highest-leverage change that day wasn’t the action endpoints or the inline RICE. It was a 30-minute data-model split, and it’s the clearest example of fixing the real bottleneck instead of a flashy adjacent one.
I had one status, Parked, doing two jobs. Sometimes Parked meant “I deliberately deprioritized this, it’s mine to pick back up whenever.” Sometimes it meant “I’m blocked waiting on someone else to do something.” Those look identical in a list and they are completely different. In one I own the next move; in the other I don’t. Lumping them together meant the question that drives a whole triage session, “what do I actually work today,” had no clean answer.
So I split it. Parked = I own the next move, I just chose not to take it yet; it gets a reason note. Waiting = they own the next move; it gets an idle timestamp so I can see how long it’s been stuck. Now “what do I work today” has a definition the page can compute: everything active, plus any Waiting ticket older than 48 hours. That filter is only expressible because the two states are separate.
Thirty minutes of modeling beat every shiny feature I shipped that day, because it modeled intent, who owns the next move, instead of appearance, how the ticket looks in the list.
Measure the right number
The trap with AI tooling is measuring the thing that got fast. Tickets viewed per hour went through the roof the day Claude started doing investigation. That number is a vanity metric. It went up precisely because I was viewing tickets without deciding on them, which is the failure, not the win.
The number that matters is decisions per hour. By that measure, before the cockpit I was at roughly ten in four hours of active triage. After, with the verbs inline and the page intact underneath, the whole backlog cleared in a fraction of that, because each decision cost one click instead of four context switches.
Related
- Trading Badge Soup for One Human Sentence: collapsing status complexity into one actionable signal per ticket
- When Working Notes Become Operational Infrastructure: how AI transforms personal workflows into structured tooling
- An Evening of Progressive Disclosure: Five Commits to Hide Everything Behind ‘Details’: collapsing UI complexity so each row stays scannable
- I Was About to Write a daily_digest.py. The Platform Already Shipped It.: building tooling on top of existing platform capabilities