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(UUIDc07c899f-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.sqlapplicate - env
SUPABASE_SERVICE_ROLE_KEYper 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:
- Pre-flight: verifica killswitch + targeting tenant simulation
- Login come HR admin del tenant simulation
- Seleziona gli scenari dovuti (cadenza + profilo), poi li esegue: prima gli
isolatein sequenza, poi il resto in parallelo (cap concorrenza) - Stampa NDJSON su stdout + salva su
tmp/simulation-runs/run-<uuid>.ndjson - (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 scenaridaily(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_eventepayload_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-searchgirano da soli → latenza pulita, non contaminata da carico auto-indotto. - GitHub Actions: due cron —
0 * * * *profilofull,30 * * * *profilocanary(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.tsraccoglieconsole.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.tsfa 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
trueo1, altrimenti l'orchestrator aborta. Default off. - Tenant guard:
assertSimTenantverifica orgId, slug, email domain. Refuse-and-exit se uno qualunque non corrisponde atecnolife-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, candidatisim-*@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
apiCallstrumentato → 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(defaultclaude-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 + dashboardHORIS_SIMULATION_CRON_01(2026-05-25) — GitHub Actions cron orarioHORIS_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 findingsHORIS_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 cruscottoHORIS_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 rivelanoHORIS_LLM_DRIVEN_SCENARIOS_01(2026-06-03) — agentellm-explorerche decide le azioni dallo stato osservato (OFF by default)HORIS_SKILL_EXPANSION_HARDENING_01(2026-06-10) — canarycanary-skill-expansionche sorveglia la latent skill discovery del Talent Partner
