Bilko — Kako support agent radi (runbook, MC #106063)

Parent: MC #106049 (Support MVP) · Ovaj task: MC #106063 (E2) · Agent: Skillforge · Datum: 2026-07-20

Vidi također: Faza A (RBAC+RLS temelj), Faza B (ticket reply + GUC aktivacija), i "Support sistem — arhitektura" (D1) za mapu svih support mehanizama.

1. Kako se support agent provisionira

1a. Zatvorena rupa — #106071 generic-invite reject

Kad su InviteService.VALID_ROLES i UserProvisioningService.VALID_ROLES prošireni da uključe support_agent (da bi gornji dedicated endpoint mogao pozvati isti servisni sloj), TRI POSTOJEĆA generička route-a (gated samo sa users:manage, dakle bilo koji admin/owner SVOJE org-e) su tiho počela prihvatati role="support_agent" i preko sebe:

Fix: nova funkcija rejectSupportAgentRoleInGenericFlow(role) u RbacHelper.kt — baca ForbiddenException("SUPPORT_AGENT_ROLE_FORBIDDEN_HERE...") na sva tri generička route-a PRIJE nego zahtjev stigne do servisnog sloja. VALID_ROLES ostaju široki (jer dedicated endpoint legitimno zove istu createInvite() funkciju) — reject je na route sloju, ne na servisnom, upravo na ta tri mjesta koja je Parisa našla, ne paralelna allowlist koja bi mogla driftati.

2. Jackson vs kotlinx.serialization — razriješena neslaganja (Momjian vs Parisa)

Zadatak je tražio da se PROČITA stvarni kod prije nego što se prepiše bilo čija tvrdnja. Pročitano: apps/api/src/main/kotlin/no/alai/bilko/plugins/Serialization.kt i SupportAgentProvisioningRoutes.kt na branch-u feat/106051-support-agent-rbac (commit 5c06350a).

Nalaz: Bruce Momjian je bio u pravu — mehanizam je Jackson's FAIL_ON_UNKNOWN_PROPERTIES (default true, nikad eksplicitno postavljen niti isključen u kodu), NE kotlinx.serialization ignoreUnknownKeys.

Dokaz iz koda:

// Serialization.kt
fun Application.configureSerialization() {
    install(ContentNegotiation) {
        jackson {
            disable(SerializationFeature.INDENT_OUTPUT)
            registerModule(JavaTimeModule())
            disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            findAndRegisterModules()
        }
    }
}

val bilkoJson = Json {
    ignoreUnknownKeys = true   // <- postoji, ali NIJE registrovan u ContentNegotiation
    ...
}

Zaključak za tim: kad god vidite @Serializable na DTO-u u ovom repou, to NE garantuje da će kotlinx pravila (npr. ignoreUnknownKeys) važiti na HTTP granici — treba provjeriti da li je taj DTO stvarno deserijalizovan preko ContentNegotiation-a (Jackson, ovaj repo) ili preko direktnog poziva na bilkoJson.decodeFromString<T>() (kotlinx, druga upotreba u istom fajlu).

3. Šta support agent VIDI

4. Šta support agent NE VIDI

5. RBAC permission-key referenca

Permission keyZnačenjeFormat
support_ticket:readČitanje/listanje tiketa preko svih organizacijacolon-format (resource:verb)
support_ticket:replyOdgovor / triage tiketa (status + resolution_note) preko svih organizacijacolon-format (resource:verb)

Zašto colon, ne dot: dispatch je originalno tražio support.tickets.read/support.tickets.reply (dot-notation). V67-ova permission_key_format CHECK constraint (key ~ '^[a-z_]+:[a-z_]+$') zahtijeva tačno jednu dvotačku, bez tačaka — svaki postojeći ključ u katalogu (npr. invoice:read, expense:create) već poštuje taj format. Umjesto proširenja constraint-a za jednu feature-preferencu imena, korišteno je support_ticket:read/support_ticket:reply (singular resource, isti obrazac).

6. RLS cross-org izuzetak — zašto i kako ograničen

Zašto postoji izuzetak uopšte: support osoblje MORA vidjeti tikete preko svih organizacija da bi triage funkcionisao — jedan support agent opslužuje sve klijente, ne samo jednu org. Standardni Bilko RLS model (org_id-scoped) bi u tom slučaju blokirao support agenta da vidi ijedan tiket van svoje (interne) org-e.

Kako je ograničen (ključna crvena-zona odluka, Momjian, MC #106051):

7. Otvoreni tech-debt

8. Merge status (na dan pisanja ove stranice)

Branch feat/106051-support-agent-rbac (azdo), tip 5c06350a. NIJE merge-ovan u azdo/main. Faza A (Parisa adversarial verify) i Faza B (GUC-execution adversarial pass) oboje još čekaju Parisa Tabriz-ov nezavisni pregled prije merge-a — to je eksplicitni merge-gate koji su i Momjian i CodeCraft ostavili otvorenim u svojim verdiktima (vidi ~/system/evidence/106051/verdict.md i ~/system/evidence/106055/verdict.md).


Revision #4
Created 2026-07-20 09:07:17 UTC by John
Updated 2026-08-10 07:37:51 UTC by John