Files
gw-triage/docs/plan.md

604 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# gw-triage Plugin Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Build the gw-triage Claude Code plugin: screenshot + Telegram message → classified, deduplicated, Italian Jira issue under the right epic, assigned to the right developer, screenshot attached.
**Architecture:** A single git repo that is both a Claude Code plugin and its own marketplace. Two skills (`triage`, `map-repo`) driven by a committed routing config (`config/routing.json`, values fetched live from Jira during this build) and per-repo knowledge profiles (`knowledge/*.md`). Jira writes go through the Atlassian MCP; screenshot attachment goes through the Jira REST API with a personal API token.
**Tech Stack:** Claude Code plugin format (plugin.json / marketplace.json / skills), Atlassian MCP (`mcp__plugin_atlassian_atlassian__*` tools), Jira Cloud REST API v3 (attachments via curl), JSON config, Markdown.
## Global Constraints
- Spec: `docs/design.md` in this repo — the plan implements it 1:1.
- **Language policy (from spec):** everything humans read is Italian (README, both skills' interactive output, Jira issues). Model-facing text is English (SKILL.md bodies, routing.json, knowledge profiles).
- **No autonomous git commits.** At the end of each task, list the files created/changed; Giuseppe stages and commits himself. (Overrides the commit steps normally mandated by the plan template.)
- Working directory for all tasks: `/Users/giuse/Documents/Work/GrowingWay/gw-triage`.
- All intra-plugin paths inside SKILL.md files use `${CLAUDE_PLUGIN_ROOT}`.
- Plugin name is exactly `gw-triage` everywhere (plugin.json, marketplace.json, docs).
- `config/routing.json` must contain zero placeholder values when Task 2 completes — every value fetched from Jira or confirmed by Giuseppe.
- Routing rules (from spec, fixed): app-android → Fabrizio, app-ios → Antonio, app-shared → Fabrizio (fallback), backend/backoffice/infra → Giuseppe.
---
### Task 1: Plugin scaffold
**Files:**
- Create: `.claude-plugin/plugin.json`
- Create: `.claude-plugin/marketplace.json`
**Interfaces:**
- Produces: plugin identity `gw-triage` v`0.1.0`; marketplace source `./`. Task 6 (map-repo skill) references the patch-version bump of this `plugin.json`; the README (Task 7) references the marketplace add/install commands.
- [ ] **Step 1: Write `.claude-plugin/plugin.json`**
```json
{
"name": "gw-triage",
"description": "Da screenshot + messaggio Telegram a issue Jira instradata al dev giusto",
"version": "0.1.0",
"author": {
"name": "GrowingWay"
}
}
```
- [ ] **Step 2: Write `.claude-plugin/marketplace.json`**
```json
{
"name": "gw-triage",
"owner": {
"name": "GrowingWay"
},
"plugins": [
{
"name": "gw-triage",
"source": "./",
"description": "Da screenshot + messaggio Telegram a issue Jira instradata al dev giusto"
}
]
}
```
- [ ] **Step 3: Verify**
Run: `claude plugin validate .` from the repo root.
Expected: validation passes. If the `validate` subcommand is unavailable in the installed CLI version, fall back to `python3 -m json.tool .claude-plugin/plugin.json && python3 -m json.tool .claude-plugin/marketplace.json` — both must parse without error.
- [ ] **Step 4: Hand off for commit** — list the two files; Giuseppe commits.
---
### Task 2: Fetch Jira coordinates and write `config/routing.json`
This task is interactive (MCP calls + Giuseppe picks epics). Run it inline in the main session, not in a subagent.
**Files:**
- Create: `config/routing.json`
**Interfaces:**
- Consumes: Atlassian MCP tools (load schemas first via ToolSearch: `select:mcp__plugin_atlassian_atlassian__getAccessibleAtlassianResources,mcp__plugin_atlassian_atlassian__getVisibleJiraProjects,mcp__plugin_atlassian_atlassian__searchJiraIssuesUsingJql,mcp__plugin_atlassian_atlassian__lookupJiraAccountId,mcp__plugin_atlassian_atlassian__atlassianUserInfo`).
- Produces: `config/routing.json` with the exact schema below. Task 5 (triage skill) reads `site`, `cloudId`, `projectKey`, `language`, and `areas.<key>.{epic,assignee.accountId,assignee.name}`.
- [ ] **Step 1: Get cloudId and site URL**
Call `getAccessibleAtlassianResources`. Record `cloudId` and the site URL (`https://<site>.atlassian.net`).
- [ ] **Step 2: Confirm the project**
Call `getVisibleJiraProjects`. Present the candidate list to Giuseppe with AskUserQuestion (expected answer: the project whose keys look like `OA-…`). Record `projectKey`.
- [ ] **Step 3: List epics**
Call `searchJiraIssuesUsingJql` with:
`project = <projectKey> AND issuetype = Epic AND statusCategory != Done ORDER BY created DESC`
fields: `summary,status`, maxResults 50. Show the list (key — summary).
- [ ] **Step 4: Map areas to epics**
AskUserQuestion: which epic corresponds to each area (`app-android`, `app-ios`, `app-shared`, `backend`, `backoffice`, `infra`). Multiple areas may share one epic. If no suitable epic exists for an area, Giuseppe names one to create; create it with `createJiraIssue` (issueTypeName `Epic`) only on his explicit confirmation.
- [ ] **Step 5: Resolve account IDs**
Call `atlassianUserInfo` for Giuseppe's own accountId. Call `lookupJiraAccountId` for "Fabrizio" and "Antonio" (confirm the matches with Giuseppe if more than one candidate).
- [ ] **Step 6: Write `config/routing.json`**
Exact schema — fill every `<…>` with the fetched values:
```json
{
"site": "https://<site>.atlassian.net",
"cloudId": "<cloudId>",
"projectKey": "<projectKey>",
"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>" } }
}
}
```
- [ ] **Step 7: Verify**
Run: `python3 -m json.tool config/routing.json > /dev/null && grep -c '<' config/routing.json`
Expected: JSON parses; grep count is `0` (no unfilled placeholders).
- [ ] **Step 8: Hand off for commit.**
---
### Task 3: Knowledge profile format (`knowledge/README.md`)
**Files:**
- Create: `knowledge/README.md`
**Interfaces:**
- Produces: the six required section headings, consumed verbatim by Task 4 (zeus profile) and by the map-repo skill (Task 6): `System identity`, `User-visible surfaces`, `Error fingerprints`, `Symptom → area heuristics`, `Domain glossary`, `Not mine`.
- [ ] **Step 1: Write `knowledge/README.md`**
```markdown
# Triage knowledge profiles
One file per company codebase, written in English, read by the `triage`
skill to classify and route reports. Generate or refresh a profile with
the `map-repo` skill, run from inside the repo being mapped.
File name: `<repo-name>.md` (lowercase). Target length 100200 lines.
Keep the exact section headings below — the triage skill relies on them.
## System identity
Name, one-line responsibility, owning developer, and which
`routing.json` area keys this system maps to.
## User-visible surfaces
Screens / pages / endpoints / dashboards a reporter might screenshot.
Include visible titles and labels exactly as they appear on screen.
## Error fingerprints
What failures from this system look like in a screenshot: banner /
dialog / toast styles, exact localized error strings (it-it and en-us),
HTTP/gRPC status codes surfaced to users, what a crash looks like.
## Symptom → area heuristics
Bullet list of `symptom → area-key`, e.g.
`payment stuck on "processing" → backend`.
## Domain glossary
Italian terms reporters actually use, mapped to system concepts
(e.g. `annunci → Announcement`, `prenotazioni → BookingOrder`).
## Not mine
Symptoms that look like this system but belong to another area — name
the correct area key for each.
```
- [ ] **Step 2: Verify**`grep -c '^## ' knowledge/README.md` returns `6`.
- [ ] **Step 3: Hand off for commit.**
---
### Task 4: Zeus knowledge profile (`knowledge/zeus.md`)
**Files:**
- Create: `knowledge/zeus.md`
- Read (source repo): `/Users/giuse/Documents/Work/GrowingWay/Zeus` — seed from `docs/context-builder.md`, `Zeus.Errors/` localized error JSONs, `Zeus.Admin/` pages, `Proto/proto/Services/`.
**Interfaces:**
- Consumes: section headings from Task 3.
- Produces: `knowledge/zeus.md` covering areas `backend`, `backoffice`, `infra`.
- [ ] **Step 1: Dispatch one exploration subagent on the Zeus repo**
Dispatch an Explore-type agent with this exact prompt:
> Survey /Users/giuse/Documents/Work/GrowingWay/Zeus to build a triage profile. Start from docs/context-builder.md for architecture. Then gather, with file evidence: (1) user-visible surfaces per service — the Blazor admin pages in Zeus.Admin (page titles, menu labels as rendered), the gRPC/HTTP endpoints in Proto/proto/Services (service + method names, what app feature each backs: announcements, booking, chat, auth/OTP, payments, policy, support); (2) error presentation — the localized error strings in Zeus.Errors (both it-it and en-us, quote 1015 representative ones with their error domains), the gRPC status codes the ExceptionInterceptor maps to, what a failed payment or SignalR drop looks like to the app user; (3) infrastructure surface — nginx/TLS termination, docker services, what an environment-down failure looks like from outside (connection refused, 502, TLS errors); (4) Italian domain vocabulary visible to end users and admins (annunci, prenotazioni, segnalazioni, host, ospite, ...); (5) boundaries — symptoms that would look like backend but are actually the Flutter app's rendering/logic, and vice versa. Return a structured report; do not write any file.
- [ ] **Step 2: Write `knowledge/zeus.md`**
Write the profile from the agent's report + `docs/context-builder.md` knowledge, following the Task 3 template exactly (six headings). Content requirements:
- `System identity`: Zeus platform = backend (`zeus_auth`, `zeus_property` gRPC APIs), backoffice (`zeus_admin` Blazor dashboard), infra (nginx edge, docker, provisioning) — owner Giuseppe — area keys `backend`, `backoffice`, `infra`.
- `Symptom → area heuristics` must cover at least: payment stuck/failed after checkout → backend; chat messages not delivered / not received → backend; OTP/SMS login not arriving → backend (Ermes); wrong or missing data shown in app with correct layout → backend; admin dashboard errors or wrong grids → backoffice; site/app entirely unreachable, 502, TLS certificate warnings → infra; visual glitches, overflow stripes, misaligned widgets → app (not mine).
- `Not mine`: rendering artifacts, platform-specific crashes, store/deployment issues → app areas.
- [ ] **Step 3: Verify**
Run: `grep -c '^## ' knowledge/zeus.md``6`. Manually check each heading is non-empty and the heuristics above are present. Sanity-check 3 error strings quoted in the profile against the actual files in `Zeus.Errors/`.
- [ ] **Step 4: Hand off for commit.**
---
### Task 5: `triage` skill
**Files:**
- Create: `skills/triage/SKILL.md`
**Interfaces:**
- Consumes: `config/routing.json` schema (Task 2), knowledge headings (Task 3), Atlassian MCP tools, `JIRA_EMAIL`/`JIRA_API_TOKEN` env vars.
- Produces: the `/gw-triage:triage` user flow.
- [ ] **Step 1: Write `skills/triage/SKILL.md`**
```markdown
---
name: triage
description: Use when converting a bug report or feature request — a screenshot and/or a short message, typically forwarded from the company Telegram chat — into a Jira issue routed to the right developer. Triggers on /triage, "crea una issue da questo", "segnala questo bug", or a pasted/dropped Telegram report with intent to track it.
---
# Triage — da segnalazione a issue Jira
You convert an informal report (screenshot + short message) into a
well-formed Jira issue assigned to the right developer.
**All interaction with the user happens in Italian.** This file is
English for precision; never quote it to the user.
The Atlassian MCP tools referenced below live under the
`mcp__plugin_atlassian_atlassian__` prefix. If they are deferred, load
them in ONE ToolSearch call before Step 0:
`select:mcp__plugin_atlassian_atlassian__getAccessibleAtlassianResources,mcp__plugin_atlassian_atlassian__searchJiraIssuesUsingJql,mcp__plugin_atlassian_atlassian__createJiraIssue,mcp__plugin_atlassian_atlassian__addCommentToJiraIssue`
## Step 0 — Preflight
1. Call `getAccessibleAtlassianResources`. If the tool is missing or
returns an authentication error, STOP and print this guide in
Italian, then end the turn:
- installa il plugin Atlassian: `/plugin install atlassian@claude-plugins-official`
- esegui `/mcp`, seleziona `atlassian` e completa il login nel browser con l'account aziendale
- poi rilancia `/triage`
2. Run `test -n "$JIRA_EMAIL" && test -n "$JIRA_API_TOKEN" && echo ok || echo missing`.
If `missing`: continue, but tell the user (Italian) that the
screenshot cannot be attached automatically and point them to the
"Token API Jira" section of the plugin README
(`${CLAUDE_PLUGIN_ROOT}/README.md`) — show the 4 token steps inline,
once, then proceed.
## Step 1 — Input
Expect a short message (usually Italian) and optionally a screenshot.
- Screenshot given as a file path (dragged into the prompt): Read it
for analysis and keep the path for Step 7.
- Pasted image (no path): analyze it; warn (Italian) that the
attachment will be skipped — it can be dragged into Jira manually.
- No screenshot: proceed on the text alone.
- Neither message nor screenshot: ask (Italian) for at least one.
## Step 2 — Load routing and knowledge
Read `${CLAUDE_PLUGIN_ROOT}/config/routing.json` and every `.md` file
in `${CLAUDE_PLUGIN_ROOT}/knowledge/` except `README.md`.
## Step 3 — Classify
Decide, using the knowledge profiles (error fingerprints, symptom
heuristics, glossaries, "not mine" sections):
- **type**: `Bug` (something broken) or `Task` (feature request /
change / improvement)
- **area**: exactly one key from `routing.json` `areas`.
Platform rules for app issues: visible Android UI or device →
`app-android`; visible iOS UI or device → `app-ios`; app issue with no
platform signal → `app-shared`.
If genuinely uncertain between two areas, ask the user ONE targeted
question in Italian (e.g. "Succede solo su iOS o anche su Android?").
Never more than one question; otherwise decide and state the assumption
in the draft.
## Step 4 — Duplicate check
Extract 24 distinctive keywords (include Italian and English variants
of domain terms). Call `searchJiraIssuesUsingJql` with:
`project = <projectKey> AND text ~ "<keywords>" AND statusCategory != Done ORDER BY created DESC`
(maxResults 10). Judge similarity by symptom, not wording.
If a likely duplicate exists: show it (key, title, status) and ask
(Italian) whether to comment on it instead of creating a new issue.
If yes → `addCommentToJiraIssue` with the new report's details (and
note the screenshot can't be attached to a comment automatically),
report the link, stop.
## Step 5 — Draft (Italian)
Build and present:
- **Titolo**: concise imperative, e.g. "Correggere overflow nella
schermata prenotazioni su Android".
- **Descrizione** with sections:
- *Contesto* — source of the report (chat Telegram, date, reporter
if known)
- *Passi per riprodurre* — inferred from screenshot/message; mark
inferred steps explicitly ("(dedotto dallo screenshot)")
- *Comportamento atteso / osservato*
- *Screenshot* — one line describing what it shows
- **Tipo**: Bug or Task. **Epic**: from routing area. **Assegnatario**:
from routing area.
Show the complete draft and ask ONE confirmation (sì / correggi …).
Apply corrections; re-confirm only if the area changed (it changes
epic and assignee).
## Step 6 — Create
Call `createJiraIssue` with: `cloudId`, `projectKey`, `issueTypeName`
(`Bug` or `Task`), `summary`, `description`, the area's
`assignee.accountId`, and `additional_fields: {"parent": {"key": "<area epic>"}}`.
If the call fails, report the raw error with a one-line Italian
summary and stop — nothing partial to clean up (the attachment step
only runs after successful creation).
## Step 7 — Attach screenshot
Only if Step 0 found the env vars AND the screenshot is a file path:
curl -s -o /tmp/gw-triage-attach.json -w "%{http_code}" -X POST \
-H "X-Atlassian-Token: no-check" \
-u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
-F "file=@<screenshot-path>" \
"<site>/rest/api/3/issue/<ISSUE-KEY>/attachments"
`<site>` comes from routing.json. Success = HTTP 200. Any failure is
non-fatal: tell the user (Italian) to drag the image into the Jira
issue manually.
## Step 8 — Report
Print in Italian: issue key with link `<site>/browse/<KEY>`, type,
epic, assignee, and whether the screenshot was attached.
```
- [ ] **Step 2: Verify**
Run: `head -5 skills/triage/SKILL.md` — frontmatter has `name` and `description`. Cross-check every routing.json field referenced (`site`, `cloudId`, `projectKey`, `areas.<key>.epic`, `areas.<key>.assignee.accountId`) against the Task 2 schema — exact key names.
- [ ] **Step 3: Hand off for commit.**
---
### Task 6: `map-repo` skill
**Files:**
- Create: `skills/map-repo/SKILL.md`
**Interfaces:**
- Consumes: `knowledge/README.md` template (Task 3); `.claude-plugin/plugin.json` version field (Task 1).
- Produces: the `/gw-triage:map-repo` flow that Fabrizio/Antonio run in the Flutter repo.
- [ ] **Step 1: Write `skills/map-repo/SKILL.md`**
```markdown
---
name: map-repo
description: Use when generating or refreshing a codebase's triage profile for the gw-triage plugin — run from inside the repo to map. Triggers on /map-repo, "mappa questo repo", "aggiorna il profilo di triage".
---
# Map-repo — profilo di triage del codebase
You generate a triage knowledge profile for the repo the user is in,
and guide them to publish it to the gw-triage plugin repo.
**All interaction with the user happens in Italian.** The generated
profile is written in English (it is read by the triage model, not by
humans).
## Step 1 — Identify the repo
- `git rev-parse --show-toplevel` — if not a git repo, ask (Italian)
to cd into the repo to map and stop.
- Repo name = basename of the toplevel, lowercased.
## Step 2 — Survey
Read `${CLAUDE_PLUGIN_ROOT}/knowledge/README.md` (the profile format).
Dispatch ONE exploration subagent with this prompt, filling <repo-path>:
> Survey the codebase at <repo-path> to build a triage profile.
> Report back, with file evidence: (1) what the system does — main
> responsibility in one line; (2) user-visible surfaces: screens /
> pages / endpoints / dashboards a user might screenshot, with their
> on-screen titles and labels; (3) error presentation: how failures
> appear to users — banners, dialogs, toasts, their exact wording in
> every language present, HTTP/gRPC codes surfaced, what a crash looks
> like; (4) the domain vocabulary users see (menu labels, entity
> names, Italian terms); (5) integrations that fail visibly (payments,
> chat, auth, push notifications); (6) what this system does NOT do —
> adjacent systems it talks to and symptoms that belong to them.
> Return a structured report; do not write any file.
## Step 3 — Write the profile
Write `<repo-name>.md` following the six headings of
`knowledge/README.md`, all sections non-empty, 100200 lines, English.
Write it to a temporary location first (the session scratchpad or
/tmp). Present a short Italian summary to the user with the file path.
## Step 4 — Publish
Ask (Italian) for the local path of the user's gw-triage checkout;
offer to `git clone <plugin-repo-url>` if they don't have one (read
the URL from `git remote get-url origin` run in
`${CLAUDE_PLUGIN_ROOT}` if available, otherwise ask).
With the user's confirmation, in the checkout:
1. Copy the profile to `knowledge/<repo-name>.md` (overwrite if
refreshing).
2. Bump the patch version in `.claude-plugin/plugin.json`
(e.g. 0.1.0 → 0.1.1).
3. `git add knowledge/<repo-name>.md .claude-plugin/plugin.json`
then `git commit -m "knowledge: add/update <repo-name> profile"`
— ask before committing, and ask again before `git push`.
4. Tell the user (Italian) that teammates receive the update with
`/plugin marketplace update gw-triage`.
```
- [ ] **Step 2: Verify**
Frontmatter valid (name + description). The six-heading reference matches Task 3's headings. The version-bump path `.claude-plugin/plugin.json` matches Task 1.
- [ ] **Step 3: Hand off for commit.**
---
### Task 7: Italian README
**Files:**
- Create: `README.md`
**Interfaces:**
- Consumes: install commands (Task 1 naming), token flow (spec), skill invocations `/gw-triage:triage`, `/gw-triage:map-repo`.
- Produces: the guide the triage preflight points users to; `<url-di-questo-repo>` and `<site>` are filled with the real Bitbucket URL (Giuseppe provides at execution) and the Task 2 site URL.
- [ ] **Step 1: Write `README.md`** (fill `<site>` from routing.json; leave `<url-di-questo-repo>` only if the remote doesn't exist yet, and flag it in the handoff)
```markdown
# gw-triage
Da uno screenshot e due righe su Telegram a una issue Jira fatta bene,
assegnata al dev giusto.
## Installazione (una volta sola)
1. **Plugin Atlassian** — in Claude Code:
`/plugin install atlassian@claude-plugins-official`
2. **Login Atlassian** — esegui `/mcp`, seleziona `atlassian` e completa
il login nel browser con l'account aziendale. Verifica che risulti
*connected*.
3. **Installa gw-triage**:
`/plugin marketplace add <url-di-questo-repo>`
`/plugin install gw-triage`
4. **Token API** (per allegare gli screenshot — facoltativo ma
consigliato): sezione sotto.
## Token API Jira
Serve solo ad allegare gli screenshot alle issue (l'MCP Atlassian non
supporta gli allegati). Senza token tutto funziona lo stesso: viene
saltato solo l'allegato.
1. Apri <https://id.atlassian.com/manage-profile/security/api-tokens>
(login con l'account Atlassian aziendale).
2. **Create API token** → etichetta `gw-triage` → scegli la scadenza →
**copia subito il token** (viene mostrato una volta sola).
3. Aggiungi le credenziali al profilo della shell:
- bash/zsh: `export JIRA_EMAIL="tu@azienda.it"` e
`export JIRA_API_TOKEN="<token>"`
- fish: `set -Ux JIRA_EMAIL tu@azienda.it` e
`set -Ux JIRA_API_TOKEN <token>`
4. Verifica:
`curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" <site>/rest/api/3/myself`
deve restituire il tuo profilo in JSON.
⚠️ Il token agisce a tuo nome su Jira: non committarlo mai. Se trapela,
revocalo dalla stessa pagina. Quando scade, `/triage` continua a
funzionare — salta solo l'allegato finché non ne esporti uno nuovo.
## Uso quotidiano
1. Salva lo screenshot dal messaggio Telegram.
2. In Claude Code: **trascina il file nel prompt** (così può essere
allegato alla issue), incolla il messaggio e invoca
`/gw-triage:triage`.
3. Rispondi all'eventuale domanda di chiarimento, conferma la bozza →
la issue viene creata e ricevi il link.
Funziona anche senza screenshot (solo testo) o con l'immagine incollata
dagli appunti — in quel caso l'allegato va trascinato in Jira a mano.
## Aggiornare la conoscenza dei repo
Quando la tua codebase cambia in modo visibile agli utenti, dalla
cartella del repo esegui `/gw-triage:map-repo` e segui le istruzioni:
il profilo aggiornato viene pushato qui e gli altri lo ricevono con
`/plugin marketplace update gw-triage`.
## Problemi comuni
- **"MCP Atlassian non disponibile"** → passi 12 dell'installazione.
- **"Allegato saltato"** → controlla `JIRA_EMAIL` / `JIRA_API_TOKEN`
(sezione Token API).
- **La issue finisce nell'epic sbagliata** → correggi in Jira e apri
una issue su questo repo per aggiustare `config/routing.json` o il
profilo in `knowledge/`.
```
- [ ] **Step 2: Verify**
`<site>` placeholders replaced with the routing.json value (grep for `<site>` → 0 matches). README is entirely Italian. The four install steps match the spec's Prerequisites section.
- [ ] **Step 3: Hand off for commit.**
---
### Task 8: End-to-end verification, packaging, announcement
Interactive — run inline with Giuseppe present.
**Files:**
- Create: none (verification + a drafted Telegram message in chat)
**Interfaces:**
- Consumes: everything above.
- [ ] **Step 1: Local install test**
Giuseppe runs in a fresh Claude Code session:
`/plugin marketplace add /Users/giuse/Documents/Work/GrowingWay/gw-triage` then `/plugin install gw-triage`.
Expected: plugin installs; `/gw-triage:triage` appears; invoking it with no Atlassian session available prints the Italian setup guide (preflight behavior); with the MCP authenticated it proceeds to ask for input.
- [ ] **Step 2: Dry-run on real reports**
Giuseppe supplies 12 real past Telegram reports (screenshot + message). Run the triage flow up to and including the draft (Step 5 of the skill) and STOP before creation. Check: correct type, correct area/assignee/epic, faithful Italian draft, duplicate check ran.
- [ ] **Step 3: One live end-to-end**
With Giuseppe's explicit go: complete the flow — issue created in Jira, screenshot attached via REST (verify HTTP 200 and the attachment visible in Jira). Giuseppe deletes the issue afterwards or keeps it if it's a real open item.
- [ ] **Step 4: Publish handoff (Giuseppe, manual)**
Checklist for Giuseppe: create the Bitbucket repo; `git init`/add remote in `/Users/giuse/Documents/Work/GrowingWay/gw-triage`; replace `<url-di-questo-repo>` in README.md with the real URL; commit; push. (No autonomous commits — listed here as his steps.)
- [ ] **Step 5: Draft the Telegram announcement (Italian)**
Deliver this text in chat for Giuseppe to paste in the group:
```
🛠️ gw-triage — da segnalazione a issue Jira in 30 secondi
Basta bug persi in chat. Da oggi: screenshot + due righe → issue Jira
già assegnata a chi di dovere.
Installazione (una volta):
1. /plugin install atlassian@claude-plugins-official
2. /mcp → atlassian → login con l'account aziendale
3. /plugin marketplace add <url-repo>
4. /plugin install gw-triage
Uso: trascina lo screenshot in Claude Code, incolla il messaggio,
/gw-triage:triage, conferma. Fine.
Guida completa (incluso il token per gli allegati) nel README del repo.
Fabrizio, Antonio: quando avete 10 minuti, dalla cartella dell'app
lanciate /gw-triage:map-repo — genera il profilo dell'app e lo pusha
nel repo del plugin, così il triage impara a riconoscere anche i
problemi Flutter.
```
- [ ] **Step 6: Final spec pass**
Re-read `docs/design.md`; confirm every section is implemented (structure, routing, knowledge, triage flow incl. preflight/duplicates/attach, map-repo, README, verification). Fix gaps before declaring done.