Guida processo

QA agenti di simulazione (Platform owner)

Agenti AI autonomi su tenant 'Tecnolife' simulation: scenari prodotto, findings classificati, cruscotto Platform.

QA agenti di simulazione (Platform owner)

Ultimo aggiornamento: 2026-06-03 Owner: Product Writer Audience: Platform owner (super_admin, ops_admin)

Scopo

Spiegare come funziona il team di agenti AI autonomi che, contro un tenant dedicato di simulazione ("Tecnolife", slug tecnolife-sim), esegue azioni realistiche del prodotto, raccoglie errori e regressioni e li ribalta sul cruscotto /workspace/platform/simulation per la review del Platform owner.

Concetti chiave

  • Tenant simulation dedicato: tecnolife-sim (UUID c07c899f-dcad-4227-8758-4cddc085c99e). Separato dal tenant produttivo paying reale chiamato "Tecnolife". Guardia hard-coded contro contaminazione cross-tenant.
  • Agente = ruolo + scenari: ogni agente impersona un ruolo del prodotto (HR admin, manager, user, owner) ed esegue una sequenza di azioni via API.
  • Findings = eventi NDJSON classificati: ogni step di ogni scenario emette un evento (request/response/latency/error). Un classifier deterministico assegna finding_hint (bug, regression, performance, ux_friction, suggestion).
  • Persistenza: opt-in. Run + findings persistiti su platform_simulation_runs + platform_simulation_findings. RLS bloccata, accesso solo Platform owner via UI o service-role.

Prerequisiti

  • ruolo Platform owner (super_admin / ops_admin / billing_admin)
  • migrations 0080_hr_candidates_soft_delete.sql + 0081_platform_simulation_findings.sql applicate
  • env SUPABASE_SERVICE_ROLE_KEY per eseguire lo script bootstrap (one-shot)

Accesso rapido

  • Cruscotto findings: /workspace/platform/simulation
  • Run agenti (CLI): node horis-saas/apps/web/scripts/simulation/orchestrator.mjs
  • Bootstrap tenant: node horis-saas/apps/web/scripts/bootstrap-tecnolife-sim.mjs

Passi operativi

1. Bootstrap del tenant (one-shot, gia' fatto)

Esegue creazione tenant + 30 utenti seed + workforce blueprint. Idempotente.

```bash cd horis-saas node apps/web/scripts/bootstrap-tecnolife-sim.mjs ```

Risultato: tenant tecnolife-sim con 1 owner + 1 admin + 1 billing_admin + 1 hr_admin + 2 manager + 30 user, 4 progetti, ~100 timesheet, ~700 presence, 51 skill catalog.

2. Eseguire un run di simulazione

```bash cd horis-saas HORIS_SIMULATION_ENABLED=true \ HORIS_SIMULATION_PERSIST=true \ node apps/web/scripts/simulation/orchestrator.mjs ```

L'orchestrator:

  1. Pre-flight: verifica killswitch + targeting tenant simulation
  2. Login come HR admin del tenant simulation
  3. Seleziona gli scenari dovuti (cadenza + profilo), poi li esegue: prima gli isolate in sequenza, poi il resto in parallelo (cap concorrenza)
  4. Stampa NDJSON su stdout + salva su tmp/simulation-runs/run-<uuid>.ndjson
  5. (Se PERSIST=true) ingesta su DB → run + findings visibili nel cruscotto

Env di scheduling (HORIS_SIMULATION_CONCURRENCY_01):

  • HORIS_SIMULATION_PROFILE = full (default, applica cadenza) | all (tutti, ignora cadenza) | canary (solo canary, per cron ad alta frequenza)
  • HORIS_SIMULATION_CONCURRENCY = N worker paralleli (default 4; 1 = sequenziale)
  • HORIS_SIMULATION_DAILY_HOUR = ora UTC degli scenari daily (default 3)

3. Leggere il cruscotto

/workspace/platform/simulation:

  • KPI strip: run totali, eventi, scenari PASS/FAIL aggregati
  • Tabella run recenti (ultimi 20)
  • Click su run → tabella findings con filtro per hint
  • Aggregati per finding_hint mostrati come chip colorati

4. Esportare findings (CSV / JSON / MD)

Card "Esporta findings" sotto la KPI strip:

  • Formato: JSON (dati completi con raw_event e payload_summary), CSV (Excel/Numbers, flat), Markdown (umano-leggibile, copy-paste)
  • Scope: "Run selezionato" (solo quello attualmente espanso) oppure "Ultimi 7 giorni" (max 100 run)
  • Rispetta il filtro hint corrente: se hai selezionato bug, esporta solo bug
  • Endpoint: GET /api/platform/simulation/export?format={json|csv|md}&run_id?&hint?&since?&limit?

5. Leggere i trend (heatmap + latenza)

Sezione "Trend & Heatmap" sotto la KPI strip (HORIS_TREND_ANALYTICS_01):

  • Heatmap scenario × giorno: una riga per scenario, una colonna per giorno. Colore della cella = worst severity osservata quel giorno (rosso = bug/regression, ambra = performance, azzurro = ux_friction/suggestion, verde = sano, grigio = nessun run). Hover per conteggi eventi/findings/hint.
  • Trend latenza P50 / P95: due line chart (overall + canary Talent Partner) con percentili per giorno. P50 linea scura, P95 linea ambra.
  • Selettore finestra: ultimi 7 / 14 / 30 giorni.
  • Aggregati su platform_simulation_findings (anche eventi sani contano per la baseline).
  • Endpoint: GET /api/platform/simulation/trends?days={1..90}

Scenari attualmente coperti

| Scenario | Ruolo | Cosa fa | |---|---|---| | hr-screen-candidate | hr_admin | Crea candidato + 2 transizioni status (trigger email transazionali) + soft-delete | | hr-upload-extract-cv | hr_admin | Crea candidato + upload PDF + extract AI + verify draft + cleanup | | hr-trash-cycle | hr_admin | Crea + soft-delete + verifica vista cestino + restore + cleanup | | hr-certification-cycle | hr_admin | Crea cert per altro user + list verify + update status + delete (SCENARIOS_02 W01) | | hr-ccnl-assignment | hr_admin | GET catalog CCNL + PATCH ccnlId/level/classification + verify persistence (SCENARIOS_02 W01) | | hr-employment-classification | hr_admin | Apprendista happy + freelance senza vat = 500 atteso + HR vat = 403 PII boundary + cleanup (SCENARIOS_02 W01) | | boundary-permissions | multi (manager+user+hr) | 8 step negative-path: manager bloccato HR, user bloccato cert altrui, HR bloccato PII self-only (SCENARIOS_02 W02) | | hr-talent-partner-search | hr_admin | POST /api/hr-chat con question consulting realistica + verify answer non vuoto + latency check (SCENARIOS_02 W03) | | user-timesheet-month | user | GET /api/timesheet/month — own month read (OPERATIONAL_EXCELLENCE_01 W02) | | user-leave-requests | user | GET /api/requests — own leave/travel list (OPERATIONAL_EXCELLENCE_01 W02) | | manager-team-capacity | manager | GET /api/team-capacity — team scope read (OPERATIONAL_EXCELLENCE_01 W02) | | manager-timesheet-reviews | manager | GET /api/timesheet/reviews?status=submitted — approval queue (OPERATIONAL_EXCELLENCE_01 W02) | | manager-ops-calendar-team | manager | GET /api/ops-calendar/team — team daily ops (OPERATIONAL_EXCELLENCE_01 W02) | | platform-tenants-overview | hr_admin+super_admin | HR blocked 403 + super_admin reads tenants 200 (OPERATIONAL_EXCELLENCE_01 W02) | | edge-race-condition-patch | hr_admin | 2 PATCH paralleli → last-write-wins verify (OPERATIONAL_EXCELLENCE_01 W03) | | edge-storage-upload-burst | hr_admin | 5 CV consecutivi → versioning is_current=1 verify (OPERATIONAL_EXCELLENCE_01 W03) | | edge-token-invalid | anon | 4 step token boundary: malformed/expired/empty/unknown-route (OPERATIONAL_EXCELLENCE_01 W03) | | canary-talent-partner-latency | hr_admin | 5-shot /api/hr-chat con P50/P95 calc + classifica performance/regression/bug per soglie 25s/45s/55s (OPERATIONAL_EXCELLENCE_01 W04) | | llm-explorer | hr_admin | Agente LLM che osserva lo stato e decide le azioni da un catalogo sicuro (LLM_DRIVEN_SCENARIOS_01). OFF by default, self-skip | | canary-skill-expansion | hr_admin | Probe daily su /api/hr-chat: chiede le skill adiacenti a Kubernetes e verifica che la risposta citi i family-siblings → regression se l'espansione non avviene (SKILL_EXPANSION_HARDENING_01) |

Email alert su fail_count > 0 inviata automaticamente a pabloliuzzi@gmail.com via Resend (OPERATIONAL_EXCELLENCE_01 W01). Killswitch alert: env HORIS_ALERT_EMAIL_ENABLED=false.

Cadenza & concorrenza (HORIS_SIMULATION_CONCURRENCY_01)

Il registry scenarios/registry.mjs assegna a ogni scenario una cadenza e un flag isolate. L'orchestrator esegue gli isolate (sensibili alla latenza) in sequenza, poi tutti gli altri in parallelo con cap.

| Cadenza | Quando gira (cron orario) | Scenari | |---|---|---| | hourly | ogni run | core HR + read manager/user + boundary + canary | | every_3h | hour % 3 == 0 | upload CV + consulting quick-wins (cert/ccnl/employment) | | every_6h | hour % 6 == 0 | talent-partner-search (LLM costoso, isolate) | | daily | hour == 3 UTC | edge case stress (race / storage burst / token) |

  • Isolate: canary-talent-partner-latency + hr-talent-partner-search girano da soli → latenza pulita, non contaminata da carico auto-indotto.
  • GitHub Actions: due cron — 0 * * * * profilo full, 30 * * * * profilo canary (monitor latenza ad alta frequenza). Concorrenza default 4.

Browser-level smoke (HORIS_BROWSER_LEVEL_TESTING_01)

Complementare al harness API-level: Playwright contro l'app deployata per catturare bug UI che le API non rivelano (errori JS runtime, layout rotti, form non cablati). Sorgenti in apps/web/e2e-browser/.

  • Cuore: helpers/console-guard.ts raccoglie console.error + pageerror

(allowlist stretta) e rileva overflow orizzontale (layout rotto).

  • 3 spec smoke: login (pubblico: form + submit cablato), workspace-dashboard

(authed: nav+main, 0 errori JS), chat-hr (authed: composer Talent Partner).

  • Auth: auth.setup.ts fa login una volta e salva lo storageState; senza

PLAYWRIGHT_USER_EMAIL/PASSWORD gli spec authed si skippano.

  • Run: cd apps/web && npm install && npx playwright install chromium && npm run test:e2e
  • CI: workflow browser-smoke.yml (nightly 02:15 UTC + dispatch manuale),

installa il browser e gira contro production; report + trace come artifact.

Killswitch e guardia (sicurezza)

  • env `HORIS_SIMULATION_ENABLED`: deve essere true o 1, altrimenti l'orchestrator aborta. Default off.
  • Tenant guard: assertSimTenant verifica orgId, slug, email domain. Refuse-and-exit se uno qualunque non corrisponde a tecnolife-sim / tecnolife-sim.test.
  • Forbidden slugs hardcoded: tecnolife, acme-trial, acme-uat, nbn → impossibile colpire per errore tenant produttivi.

Classificazione finding_hint

| Hint | Severity | Quando viene emesso (regola classifier) | |---|---|---| | bug | high | error non-null oppure HTTP status 5xx o 0 | | regression | high | HTTP 4xx diverso da 404/409 (esiti attesi su risorse mancanti / conflitti) | | performance | medium | response.latencyMs > 3000 | | ux_friction | low | override esplicito dallo scenario (es. warning fail-soft sull'extract) | | suggestion | low | override esplicito (per ora non emesso automaticamente) | | null / info | info | tutto sano |

Tutti gli eventi finiscono in platform_simulation_findings anche se sani — necessari per trend e baseline.

API endpoint

| Metodo | URL | Descrizione | |---|---|---| | GET | /api/platform/simulation/runs | Lista paginata run recenti | | GET | /api/platform/simulation/findings?run_id=...&hint=... | Findings per run, filtrabili per hint/severity/scenario, con aggregati | | GET | /api/platform/simulation/trends?days=... | Heatmap scenario × giorno + trend latenza P50/P95 sulla finestra (HORIS_TREND_ANALYTICS_01) |

Tutti gated da PLATFORM_PERMISSIONS.TENANTS_READ.

Sicurezza & GDPR

  • I dati del tenant sim sono finti (utenti @tecnolife-sim.test, candidati sim-*@tecnolife-sim.test). Nessun PII reale.
  • Tabelle platform_simulation_* su Platform scope, RLS attiva senza policy: nessun cliente vede i suoi dati nei findings (e non potrebbe — il tenant sim e' separato).
  • Cleanup: gli scenari soft-deletano i candidati creati. Per pulizia totale del tenant sim, eseguire SQL manuale via Studio.

Esplorazione LLM-driven (HORIS_LLM_DRIVEN_SCENARIOS_01)

Oltre agli scenari scripted, l'agente llm-explorer usa un modello Claude (riuso stack ai + @ai-sdk/anthropic) per decidere le azioni in base allo stato osservato, così da trovare edge case non previsti.

  • Catalogo vincolato: solo tool sicuri (list/get/create/patch/soft-delete

candidato + finish). Nessun URL arbitrario; email candidati forzate su @tecnolife-sim.test. Cleanup automatico dei candidati creati.

  • OFF by default: richiede HORIS_SIMULATION_LLM_ENABLED=true +

ANTHROPIC_API_KEY. Se disabilitato emette un evento info e ritorna ok (zero costo LLM). Cadenza daily, isolate (latenza pulita).

  • Findings: ogni azione passa per apiCall strumentato → classificata

come gli altri scenari (un 500 durante l'esplorazione = bug). Le anomalie segnalate dall'agente nel summary → hint suggestion (review umana).

  • Env: HORIS_SIMULATION_LLM_MODEL (default claude-haiku-4-5-20251001),

HORIS_SIMULATION_LLM_MAX_STEPS (default 8). Abilitabile in CI via repo variable HORIS_SIMULATION_LLM_ENABLED.

Programma di provenienza

  • HORIS_AGENT_SIMULATION_01 (2026-05-25) — 4 wave: bootstrap Tecnolife + harness agenti + pipeline findings + dashboard
  • HORIS_SIMULATION_CRON_01 (2026-05-25) — GitHub Actions cron orario
  • HORIS_SIMULATION_SCENARIOS_02 (2026-05-25) — coverage HR consulting + boundary + Talent Partner (8 scenari)
  • HORIS_SIMULATION_EXPORT_01 (2026-05-27) — export CSV/JSON/MD dei findings
  • HORIS_OPERATIONAL_EXCELLENCE_01 (2026-05-27) — email alert su fail + manager/user/owner scenari + edge case stress + canary Talent Partner P50/P95 (18 scenari)
  • HORIS_STORAGE_RESILIENCE_01 (2026-05-28) — retry su upload CV burst (fix bug trovato dal cron)
  • HORIS_TREND_ANALYTICS_01 (2026-06-03) — heatmap scenario × giorno + trend latenza P50/P95 nel cruscotto
  • HORIS_SIMULATION_CONCURRENCY_01 (2026-06-03) — orchestrator parallelo (cap concorrenza) + scheduling per-scenario (canary ad alta frequenza)
  • HORIS_BROWSER_LEVEL_TESTING_01 (2026-06-03) — Playwright smoke (login + dashboard + chat-hr) per bug UI che le API non rivelano
  • HORIS_LLM_DRIVEN_SCENARIOS_01 (2026-06-03) — agente llm-explorer che decide le azioni dallo stato osservato (OFF by default)
  • HORIS_SKILL_EXPANSION_HARDENING_01 (2026-06-10) — canary canary-skill-expansion che sorveglia la latent skill discovery del Talent Partner

Guide correlate