Alle Beiträge

From Jira Ticket to Merge Request: An AI Pipeline with Claude Code

How Claude Code reads tickets, implements them, tests, attaches screenshots and opens merge requests on a real project – and the guardrails that make sure a human still has the final say.

· 8 Min. Lesezeit

AI-generated
Illustration of an AI pipeline: ticket, Claude Code, quality gate (pnpm check), screenshots, merge request and human review as connected stations; a dashed arrow labelled [ai:claude-code] leads from the review back to Claude Code.
Note: This post was also written with Claude – from inside the repository it describes – and saved to the CMS as a draft through this website's MCP server.

The last post was about how this website is built and shipped. This one is about a bigger project – or rather not the project itself, but the way the work on it gets done: a large share of the implementation runs through Claude Code, embedded in a pipeline of Jira, Git, CI and reviews.

The short version: the AI writes a lot of code. But it works inside a frame of rules, scripts and automatic checks built so that mistakes show up before they are merged – and so that business decisions always stay with a human.

The setting

Technically it is an Nx monorepo with an Angular frontend and a NestJS backend. The requirements are a document with about 250 numbered items, and the backlog holds roughly 150 epics and stories. Big enough to lose track quickly without structure – for a human as much as for an AI.

The core idea: the repo explains itself

The heart of the setup is unspectacular: a CLAUDE.md in the repo root. Claude Code reads it automatically at the start of every session. By now it is a fairly thick document – on purpose.

It contains not just the stack and commands but above all the why behind decisions:

  • Why the Nx daemon is disabled (its cold start took up to 31 minutes on Windows).
  • Why prisma db push is off limits (it once wiped test data).
  • Why request-scoped providers in NestJS are a mistake here (CQRS handlers don't see the request – and the symptom looked like a login problem).
  • Why a path alias in the frontend was rolled back.

Every one of these rules came from a real mistake. A model that only reads "don't do X" finds a creative way around it. One that knows the reason also recognises the variant nobody thought of when the rule was written.

For the backend, CLAUDE.md also fixes a clear layering: controller → service façade → command/query bus → handler → repository port → Prisma adapter. Nothing outside the persistence folder may import the ORM client. Rules like this matter even more for an AI than for people: otherwise it simply copies whatever the nearest code does.

Slash commands: workflows instead of prompts

Recurring procedures live as slash commands in .claude/commands/ – Markdown files that describe a whole workflow:

  • /ticket PROJ-123 – work a Jira ticket end to end.
  • /check-comments – find unanswered human comments on tickets and turn them into a plan.
  • /standup – put together the daily standup from Jira status and Git activity.
  • /backlog-sync – mirror the backlog from a YAML file into Jira.

On top of that there are skills (add-feature, add-ui-component) for scaffolding by project convention, and a review subagent that checks changed frontend code against the conventions without changing anything itself.

/ticket: the life of a ticket

The most important command is /ticket. It has five phases, and none may be skipped:

  1. Understand. Read the ticket and its comments, check blocking tickets, look up the referenced requirements. If ticket and requirement disagree, the requirement wins – and the deviation is commented on the ticket. If the ticket contains an open question: don't guess, ask.
  2. Plan. A short plan as a ticket comment: affected files, migrations, endpoints, testing approach. Above 8 story points I have to approve the plan before any code is written. Then: ticket to "In Progress", create a feature branch.
  3. Implement. Schema first, then tests before or alongside the code. Acceptance criteria of the form "GIVEN … WHEN … THEN …" become test cases one to one – the numbers in the ticket are expected values, not examples.
  4. Verify. pnpm check must be green. Then each acceptance criterion gets its own answer: met, partly met, not met, not verifiable. Reporting "not checked" as "met" is explicitly a failure.
  5. Hand over. Push the branch, open a merge request, comment the criteria list on the ticket and move it to "QA".

And then there is a "Never" list: never move a ticket to "Done", never merge yourself, never push to main, never comment out a test to make things green, never reword an acceptance criterion so it fits. Acceptance is done by a human.

Screenshots as evidence

One rule has paid off in particular: every visible change needs screenshots on the ticket – always at 1440 px and 375 px width. One Playwright script captures the page, another attaches the images to the Jira ticket.

The point is falsifiability. "Form implemented cleanly" can't be disproved; a picture can. The very first screenshot in the project immediately exposed a bug that had slipped through code review: a form showed every validation error while still untouched.

Importantly, Claude has to look at every screenshot itself before attaching it. A blank or broken image is worse than none – it looks like proof.

Who wrote this comment?

A problem I underestimated: everything written to Jira from the repo goes through my account. Author and avatar are always mine – whether I wrote the text or the AI did.

That is not just about transparency. Claude reads comments back to pick up new instructions. Without a way to tell them apart, it would have read its own status reports as fresh instructions from a human on the next run – and acted on them.

The fix is a marker every AI comment starts with:

🤖 **Claude Code** `[ai:claude-code]`
Codebeispiel (plaintext)

A small script (comments.mjs) uses it to classify comments as AI or HUMAN, can show only unanswered human input, and with --scan finds every open ticket where someone is still waiting for an answer. Comments from before the marker existed show up as HUMAN? with a warning – because, as it turned out, every "open" comment on five old tickets was actually a status report by Claude itself.

Whatever is left becomes a ticket

Another rule: anything Claude notices but deliberately doesn't fix becomes a Jira ticket before the current ticket is handed over. A finding that only lives in a comment or a TODO is gone in two weeks.

A script creates the follow-up ticket, links it and moves it straight to "To Do". That last part is not a detail: Jira creates new issues in the initial status, which on our board is the backlog column nobody triages. A follow-up there would be just as lost as a comment.

There is also a docs/tickets.md mirroring the current Jira state, so later sessions (and /standup) know what is open. It must be updated in the same pass as every status change – after it once silently went stale for over a week and listed thirteen tickets as "QA" that had long been done.

Backlog as code

The backlog doesn't live in Jira but in a backlog.yaml in the repo. A sync script mirrors it into Jira idempotently: epics before stories, recognises existing issues by a label, never touches status, assignees or comments, and never deletes anything.

The reason is very practical: creating around 150 tickets through individual MCP tool calls costs many times more tokens than a single script run. For single tickets that need judgement, Claude still uses the Atlassian MCP directly.

The pipeline: one command, two CI systems

The quality gate is a single command: pnpm ci:verify – lint, warning budget, formatting, all unit tests and all builds. pnpm check is an alias for it. Locally, for Claude and in CI, exactly the same thing runs. On top of that, pnpm ci:integration runs the tests that need a real PostgreSQL database – everything mocks can't prove, such as data staying cleanly separated.

The rule: new checks always go into package.json, never into just one CI file. Otherwise two CI systems eventually check different things without anyone noticing.

Two CI systems because the project moved: GitLab is now the source of truth – merge requests, build, Docker images, deployment by tag. GitHub is only a push mirror. That was necessary because the gate has to run before the merge, and the only pipeline that ran reliably was GitLab's. The /ticket command was made forge-agnostic accordingly: whether a pull request or a merge request is created depends only on where origin points.

The linter as a signal

ESLint warnings don't fail CI. That means their count can only grow – and it did, to 49 in the backend, without anyone noticing.

Now there is an .eslint-budget.json with a ceiling per project and a script that enforces it in the gate. The number may only go down: whoever fixes warnings lowers the budget in the same merge request. Raising it to turn a red check green stands out. The backend is at 25 now.

With an AI on the team, a ratchet like this is gold: it has no qualms about ignoring a warning as long as the build is green.

What I learned

  • Rules need reasons. The best sections of CLAUDE.md briefly tell what went wrong. That prevents not only the same mistake but its relatives too.
  • Prose is not evidence. Screenshots, test cases from acceptance criteria and a criteria list that says "not checked" are more honest than any summary.
  • Provenance is not a nice-to-have. As soon as an agent reads its own text back, it has to be able to recognise it.
  • If it's not in a ticket, it doesn't exist. True for people, and even more so for an AI without memory between sessions.
  • The AI doesn't do acceptance. It implements, tests and reports. Whether something is right from a business point of view – especially calculation logic – is checked by a human, and at the end Claude names the test cases to recalculate.

Conclusion

The biggest lever wasn't a particularly clever prompt but infrastructure around the model: a repo that explains itself, workflows as files, scripts for everything mechanical, and a gate nobody can bypass – the AI included. Inside that frame Claude Code is a surprisingly reliable team member. Outside of it, it would just be fast.

© 2026 Eme. See you space cowboy 2e78c88