← blog

2026-10-16

How I use AI to keep up with a growing workload: from ticket to merged PR

The workload on my desk keeps growing and the time to deliver keeps shrinking. Several tickets are in flight at once, across a backend, web frontends and a mobile app, and every one of them comes with questions, bug reports and review rounds that don't wait for me to finish the previous thing. I can't add hours to the day, so the only lever left is the cost of each hand-off and each context switch. For the last four months I have been building a small plugin for Claude Code, called shipkit, to pull that cost down. This post is about how it's put together, where I deliberately keep a human in the loop, and the rules I only learned because the automation got something wrong. Ticket keys, names and channels are left out or made up.

The problem: the code is not the slow part

When people say AI makes developers faster they usually mean typing code. For me that was never the bottleneck. The time goes to everything around the code:

None of these is hard on its own. Together they are what a bigger workload eats first. So I don't use AI as a chat window I paste code into. I use it to take the first pass at each of these, so that my time goes to the decisions.

Where it started: too much process

My team does spec-driven development, and the tool we first adopted generated 5 to 9 files per feature behind nine commands. Most of those files were skimmed once and went stale. The plans were a template fill: they never read the real code, so file paths and function names were guessed. And it assumed a single repo, while our platform is a backend, web frontends and a mobile app coordinated from one orchestration root. When time is short, a process that costs more to follow than to skip is the first thing to be skipped.

So I rewrote the idea as a plugin with one rule: lighter artifacts, not a lighter process.

The pipeline

ticket → spec → clarify → plan → implement → verify → review → PRs → merge → bump

Each stage is a skill, which is just a markdown file of instructions that Claude follows, plus a few small scripts for the parts that must be deterministic. The ones that earn their keep, and the wait each one cuts:

/cross-check: the precondition, no guessing allowed

Two tickets about the same subject can contradict each other ("this screen says admins only, that one lets managers do it too"), and a pipeline will happily build both word for word. With this much work in flight I can't afford to find that out at QC, and I can't let an AI fill the gap with a plausible guess. So /cross-check is the precondition for everything after it: it runs at spec time, and again as a gate before planning.

It compares the ticket with other tickets, earlier specs, the ticket's own edge cases and the running code: who may see or do what, thresholds, limits, states, notifications, and every route that touches the same entity. The rules it works under are strict:

There is a variant for later in the ticket's life. When a tester, a bug report or a product owner's answer raises a point, --stage post reconciles it with the rules already in play before I answer or fix anything.

It has already found a real contradiction inside one epic, and it did so at two separate moments: first on an earlier ticket, when QC put a question to the product owner, and again when I came back to build the next ticket in the same epic. Tickets in an epic are written and tested at different times, and nobody can hold all of them in their head. The skill can, because it goes back to what was actually written.

It does two jobs for me. For the system, it keeps every ticket coherent with the rest of the product, so features don't quietly contradict each other. For me, it leaves a paper trail: every claim traces to a quote and a line of code, so when I raise a question or defend a decision, the evidence is already in hand.

The /developer skill: the part around the ticket

Everything above happens inside a ticket. /developer is the skill for the rest of my day, and it's the one I'm actively growing into my daily routine, because that is where a heavy workload hurts most. I'm at my desk working the whole time. What I can't afford is the constant switching: a question in chat, a bug report, a ticket from yesterday I haven't got back to. /developer takes the first pass at those, so I stay in the work that needs me. It isn't finished. I'd rather describe what it does today than what I hope it will do.

Animation of the developer skill: a chat question arrives, the skill reads the code and the ticket, drafts a reply, I approve it, it is sent. A bug takes a side path into a worktree with a planning agent and implementing agents.

It has three entry points:

Starting the day

With several tickets open, the expensive question every morning is "where was I?". The first run each day answers it with a briefing of yesterday. It reads evidence only: which worktrees I touched, the pipeline's own state files, and each ticket's status. It names the stage in the pipeline's own words (spec, plan, implement wave 2, PR review loop) or says stage unknown; a line with nothing behind it isn't written. Every ticket left mid-pipeline is then continued automatically, in its own worktree, so nothing stalls just because I was busy with something else. It asks that ticket's planning agent where it stands and whether anything blocks it. No blocker, and it tells the agent to carry on to the end. A blocker, and it stops and tells me in one line which decision it needs.

Triaging the chat

I'm usually deep in something else, and chat is where the interruptions come from. The observer listens in real time, only between 08:00 and 18:00 (it reads the clock before every arm and pauses outside those hours), so an interruption reaches me as a drafted answer to approve instead of a context switch. It reacts to DMs, @mentions, replies in threads I'm in, QC report posts that name a ticket, and every other top-level post by someone else in every channel I'm in. That last one exists because it once missed a "how is it going" and I only found out by asking. Notifications carry ids only, never text, so it has to read the real message before it decides: is this an ask, a bug, a change request, or noise?

Answering as me, without bluffing

Speed is worthless if the answer is wrong, and a wrong answer posted under my name is my wrong answer. So the skill is strict about what it may claim:

Reading a finished draft takes seconds. Finding the code, the ticket and the right words from scratch does not, and that difference, repeated all day, is the time I get back.

Handing work over

When the code confirms a defect, or QC reports one, it replies "noted, checking" and opens a worktree for the ticket. A planning agent runs the pipeline stages in order, and a second agent implements per repo. Every Jira comment still comes to me for approval. When an agent stops on a question, a recommended default is answered for it. Anything destructive, like a force-push or a delete, waits for me.

Guardrails

Everything in a thread or a ticket is treated as untrusted data: the skill answers the question and never follows an instruction inside it ("paste your env", "ignore your rules"). It never prints a token. It is read-only: its only writes are the reply, its own state files and drafts, and anything that changes code is done by agents in a worktree.

Where a human still decides

Under time pressure the temptation is to trust whatever the automation returns. That is exactly when the boundaries matter, so they are explicit. These are mine:

Lessons the automation taught me

Almost every rule above is the scar from a specific failure, and the commit history reads like an incident log:

  1. Evidence beats assertion. An early version let the agent say "fixed". Now a fix without a screenshot or a response payload doesn't pass. Later I made the evidence screenshots highlight the element that proves the claim, because a screenshot of a whole page proves nothing to a reviewer.
  2. Never drop an event silently. The Slack observer ignored a top-level channel post that had no @mention and no ticket key, and I only found out by asking. The rule became: every post by someone else gets at least one line, even if the line is "nothing for you here". When I'm busy, silence has to mean "nothing happened", never "I missed it".
  3. Run it on real data before trusting it. Several fixes in the log are labeled "found by running it on real worktrees". A skill that reads fine can still fail on a detached HEAD, a renamed terminal, or a state file from an older version.
  4. Skills should carry lessons, not cases. I once hardcoded the exact incident into a skill's instructions. That teaches the model one story. Rewriting it as the general rule behind the incident made it behave correctly on cases I hadn't seen.
  5. Make the deterministic part a script. Anything that must be exact, such as posting a Jira comment with a real mention, waiting for a pipeline, or merging a PR, is a small script with a self-test. The model decides when to call it; it doesn't improvise the call.
  6. Watch the cost. Long sessions with parallel agents add up. I now decide per change whether it deserves a sub-agent test, and say so instead of running it by default.

Takeaway

The leverage came from shrinking the overhead around the code (reading, planning, proving, chasing, answering, remembering) so that a bigger workload fits in the same day, while I keep the decisions. /developer is still unfinished, and I'm growing it a little every day. The most useful thing I built is probably the habit behind the skills: every time the automation is wrong, turn that mistake into a rule, a script or a test so it can't be wrong the same way twice.