Skip to content

Fix/mvvm view callbacks - #10

Merged
angelamaule merged 247 commits into
mainfrom
fix/mvvm-view-callbacks
Sep 2, 2026
Merged

Fix/mvvm view callbacks#10
angelamaule merged 247 commits into
mainfrom
fix/mvvm-view-callbacks

Conversation

@angelamaule

Copy link
Copy Markdown
Contributor

No description provided.

dieguz22 and others added 30 commits July 20, 2026 14:25
…iviso con registry per dominio, tabella workflow_tasks polimorfica e propagazione del correlation id nel worker
…azioni, sostituendo la colonna base64 con path su storage, enum dedicati e colonne di workflow
…age a oggetti, prompt visivo scritto dal modello testuale, upload manuale su POST e stream SSE di avanzamento
…dicate, worker separato e client Bedrock immagini su region propria
…n stati espliciti della copertina e ripristino dell'empty state
…incluso il degrado della copertina che non invalida la bozza
…ona delle comunicazioni e alla nuova struttura dei servizi
…lle comunicazioni AI Assistant

Implementa UC-6 (rigenera bozza mantenendo prompt/tono/stile), UC-7 (scarto con
conferma, che esclude la bozza dallo storico attivo) e UC-23 (eliminazione
definitiva di un elemento dello storico, con conferma).
subnetMusk and others added 26 commits August 22, 2026 14:45
Lo scroll (Overview, Assistant) passa da una callback nel costruttore
del ViewModel a un segnale osservato dalla View (pendingScrollTarget),
chiudendo l'unica violazione MVP rimasta nel pattern. L'anteprima del
documento selezionato si sposta da sub-document-list.ts al ViewModel
della pagina Copilot, cosi' nessun componente figlio parla piu' con un
servizio dati bypassando il VM. Aggiunge anche una guardia
anti-concorrenza a MvpStateStore.reload(), assente rispetto a loadOnce().
copilot.duration/assistant.duration (istogramma con mediana su dieci
intervalli) e copilot.processing_seconds/assistant.generation_seconds
(media per fase con residuo di orchestrazione) diventano una singola
media in secondi, calcolata sulle stesse durate gia' raccolte da
workflowDurations(). Le tre ripartizioni per categoria (review, download,
field_confidence) restano invariate.

Rimuove di conseguenza i due componenti Angular diventati privi di
utilizzo (metric-distribution, metric-phases) e gli helper di charts.ts
usati solo da loro; MetricPart trasloca in metric-breakdown.ts, l'unico
consumatore rimasto.
Elimina App\Mvp\Observability (DomainMetricCatalog, PrometheusExporter,
DlqDepthProbe, MetricsRecorder), l'endpoint /internal/metrics e il
middleware RecordHttpMetrics: lo stack di monitoraggio operativo (vedi
commit successivo per Docker/CI) e' sovradimensionato per la scala del
progetto, allineato a un modello AWS di produzione mai raggiunto qui.

Le chiamate a recordDomainCounter()/recordHttp() sono rimosse dai punti
di strumentazione (listener di dominio, adapter, worker) senza toccare
la logica di business che le circondava; due listener esistevano solo
per registrare una metrica e sono stati eliminati per intero. Restano
invariati l'audit log di compliance e i correlation ID, che l'ADR 0006
descriveva nello stesso documento ma non condividono codice con lo
stack rimosso.
…osso

Segue il commit precedente (rimozione di App\Mvp\Observability): sistema
la parte che lo orchestrava. docker-compose.yml perde i sette servizi
osservabilita', la rete dedicata e i relativi volumi; traefik resta solo
router di edge, senza piu' instradare le dashboard ne' esporre le proprie
metriche Prometheus. nginx smette di servire /internal/metrics sia sul
listener pubblico che su quello interno :8081, ormai senza piu' nulla da
esporre. La CI perde lo step di validazione OTel/Prometheus e i controlli
smoke sulle dashboard; lo script di mirror immagini non elenca piu' i
sette servizi rimossi.
Aggiunge l'ADR 0014, segna come superata la parte di ADR 0006 su
OTel/Prometheus/Grafana (l'audit trail resta accettato), ed elimina il
runbook e i diagrammi dedicati. Aggiorna i runbook operativi ancora in
uso (local-development, dlq-recovery, ci-cd, le due pipeline) perche'
non indichino piu' comandi o dashboard che non esistono.

Corregge anche la tracciabilita' verso il Capitolato: il requisito
"CloudWatch Logs/Metrics/Alarms, X-Ray" era coperto per intero dallo
stack rimosso, ora lo e' solo in parte (audit trail e log strutturati
restano, l'equivalente locale di Metrics/Alarms/X-Ray no) — segnalato
esplicitamente invece di lasciare la mappatura invariata.

IMPLEMENTATION_OVERVIEW.md, essendo un'analisi puntuale datata, riceve
una nota di cappello invece di una riscrittura integrale delle sezioni
di dettaglio; i punti a piu' alta visibilita' (sommario, mappa della
codebase, diagramma di architettura) sono comunque aggiornati.
…-27, UC-56)

Le due dashboard mostravano metriche operative (generazioni/elaborazioni
fallite o bloccate, copertine non riuscite, ripartizioni di stato) mai
richieste da nessun caso d'uso del documento di Analisi dei Requisiti, e
ne mancavano alcune esplicitamente richieste: il conteggio delle
configurazioni di prompt salvate (UC-27.1), l'elenco dei feedback
testuali recenti (UC-27.3), la percentuale di classificazioni corrette
senza intervento manuale (UC-56.2) e quella dei destinatari riconosciuti
in automatico (UC-56.4).

Per UC-56.4 il confronto usa `ai_payload`, lo snapshot immutabile
scritto una sola volta da ExtractSubDocumentFieldsService e mai più
toccato da una correzione manuale (verificato che quest'ultima scrive
solo le colonne tipizzate): un sotto-documento conta come "riconosciuto
correttamente" solo se l'AI ha davvero letto qualcosa (altrimenti due
campi assenti su entrambi i lati passerebbero per una corrispondenza) e
quel qualcosa coincide ancora coi valori correnti.

Rimosse le metriche non richieste sia dal backend (dove il codice che le
calcolava è sparito insieme a loro) sia dalla presentazione frontend;
`copilot.ocr_confidence` resta nel contratto ma esce dal pannello
perché la Overview la legge ancora. Le etichette del "resto" nelle
schede a quota (`auto_classified`, `needs_review`) descrivono ora solo
ciò che è vero per l'intero insieme che rappresentano, non solo per il
caso più comune.

Nuovo componente RecentFeedbackListComponent per UC-27.3, con
un'etichetta per scheda (non condivisa) per restare distinguibile a uno
screen reader anche con più voti in elenco.
Una revisione ha trovato tre problemi in "recipient_auto_matched" e
"auto_classified" già in produzione: un sotto-documento in cui l'AI non
aveva riconosciuto nulla del destinatario passava per una corrispondenza
riuscita (due assenze non sono un successo); il denominatore di
"auto_classified" era l'intero conteggio dei sotto-documenti, diluendo
la percentuale con quelli ancora da revisionare o in quarantena — che
non sono né un successo né un fallimento della classificazione
automatica, sono semplicemente non conclusi.

Sistemando quest'ultimo è emersa la stessa contaminazione anche in
"needs_review": un sotto-documento nasce con review_status a
"needs_review" per default, prima ancora che la sua estrazione giri
(ProcessDocumentService crea la riga e solo dopo la estrae), quindi in
un documento con più destinatari un segmento in coda può risultare
"sotto soglia" per un motivo che non ha nulla a che fare con la
confidenza. La distinzione ora si basa sulla presenza di ExtractedData,
scritta solo quando l'estrazione è davvero girata (la quarantena la
cancella): sia "needs_review" che il denominatore di "auto_classified"
contano solo i sotto-documenti valutati per davvero.

Le etichette del "resto" nelle schede a quota sono state riallineate a
questi denominatori corretti, e il mosaico del pannello Co-Pilot è
stato riordinato: la griglia CSS non ricompone da sola le righe per
colmare i vuoti, quindi l'ordine delle schede conta quanto il loro
numero di celle — le due strette ora precedono le tre larghe, così le
righe si chiudono senza spazi vuoti (lo stesso valeva, inosservato, per
il pannello Assistant).
…che EMF (RVC16-OB, ADR 0015)

Dopo la rimozione dello stack self-hosted (ADR 0014), reintroduce un
riscontro a RVC16-OB con la minima superficie operativa possibile: i log
JSON strutturati esistenti (CorrelateRequests) restano instradabili verso
CloudWatch Logs via log driver del container, e le metriche (traffico,
latenza, errori per richiesta, profondita' DLQ) vengono emesse in formato
CloudWatch Embedded Metric Format su un canale di log dedicato, senza
endpoint di scrape ne' collector.

RecordRequestMetrics ascolta RequestHandled invece di essere un
middleware: un'eccezione che risale la pipeline (validazione,
autorizzazione, 500) verrebbe tradotta in risposta fuori dalla pipeline
stessa, e il codice dopo $next() in un middleware non verrebbe mai
eseguito - proprio il caso che la metrica Errors deve intercettare.
…ioni mancanti (RF34, RF56, RF95, RF101/102/105)

- RF34-OB: export PDF del report riepilogativo della dashboard AI Assistant
  (AssistantMetricsReportController/Renderer), riusando i dati gia' calcolati
  da MvpStateService::assistantState() e lo stack Dompdf gia' in uso.
- RF56-OB: anteprima del documento originale non splittato (UC-40.2), nuovo
  metodo previewOriginal() su PreviewDocumentUseCase/Service, link "Documento
  originale" nel dettaglio sotto-documento, indipendente dallo stato
  dell'anteprima splittata cosi' resta un'alternativa reale quando quella
  fallisce.
- RF95-OB: validazione email sul destinatario del messaggio di invio
  (UpdateSendMessageRequest + frontend), con un validator condiviso
  (emailValidator) che tollera gli spazi di contorno tagliati al salvataggio -
  applicato anche a recipientEmail, che aveva lo stesso bug latente.
- RF101/102/105-OB: nome/cognome, azienda e descrizione mostrano "Non
  disponibile" invece di un campo vuoto quando mancano, restando modificabili
  in edit mode.

Aggiunge test di dominio puro per previewOriginal() (prima assenti, a
differenza del preview() gemello) ed estende InMemoryDocumentRepository/
OriginalDocumentRecord con originalFilename.
…F41-OB)

Nella sezione Metriche dell'AI Assistant, un filtro separato da quello dello
storico permette di restringere per tono, stile e intervallo di date; i
numeri Co-Pilot restano sempre sul totale del tenant, dato che tono e stile
non esistono sui documenti. Corregge anche un ciclo di richieste infinito
nello store (effect() che leggeva e riscriveva lo stesso segnale di
loading), risolto con untracked().
…x/mvvm-view-callbacks

# Conflicts:
#	apps/frontend/src/app/features/assistant/assistant-page.ts
#	apps/frontend/src/app/features/assistant/assistant-page.view-model.spec.ts
#	openapi/v1/alittlebyte-mvp-api.yaml
Tre effect() (Co-Pilot sub-document-list, Assistant generated-communication-
preview, communication-generator-panel) risincronizzavano il form ad ogni
nuovo riferimento dell'input sorgente, non solo quando il dato osservato
cambiava davvero: qualunque mutazione altrove nella pagina (un preferito, una
revisione, un'eliminazione) rimpiazza lo stato condiviso e produce un nuovo
riferimento anche per l'elemento su cui si sta scrivendo, cancellando
silenziosamente modifiche non salvate. Ora ciascun effect confronta id o
contenuto prima di risincronizzare.

Corregge anche reloadFilteredAssistantMetrics() (RF38-OB): un cambio di
filtro mentre il precedente e' ancora in volo annullava la nuova richiesta
invece di sostituire quella vecchia, perdendo il filtro piu' recente.

Trovati da una revisione indipendente su tutto il frontend.
…kend

Trovati da una revisione indipendente su tutto l'applicativo (dominio,
HTTP/Support, contratto OpenAPI):

- Race condition su approvazione/scarto bozza (CommunicationDraftService):
  update()/save()/discard() ora usano lock pessimistico + transazione come
  favorite()/unfavorite(), altrimenti due richieste quasi simultanee
  potevano far vincere l'ultima a scrivere, bypassando l'invariante di
  dominio "una bozza scartata non torna approvata".
- Doppio avvio del workflow AI/OCR (StartCommunicationWorkflowService,
  StartDocumentWorkflowService): il controllo "e gia in corso?" e la
  scrittura che lo rende vero ora girano nello stesso lock, altrimenti due
  richieste ravvicinate potevano avviare due esecuzioni Step Functions per
  la stessa comunicazione/documento.
- Difesa in profondita sul tenant per le mutazioni di Communications
  (favorite/unfavorite/update/save/discard, delete, rate, cover
  update/remove): oggi il controllo HTTP le protegge tutte, ma un caso
  d'uso applicativo non dovrebbe fidarsi solo di chi lo chiama (stesso
  principio gia in uso per Documents e PromptConfiguration).
- ExtractedDataChanges::fromRawFields() rifiuta ora campi fuori dalla
  whitelist dei dieci correggibili manualmente, invece di accettare
  qualunque chiave.
- SendMessageController non costruisce piu a mano le risposte d'errore:
  i due domain exception passano dal gestore centralizzato in
  bootstrap/app.php, cosi portano sempre requestId/correlationId come
  ogni altro errore dell'app.
- Contratto OpenAPI allineato al comportamento reale: 403 dichiarato sulle
  6 rotte Documents che lo applicano davvero (stream, delete, preview,
  original-preview, send-preview, send-export), 422 dichiarato per le
  ultime due; aggiunti i due test di contratto e i due test cross-tenant
  mancanti (stream, delete) che l'avrebbero gia intercettato.
- Content-Disposition dei preview documento ora rimuove anche i caratteri
  di controllo (CR/LF inclusi) dal nome file, non solo le virgolette.
…o, non contenuto)

Trovati da una seconda revisione indipendente, dopo il primo giro di
correzioni:

- generated-communication-preview.ts: l'effect che chiude la conferma
  "Scarta bozza" si resettava su ogni nuovo riferimento di draft(), non
  solo quando la bozza cambiava davvero — stesso bug gia' corretto sui due
  effect vicini nello stesso file, rimasto su questo terzo. Ora confronta
  l'id, come gli altri.
- copilot-page.ts: il commento diceva "non legge currentPage() apposta per
  non duplicare la richiesta a cambio pagina", ma vm.reload() la legge
  comunque al suo interno, diventando una dipendenza nascosta dell'effect
  che lo chiama. Ogni click su "pagina successiva" scatenava due richieste
  invece di una. Corretto con untracked(), stesso schema gia' usato in
  mvp-state.store.ts.
…ficabile dai test

Trovati da una seconda revisione indipendente, dopo il primo giro di
correzioni:

- StartCommunicationWorkflowService::regenerate() riceveva gia' l'Actor ma
  non lo usava per il controllo tenant, a differenza di tutti i suoi metodi
  fratelli. L'Actor non e' piu' nullable (l'unico chiamante HTTP ne passa
  sempre uno reale).
- ReviewDocumentService (correzione dati estratti + marca come revisionato)
  non aveva nessun controllo tenant, a differenza degli altri casi d'uso
  del modulo Documents. Stesso trattamento: Actor obbligatorio, controllo
  contro l'originale del sotto-documento.
- Aggiunti i test diretti mancanti per update()/discard()/unfavorite() di
  CommunicationDraftService (avevano solo save()/favorite()).
- I due test sull'errore di SendMessageController ora verificano anche che
  requestId/correlationId siano davvero presenti in risposta, non solo
  error.code — l'unica cosa che quella correzione doveva garantire.
- I fake InMemoryCommunicationRepository/InMemoryDocumentRepository ora
  contano le letture passate da findXForUpdate(): senza, un futuro regresso
  che rimuovesse il lock pessimistico (tornando a findX()) non avrebbe
  fatto fallire nessun test, perche' le due implementazioni nel fake erano
  identiche. Aggiunto l'assert su questo contatore ai test che esercitano i
  metodi protetti dal lock (favorite/save/discard di CommunicationDraftService,
  start() di entrambi gli StartXWorkflowService).
Import non in ordine alfabetico in CommunicationDraftService.php,
indentazione della continuazione @throws non allineata in
ExtractedDataChanges.php.
… codebase

Backend (Communications, Documents, HTTP/Support/Workflow) e frontend
(core, shared, layout, features) mantengono solo il perche' non ovvio
in 1-2 righe; tolte le narrazioni "prima faceva X, ora fa Y" e i
docblock che ripetevano in prosa citazioni RF/UC/ADR gia' presenti.
Nessuna modifica di logica: verificato con pest (495 passed), pint
(413 file), frontend lint/typecheck e jest (510 passed).

Include anche le assertion su forUpdateReadCount() in
RateCommunicationServiceTest, rimaste non commitate da una sessione
precedente.
…rivy in CI

Dipendenza transitiva di laravel/framework (vincolo ^2.8.1, gia'
compatibile con 2.10.0): la scansione immagine in CI segnalava
GHSA-8rr7-cvq3-gmfh, GHSA-f8fg-pg57-v4j8, GHSA-j8pm-gj4c-rq4x e
GHSA-jjv6-8j6v-6j52 (DoS/XSS) sulla 2.9.0. Aggiornate anche
nette/schema, nette/utils e mtdowling/jmespath.php come conseguenza
della risoluzione delle dipendenze. Verificato: pest (495 passed),
scansione Trivy sull'immagine di produzione pulita.
@angelamaule
angelamaule merged commit e02ebe7 into main Sep 2, 2026
11 of 12 checks passed
@angelamaule
angelamaule deleted the fix/mvvm-view-callbacks branch September 7, 2026 14:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants