Inspiration
A bug is usually fixed where it was reported. The flaw behind it, such as dates formatted in the viewer's timezone, one-cent rounding errors or a missing idempotency key, lives on in every other place that shares the pattern, and returns as a new ticket a month later. The knowledge of why the first one happened sits in a closed ticket and in someone's head. We wanted the ticket history to work as a team memory: fix a bug once, everywhere.
What it does
When a bug is filed or triaged, Bug-Class Memory:
- Compares it with the resolved bugs on your Jira site and ranks the similar ones.
- If several earlier fixes share a bug class (for example "UTC date truncation"), says so, with the root causes, the patterns to search the code for and the files touched before.
- After the fix, records the root cause, class, pattern and files on the issue with one call, so the next bug of that class is found faster.
It is deliberately cautious. A class is only called "likely" when the evidence is strong. A single stray shared word shows up as a weak match, with no class and no checklist, and nothing is posted on a bug without a strong match.
It works in five places, on one engine:
- Issue panel (
jira:issuePanel): the likely class, similar fixes, search patterns, a checklist, and a Record this fix form. - New-bug hint (
triggeronavi:jira:created:issue): nobody has to remember to open the panel. When a Bug is created and a class is likely, the app comments within seconds with the class, the earlier fixes, what to search for and the files touched before. Weak matches, other issue types and resolved bugs get nothing. - Project page (
jira:projectPage): which kinds of bug keep coming back in a project, how many of its resolved bugs have a fix record, and which open bugs already match a class. - Rovo agent (
rovo:agentwith arovo:skill): Bug-Class Triage answers "is this the same kind of bug as one we fixed before?", records a fix on request, and backfills: it reads closed bugs that have no record, proposes a root cause and class for each with the sentence it relied on, and records nothing until a person confirms. Rovo asks the human to confirm before every write. - MCP server (
rovo:mcp):find-similar-fixes,list-unrecorded-bugsandrecord-fixare exposed so a coding agent that already has your repository can call them while it fixes the bug: triage, search the code for the returned patterns, fix every sibling site, then report the fix back. Jira cannot read your repository, which is exactly why this route exists. The same Agent Skills file that the Rovo agent uses is published in the repository for Claude Code and other agents.
In the video, a coding agent triages a real bug, finds the two files that still have the flaw, fixes them, leaves alone the files fixed before, and records the fix on the Jira ticket.
How this differs from asking Rovo. Rovo can already search tickets, and we tested it: on a bug with no record of its own, the default Rovo chat found the right class too, but it took about three minutes and offered generic search patterns instead of the ones recorded from the team's own fixes. Bug-Class Memory answers in under a second from a structured record, says "no close match" when there is none, comments on a bug the moment it is filed, shows the project-level picture, and lets the developer's coding agent do the code search. The records it writes also make Rovo's own answers better.
How we built it, and how it meets the Forge requirement
Bug-Class Memory is a Forge app that runs entirely on Atlassian's platform:
| Forge module | Use |
|---|---|
jira:issuePanel (UI Kit) |
The triage panel and the Record-fix form |
jira:projectPage (UI Kit) |
Recurring classes, record coverage and open bugs per project |
trigger |
The hint on a newly created Bug |
rovo:agent |
Bug-Class Triage, with three actions as tools |
rovo:skill |
The Agent Skills file that teaches the triage, search, fix, record loop and the backfill |
rovo:mcp |
The same three tools for external coding agents |
action x3 |
find-similar-fixes (read), list-unrecorded-bugs (read), record-fix (write) |
function |
The resolver, the action handlers and the trigger handler |
Scopes: read:jira-work and write:jira-work only. The app adds comments and a label: when a fix is recorded, and a hint on a new Bug with a likely class.
- Jira is the store. A fix record is a few labelled lines in a comment (
Root cause,Class,Fix,Files,Pattern) plus abugclass-...label. There is no Forge storage, no external server and no copy of your data. The records are readable by people, and bugs resolved before the app existed are read too if their comments use the same labels, or after the backfill. - One engine. The panel, the project page, the hint, the agent and the MCP server call the same functions, so behaviour is identical everywhere. Calls run as the invoking user, so Jira permissions decide what is visible. The one exception is the new-bug trigger, which has no user and runs as the app. Errors come back as data, so the model can tell the user what to correct.
- The hint is never a record. It quotes the root causes of earlier bugs, so it carries a marker and the record parser skips it. Unit tests cover a hint next to a real record in both orders.
- Ranking. An IDF-weighted cosine over word sets, so rare shared words (
cent,retry) count more than common ones. Matches are grouped by class, so repeated evidence beats one slightly better match. A class needs a score of at least 0.18: on the demo data, junk bugs peak near 0.10 and real repeats start at 0.26. The project page uses the same ranking and threshold as the panel. - "Resolved" means status category Done, not the resolution field, because many workflows never set one.
- 37 unit tests cover the record format, the ranking, the backfill, the hint and the project overview. The app passes
forge lint, and the Forge CLI reports it as eligible for Runs on Atlassian.
Challenges we ran into
- A manifest cannot mix Bitbucket workspace scopes with Jira site scopes, and Forge storage is per installation. Our first design (a Bitbucket trigger writes, Jira reads) was impossible. We pivoted to a Jira-only app and used the developer's own coding agent, through MCP, for the code side.
- Precision. Our first version called a "likely class" on junk bugs that shared one word. We added a calibrated threshold and a
confidenceresult (strong, weak, none), and the panel now says "No close match" instead of inventing a class. - Making the agent safe to write. Recording a fix writes to Jira, so the Rovo agent asks the user to confirm first, backfill proposals are shown with the sentence they rely on, and every tool returns structured errors the model can act on.
- Events have no user. The new-bug trigger cannot run as the user, so we proved it can read, search and comment as the app on a team-managed project before building on it, and kept every other path running as the user.
- A hint that quotes other bugs can corrupt the memory. We made it unreadable as a record and kept it out of the backfill's evidence.
Accomplishments we're proud of
- One engine behind five surfaces, all verified end to end on a live site, including all three ways of recording a fix.
- A data model that needs no database, no server and no data copy: the memory lives in the tickets people already write.
- A tool that is honest about uncertainty, and about how it compares to what Rovo already does.
What we learned
Forge's constraints shaped a better design: the memory belongs where the team already writes, and the agent that has the code should do the code search. rovo:mcp turns a Jira app into a tool for any coding agent, and an Agent Skills file lets the same instructions serve Rovo and a coding agent.
What's next
- A per-project switch for the new-bug hint.
- Semantic ranking alongside the lexical one, for bugs described in very different words.
- Reading Bitbucket pull requests (a separate app, because of the scope rule above) to link fixes to the code they changed.
- Marketplace listing.
Built With
- agent-skills
- atlassian-forge
- atlassian-mcp
- bitbucket
- forge-ui-kit
- javascript
- jira-cloud
- mcp
- node.js
- rovo
- rovo-agents
- rovo-skills
Log in or sign up for Devpost to join the conversation.