Přihlášený uživatel klikne na tlačítko “Nahlásit problém” ve Studio toolbaru, vyplní modal formulář a odešle report TalkIDE platformního bugu nebo feature requestu. BE rozšiřuje reporter_agent whitelist o hodnotu USER a aplikuje rate limit 5 reportů za hodinu per-user-id.
- Přístup pouze pro přihlášené uživatele — user JWT povinný. Bez JWT → 401 Unauthorized.
severity = CRITICALuživatel nastavit nemůže — dropdown nabízí jen LOW / MEDIUM / HIGH. CRITICAL je admin-only (UC-09004).targetje vždyPLATFORM— uživatel nevidí volbu. Modal posílá výhradnětarget=PLATFORM(Phase 2 scope). BE endpoint validujePROJECTcestu per UC-09005 (Phase 3, live).kind = INCIDENTnení dostupný — přijde s environments feature (#84).- Rate limit scope je
reporter_user_id(nesession_id— user v tuto chvíli nemusí mít aktivní Mara konverzaci). - Dedup okno 10 minut (stejný klíč jako UC-09001):
(tenant_slug, target, SHA256(title.lowercase().trim()))+reported_at > now() - 10 min. - Pokud je user ve Studiu otevřeného projektu, BE obohacuje kontext o
project_idpřes volitelný headerX-Current-Project. - UX záměrně odděluje “Report TalkIDE bug” (tato UC) od “Report bug v aplikaci” (Phase 3 UC-09005) — uživatel nesmí zaměnit scope.
Scope poznámka: Tento UC pokrývá FE-initiated PLATFORM reporting flow (tlačítko ve Studio/Workspace). BE endpoint
POST /api/issuesje sdílený i s UC-09005 (USER+PROJECT, Phase 3). Modal v této Phase 2 implementaci posílá výhradnětarget=PLATFORM, ale BE endpoint validuje obě cesty. USER +target=PROJECTs validnímproject_idje plně podporováno v BE — viz UC-09005 aIssueTrackingTestCases(5 regresních testů USER+PROJECT → 201).
Flow — Uživatel reportuje nový PLATFORM issue
sequenceDiagram
actor User
User->>+FE: klikne na "🐛 Nahlásit problém" v Studio toolbaru
FE->>FE: otevře ReportIssueModal
Note over FE: Prefill: title prázdný<br/>kind = BUG (default)<br/>severity = MEDIUM (default)<br/>X-Current-Project header<br/>připraven z aktivního projektu (pokud existuje)
User->>FE: vyplní title, description, kind, severity a odešle formulář
FE->>FE: validace formuláře
alt formulář nevalidní (prázdné pole, title > 80 znaků)
FE-->>User: zobrazí inline chybové hlášky pod poli
end
FE->>+BE: POST /api/issues <br/> Authorization: Bearer {userJWT} <br/> X-Reporter-Agent: USER <br/> X-Current-Project: {projectId} (Long, volitelný) <br/> { target, kind, title, description, severity }
BE->>BE: ověří JWT → extrahuje reporter_user_id
alt JWT chybí nebo je neplatný
BE-->>FE: 401 Unauthorized
end
BE->>BE: validuje X-Reporter-Agent header
alt X-Reporter-Agent není USER
BE-->>FE: 400 Bad Request (VALIDATION)
end
BE->>BE: validuje request body (target, kind, title, severity)
Note over BE: target=PROJECT bez project_id → 400 ISSUE_PROJECT_ID_REQUIRED<br/>target=PROJECT s validním project_id → povoleno (UC-09005 cesta)
alt nevalidní tělo requestu
BE-->>FE: 400 Bad Request (VALIDATION)
end
BE->>DB: rate limit check: COUNT issues WHERE reporter_user_id=? AND reported_at > now()-60min
alt >= 5 reportů za hodinu
BE-->>FE: 429 Too Many Requests (RATE_LIMIT_EXCEEDED)
FE-->>User: "Dosáhl jsi limitu 5 reportů za hodinu. Zkus to znovu za chvíli."
end
BE->>DB: dedup query: EXISTS issues WHERE tenant_slug=? AND target=? AND SHA256(title)=?<br/>AND reported_at > now() - interval '10 minutes'
alt dedup hit
BE->>DB: INSERT issue_comments (author_agent=USER, body="Nahlásil jsem znovu. [kontext]")
BE-->>FE: 200 OK { id, url, deduplicated: true, comment_id }
FE-->>User: "Stejný problém byl nahlášen před chvílí jako #ID. Přidali jsme tvou poznámku."
end
BE->>BE: server-side context enrichment (synchronní, fail-soft)
Note over BE: context_jsonb:<br/>· projectState: tenant_slug, projectId (z X-Current-Project), deploy URL, last build SHA<br/>· (k8sEvents a podLogSnippet pouze pokud X-Current-Project přítomen)
BE->>DB: INSERT issues (id=UUID, target=PLATFORM, tenant_slug, reporter_user_id,<br/>reporter_agent=USER, session_id=NULL, title, description,<br/>kind, severity, status=OPEN, context_jsonb, reported_at=now())
BE-->>-FE: 201 Created { id, url, deduplicated: false }
FE->>FE: zavře modal
FE-->>-User: toast "Problém nahlášen. ID: #{zkrácenéId}" (prvních 8 znaků id; bez klickatelného odkazu)
REST API
POST /api/issues
Autentizace: User JWT (Bearer token). Chybějící nebo neplatný JWT → 401 Unauthorized.
Povinné headery:
| Header | Hodnota | Poznámka |
|---|---|---|
Authorization | Bearer <userJWT> | Povinný |
X-Reporter-Agent | USER | Povinný; BE akceptuje MARA nebo USER (Phase 1 whitelist rozšířen touto UC) |
X-Current-Project | <projectId> (Long, číselné ID projektu, např. 42) | Volitelný; přítomný pokud uživatel reportuje ze Studio konverzace s otevřeným projektem; umožňuje BE enrichment o project state; pokud hodnota není parsovatelná jako Long, BE ji ignoruje (fail-soft) a issue se uloží bez project_id enrichmentu + warn log |
Request body:
{
"target": "PLATFORM",
"kind": "BUG",
"title": "Build selhává — Kaniko nemůže zapisovat do registry",
"description": "Po kliknutí na Deploy se zobrazí chyba `unauthorized: authentication required`. Opakoval jsem deploy třikrát, stejný výsledek. Naposledy to fungovalo včera odpoledne.",
"severity": "HIGH"
}
201 Created (nový issue):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://talkide.app/studio/issues/550e8400-e29b-41d4-a716-446655440000",
"deduplicated": false
}
200 OK (dedup hit — stejný issue nahlášen v posledních 10 minutách):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://talkide.app/studio/issues/550e8400-e29b-41d4-a716-446655440000",
"deduplicated": true,
"comment_id": "661f9511-f3ac-52e5-b827-557766551111"
}
400 Bad Request (validace):
{
"code": "VALIDATION",
"message": "title must not exceed 80 characters"
}
401 Unauthorized (chybí nebo neplatný JWT):
{
"code": "UNAUTHORIZED",
"message": "Authentication required"
}
403 Forbidden (severity=CRITICAL nebo nepovolený X-Reporter-Agent):
{
"code": "FORBIDDEN",
"message": "CRITICAL severity can only be set by admin during triage"
}
429 Too Many Requests (rate limit):
{
"code": "RATE_LIMIT_EXCEEDED",
"message": "Maximum 5 issue reports per hour exceeded. Try again later."
}
Auto-context enrichment (server-side, synchronní, fail-soft)
Pokud je přítomen header X-Current-Project, BE synchronně sbírá a ukládá do context_jsonb:
| Pole | Zdroj | Detail |
|---|---|---|
projectState | TalkIDE DB | tenant_slug, projectId (z X-Current-Project), aktuální deploy URL, SHA posledního úspěšného buildu. Pokud enrichProjectState selže (DB exception), context_jsonb obsahuje projectState = null a errors["projectStateError"] s popisem chyby. |
k8sEvents | Kubernetes API (fabric8) | Warning/Error eventy za posledních 5 minut z tenant-<slug> namespace + talkide namespace (max 20 položek) |
podLogSnippet | Kubernetes API (fabric8) | WARN/ERROR řádky za posledních 2 minuty, max ~200 řádků celkem, pody v talkide namespace |
errors | BE enrichment | Klíče projectStateError, k8sEventsError, podLogSnippetError — přítomny pouze při selhání dílčího sběru; hodnota = string popis chyby |
Pokud X-Current-Project header chybí (uživatel reportuje mimo kontext projektu), context_jsonb obsahuje pouze { "projectState": null }.
K8s sběr je fail-soft: selhání enrichmentu nesmí zablokovat vytvoření issue. Nedostupné pole = null + záznam v errors["<poleName>Error"]. Issue se vždy uloží.
UX Guidelines
Studio toolbar tlačítko
- Umístění: toolbar vpravo nahoře, vedle uživatelského menu.
- Label: ”🐛 Nahlásit problém” (desktop) nebo jen ikona 🐛 (mobilní breakpoint).
- Viditelné vždy — ve Studiu i na dashboard stránkách (není vázáno na konkrétní projekt).
ReportIssueModal — wireframe (textový)
┌─────────────────────────────────────────────────────────┐
│ 🐛 Nahlásit problém s TalkIDE [×] zavřít │
├─────────────────────────────────────────────────────────┤
│ ℹ️ Reportuješ bug nebo nápad pro tým TalkIDE. │
│ Pokud hledáš bug v tvé aplikaci, jdi do │
│ záložky "Problémy aplikace" → [odkaz] (Phase 3) │
├─────────────────────────────────────────────────────────┤
│ Název * │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Stručný popis problému (max 80 znaků) │ │
│ └───────────────────────────────────────────────────┘ │
│ [Zbývá: 80 znaků] — live counter │
│ │
│ Popis * │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Markdown editor — co se stalo, kroky reprodukce, │ │
│ │ co jsi očekával a co se stalo místo toho │ │
│ │ │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ Typ Závažnost │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ BUG ▼│ │ MEDIUM ▼│ │
│ └─────────────┘ └─────────────┘ │
│ BUG / FEATURE LOW / MEDIUM / HIGH │
│ │
│ [Zrušit] [Odeslat report] │
└─────────────────────────────────────────────────────────┘
Klíčové UX detaily:
- Scope klarifikace: Modal zobrazí informační banner “Reportuješ problém s TalkIDE platformou (build, deploy, Studio). Pokud hledáš bug ve své aplikaci, použij záložku Problémy aplikace.” — zabraňuje záměně scope.
- Live counter: Pole
titlezobrazuje zbývající počet znaků do limitu 80 v reálném čase. - Markdown preview: Pole
descriptionpodporuje toggle mezi editačním a preview módem (md rendering). - Severity tooltip: Dropdown pro závažnost obsahuje tooltip s rubrikem:
- LOW: Kosmetické — neblokuje práci
- MEDIUM (výchozí): Zpomaluje práci, existuje obejití
- HIGH: Blokuje klíčovou operaci, nelze pokračovat
- Submit button state: Deaktivovaný dokud
titleadescriptionnejsou vyplněny. - Loading state: Po odeslání tlačítko zobrazuje spinner, formulář je zablokovaný.
- Po úspěchu: Modal se zavře, zobrazí se toast “Problém nahlášen. ID: #{zkrácenéId}” kde
zkrácenéId= prvních 8 znakůidz BE response. Toast neobsahuje klickatelný odkaz na detail — klickatelný link (využijeurlz response) je naplánován na Phase 3.urlje v response přítomna pro budoucí rozšíření. - Dedup feedback: Pokud BE vrátí
deduplicated: true, modal zobrazí informaci “Stejný problém byl nedávno nahlášen jako #{id}. Přidali jsme tvou poznámku.” místo standardního “odeslán” toastu. - Rate limit feedback: 429 → modal zůstane otevřen, zobrazí error banner “Dosáhl jsi limitu 5 reportů za hodinu. Zkus to prosím za chvíli.”
- Phase 3 placeholder: Odkaz na “Problémy aplikace” záložku v banneru vede na budoucí
/studio/issues/project— dokud Phase 3 není live, odkaz je disabled nebo vede na placeholder stránku.
Frontend
Validace
| Pole | Constraints | Velikost | Pattern | Poznámka |
|---|---|---|---|---|
title | not_blank | 1–80 | — | Live counter; trim před validací |
description | not_blank | 1–10 000 | — | Markdown; trim před validací |
kind | required, in(BUG, FEATURE) | — | — | Výchozí hodnota: BUG |
severity | required, in(LOW, MEDIUM, HIGH) | — | — | Výchozí hodnota: MEDIUM; CRITICAL skrytý |
Headery sestavené FE
| Header | Hodnota | Kdy |
|---|---|---|
X-Reporter-Agent | USER | Vždy |
X-Current-Project | String(currentProjectId) — číselné Long ID aktivního projektu (např. "42") | Pokud je uživatel v kontextu otevřeného projektu |
Backend
Validace
| Pole / Kontrola | Constraints | Poznámka |
|---|---|---|
Authorization header | required, platný JWT | Chybí nebo neplatný → 401 |
X-Reporter-Agent header | required, in(MARA, USER) | Jiná hodnota → 400 VALIDATION |
target | not_blank, in(PLATFORM, PROJECT) | Modal posílá PLATFORM (Phase 2 scope). Endpoint akceptuje i PROJECT per UC-09005; PROJECT bez project_id → 400 ISSUE_PROJECT_ID_REQUIRED |
kind | not_blank, in(BUG, FEATURE) | INCIDENT → 400 “kind=INCIDENT not yet implemented (see #84)“ |
title | not_blank, max 80 chars | Trim whitespace před uložením |
description | not_blank, max 10 000 chars | Markdown; trim před uložením |
severity | nullable, default MEDIUM, in(LOW, MEDIUM, HIGH) | CRITICAL → 403 Forbidden |
| Rate limit | max 5 / 60 min / reporter_user_id | Počítáno z reported_at v issues pro daný reporter_user_id |
| Dedup | (tenant_slug, target, SHA256(title.lowercase().trim())) + reported_at > now()-10min | Viz dedup flow; při hitu → 200 OK s komentářem |
X-Current-Project header | optional, Long (číselné ID projektu) | Pokud přítomen, BE parsuje hodnotu jako Long a resolvuje projekt z DB pro enrichment; neparsovatelná hodnota (není Long) → BE ignoruje hlavičku, fail-soft, warn log, issue se uloží bez enrichmentu; neexistující projectId → enrichment přeskočen (warn log), issue se uloží |
Test Cases
| GIVEN | WHEN | THEN |
|---|---|---|
| Přihlášený user, platný JWT, vyplněný formulář (title, description, kind=BUG, severity=MEDIUM) | POST /api/issues s X-Reporter-Agent: USER, target=PLATFORM | 201 Created; issue uložen s reporter_agent=USER, status=OPEN, severity=MEDIUM; context_jsonb obsahuje alespoň { "projectState": null }; FE zobrazí toast “Problém nahlášen. ID: #{prvních 8 znaků id}” |
Přihlášený user, platný JWT, vyplněný formulář + X-Current-Project header s platným projectId | POST /api/issues | 201 Created; context_jsonb.projectState obsahuje projectId a deploy data projektu |
User odesílá severity=CRITICAL (manipulace requestu mimo FE) | POST /api/issues s X-Reporter-Agent: USER | 403 Forbidden, code=FORBIDDEN; issue nevznikne |
User odesílá target=PROJECT bez project_id (manipulace requestu) | POST /api/issues s X-Reporter-Agent: USER | 400 Bad Request, code=ISSUE_PROJECT_ID_REQUIRED; issue nevznikne. Poznámka: USER + target=PROJECT s validním project_id je podpořeno UC-09005 (Phase 3) — ten case sem nepatří. |
User odesílá title delší než 80 znaků | POST /api/issues s X-Reporter-Agent: USER | 400 Bad Request, code=VALIDATION |
User odesílá description delší než 10 000 znaků | POST /api/issues s X-Reporter-Agent: USER | 400 Bad Request, code=VALIDATION |
| User odeslal 5 issues za posledních 60 minut | 6. POST /api/issues s X-Reporter-Agent: USER | 429 Too Many Requests, code=RATE_LIMIT_EXCEEDED |
Issue se stejným (tenant_slug, target, title hash) existuje a reported_at > now()-10min | POST /api/issues se stejným titulem | 200 OK, deduplicated=true; nový komentář přidán; nový issue nevznikne |
| Chybí Authorization header | POST /api/issues | 401 Unauthorized, code=UNAUTHORIZED |
| Neplatný JWT (expirace, bad signature) | POST /api/issues | 401 Unauthorized |
X-Reporter-Agent header chybí nebo má jinou hodnotu než MARA/USER | POST /api/issues | 400 Bad Request, code=VALIDATION |
X-Current-Project odkazuje na neexistující projectId | POST /api/issues | 201 Created; enrichment přeskočen (warn log); context_jsonb.projectState = null |
X-Current-Project obsahuje neparsovatelnou hodnotu (např. UUID řetězec místo Long) | POST /api/issues | 201 Created; BE ignoruje hlavičku (fail-soft, warn log); issue uložen bez project_id enrichmentu |
| K8s API nedostupné při enrichmentu | POST /api/issues s X-Current-Project | 201 Created; context_jsonb.k8sEvents = null, context_jsonb.errors.k8sEventsError přítomen |
BE změna: rozšíření reporter_agent whitelistu
Phase 1 (UC-09001) akceptuje v hlavičce X-Reporter-Agent pouze hodnotu MARA. Tato UC rozšiřuje whitelist na MARA | USER.
Konkrétní změna:
Třída IssueController (nebo middleware pro validaci headeru) rozšiřuje akceptované hodnoty:
Před: X-Reporter-Agent in [MARA]
Po: X-Reporter-Agent in [MARA, USER]
Auth logika se liší:
MARA→ Agent JWT (stávající chování UC-09001 beze změny)USER→ User JWT — BE extrahujereporter_user_idze standardního JWT claimusub;session_idse ukládá jakoNULL(user nemá Mara session UUID)
Rate limit scope:
MARA: persession_id(stávající chování)USER: perreporter_user_id(nové chování — user nemá konzistentní session UUID)
References
- UC-09001 Report Platform Issue — Mara-initiated flow (stejný endpoint, jiný reporter_agent)
- UC-09004 Admin Triage Issue — admin CRITICAL override, status transitions
- UC-09005 Report Project Issue — Phase 3: target=PROJECT (zatím není live)
- README — Reporting authority
- README — Klíčová omezení Phase 1
- GitLab issue talkide-be#87
FEEDBACK
Zadání bylo velmi podrobné — scope, field constraints, rate limit scope a UX nápady byly popsány dostatečně na přímý přepis. Chybělo upřesnění dvou věcí: (1) zda description má i BE limit 10 000 znaků (FE ano, ale BE validační tabulka v UC-09001 limit nezmiňuje — předpokládal jsem symetrii); (2) jak přesně BE resolvuje tenant_slug pro USER reporty (u Mara je to z agent JWT, u USER JWTs to závisí na claims struktuře). Usnadnilo by práci stručná odpověď “user JWT obsahuje / neobsahuje tenant_slug claim” na začátku zadání.
Thanks for the feedback.