From 1fd949427a25ba696e87e9057c90ba85ddc5ccd6 Mon Sep 17 00:00:00 2001 From: Murphy Freelance <70611945+murphy-freelance@users.noreply.github.com> Date: Sat, 4 Jul 2026 12:07:09 +0200 Subject: [PATCH] Initial release: triage and map-repo skills, zeus knowledge profile, Jira routing config --- .claude-plugin/marketplace.json | 13 + .claude-plugin/plugin.json | 8 + README.md | 68 ++++ config/routing.json | 14 + docs/design.md | 157 +++++++++ docs/plan.md | 603 ++++++++++++++++++++++++++++++++ knowledge/README.md | 39 +++ knowledge/zeus.md | 145 ++++++++ skills/map-repo/SKILL.md | 62 ++++ skills/triage/SKILL.md | 127 +++++++ 10 files changed, 1236 insertions(+) create mode 100644 .claude-plugin/marketplace.json create mode 100644 .claude-plugin/plugin.json create mode 100644 README.md create mode 100644 config/routing.json create mode 100644 docs/design.md create mode 100644 docs/plan.md create mode 100644 knowledge/README.md create mode 100644 knowledge/zeus.md create mode 100644 skills/map-repo/SKILL.md create mode 100644 skills/triage/SKILL.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000..36a1409 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,13 @@ +{ + "name": "gw-triage", + "owner": { + "name": "GrowingWay" + }, + "plugins": [ + { + "name": "gw-triage", + "source": "./", + "description": "Da screenshot + messaggio Telegram a issue Jira instradata al dev giusto" + } + ] +} diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..d725bc5 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "gw-triage", + "description": "Da screenshot + messaggio Telegram a issue Jira instradata al dev giusto", + "version": "0.1.0", + "author": { + "name": "GrowingWay" + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..0f709cf --- /dev/null +++ b/README.md @@ -0,0 +1,68 @@ +# 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 https://git.growingway.com/giuse/gw-triage.git` + `/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 + (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=""` + - fish: `set -Ux JIRA_EMAIL tu@azienda.it` e + `set -Ux JIRA_API_TOKEN ` +4. Verifica: + `curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" https://growingway.atlassian.net/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, `/gw-triage: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 1–2 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/`. diff --git a/config/routing.json b/config/routing.json new file mode 100644 index 0000000..91fe413 --- /dev/null +++ b/config/routing.json @@ -0,0 +1,14 @@ +{ + "site": "https://growingway.atlassian.net", + "cloudId": "17d1d26b-b49d-4a2a-9dcc-2fb4ca8caa4f", + "projectKey": "OA", + "language": "it", + "areas": { + "app-android": { "epic": "OA-16", "assignee": { "name": "Fabrizio", "accountId": "712020:0e7bbdad-2c1d-4acf-bbac-60e029849d14" } }, + "app-ios": { "epic": "OA-16", "assignee": { "name": "Antonio", "accountId": "712020:b76d8d21-0286-4ad6-9585-130e0a38179b" } }, + "app-shared": { "epic": "OA-16", "assignee": { "name": "Fabrizio", "accountId": "712020:0e7bbdad-2c1d-4acf-bbac-60e029849d14" } }, + "backend": { "epic": "OA-18", "assignee": { "name": "Giuseppe", "accountId": "712020:f9181c5e-ce10-455b-84b1-c9266186e576" } }, + "backoffice": { "epic": "OA-15", "assignee": { "name": "Giuseppe", "accountId": "712020:f9181c5e-ce10-455b-84b1-c9266186e576" } }, + "infra": { "epic": "OA-28", "assignee": { "name": "Giuseppe", "accountId": "712020:f9181c5e-ce10-455b-84b1-c9266186e576" } } + } +} diff --git a/docs/design.md b/docs/design.md new file mode 100644 index 0000000..1e8708d --- /dev/null +++ b/docs/design.md @@ -0,0 +1,157 @@ +# 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. + +```json +{ + "site": "https://.atlassian.net", + "cloudId": "", + "projectKey": "", + "language": "it", + "areas": { + "app-android": { "epic": "", "assignee": { "name": "Fabrizio", "accountId": "" } }, + "app-ios": { "epic": "", "assignee": { "name": "Antonio", "accountId": "" } }, + "app-shared": { "epic": "", "assignee": { "name": "Fabrizio", "accountId": "" } }, + "backend": { "epic": "", "assignee": { "name": "Giuseppe", "accountId": "" } }, + "backoffice": { "epic": "", "assignee": { "name": "Giuseppe", "accountId": "" } }, + "infra": { "epic": "", "assignee": { "name": "Giuseppe", "accountId": "" } } + } +} +``` + +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 100–200 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 + +0. **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). +1. **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. +2. **Load** `config/routing.json` and all `knowledge/*.md` via `${CLAUDE_PLUGIN_ROOT}`. +3. **Classify**: type (Bug vs feature → Task), area, platform. If confidence is low, ask one targeted question instead of guessing. +4. **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. +5. **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. +6. **Confirm**: show the full draft, one yes/adjust checkpoint. +7. **Create** via `createJiraIssue` (project, type, summary, description, parent epic, assignee accountId). +8. **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. +9. **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=@" \ + "/rest/api/3/issue//attachments" +``` + +### API token setup (goes in README, one-time per developer) + +1. Open (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=""` + - fish: `set -Ux JIRA_EMAIL you@company.com` and `set -Ux JIRA_API_TOKEN ` +4. Verify: `curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" /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/.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 ` 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 1–2 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. diff --git a/docs/plan.md b/docs/plan.md new file mode 100644 index 0000000..2b17866 --- /dev/null +++ b/docs/plan.md @@ -0,0 +1,603 @@ +# 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..{epic,assignee.accountId,assignee.name}`. + +- [ ] **Step 1: Get cloudId and site URL** + +Call `getAccessibleAtlassianResources`. Record `cloudId` and the site URL (`https://.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 = 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://.atlassian.net", + "cloudId": "", + "projectKey": "", + "language": "it", + "areas": { + "app-android": { "epic": "", "assignee": { "name": "Fabrizio", "accountId": "" } }, + "app-ios": { "epic": "", "assignee": { "name": "Antonio", "accountId": "" } }, + "app-shared": { "epic": "", "assignee": { "name": "Fabrizio", "accountId": "" } }, + "backend": { "epic": "", "assignee": { "name": "Giuseppe", "accountId": "" } }, + "backoffice": { "epic": "", "assignee": { "name": "Giuseppe", "accountId": "" } }, + "infra": { "epic": "", "assignee": { "name": "Giuseppe", "accountId": "" } } + } +} +``` + +- [ ] **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: `.md` (lowercase). Target length 100–200 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 10–15 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 2–4 distinctive keywords (include Italian and English variants +of domain terms). Call `searchJiraIssuesUsingJql` with: +`project = AND text ~ "" 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": ""}}`. + +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=@" \ + "/rest/api/3/issue//attachments" + +`` 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 `/browse/`, 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..epic`, `areas..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 : + +> Survey the codebase at 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 `.md` following the six headings of +`knowledge/README.md`, all sections non-empty, 100–200 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 ` 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/.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/.md .claude-plugin/plugin.json` + then `git commit -m "knowledge: add/update 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; `` and `` are filled with the real Bitbucket URL (Giuseppe provides at execution) and the Task 2 site URL. + +- [ ] **Step 1: Write `README.md`** (fill `` from routing.json; leave `` 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 ` + `/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 + (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=""` + - fish: `set -Ux JIRA_EMAIL tu@azienda.it` e + `set -Ux JIRA_API_TOKEN ` +4. Verifica: + `curl -s -u "$JIRA_EMAIL:$JIRA_API_TOKEN" /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 1–2 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** + +`` placeholders replaced with the routing.json value (grep for `` → 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 1–2 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 `` 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 +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. diff --git a/knowledge/README.md b/knowledge/README.md new file mode 100644 index 0000000..44dbe16 --- /dev/null +++ b/knowledge/README.md @@ -0,0 +1,39 @@ +# 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: `.md` (lowercase). Target length 100–200 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. diff --git a/knowledge/zeus.md b/knowledge/zeus.md new file mode 100644 index 0000000..70bbbd4 --- /dev/null +++ b/knowledge/zeus.md @@ -0,0 +1,145 @@ +## System identity + +Zeus is the .NET 8 multi-service platform powering the GrowingWay property-rental product. +Owner: Giuseppe. Area keys: `backend`, `backoffice`, `infra`. + +Three deployable services: +- `zeus_auth` — Auth, User, Policy, Devices, Settings gRPC APIs (host `auth.{DOMAIN}`) +- `zeus_property` — Announcement, Booking, Chat, Review, Notification gRPC APIs + SignalR payment and chat hubs (host `property.{DOMAIN}`) +- `zeus_admin` — Blazor Server backoffice dashboard (host `admin.{DOMAIN}`) + +Infrastructure: nginx edge (SNI/L4 passthrough on :443) → per-env nginx web (TLS termination, L7 host-header routing) → backend containers. Redis for SignalR session registries. PostgreSQL (remote, never touch locally). Google Cloud Storage for images. Stripe for payments. Ermes (external gRPC) for SMS. + +## User-visible surfaces + +### zeus_admin (backoffice) pages — URL : nav label : heading +- `/` : Home : **Panoramica** — summary: host count, annunci count, media annunci/host +- `/Users` : Users — backoffice account management +- `/support-levels` : Livelli Supporto : **Livelli di Supporto** — RBAC for support operators +- `/Policy` : Policy — policy types and versions +- `/amenities` : Servizi : **Gestione Servizi** — amenity categories and types +- `/tags` : Announcement Tags : **Gestione Tag Annunci** +- `/reviews` : Recensioni : **Recensioni** — review parameters +- `/Verifications` : Verifiche Utenti : **Richieste di Verifica** — KYC queue (fronte/retro documento + selfie) +- `/Hosts` : Host : **Host** — host list +- `/Hosts/{id}` — host detail: Dati Personali, Stripe onboarding, announcements, bookings, availability block +- `/Announcements/{id}` — annuncio detail: Titolo, Descrizione, Meta Descrizione, Indirizzo +- `/Bookings` : Prenotazioni : **Prenotazioni** — columns: Stato, Pagamento, Guest, Host, Check-in, Check-out, Importo, Prossima cattura, Creata +- `/Bookings/{id}` : **Prenotazione** — sections: Dettagli prenotazione, Sessione di pagamento, Tentativi; actions: Cattura ora / Ripianifica / Force Accept / Force Reject / Force Cancel +- `/Guests` : Guest — guest user list +- `/support/chats` : Chat Supporto : **Chat Supporto** — columns: Nome, Cognome, ID Utente, Ultima attività +- `/support/chats/{userId}` — live support chat conversation +- `/reports` : Segnalazioni : **Segnalazioni** — chat-report moderation; columns: Segnalante, Segnalato, Motivo, Stato (Open / Reviewed / Dismissed) +- `/support` : **Apri sessione di supporto** / **Sessione di supporto attiva** — operator enters user's Codice Supporto; session becomes active after user acceptance +- `/support/user` : Utente Target — data view for active support-session user + +### zeus_property gRPC services (app-facing) +`GuestAnnouncementService` (browse/search listings), `GuestBookingService` (Book, RetryPayment, CancelBooking, GetSummary), `GuestChatService` (SendMessage, GetMessages, RequestSupportChat, ReportUser, BlockUser), `GuestReviewService`, `GuestNotificationService` (ListNotifications, MarkNotificationRead), `GuestReceiptService`, `GuestFeedbackService`, `HostPropertyService` (listing CRUD, image upload), `HostBookingService` (ConfirmReservation, ghost bookings), `HostOnboardingService` (Stripe Connect), `HostReferralService`. + +SignalR hubs: `/api/guest/payments` (PaymentHub — BindSession → IssuePayment → Success/Failed callbacks), `/api/guest/chat` (ChatHub — real-time delivery). + +### zeus_auth gRPC services +`AuthAuthenticationService` (Register, Login, ConfirmPhoneNumber, Refresh, RecoverPassword, VerifyConfirmationCode), `AuthUserService` (profile, KYC upload + submit), `AuthPolicyService` (SubscribeToPolicy, streaming updates), `AuthDevicesService`, `AuthSettingsService`, `AuthLogService` (Flutter log ingest), `PrivacyService` (account deletion). + +## Error fingerprints + +Errors surface as gRPC status codes (and mapped HTTP codes for transcoding). The Flutter app displays the localized `message` field from the gRPC `Status` detail. + +### Localized error strings (it-IT / en-US) + +| Key | it-IT | en-US | +|-----|-------|-------| +| `WRONG_PASSWORD` | "La password inserita non è corretta." | "The password you entered is incorrect." | +| `INVALID_CONFIRMATION_CODE` | "Il codice di verifica inserito è incorretto o scaduto." | "The verification code you entered is incorrect or has expired." | +| `SMS_SERVICE_UNAVAILABLE` | "Stiamo avendo problemi nell'invio degli SMS. Riprova più tardi." | "We're having trouble sending text messages right now. Please try again later." | +| `INVALID_ACCESS_TOKEN` | "La tua sessione è scaduta. Effettua nuovamente l'accesso." | "Your session has expired. Please log in again." | +| `ANNOUCEMENT_NOT_FOUND` | "Abbiamo riscontrato un errore nel recuperarti questo annuncio" | "We're having trouble getting this announcement." | +| `APARTMENT_UNAVAILABLE` | "Questo appartamento non è più disponibile." | "This apartment is no longer available." | +| `PRICE_CHANGED` | "Il prezzo per questo appartamento è cambiato." | "The price for this apartment has changed." | +| `PAYMENT_FAILED` | "Il pagamento non è andato a buon fine. Riprova." | "The payment was unsuccessful. Please try again." | +| `UNBOUND_PAYMENT_SESSION` | "Qualcosa è andato storto. Riprova più tardi." | "Something went wrong. Please try again later." | +| `TOO_MANY_CODE_REQUESTED` | "Sono stati richiesti troppi codici per questo numero di telefono oggi." | "Too many codes were requested for this phone number today." | +| `TERMS_NOT_ACCEPTED` | "Devi accettare i termini e le condizioni per continuare." | "You must accept the terms and conditions to continue." | +| `NO_HOST_DATA_FOUND` | "Configurazione host non trovata. Completa il processo di registrazione." | "Host configuration not found. Please complete the onboarding process." | +| `ANNOUNCEMENT_HAS_NO_IMAGES` | "Aggiungi almeno un'immagine a questo annuncio." | "Please add at least one image to this announcement." | + +Admin-only (Zeus.Admin/errors.json): + +| Key | it | en | +|-----|----|-----| +| `INVALID_SUPPORT_CODE` | "Il codice di supporto inserito non è valido" | "The support code is not valid" | +| `UNAUTHORIZED_SUPPORT_REQUEST` | "Questa richiesta non è ancora stata accettata" | "This request has not yet been accepted" | + +### gRPC status codes and infrastructure errors + +| Scenario | gRPC status | Code | +|----------|------------|------| +| Backend container down / nginx upstream fail | `Unavailable` | 14 | +| DB unreachable (first call) | `Internal` | 13 | +| Unhandled exception | `Internal` | 13 | +| Auth failure / expired JWT | `Unauthenticated` | 16 | +| Validation errors | `InvalidArgument` | 3 | +| Rate limit (OTP flood / SMS flood) | `ResourceExhausted` | 8 | +| State-guard violations | `FailedPrecondition` | 9 | +| Stripe payment declined | `Aborted` | 10 | + +Payment SignalR failure codes: `Failed(code)` where `code` is a Stripe string (`card_declined`, `insufficient_funds`, `amount_mismatch`) or a Zeus key (`PAYMENT_FAILED`, `UNBOUND_PAYMENT_SESSION`). These raw codes reach the Flutter app directly — they are NOT localized by the server. + +TLS failure appears as `CERTIFICATE_VERIFY_FAILED` or `HandshakeException` in Flutter logs (not a Zeus error; see `infra`). + +## Symptom → area heuristics + +- payment stuck / perpetually "processing" after booking confirmation → `backend` (Stripe webhook not processed; check `StripeWebhookController` logs for signature failure) +- "Il pagamento non è andato a buon fine" before card entry → `backend` (`PAYMENT_SESSION_CLOSED` or `UNBOUND_PAYMENT_SESSION`; admin may have force-cancelled the session) +- payment `Failed` callback with Stripe code (`card_declined`, `insufficient_funds`) → `backend` (Stripe declined; retry flow in app is correct) +- chat messages sent but other party never receives → `backend` (check `SendMessage` gRPC response + Redis chat registry; `SetOnline` may have expired) +- OTP / SMS login code not arriving → `backend` (Ermes SMS service; key `SMS_SERVICE_UNAVAILABLE`) +- OTP code rejected as wrong or expired → `backend` (`INVALID_CONFIRMATION_CODE`) +- wrong or missing data shown in app with correct layout → `backend` (data model or repository issue) +- announcement detail missing, showing error toast → `backend` (`ANNOUCEMENT_NOT_FOUND`) +- booking dates show as unavailable despite being free → `backend` (`APARTMENT_UNAVAILABLE` — availability row state) +- "La tua sessione è scaduta" in the middle of a session (not at launch) → `backend` (`INVALID_ACCESS_TOKEN` returned by server; JWT refresh failed) +- host onboarding "Completa il processo di registrazione" on every login → `backend` (`NO_HOST_DATA_FOUND` — HostData row missing) +- notification badge count never resets → `backend` (`MarkNotificationRead` failing server-side, e.g. stale notification ID) +- user cannot leave a review after a stay → `backend` (reviews are only allowed once the booking reaches `Completed`; check booking status) +- admin dashboard page errors, wrong data in grids, action buttons fail → `backoffice` +- admin support code rejected ("Il codice di supporto inserito non è valido") → `backoffice` +- site / app entirely unreachable, no HTTP response at all → `infra` (nginx.edge or docker stack down; TCP connection refused on :443/:80) +- 502 from nginx, gRPC status 14 "Unavailable" → `infra` (upstream container crash) +- TLS certificate warning or `CERTIFICATE_VERIFY_FAILED` → `infra` (Let's Encrypt cert expired or nginx misconfigured) +- visual glitches, overflow stripes, layout overflow, misaligned widgets → `app-shared` (Flutter rendering; not Zeus) +- platform-specific crash on Android or iOS (no network call) → `app-android` / `app-ios` + +## Domain glossary + +| Italian term | System concept | +|--------------|----------------| +| Annuncio / Annunci | `Announcement` entity; listing published by a host | +| Host | A user who completed Stripe onboarding; can publish annunci | +| Ospite / Guest | A user making a booking | +| Prenotazione / Prenotazioni | `BookingOrder` entity | +| Check-in / Check-out | Arrival and departure dates on a booking | +| Sessione di pagamento | `PaymentSession` — Stripe PaymentIntent/SetupIntent lifecycle; statuses: Ongoing, AwaitingCapture, Succeeded, Failed, Cancelled | +| Segnalazione / Segnalazioni | `ChatReport` — chat moderation report; statuses: Open, Reviewed, Dismissed | +| Verifica / Verifiche Utenti | `VerificationRequest` — KYC identity check; statuses: Draft, Pending, Approved, Rejected | +| Recensione / Recensioni | `AnnouncementReview` — post-stay review; only possible after booking `Completed` | +| Servizi | Amenities (wifi, parking, …); `GuestAmenityService` | +| Notifica | In-app notification; `GuestNotificationService` | +| Codice Supporto | Short-lived code user reads aloud to operator to start a support session | +| Sessione di supporto | Admin operator impersonation session for support | +| Livelli di Supporto | RBAC levels for admin operators | +| Codice invito | Referral code; `HostReferralService` | +| Cattura / Cattura ora | Stripe delayed capture; admin action on booking detail | +| Panoramica | Admin dashboard home | + +## Not mine + +- Visual rendering artifacts, overflow widgets, clipped text, wrong font size → `app-shared` +- "Sessione scaduta" toast at app launch with no network request in logs → `app-shared` (Flutter JWT expiry check done locally — no server call) +- Search map shows no results despite listings existing (no gRPC error) → `app-shared` (Flutter map range / filter state) +- Chat messages in wrong order or duplicated → `app-shared` (Flutter merge of historical stream + SignalR without dedup) +- Announcement images load as blank (HTTP 200 from GCS) → `app-shared` (Flutter image widget render failure; HTTP 403 = expired GCS URL = `backend`) +- Host calendar shows wrong blocked dates after refresh → `app-shared` (stale local cache; re-calling `GetReservations` returns correct data) +- Android-specific crash, iOS-specific crash → `app-android` / `app-ios` +- Store deployment failures, app binary issues → `app-android` / `app-ios` +- Push notification delivery on device (FCM/APNs) → `app-shared` (not Zeus; Zeus only stores tokens via `AuthDevicesService`) diff --git a/skills/map-repo/SKILL.md b/skills/map-repo/SKILL.md new file mode 100644 index 0000000..febed78 --- /dev/null +++ b/skills/map-repo/SKILL.md @@ -0,0 +1,62 @@ +--- +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 : + +> Survey the codebase at 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 `.md` following the six headings of +`knowledge/README.md`, all sections non-empty, 100–200 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 ` 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/.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/.md .claude-plugin/plugin.json` + then `git commit -m "knowledge: add/update 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`. diff --git a/skills/triage/SKILL.md b/skills/triage/SKILL.md new file mode 100644 index 0000000..2b1b93a --- /dev/null +++ b/skills/triage/SKILL.md @@ -0,0 +1,127 @@ +--- +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 /gw-triage: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 `/gw-triage: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. + +If no knowledge profile covers the symptoms, classify using the routing.json area list alone and state the lower confidence explicitly in the draft. + +## Step 4 — Duplicate check + +Extract 2–4 distinctive keywords (include Italian and English variants +of domain terms). Call `searchJiraIssuesUsingJql` with: +`project = AND text ~ "" 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": ""}}`. + +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=@" \ + "/rest/api/3/issue//attachments" + +`` 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 `/browse/`, type, +epic, assignee, and whether the screenshot was attached.