Skip to content
| Marketplace
Sign in
Visual Studio Code>Visualization>JIRA Triage LedgerNew to Visual Studio Code? Get it now.
JIRA Triage Ledger

JIRA Triage Ledger

BW Digital Systems & Publishing

|
1 install
| (0) | Free
Runs the triage cycle over a JIRA backlog and enforces what a prompt cannot: repositories refreshed before anything is read, every verdict kept in a ledger the team shares, nothing posted without a tick.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

JIRA Triage Ledger

Runs the triage → report → comment → state cycle over a JIRA backlog, in whatever project the editor has open.

It is not a JIRA client and it does not replace anybody's judgement. It does the part a prompt cannot: it prepares a deterministic input, enforces the steps, keeps state the whole team shares, and shows the result.

What it does

Renders a report as something to work with. A report written to one of the forms is one scrolling page: the verdict and the figures at the top, a row of anchors under them, and the sections in the order they are acted on, each drawn by what the form says it is – a table of load as bars, a list of items with named fields, a line of comments to send. A word the form knows is coloured; a word it does not know is left plain rather than guessed into a colour. A report in the older lettered shape opens as before: one tab per section, keyed on the section letter so a report and its translation are read the same way, with verdict tables painted rather than reproduced – the decision cell is a pill, the decisions are counted into tiles above the grid, and the long columns are clamped to two lines so the eye can travel down the column that matters. A table fills the panel and wraps inside it: no table scrolls sideways and neither does the page, because a reader who has to drag a bar to reach the last column loses the column the row is about. Each column is as wide as what it holds: a key, a figure or a name is drawn exactly as wide as it is, a word is never broken in the middle, and the rest of the width goes where it makes the table shortest – in every report, in the panel and in a saved page.

Opens a report in the same tick as the click. The tree node carries the path, the file is read synchronously, and the Markdown is rendered by a renderer compiled into the bundle. No loading screen, nothing waiting on a scan.

Separates "we looked and there is nothing" from "nobody looked". A group nobody established renders differently from an empty one and never shows a zero. An empty list read as a real zero is a wrong answer that looks like a right one.

Will not run on sources nobody fetched. The remote-tracking refs are refreshed first, because ahead/behind figures are exactly as old as your last fetch — without that, a checkout nobody has fetched reports level whatever the server holds, and the report describes a tree the team does not have. Repositories that are behind are fast-forwarded; one that will not fast-forward is reported rather than merged, and a dirty one is never touched at all. A repository that is behind, unreadable, at odds with its remote or that could not be refreshed stops the run, because a triage is worth what the code it read is worth and a report from a five-day-old tree is worth less than none — it looks current. What it says when it stops is one sentence: what went wrong, said once however many repositories share it, why that matters here, and what clears it. Never "pull them", which is advice that cannot be taken on the morning pulling is the thing that failed.

Stop means the run did not happen. Stop – in the view title, on the run panel, or offered beside “a run is already in progress” – ends the whole run at once: the step in progress is left where it is, a scan of the code or of the SonarQube server ends at the next file or project it reaches, nothing is handed to the agent after it, and Run works again straight away. A run still starting – levelling the reports, preparing the working directory, asking about a freeze date or a token – does not begin, and the question it asked is closed. The agent is closed with its console, and Stop waits until the agent's processes have ended – killing what would not end on its own – before it touches a file. Where the system will not list its processes they are followed by number, and on Windows the whole tree is ended with taskkill; where it still cannot be seen whether the agent ended, Stop says so, puts the files back, and looks again a few seconds later, putting back anything that changed meanwhile. Then what the run wrote is put back as it was when Run was pressed: the files under state/, local/ and scratch/ are restored, so are the hand-written half of the map and the priorities file, and the reports the run added are removed, in subfolders as well, while the reports that were already there – with the ticks you made in them meanwhile, renamed or archived – are not touched. What the run staged in git is unstaged, and the commits it made on top of your branch – not pushed, touching only files the run wrote – are undone, their changes put back with the rest; git's reflog still has them. Never put back: the comment register (state/comments.jsonl) and the record of which reports had their comments handed over (state/published.json) – comments ticked while a run works go to the same agent, which may post them before Stop, and a comment in JIRA stays there – the backlog read from JIRA, the acknowledgements (state/acknowledgements.json), and anything git brought in during the run. Once the report has landed and the agent is only finishing up, Stop asks first, because putting the files back removes the report; closing the console keeps everything. The copy Stop puts back is kept on disk, in local/stop-copy/, which git ignores, so Stop also works after the window is reloaded and from another window on the same folder: the window that started the run is asked to stop it, and when that window is gone, the one where Stop was pressed closes the agent's console and puts the files back itself. A run started on another computer can be stopped only there; Stop here clears its claim and leaves the files as they are. A file over 64 MB, or beyond 512 MB of copy in all, is not copied: the run names such files as it starts, and Stop reports the ones that changed instead of putting them back.

Records who decided what. The working directory is shared, so a run one person files is read by another. Reports name whoever filed them, and verdicts, acknowledgements and runs carry the address git already knows you by — nothing to set up, and nothing that can disagree with the commit carrying the record. Where git has no address, records are written without an author rather than with a guessed one.

Shows every proposed comment as its own row, with the reason beside it. Which ones go out is the reader’s judgement on the text in front of them; nothing here measures a comment or refuses one. The evidence belongs in the report, and the procedure says so — because evidence in a comment is noise, not because it takes up room.

Deletes a report only when you say so, and says what it is not deleting. Right-click a report — or several — and it names every file it is about to take, including the translation, because half a report is worse than none. The files go to the recycle bin and anything committed is still in the history. The verdicts, the comment register and the answered corrections stay: they are the decisions the report describes, not the report.

Says whether a report has left this machine. A row says not committed, changed since commit or not pushed; one already in the upstream says nothing, because that is the ordinary end and a mark on every row is a mark nobody reads. One report goes out from its own context entry, or everything waiting goes in one commit from the title bar — confined to the working directory, so nothing half-finished elsewhere in the repository is swept up with it. When somebody else has filed reports this checkout has not got, the list says so and offers to fast-forward. When both sides have filed – a colleague pushed while your run worked – sending, getting and Refresh put your unpushed commits on top of the server's without a second question, as long as those commits hold nothing but the working directory and every file both sides changed is one this extension joins on its own: a seen-list, the comment and sprint-note registers, a sprint snapshot. Edits and staged changes elsewhere in the repository are carried across as they are; where one sits in a file the server's commits change, or an untracked file sits where they put one, it stops and changes nothing. A commit that also changes the project around the working directory is asked about first, and two edits of one report or verdict are left to you. Beyond that it does not touch your history: what is committed, when, and by whom is your workflow.

Says one thing in the status bar, and it is the most urgent one. Three facts compete for that line: somebody else has filed reports this checkout has not got, nothing of any kind has run here for too long, and the kind you run has fallen behind while something else has not. The first is cleared by pressing, and nothing else on screen says it. Red is spent on the second and nothing else, because it is the only one that means nobody is looking at the backlog at all; your own kind lagging while the team works is amber, and reports waiting are neither, because news in red is how a warning becomes furniture. Days are counted in working days, so a weekend does not turn the alarm into furniture either.

Shows every kind of run at once, in the State view. One line per kind with the date of the last one and how long ago, the kind you run marked as yours, and a line for what other people have filed that has not reached this checkout. The strip has room for one of those and picks it; the pane has room for all of them. A kind nothing has ever run and nobody here runs is left out rather than listed as never run — a project that does not do sprint plans is not behind on sprint plans. Where nothing could count what is waiting it says which of the three reasons that was, rather than a nought.

Checks the reports repository on a timer, every fifteen minutes, so a report a colleague pushed this morning appears without anybody starting a run. Refs only: no branch moves, nothing is merged, and the working tree is left byte for byte — which is why a timer may do it unasked. Refresh does more: it is somebody asking for the current state in as many words, so it fetches and then brings the reports onto this disk – a fast-forward, or the replay described above. One press, not a press and then a second press for the thing the first one told you about. A local change the server's commits would overwrite stops it, and it says so.

Configures itself in a form. Every project fact this extension reads has a field behind the gear: the tracker, the repositories, the documentation, the evidence, the languages, the team, the SonarQube server and the projects its report covers, what the code consistency report leaves out, and the working directory. Values are read from the file's own words and written back into it value by value, so the comments beside each key survive. The whole file exports to one path and replaces from another, which is how a second project starts from a worked example rather than a list of key names.

Says what the button is about to do. The run panel opens with one sentence naming the whole arc, and it names the destination, because the two are different experiences: Run starts the agent in a console with the task attached, and Copy Prompt goes through the same steps for a technical triage and puts the procedure on the clipboard instead, for an agent you start yourself.

Reads JIRA itself. This extension needs JIRA — the comments a run drafts are posted there, and the backlog snapshot everything else is measured against comes from there — so a stored token is the whole of what it needs and there is no second switch. When JIRA cannot be read, the State view says so and why. It takes the backlog snapshot itself — every issue, finished ones included, folded into what is already there and never replacing it, because a key that has been seen must never stop having been seen; a read that came back short, or empty, is not written at all. A run reads the threads and says in its panel how many are waiting on us, and checks your priority anchors against what JIRA holds, so an epic somebody closed last week is named as drift instead of silently producing confident verdicts; its report is the wider answer. Between runs, Refresh sweeps the threads this account is in – the issues assigned to you or that you raised, still open, changed recently, because JIRA cannot be searched for the threads you only commented in – and Show Log says what it found. An issue counts as finished when its status is called Done, Closed or Resolved: by name, never by status category, because In Review sits in the Done category in an ordinary workflow while the work is plainly not finished.

Audits the documentation as a run of its own. The documentation audit, under Run, asks the other question: every documented capability against the code that was meant to build it, in one matrix, with the coverage arithmetic reconciled rather than declared. It files its own report beside the triages and reads in the same panel.

Runs the procedure in a console of its own. The agent is started on the task, or handed it when one is already working; the console closes itself when the agent finishes cleanly, and stays open when it does not. Starting a run is the instruction to run it, so nothing waits for a keypress.

The report

Every proposed comment is a row of its own with the reason beside it, and which ones go out is your judgement on the text in front of you — nothing here measures a comment or refuses one. Tick the ones to post and press Hand the ticked comments to the agent (the command Publish Ticked Comments): the extension asks once, naming the issues, and hands them to the agent, which posts them under your account. A comment the route gate stops cannot be ticked, and says why. Once comments are sent their boxes stay locked, in every open panel of that report, and a newer report of the same kind locks every comment in the older one. Where a report exists in more than one language the header offers both, and switching keeps your ticks and the tab you were on. The − and + buttons size the report and nothing else in the editor. Zapisz jako MD and Zapisz jako HTML save the report as a file to send: the Markdown as it is, or one self-contained page with no script and nothing fetched, which a browser can also print to PDF.

The State view

A notification window, and only that. A configuration that cannot be used is said first, with the value that is wrong. Then the alerts – a run that stopped, at which step and why; JIRA not being read, and why; work on this disk nobody else can see until it is pushed; reports somebody else filed that this checkout has not got – one line each, coloured down the side, with a button only where a command really does that thing. Under them, the switch for a second-language copy of each report, the dev freeze the last sprint check was given, then when each kind of run last happened here, and at the foot one line saying how old the figures are: the refs are refreshed every quarter of an hour, so a server that stops answering is visible here rather than at the next run that will not start. It lists nothing else.

A run

What the button is about to do, said before it does it, and then who is being waited on. A step that reads for minutes before the handoff – the code of every repository for the consistency report, the SonarQube server for its assessment – says what it is reading and that the agent starts when it is done. Stop the run stays on the panel for as long as the run holds the working directory – while it works and until the agent has finished the task, whether or not the console reports the agent working. Once the report is written, an agent that is only holding its session open can be ended with Close the agent console, which keeps everything it wrote. Show Run Progress opens the panel again after it was closed.

Install

From the Marketplace, or build a package and install it:

npm install
npm run vsix
code --install-extension build/jira-triage-ledger-0.3.5.vsix

npm run vsix first asks the Marketplace which version it has, and builds nothing when that version is the same as the one in package.json or higher, or when the Marketplace cannot be asked.

Getting started

Open the folder you work in and run Set Up JIRA Access from the view title. It is five steps on one page, each saying what it is for, with a button that does it and a badge saying whether it is behind you. The first step asks for the two values nothing can guess — the address you open JIRA at, and the letters your issues start with — in two input boxes, and writes them into .jira-triage.json for you. A later step asks JIRA a single question, so a wrong address or a revoked token is found there rather than twenty minutes into a run.

The rest of the configuration is that file, which Configure This Project creates from a commented template and opens as a form:

{
  "jira": { "site": "example.atlassian.net", "projectKey": "PROJ" },
  "repos": ["doc", "backend", "frontend"],
  "docs": { "root": "doc" },
  "storage": ".jira-reports"
}

The folder holding that file is the project root and every path is relative to it. The form has a field for every key, in this order:

  • jira.site and jira.projectKey – the host you open issues at and the letters your issues start with. jira.programProject names neighbouring projects tracked alongside this one, separated by commas: issues in them are reported, never commented on without being asked for.
  • repos – the checkouts the evidence comes from, one per line. Each has to be a working tree, not a folder holding several.
  • docs.root – the documentation repository, and every Markdown file in it counts. Leave it out for a project without one, and the sieve classifies on the issue alone and says so.
  • evidence.map and evidence.manualMap – the generated half of the project map, read for context and never written to, and the hand-written half, the only file corrections are applied to. Without the second, a report lists corrections and applies none.
  • language.chat, language.deliverables and language.translations – the language the agent talks to you in, the language reports are written in (English unless set), and the languages each report is also written in.
  • team.members – who counts as this project's capacity, one per line: the e-mail address of the person's JIRA account, then optionally their name as JIRA shows it and hours a day, separated by |; eight hours unless written. A person is recognised by the address or by the name, so give both: where JIRA hides addresses, as many Cloud sites do, the name is all there is to go by. Two different addresses are two people, whatever they are called.
  • sonarqube.url – the SonarQube server's address and nothing more. An address with a user name or password in it is refused, because the file is shared through the repository.
  • sonarqube.projects – the SonarQube projects the SonarQube report covers, one per line in the form. A line is a project key, such as orchard-press, or the link to the project's page copied from the browser, such as https://sonarqube.example.com/dashboard?id=orchard-press: the key is taken from its id and saved alone, so a key with a colon in it needs no decoding by hand. A link to another server than sonarqube.url is saved by its key, with a note beside the field, because the report looks the key up on the server in sonarqube.url. A link that names no project – the list of all projects, a file inside one – is not saved, and the form says why. The report finds which repository holds each project's code; left empty, it covers the projects it finds in the repositories' analysis configuration or by their names. Either way, a repository without a project is mentioned, not counted against anything.
  • consistency.exclude – what the code consistency report leaves out, such as demo projects, examples and samples, one pattern per line, matched like a line of .gitignore: a name alone, such as demo, is every folder or file so called, at any depth; a path with a slash, such as web/demo, is read from the repository or from the project folder; * is any part of one name, ? one character, and ** any number of folders, as in **/demo/**. Letter case is ignored. Dependencies, build output, hidden folders and generated or minified files are left out without being listed.
  • storage – the extension's own working directory: reports, ledger and anchors live there, scratch/ inside it is git-ignored, and the ignore rules are maintained by the extension rather than left to a person. Nothing is created until there is something to write.

The working directory is the extension's only footprint in your project. It holds no copy of your code, your issues or your documentation — it holds what the runs produced.

Setting up JIRA access

Reading JIRA starts as soon as a token is stored. Set Up JIRA Access walks through it in five steps on one page; the short version:

  1. Say where JIRA is. The first step asks for the host you open issues at — acme.atlassian.net, or jira.company.com/jira where the server lives under a context path — and then for the project key. Both go into .jira-triage.json. The template ships with a placeholder host, and a configuration still carrying it is refused rather than tried: every request against a placeholder fails in a way that reads like JIRA being down.
  2. Get a token. id.atlassian.com → Security → API tokens → Create API token. Give it a label you will recognise. Copy it before you close the page — Atlassian shows it once and never again.
  3. Store it. Set JIRA API Token on the setup page, paste, Enter. It goes into VS Code's secret store, which is the operating system's credential store: Credential Manager on Windows, Keychain on macOS, the keyring on Linux. Never into settings, never into a file, never into a URL. The next box asks for the email address of the account that made the token, filled in from your git config user.email, and stores it with the token. Atlassian authenticates by the pair, and your own comments are recognised by that address – set it wrong and every comment you wrote reads as somebody else speaking last.
  4. Check it. Test the connection asks JIRA for one issue and says what came back. A wrong address, a typo in the project key, a revoked token and a VPN that is off all look identical until something actually uses them.
  5. Run the first triage. It needs claude on your PATH; without it the console opens and the shell says the command was not found.

A token belongs to one account and carries exactly that account's permissions. This extension only ever reads with it — canPost is a hard false — and revoking it on that page stops it working everywhere, immediately. Forget JIRA API Token removes it from this editor only.

Runs

Run asks which, and remembers the answer. Eight kinds, each asking a different question of the same project, and the last one you ran is offered first so Enter repeats it. Every report is written in English unless language.deliverables says otherwise, and Add PL translation in the State view writes a Polish copy beside it, which the report's header switches to. The Polish copy translates the comments too, so they can be read before they are ticked; what goes to JIRA is always taken from the English version, whichever one is open.

Technical triage — what each issue actually is, judged against your own documentation and code before it is judged against the reporter's wish, with proposed comments to tick and send.

Management triage — what needs your decision, in the order the newest technical triage gives, who is waiting on whom, what came in and what it really is, and what is filed under the wrong type. Its comments carry decisions rather than claims about code: a comment that cites code is written by whoever read the code.

Estimation check — what work costs here, calibrated on issues this team has already closed. The calibration is the first table in the report, and a band with too little closed work behind it stays unset and says so. With nothing to calibrate on the report is one section long, because pricing without calibration is not a worse estimate, it is guessing in a table.

Sprint plan — what fits in the next sprint, ordered by priority anchor and by dependency. The first item that does not fit is shown rather than dropped: where the line falls is the answer you are looking for.

Sprint check — how the sprint that is running is going, several times a day if you want. Six detectors are computed rather than sensed: the issue that closes the sprint, what has stopped moving, what went backwards, what was added, an estimate zeroed with work still to do, and what changed hands. How long a thing has stood still is counted across checks, which no single fetch can see. Nothing about the sprint is configured: the workflow order is the board's columns, the priorities are in JIRA's own order, and the issue that closes the sprint is the one whose title carries the sprint's name. It asks once per sprint when work stops – the date it counts working days to – and files the answer under the sprint the check reads from JIRA. A later check uses that date without asking only while the sprint the last check read is still running: before its freeze, or past it but before the sprint's end on the board. The first check of the next sprint asks again, rather than counting to last sprint's. The load is measured for the people in team.members, with the work nobody holds as its last row; anybody else holding work is listed apart from them, and with no roster everybody holding an issue counts.

Documentation audit — every documented capability against the code that was meant to build it.

SonarQube assessment – what SonarQube reports for each project sonarqube.projects lists, or for each one found in the repositories when it lists none, checked against the code rather than repeated. It opens with a short summary, and puts problems with a real effect – each followed in the code from the input to where the effect shows – apart from findings that only cost upkeep and from noise, including shapes an interface or a framework forces. Every problem says how sure it is – one word, with what was not verified in a line of its own – and which commit brought it in, without anybody's name, and a change from the last two weeks is marked as work in progress. The share of work done with a coding agent is given beside its share across the whole history, the ways of working behind the problems are listed with their commits, and when a consistency report of the same day exists, the findings are set beside it. The issues of the new-code period – the ones the quality gate counts – are told from the rest, the gate's conditions come from the gate itself, including those a project has no value for yet, and their values are written a row per project. A rule the profile leaves off is proposed only where a problem found in the code shows it is needed. A problem found beside an issue, which no rule marks, is labelled as such, and the problems earlier assessments found stay in the report, with their place in the code, until that code changes. Each place in the code is a path from the repository root with its line. Each finding worth marking gets a proposed note, and the notes not yet used are repeated. A project no repository is found for is still read, only without the history of its findings and the age of its analysis. It reads with your own token and changes nothing on the server, so it needs nothing from whoever administers SonarQube.

Code consistency – places that solve the same problem in different ways, found mechanically and then judged by what they do. First come divergences that give a different result for data that really arrives – each followed in the code from the input to where the result is read, and offered as a candidate for an issue – then divergences nothing triggers today, and only then several ways of doing one thing that cost upkeep but behave the same. A way counts as adopted only where a document in the repositories says so – instructions for coding agents, contribution guides, read-me files, specifications, architecture decisions, the documentation folder – and only those become rules for coding agents; where no document says, the choice is left to the team. Every finding says how sure it is – one word, with what was not verified in a line of its own – and which commit brought it in, without anybody's name, and a change from the last two weeks is marked as work in progress. Demo and example code, copies in separately deployed parts that have not drifted apart, and matters of taste are listed outside the assessment. When a SonarQube report of the same day exists, the findings are set beside it. What consistency.exclude lists – demo projects, examples, samples – is not read, and the report says how many files each exclusion left out. Code in a language the scan does not read, such as Rust, is not assessed either, and the line under the verdict names the language, its files and the repositories it leaves out.

None of them writes anything to JIRA or SonarQube, and none of them re-judges what another has decided: the verdicts live in the ledger and the later runs read them.

The sprint plan and the sprint check need to know who the team is. That lives in .jira-triage.json under team, because it is a fact about the project rather than about you — the commented template shows the shape. With no roster both still run: the sprint plan says the roster is missing and draws no limit, and the sprint check counts everybody holding an issue, rather than either deciding for itself who counts. A member is matched to the person an issue is assigned to by the e-mail address of their account, which the sprint check reads from JIRA where JIRA gives it, or by the name JIRA shows for them.

Reports you already have

Point storage at a directory that already holds reports and they appear in the list, under the names they already have. Two naming conventions are read: the newer one, where the prefix is the run kind — triage_2026-09-11.md — and the older one that a slash-command workflow writes, jira_triage_2026-09-11.md. A _pl suffix marks a translation and pairs it with its original; _v2 marks a second version of the same day's report.

Nothing is copied or converted. The files stay where they are and are read in place, so a directory shared through git stays shared, and nothing in it is rewritten by being listed.

Commands

Every command is in the Command Palette under JIRA Triage Ledger, and most are also where they are used.

In the title bar of the Runs and State views:

  • Run – asks which kind and starts it. Stop Run takes its place while a run or its agent works.
  • Refresh – fetches the reports and brings them level, then reads everything again, JIRA included.
  • Filter Runs – shows only the kinds and the people you pick. Clear Filter shows everything again.
  • Show Archived Reports – lists the reports moved to archive/. Show Current Reports goes back.
  • Configure This Project – opens .jira-triage.json as a form, creating it from the template first when there is none.
  • Commit and Push Everything Not Shared Yet – one commit of everything waiting in the working directory, pushed (Runs view).
  • In the title bar's menu: Copy Prompt, Set Up JIRA Access, Forget JIRA API Token, Set SonarQube Token, Show Run Progress, and Show Log, which opens the extension's log – what each step read, what the JIRA sweep found, what Stop put back.

On a report in the Runs list:

  • Open Report.
  • Archive Report – moves the report and its translations into archive/ beside them. Nothing is deleted, and the list stops showing them.
  • Delete Report – names every file it takes and sends them to the recycle bin.
  • Commit and Push This Report – on a report that has not left this machine.
  • Get Reports Filed by Others – on the line saying reports are waiting: fetches the reports repository and brings it level – a fast-forward, or your unpushed reports put on top of the server's – or says why it cannot.

In a report, Publish Ticked Comments is the button that hands the ticked comments to the agent.

Only in the Command Palette: Forget SonarQube Token, and Write a Translated Copy of Each Report, the same switch as the box in the State view.

Procedures

The procedures each run hands the agent are compiled into the extension, and nothing in the project replaces them. A copy kept in the working directory goes stale the day the extension changes, and a run that quietly follows old instructions is worse than one that follows none, so a file in <storage>/procedures/ is not read.

Settings

There are no editor settings. Everything specific to one project lives in .jira-triage.json. The JIRA account and token, and the SonarQube token, live in the editor's secret store; Forget JIRA API Token and Forget SonarQube Token remove a token from it, and JIRA or SonarQube is not read again until another one is stored.

The agent started in the console is claude, and a run of any kind is expected every working day before the status bar says one is overdue. Both were settings once, and nobody had a reason to change them. A setting left in your settings.json from an earlier version is ignored; the account is moved into the secret store the first time this version starts.

The route gate, and the hole it leaves

A comment that says how the system behaves is a claim about a path: where a value is written, which route the surface in the report actually takes, and where the effect is read. Reading one end and assuming the middle is how a comment goes out under your name with a premise a colleague refutes in the same issue half an hour later.

So before a comment like that is drafted, the procedure asks for a file — .jira-reports/state/routes/<KEY>.md — listing every link from the surface to the read, each with file:line, the commit it was read at, and whether it was read. Under it, a section naming what the conclusion rules out. The gate reads that file. A comment whose table is missing, stale, short of a link, or citing a file the table does not, is shown in the panel with its tick box dead and the reason beside it, and the publish command refuses it a second time if the box is reached any other way.

A comment that asserts nothing about behaviour — "yes, we are closing this" — needs no table. It carries one line saying so, and then it may cite nothing: a file:line is a claim.

What this does not do. It checks the form, not the truth. Writing tak beside a link nobody opened passes. What changes is that an invisible omission becomes a false line in a file that stays beside the comment — the difference between a mistake nobody can see and one somebody can point at.

The hole. Publication is the agent's, through its own JIRA access, so this gate stops what goes through the panel and nothing else. An agent asked "just post this" in its own console never meets it.

Closing it, if your agent supports hooks

Claude Code can refuse a tool call before it runs. The extension does not write this for you — it writes nothing outside its working directory — so it is yours to add, in your project's .claude/settings.local.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__atlassian__addCommentToJiraIssue",
        "hooks": [{ "type": "command", "command": "python .claude/hooks/route_table_gate.py" }]
      }
    ]
  }
}

The script reads the same files under the same rules and answers {"permissionDecision": "deny", "permissionDecisionReason": "..."} when the table is not there. Keep its rules and the extension's in step: a hook that accepts a table the panel rejects teaches that the panel is the thing to work around.

What it deliberately is not

Not a JIRA client. No backlog browsing, no issue editing, no sprint management. One cycle, done properly.

Not universal, and it says so. The method assumes a documentation repository treated as the source of truth, with an issue judged against it before it is judged against the reporter's wish. Without such documentation the sieve degenerates into "everything is a wish", which is why leaving docs.root out is a real option rather than an oversight.

Not a writer to JIRA. It reads, when you let it. It never posts: comments are drafted by the agent, approved by you in the report, and go out through the agent. A comment is practically irreversible and a worklog is completely irreversible, so the write path stays where a person is already looking.

Requirements

VS Code 1.104 or later, and git on the PATH for the repository preflight. claude on the PATH for the runs themselves. No runtime dependencies.

Reading JIRA needs an Atlassian API token, stored through Set Up JIRA Access in the editor's secret store — never in settings, never in a file, never in a URL.

The SonarQube assessment needs sonarqube.url in .jira-triage.json and a personal SonarQube token with read access – a User Token from My Account → Security → Generate Tokens in SonarQube – stored through Set SonarQube Token in the same secret store. The first SonarQube run asks for it if it is missing.

What leaves your machine

Your JIRA site, and only once a token is stored: searches and comment threads, over HTTPS, authenticated as you. Your SonarQube server, when a SonarQube assessment runs: read-only requests, authenticated as you. Nothing else. There is no telemetry, no update check and no service belonging to this extension — it has no runtime dependencies to phone home with. What the agent does with the task it is handed is between you and your agent.

Building from source

npm install
npm run compile     # type-check, lint, bundle
npm test            # the whole suite, no extension host

Every surface is decided in a module that does not import vscode, so all of them render without an editor:

node scripts/preview.ts <project directory>
node scripts/preview.ts <project directory> --pages ./out-pages

That prints the tree, the State view, the badge, the status bar and the gate results from the same functions the editor calls. scripts/gallery.ts goes further: it writes every screen in every state it can be in — the first run, the empty ones, the broken ones, a report with nothing in it, a run stopped at a gate — as a browsable index. Those are the states nobody has on their disk when they want to look at them, and they are exactly the ones that ship wrong. The webview pages – view/reportPage.ts, view/statePage.ts, view/runPage.ts, view/setupPage.ts and view/configPage.ts – return their HTML the same way, so a page can be written to a file and opened in a browser. That is not a convenience: the verdict tables spent a release rendering as two columns instead of four, with every unit test passing, because a cell had been given a display that stops it being a table cell.

Licence

MIT.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft