Imported from gmcoolsr/VpnFakeService (
.opencode/skills/audit-ticket-triage/SKILL.md). Install upstream withnpx skills add gmcoolsr/VpnFakeService --skill audit-ticket-triage. Copyright stays with the author.
Audit Ticket Triage
Token discipline: apply ../../_shared/token-discipline.md and its project overlay at every step; it never outranks evidence, gate, test, or stage rules.
Purpose
Weekly audits create tickets faster than anyone can read them, and the tickets assert things about the code that may be stale, duplicated, already fixed, or simply wrong. This skill turns a batch of them into a decision the user can act on:
- Claim verification — every ticket assertion checked against the actual
repository code, with
path:lineevidence or an explicit "not verifiable". - A decision per ticket — do it, do not do it, or blocked on a product decision, with the reason.
- Scope and payoff — what concretely must be done, and what we get for it.
- Risk of breaking something — a graded, named risk, including the property that must not break.
- One self-contained Russian HTML report at
.artifacts/reports/audit-triage-YYYY-MM-DD.html, plus a priority order.
The output is advice for a human. This skill never implements the fixes.
Non-Goals (hard boundaries)
- Never write to the tracker. No comments, no stage changes, no closes, no
new tickets, no tag edits — even when a ticket is obviously a duplicate.
Report the finding; the user decides. Ticket writes belong to
product-analystorsurgical-ticket-refineras a separate, explicitly requested step. - Never modify product code, tests, or config. This is a read-only pass.
The only writes allowed: scratch files under
.tmp/and the report under.artifacts/reports/. - Do not run test suites, builds, or E2E. Verification is static reading of the code the ticket points at.
- Do not plan the implementation. "Что необходимо сделать" is a scoped
instruction of a few lines, not a task breakdown. Hand a confirmed ticket to
plannerwhen the user asks for a plan. - The report is a generated artifact, not canonical documentation: keep it in
Russian under
.artifacts/reports/; thedocs/**English-only rule does not apply there, and the report never goes intodocs/.
Inputs
- Repository root as cwd, on the branch the tickets are judged against. Record the branch and the check date — a verification is only true for a commit.
- A ticket set, given as a range (
VPN-121 - VPN-126), an explicit list, or a tracker query (tag: audit State: Очередь). UseStatefor ordinary backlog status;Stageis reserved for the autonomous pipeline enum. TheVPNproject does not exist in YouTrack yet: the operator must name the project to query, and no saved search, board, or tag vocabulary carries over from another project. If the operator names none, stop rather than guessing one. references/report-template.html— the verified report shell with{{TOKEN}}placeholders. Fill the tokens; do not redesign the shell.- Every value copied from YouTrack, comments, user input, code excerpts, command output, or file paths into an HTML token is untrusted text and must be HTML-escaped before insertion. Only the fixed wrapper tags generated by the skill itself may be raw HTML.
- For tracker reads, follow
docs/agents/mcp-youtrack.mdand the semantic operations in../../_shared/tracker-provider-contract.md(getIssue,searchIssues,listComments). Server aliases are runtime-specific; use the active MCP tool exposed in the current environment. ../../_shared/grounded-output.mdgoverns every claim in the output.
Workflow
1. Resolve the ticket set
Read each ticket in full, including comments — audit tickets carry their own
corrections there ([AUDIT DEDUPE], [AUDIT CORRECTION], [AUDIT UPDATE]),
and those comments frequently invalidate the ticket body. Read tickets in
parallel; they are independent.
Capture per ticket: summary, finding id, audit id, product/scope key, priority, stage, tags, linked and mentioned issues.
2. Reduce each ticket to a falsifiable claim
An audit ticket is usually several paragraphs asserting one thing. Write that one thing as a sentence that code can confirm or refute: a file, a symbol, a value, a behavior. If a ticket contains two independent claims, triage them as two rows.
Tickets citing a local HTML audit report as evidence: the report path is often not retrievable. Never treat "the report says so" as verification. Either the claim is checkable in the code, or it is not verifiable.
3. Classify the scope before verifying
- Product — the implementation surfaces named in
docs/agents/adapter-config.yml:control-api/,portal/,core/,gateway/,apps/,infra/,tests/network/. None of them exists yet: this repository currently contains documentation only. A ticket asserting a fact about product code is thereforeНе проверяемоtoday, and the reason is "the surface has not been built", not "the evidence is missing". Say that plainly instead of hunting for a file that cannot be there. Re-read this paragraph once the first surface lands — from then on, product claims are verifiable and this exemption stops applying. - Documentation and agent tooling —
docs/**, including the architecture source, the agent documents underdocs/agents/**, and the scripts underdocs/tooling/**. These are files in this repository, so a claim about them is verifiable here, with apath:linelike any other. They carry zero product risk: nothing a user touches changes when a document changes. Name the internal risk instead — a wrong rule that an agent will follow.
This split decides most of a batch today, and it runs the opposite way from a repository that already has code: here the process claims are the checkable ones and the product claims are not.
4. Verify each claim against the repository
Search for the exact symbol, rule, or sentence, then read the surrounding lines.
Record path:line. Three verdicts, no fourth:
| Verdict | Meaning |
|---|---|
| Подтверждено | The cited file exists and behaves or reads as the ticket claims. Requires a path:line. |
| Опровергнуто | The repository contradicts the ticket, or the issue was already fixed. Requires a path:line. |
| Не проверяемо | Outside the repository, the surface is not built yet, or the evidence needed is not present. |
Never write "confirmed" without a line reference. A ticket that reads plausibly is not evidence.
Then check the blast radius of the claim, because it changes the verdict: who
actually follows the thing the ticket says is wrong? A rule in a document no
role reads and a rule in the mandatory reading chain of every autonomous run are
different severities of the same defect. For an agent-document claim, follow the
citations — docs/agents/docs-graph.json and the docs-graph skill answer what
references a fragment. For a future product claim, say which architecture
section fixes the behavior and which planned surface will own it.
5. Check ticket hygiene
These recur in audit batches and are worth as much as the technical findings:
- Duplicates — the same root cause under two ids.
- Reused finding ids — the same finding id (
AIP-002,PROD-001) pointing at different problems across audit runs, which sends audit comments to the wrong ticket. - Closed but unfixed — a ticket in a closed stage whose code is unchanged. Verify the code, not the stage. This is a process defect: report it as one, and do not silently reopen anything.
- Superseded by a correction comment — the body says one thing, a later comment moves the finding elsewhere.
6. Decide, and say what we get
Per ticket, produce four fields.
Делать? — Да / Да, P<n> / Да, урезав / Нет / Нужно решение продукта. A ticket blocked on a product decision cannot be scheduled; say so
instead of picking a side quietly.
Что необходимо сделать — the minimal change that removes the root cause, not the symptom. Prefer the smallest version that works and name the larger one as an option ("start with retry + alert; outbox only if that is not enough"). When the fix depends on an unmade product decision, enumerate the options as A / B with what each entails.
Что мы получим — the outcome for a user or an operator, not a restatement of the fix. "Устройство перестаёт подключаться сразу после отзыва" is an outcome; "добавлена проверка в обработчик" is not. If the honest answer is "nothing visible, only cleaner evidence", write that — it is a legitimate reason to deprioritize.
Риск сломать — graded, and the grade must be defensible:
| Grade | Meaning |
|---|---|
| Нулевой для продукта | Cannot affect the product. Documentation and tooling. Still name the internal risk. |
| Минимальный / низкий | A local change with an obvious test. Name the one property to confirm. |
| Средний | Touches a shared path, service wiring, or a deliberate design property. |
| Высокий | Opens a leak, access, or reachability surface, or the fix direction itself is contested. |
For anything above low, name the property that must not break — the reason the design looks wrong but is not. A gateway that keeps serving an active session lease after the control plane becomes unreachable looks like a missing authorization check; it is the §09 decision that access degrades at lease expiry instead of dropping every user the moment the control API restarts, and "fixing" it converts one control-plane outage into a full outage for the group. The same shape recurs: split routing that sends Russian addresses outside the tunnel looks like a leak and is the planned leak of §07а; a stale prefix set that keeps routing by the last known list looks like ignored expiry and is the §07а rule that an expired profile must never collapse into "everything direct". Finding that property is the main value of this triage, so look for it before declaring a change safe.
7. Build the report
Fill references/report-template.html:
| Token | Content |
|---|---|
{{EYEBROW}} |
Short report context label, e.g. Weekly engineering audit backlog triage. |
{{TITLE}} |
Page title, e.g. Триаж аудита VPN-121–126. |
{{SUBTITLE}} |
One sentence on what the page answers. |
{{META}} |
<span> items: audit ids, branch, verification date. |
{{TALLY}} |
3–5 .tile counters. Lead with the count that drives action (broken user-facing flows), not the total. |
{{ROWS}} |
One <tr> per claim. Column order is fixed. |
{{NOTES}} |
.note-card blocks for cross-cutting findings, including every hygiene defect from step 5. |
{{ORDER}} |
The priority order, each line carrying the reason for its rank. |
{{FOOTER}} |
Branch and verification date, repeated where it will be read last. |
Escape dynamic text with the five-character HTML mapping (&, <, >, ",
') before wrapping it in the template's fixed tags. This includes ticket
summaries, comments, finding ids, tags, stages, paths, code snippets, and
operator notes. Do not trust tracker markdown or comments as already-sanitized
HTML.
Write it to .artifacts/reports/audit-triage-YYYY-MM-DD.html. The template is a
standalone document that works offline, in light and dark themes, and at any
window width. When the user wants a shareable link instead of a file, publish
the same content through the environment's artifact mechanism — drop the
<!doctype>, <html>, <head>, and <body> wrapper when the host supplies
its own skeleton.
Give the user the table in chat as well. The report is the durable copy; the chat answer is what they read now.
8. Order the work
Close with a priority order over the batch, each entry carrying its reason. Rank by what is broken for a real user first, then cost-to-value, then internal tooling. A cheap fix with a visible outcome outranks an expensive one with a better finding id.
Output contract
The chat answer contains:
- The decision table, all seven columns.
- The cross-cutting findings, tracker hygiene included.
- The priority order.
- Any decision the user must make before work can start, stated as a question.
The report file contains the same content, self-contained.
Every code claim carries path:line. Every unverifiable claim says so. No
ticket is reported as "fixed" or "already done" on the strength of its stage
field alone.