Files
gw-triage/docs/design.md

10 KiB
Raw Permalink Blame History

gw-triage — Telegram report → Jira issue plugin

Date: 2026-07-03 Status: draft for review

Problem

Bugs and feature requests reported in the company Telegram chat get forgotten. We want a repeatable path from a screenshot + short message to a well-formed, correctly routed Jira issue — usable by all three developers regardless of which codebase the problem belongs to.

Solution overview

A Claude Code plugin distributed from a shared git repo that doubles as its own plugin marketplace. Two skills:

  • triage — screenshot + message → classified, deduplicated, Italian-language Jira issue created under the right epic, assigned to the right developer, with the screenshot attached.
  • map-repo — run inside any company codebase; generates that repo's triage profile (knowledge file) and guides the developer to commit it back to the plugin repo.

Codebase knowledge lives in the plugin repo itself, so every install can route across all company codebases, not just the one it happens to run in.

Repo structure

gw-triage/
├── .claude-plugin/
│   ├── plugin.json          name, version, description
│   └── marketplace.json     repo doubles as its own marketplace
├── skills/
│   ├── triage/SKILL.md
│   └── map-repo/SKILL.md
├── knowledge/
│   ├── zeus.md              generated during initial build (BE / BO / provisioning)
│   └── README.md            profile format spec + template for the other repos
├── config/
│   └── routing.json         Jira coordinates + team routing (see below)
├── docs/
│   └── design.md            this document
└── README.md                install steps, prerequisites, API token setup

Routing configuration (config/routing.json)

The deterministic part of triage. Real values (project key, epic keys, account IDs, cloudId, site URL) are fetched from Jira via the Atlassian MCP during the initial build and committed.

{
  "site": "https://<site>.atlassian.net",
  "cloudId": "<fetched>",
  "projectKey": "<fetched>",
  "language": "it",
  "areas": {
    "app-android": { "epic": "<key>", "assignee": { "name": "Fabrizio", "accountId": "<id>" } },
    "app-ios":     { "epic": "<key>", "assignee": { "name": "Antonio",  "accountId": "<id>" } },
    "app-shared":  { "epic": "<key>", "assignee": { "name": "Fabrizio", "accountId": "<id>" } },
    "backend":     { "epic": "<key>", "assignee": { "name": "Giuseppe", "accountId": "<id>" } },
    "backoffice":  { "epic": "<key>", "assignee": { "name": "Giuseppe", "accountId": "<id>" } },
    "infra":       { "epic": "<key>", "assignee": { "name": "Giuseppe", "accountId": "<id>" } }
  }
}

Routing rules encoded here:

Signal Area Assignee
Android UI / device visible in report app-android Fabrizio
iOS UI / device visible in report app-ios Antonio
Flutter issue, platform not determinable app-shared Fabrizio (agreed fallback)
API errors, data wrong/missing, payments, chat/SignalR, auth backend Giuseppe
Blazor admin dashboard backoffice Giuseppe
TLS, nginx, deploys, environments down infra Giuseppe

Multiple areas may point to the same epic (e.g. one "App" epic for all three app areas) — the schema allows it; the actual mapping is decided when the epics are fetched.

Knowledge profiles (knowledge/*.md)

One per repo, triage-oriented (not an architecture doc), roughly 100200 lines:

  • System identity — name, one-line responsibility, owning developer.
  • User-visible surfaces — screens, endpoints, dashboards a reporter might screenshot.
  • Error fingerprints — what failures from this system look like in a screenshot: error banner styles, localized error messages (it-it / en-us), HTTP/gRPC status codes, stack trace shapes, UI toolkit appearance.
  • Symptom → area heuristics — e.g. "payment stuck on processing → backend (Stripe webhook / SignalR), not app".
  • Domain glossary — the Italian terms reporters actually use (annunci, prenotazioni, segnalazioni, host…).
  • "Not mine" pointers — symptoms that look like this system but belong elsewhere.

The Zeus profile is generated during the initial build, seeded from docs/context-builder.md plus a fresh exploration pass. The Flutter repo profile(s) are generated later by Fabrizio/Antonio running map-repo.

triage skill flow

  1. Preflight: verify the Atlassian MCP tools are available and authenticated, and whether JIRA_EMAIL / JIRA_API_TOKEN are set. If the MCP is missing/unauthenticated, stop and print the setup guide steps (in Italian) — the guide effectively appears on first opening for anyone with incomplete setup. If only the token vars are missing, continue but warn that attachments will be skipped (and show the token steps once).
  2. Input: the Telegram message text + screenshot. Preferred: the screenshot dropped into the prompt as a file (drag & drop inserts a path — enables both analysis and attachment upload). Pasted clipboard images work as a degraded mode: classification happens, attachment is skipped with a warning. Message-only reports (no screenshot) are also valid.
  3. Load config/routing.json and all knowledge/*.md via ${CLAUDE_PLUGIN_ROOT}.
  4. Classify: type (Bug vs feature → Task), area, platform. If confidence is low, ask one targeted question instead of guessing.
  5. Duplicate check: JQL search in the project (text ~ keywords, recent first) via the Atlassian MCP. If a likely match exists, show it and offer to comment on it instead of creating a new issue.
  6. Draft in Italian: concise imperative title; description with contesto, passi di riproduzione (inferred from screenshot where possible), comportamento atteso vs osservato, textual description of the screenshot; issue type; parent epic; assignee.
  7. Confirm: show the full draft, one yes/adjust checkpoint.
  8. Create via createJiraIssue (project, type, summary, description, parent epic, assignee accountId).
  9. Attach screenshot via Jira REST (see below). Skip with an explicit warning if the env vars are missing or the image was pasted rather than dropped.
  10. Report the issue key + browse URL.

Screenshot attachment (Jira REST)

The Atlassian remote MCP has no attachment upload, so the skill uses the plain REST API:

curl -s -X POST \
  -H "X-Atlassian-Token: no-check" \
  -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
  -F "file=@<screenshot-path>" \
  "<site>/rest/api/3/issue/<KEY>/attachments"

API token setup (goes in README, one-time per developer)

  1. Open https://id.atlassian.com/manage-profile/security/api-tokens (log in with your Atlassian account).
  2. Click Create API token, label it gw-triage, set the expiry, and copy the token immediately — it's shown only once.
  3. Export the credentials in your shell profile:
    • bash/zsh: export JIRA_EMAIL="you@company.com" and export JIRA_API_TOKEN="<token>"
    • fish: set -Ux JIRA_EMAIL you@company.com and set -Ux JIRA_API_TOKEN <token>
  4. Verify: curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" <site>/rest/api/3/myself returns your profile JSON.

Security note in README: the token acts as you on Jira — never commit it, and revoke it from the same page if leaked. When the token expires, triage keeps working; only attachments are skipped until a new token is exported.

Language policy: everything the team reads is Italian — the README (including these token steps), all interactive output of both skills (questions, drafts, confirmations, warnings, final reports), and the generated Jira issues. Model-facing internals stay in English: SKILL.md instruction text, routing.json, and the knowledge profiles.

map-repo skill flow

  1. Verify the current directory is a git repo; identify the repo name.
  2. Spawn an exploration agent to survey: README/docs, project structure, entry points, user-facing surfaces, error handling and localized error messages.
  3. Write knowledge/<repo>.md following the format in knowledge/README.md.
  4. Guide the developer to publish it: clone/pull the plugin repo, add the profile, bump the plugin patch version in plugin.json, commit and push (direct push — three-person team). Others get it on their next plugin update.

Error handling / degraded modes

  • Atlassian MCP missing or unauthenticated → stop with setup instructions (README covers install + auth).
  • No knowledge profile matches the symptoms → classify on generic heuristics, state the lower confidence in the draft.
  • Attachment env vars missing / pasted image → create the issue anyway, warn that the attachment was skipped and it can be dragged into Jira manually.
  • Jira create call fails → surface the raw error; nothing partial to clean up (attachment only runs after successful creation).

Prerequisites (per developer)

The README opens with this sequence (in Italian):

  1. Install the Atlassian plugin — in Claude Code run /plugin install atlassian@claude-plugins-official (the official Anthropic directory, available out of the box; alternatively browse with /plugin).
  2. Authenticate with Atlassian — run /mcp, select atlassian, and complete the browser OAuth login with your company Atlassian account. Verify: the /mcp entry shows as connected.
  3. Install gw-triage/plugin marketplace add <git-url-of-this-repo> then /plugin install gw-triage.
  4. Screenshot attachments (optional but recommended) — export JIRA_EMAIL / JIRA_API_TOKEN as described in the token setup section.

Out of scope / future

  • Telegram bot → headless Claude automation (revisit once classification quality is proven).
  • Severity/priority inference.
  • Attachment upload via MCP, if Atlassian ever exposes it.

Verification plan

  • Dry-run triage on 12 real past Telegram reports; check classification, routing, and draft quality.
  • One live end-to-end run: create a real issue with attachment, verify in Jira.
  • map-repo dry-run on Zeus itself (output compared against docs/context-builder.md), later by Fabrizio/Antonio on the Flutter repo.