Skip to content

Latest commit

 

History

History
377 lines (268 loc) · 128 KB

File metadata and controls

377 lines (268 loc) · 128 KB

LOG

I riferimenti a docs/note-gestori-lod/ e docs/campagna-parlamento-aperto/ rimandano a cartelle di lavoro non versionate (in .gitignore): bozze di segnalazione ai gestori dei dati e materiali di campagna, che restano locali. Su GitHub quei percorsi non esistono; sono citati per tracciare dove è stata portata ogni segnalazione.

2026-07-26

  • v0.27.0 — release minor. Tre fix nati dalla gap analysis news-driven della mattina, tutti sulla stessa famiglia di difetto: un vuoto che sembra un'assenza di dato ma è un problema di identificatori o di criterio. In vote-detail una sigla di gruppo che non esiste nel dataset votazioni dava zero righe senza dirlo; in committees il Senato mostrava 12 organi su 27; in which la Giunta per le autorizzazioni non era raggiungibile e "immunità parlamentare" portava alla ricerca per nome. 43 tool invariati, 219/219 test verdi. In tutti e tre i casi il codice si limita a interrogare il dato e a rendere visibile ciò che manca: nessuna mappatura inventata a valle, e i buchi della fonte finiti nelle note ai gestori e nel wiki LOD.

  • fix committees (Senato) — mancavano le Giunte e 16 organi attivi su 27 (#82, PR #83) — --chamber senato --legislature 19 restituiva 12 righe su 27 organi con sedute, più nessuna delle 4 Giunte: oltre la metà dell'attività di commissione era invisibile, senza alcun segnale. Due cause opposte nella stessa query. Prima: il filtro per legislatura passava dalle sedute, e nel LOD nessuna Giunta ha osr:SedutaCommissione, in nessuna legislatura — pur essendo descritte e operative (0-21 ha 11 triple, categoria Giunte, sottotitolo "(Art. 19 del Regolamento)"). Sparivano quindi organi attivi, tra cui la Giunta delle elezioni e delle immunità parlamentari, cioè la sede delle autorizzazioni a procedere: esattamente ciò che si cerca quando una vicenda giudiziaria tocca un parlamentare. Seconda: osr:titoloBreve era un pattern obbligatorio, e i 16 URI commissione/2-* referenziati dalle sedute ma mai descritti nel grafo (zero triple come soggetto; zero osr:Commissione con quel prefisso su 279) venivano scartati in silenzio — incluso commissione/2-1 con 647 sedute, secondo organo più attivo della legislatura. Ora il ramo --legislature unisce due insiemi: gli organi con sedute (con il conteggio) e quelli con osr:denominazione valida nel periodo (session_count 0), dove l'intervallo della legislatura si ricava dal grafo stesso con MIN/MAX di osr:dataSeduta — stesso pattern temporale già usato per i gruppi in senator-group-members. titoloBreve diventa OPTIONAL: gli organi senza anagrafica compaiono con uri e sedute, campi di testo vuoti, perché il nome non è ricavabile da nessuna parte del grafo e inventarlo — anche con un segnaposto — significherebbe scrivere in un campo dati qualcosa che nella fonte non c'è. Risultato: da 12 a 75 organi, tutte e 4 le Giunte presenti. Dalla review è arrivato un difetto vero e non teorico: la seconda query aveva LIMIT senza ORDER BY, quindi il troncamento era arbitrario e con il limite di default costava già due organi (73 → 75); ora entrambe le query hanno ordinamento deterministico e tetto interno fisso, e il --limit si applica alla classifica finale dopo il merge. +5 test unitari sul merge, +3 live (Giunte presenti, organi senza nome visibili e mai riempiti, limite che tronca dopo il merge). Trappole documentate in docs/lod-wiki/senato/sedute-commissione.md; i due difetti della fonte sono il punto 13 della nota al Webmaster Senato e la nuova scheda B6 del dossier di campagna, con il pattern comune a B5 dichiarato esplicitamente: identificatori referenziati e mai descritti, alla Camera sui gruppi, al Senato sugli organi.

  • fix which — la Giunta per le autorizzazioni era irraggiungibile, "immunità parlamentare" portava altrove (#78, PR #81) — which "giunta per le autorizzazioni" e which "autorizzazione a procedere" non trovavano nulla, pur essendo il tema pienamente servito da committees + committee-members; peggio, which "immunità parlamentare" instradava su search find, cioè "cerca un parlamentare per nome", e un match sbagliato è peggio di nessun match per chi si fida dell'output. La causa: il termine "parlamentare" nudo su search find, che lo scorer premia con 60 punti quando la query lo contiene — quindi qualunque domanda con quella parola dentro finiva lì, compreso "gruppo parlamentare". Diventa "cerca parlamentare": chi cerca "parlamentare" da solo trova ancora il comando, chi cerca altro no. Aggiunti a committees list e committee-members list i termini su giunta, autorizzazione a procedere, immunità e insindacabilità. Dalla review è emerso che "giunta" nuda ricreava lo stesso difetto ("giunta regionale", "delibera di giunta" pescherebbero un comando che copre solo il Parlamento): diventa "giunta parlamentare", con test che lo verifica. Lo scorer non è stato toccato: irrigidirlo avrebbe cambiato il punteggio di tutte e 33 le voci per curare due termini mal scelti. Una misura su query realistiche mostra che la classe di difetto resta (per esempio "voto di fiducia al senato" propone il comando Camera), ma which è un aiuto all'orientamento che elenca i candidati con esempio pronto, non un tool che produce dati: sproporzionato riscriverlo. +5 test.

  • fix vote-detail--group-acronym non restituisce più zero righe in silenzio (#77, PR #80) — la sigla passata dall'utente finiva dritta in un FILTER(STR(?_sg) = "…"): se non esisteva nel dataset votazioni il risultato era vuoto, con exit 0 e nessun avviso, indistinguibile da "il gruppo non ha votato". E le sigle divergono per davvero: groups list espone AZ-PER-RE e IV-CR, i voti della stessa legislatura portano APERRE e IVICRE, e il Misto lato voto è disaggregato in M-ALT/M-MIN/M-+EUR. Seguendo il flusso documentato (cerca la sigla in groups, poi filtra) si concludeva il falso. Nemmeno group_uri era una chiave utilizzabile: per Azione è gr4135 nella risorsa gruppo e gr4212 sui voti, e le componenti del Misto ce l'hanno vuoto. Ora, quando --group-acronym è valorizzato, una query leggera recupera le sigle realmente presenti in quella votazione e la richiesta viene risolta contro quelle (esatta → case-insensitive → senza punteggiatura); se risolve, la query principale è invariata, quindi performance e semantica del LIMIT non cambiano. Se non risolve: righe vuote più una nota (stderr in CLI, testo nell'MCP) con l'elenco delle sigle presenti, la più simile in testa (Dice sui bigrammi, soglia 0,4) e la spiegazione per il Misto. Nessun alias AZ-PER-RE → APERRE nel codice: il disallineamento è della fonte e si romperebbe al primo cambio di denominazione — il codice interroga il dato e rende visibile il problema, non lo compensa. +10 test sulle due funzioni pure, 199/199 verdi. Emerso dal report news-agent 2026-07-26_08-58.md. Residuo noto: le sigle sono raccolte senza applicare --vote-type, quindi una sigla valida ma senza voti di quel tipo resta un vuoto senza nota — comportamento corretto (il gruppo ha votato, non in quel modo), fuori dallo scopo di #77.

  • segnalata a monte la radice del problema — punto 9 della nota docs/note-gestori-lod/camera-assistenza-dati.md: sigla della risorsa gruppo diversa da quella dei voti (gr4211: IV-CR nella label, IVICRE in ocd:siglaGruppo), URI referenziati dalle votazioni ma mai descritti (gr4212 con 160.881 voti e gr4234: zero triple come soggetto), denominazione storica sovrascritta (gr4135 ha oggi la label AZ-PER-RE ma i suoi 88.475 voti portano AIV-RE), componenti del Misto senza URI, e nessuna proprietà "sigla" sulla risorsa gruppo — l'acronimo è estraibile solo dalla label, che per giunta arriva troncata (punto 4 della stessa nota). Il fenomeno è generale e vale per entrambe le camere, quindi è anche scheda B5 del dossier di campagna (docs/campagna-parlamento-aperto/02-dossier-tecnico.md) con riga in tabella di sintesi: al Senato il legame voto→gruppo non esiste proprio (la votazione punta ai senatori con osr:favorevole/osr:contrario/osr:astenuto, nessuna proprietà di gruppo) e va ricostruito dalle adesioni valide alla data.

  • gap analysis news-driven (docs/news-agent/2026-07-26_08-58.md) — 6 notizie mai testate prima (Giunta autorizzazioni/chat Delmastro, riforma forense al Senato, mozioni di sfiducia Santanchè e Nordio, DL 130/2020 immigrazione e DL 137/2020 Ristori). Copertura piena su 5 storie su 6, storico leg. 18 lato Camera senza degrado. Tre issue aperte con triage: #77 (sopra), #78 (which non trova "giunta per le autorizzazioni" e instrada "immunità parlamentare" verso search find, un falso positivo peggiore di un no-match), #79 (confidence_vote=true marca anche le mozioni di sfiducia individuale ex art. 115: è ocd:richiestaFiducia della fonte, non un valore derivato, quindi documentation + gap-dataset). Un quarto rilievo del report — quoting CSV rotto in groups listnon è stato aperto: verificato con un parser CSV vero su leg. 17, 18 e 19, tutte le righe hanno 6 colonne e il campo con le virgole è quotato correttamente. Era un artefatto del cut -d, usato per leggere l'output, non un difetto.

2026-07-25

  • v0.26.2 — release patch. Due fix e la misura del limite di dimensione delle richieste al Senato (2047 byte accettati, 2048 rifiutati), che spiega e chiude una classe di 403 finora attribuita al blocco anti-burst. PR #75 e #76, mergiate via squash. 43 tool invariati, 189/189 test verdi.

  • fix senato-votes — eliminati i 403 sistematici da query troppo lunga, e misurato il limite vero dell'endpoint Senato (#72, PR #76) — senato-votes --keyword falliva sempre con 403 a meno di aggiungere --final-vote true o --confidence-vote false. Non era il blocco anti-burst: l'endpoint accetta una request-URI (/sparql?query=…) fino a 2047 byte e da 2048 in su risponde con una pagina 403 HTML — non un 414, non un errore SPARQL. Confine trovato per bisezione al singolo byte, con query di controllo a 200 subito prima e subito dopo ogni misura (un blocco per frequenza respinge anche l'hello-world; qui no). Il POST è sempre rifiutato, anche su 448 byte, quindi il limite non si aggira spostando la query nel corpo: l'unica strada è tenere corta la GET. La Camera, per confronto, accetta query da 4 KB: votes.ts:369 ha lo stesso pattern OR-chain ma non è a rischio e resta invariato. Sotto la soglia finivano tre query, non una: le OR-chain FILTER(STR(?f) = "S.<num>" || …) in due punti distinti (supplemento fiducie e Fallback 1), con 53 fiducie in leg. 19, ora spezzate in blocchi da 25 con merge invariato first-wins; e soprattutto la query principale, che sfora da sola appena la keyword è multi-parola (2059 byte con "corte dei conti"), dove non c'è nessuna lista da spezzare — il chunking non l'avrebbe mai risolta. Rimossa l'indentazione, che finisce dritta nella URI, e ridotto l'header dei prefissi ai due effettivamente usati (xsd: solo quando serve): 2081 → 1926 byte. La keyword entra tre volte nella query (label, osr:titolo, osr:titoloBreve), quindi la lunghezza cresce di circa tre volte la keyword e il margine non è infinito: BIND risolverebbe alla radice ma questa istanza Virtuoso non lo supporta, perciò assertQueryFits blocca in partenza con un messaggio che dice cosa fare, invece di lasciare arrivare un 403 opaco. Nuovi helper src/core/chunk.ts e src/core/senato-query-size.ts (+10 test, incluso il confine esatto 2047/2048, così il prossimo filtro aggiunto non rompe la soglia in silenzio). Verifica: la fiducia sul DL 48/2025 del 2025-06-04 (109-69-1) torna nei risultati ed è proprio una riga che arriva dal supplemento — il suo label non contiene "sicurezza" — quindi il chunking non perde nulla; 189/189 test verdi. Lezione: i due test senato-votes rossi da giorni sembravano rate limit della suite, ma fallivano anche isolati in 0,6 s con l'endpoint raggiungibile — era il difetto.

  • fix bill-progress (Camera) — presentation_date e initiative_description non sono più vuoti (#74, PR #75) — sul ramo Camera le due colonne erano hardcoded a stringa vuota pur essendo il dato sulla stessa risorsa già interrogata dalla query, e già letto senza problemi da bills list. initiative_description non è ricavabile in nessun altro modo dalla timeline (governativa, parlamentare o popolare è la prima cosa che si guarda in un iter), e presentation_date di solito coincide con la data del primo statoIter ma non sempre: in leg. 19 divergono su 23 atti, quindi dedurla dalla prima riga era un'euristica sbagliata in quei casi. Fix a costo zero: due OPTIONAL (dc:date, ocd:iniziativa) nella query esistente, nessuna chiamata di rete in più. Proiettarle in SELECT DISTINCT è sicuro perché sono proprietà dell'atto con cardinalità 1 — verificato prima di editare su leg. 17, 18 e 19: zero atti con più di un valore, nessuna moltiplicazione di righe (ac19_824: 6 righe prima, 6 dopo). nature resta vuota: dc:type è semantica diversa, fuori dallo scopo.

  • documentazione della trappola — la skill CLI (skills/italian-parliament-cli/SKILL.md) ora distingue i due 403 del Senato, che si presentano identici ma vogliono reazioni opposte: "Query troppo lunga" (errore locale, prima di uscire in rete) → accorciare keyword o restringere le date, ritentare è inutile; 403 dall'endpoint → richieste troppo ravvicinate, aspettare, ritentare a raffica peggiora. Il materiale è confluito anche nella campagna (docs/campagna-parlamento-aperto/02-dossier-tecnico.md, voci D2 e D3 + tabella di sintesi) e nella nota al Webmaster Senato (punti 11 e 12, da inviare): il limite di dimensione sommato al POST rifiutato rende una query lunga non inviabile in alcun modo, e il fatto che tutte le cause di rifiuto rispondano con lo stesso 403 HTML porta i client a ritentare identico invece di accorciare — codici HTTP corretti (413/414, 429 con Retry-After) ridurrebbero il traffico inutile verso i loro server. È il gap con il costo di intervento più basso dell'intero dossier: configurazione, non tecnologia.

2026-07-20

  • fix votes (Camera) — i voti su emendamento con descrizione a codice secco ora si agganciano all'atto — sulla seduta s19_689 (14/7/2026, prima lettura della legge elettorale) l'intera giornata è priva di ocd:rif_attoCamera alla fonte e solo due votazioni su dodici citano "PDL 2822-A" nel testo: le altre dieci hanno dc:description a codice secco ("EM 1.1077", "SUBEM 0.1.1077.4") e restavano senza bill_uri. Tra queste il voto sull'emendamento preferenze, 188 contrari / 187 favorevoli, cioè il voto che ha deciso la vicenda: un giornalista che filtrava i voti per numero di atto lo perdeva. Aggiunto un terzo livello di fallback in cascata (inheritBillFromSession): quando l'intera seduta verte su un solo atto, quello è l'atto anche dei voti muti. La monotematicità è calcolata con una query dedicata sulla seduta completa — mai sulle righe già filtrate in memoria, dove un --date-from o un LIMIT stretto possono mostrare un solo atto mentre la seduta ne tratta quattro — e l'insieme degli atti unisce rif_attoCamera e i numeri citati nelle descrizioni delle altre votazioni (su s19_689 il primo insieme è vuoto). Mozioni e risoluzioni sono escluse: sono AIC a sé stanti, ereditare l'atto della seduta le legherebbe a un provvedimento che non le riguarda. Impatto misurato: sui 1000 voti del 2026 almeno 178 righe guadagnano il collegamento all'atto (copertura 369→547). Solo il 37% delle sedute leg. 19 è monotematico, quindi il resto resta deliberatamente senza aggancio invece di rischiare attribuzioni sbagliate. Estratto l'helper resolveActUris condiviso coi fallback precedenti (nessun URI fabbricato: il numero base è sempre verificato via dc:identifier). +4 test (aggancio, non-regressione su seduta multi-atto, guardia mozioni/risoluzioni, coerenza legislatura↔atto su finestra cross-legislatura). Nuova nota wiki OKF docs/lod-wiki/camera/votazioni-atto-collegato.md con i conteggi del buco per legislatura e le tre famiglie di dc:description. Emerso dal report news-agent 2026-07-20_19-49.md. Residuo noto: --bill-code filtra dc:description lato server, quindi a monte dell'ereditarietà — i voti a codice secco restano fuori da quel percorso e vanno raggiunti per intervallo di date filtrando poi su bill_number. Renderlo consapevole della seduta significa selezionare l'intera seduta e filtrare a valle, con il LIMIT che non varrebbe più sulle righe restituite: tenuto fuori deliberatamente.

  • fix bill show (Camera) — testo ripulito da entità HTML, tag e suffisso ^^xsd:stringlabel, title e description uscivano grezzi dal grafo: entità non decodificate (&quot;, &lt;em&gt;, &nbsp;) e, su circa 6.500 atti su 269.816, il suffisso ^^http://www.w3.org/2001/XMLSchema#string incollato dentro al valore del literal alla fonte (verificato in JSON crudo dall'endpoint, non è un artefatto del nostro parsing). Caso concreto: ac19_2316, la legge sull'intelligenza artificiale. bills list applicava già l'helper decodeHtml, bill show era rimasto indietro: stessa classe di bug chiusa per votes in v0.26.1. Applicato l'helper esistente ai tre campi di testo, nessun codice nuovo. +1 test. Emerso dal report news-agent 2026-07-20_19-49.md.

2026-07-19

  • v0.26.1 — release patch. Fix votes list (Camera): label/title/description arrivavano con entità HTML doppiamente codificate (es. all&amp;#39;emergenza invece di all'emergenza), perché votes.ts era l'unico tool a non applicare l'helper decodeHtml già esistente e usato da bills.ts, bill-progress.ts e altri. Emerso dal report news-agent 2026-07-19_08-11.md (agente news-driven-cli-gap-analyzer): 6 notizie testate a spettro temporale (oggi, 2025/leg.19, 2020/leg.18), copertura confermata solida su voti/iter/interventi/profili, unico gap reale questo bug. PR #70, mergiata via squash. 43 tool invariati, 172/174 test verdi (2 fallimenti senato-votes per 403 dell'endpoint Senato, non collegati).

2026-07-17

  • v0.26.0 — release minor. senato-votes guadagna la colonna ddl_title (titolo del DDL/documento collegato, utile sui voti con label generico) e --keyword ora cerca anche in osr:titoloBreve; --ddl-uri include le fiducie anche su seduta diversa dal voto forte; camera-amendments copre gli emendamenti sugli atti storici (fallback indice ostr) senza più mascherare i fallimenti di fetch come "nessun emendamento". PR #69, review Greptile+Copilot triagata (2 fix reali, 1 falso positivo, 1 residuo documentato deliberatamente non corretto). 43 tool invariati, dettaglio nelle voci sotto.
  • fix senato-votes--keyword ora matcha anche osr:titoloBreve, non solo osr:titolo (review PR #69) — il match tematico sul DDL/documento collegato guardava solo osr:titolo, incoerente con ddl_title che usa osr:titoloBreve come prima scelta: una ricerca poteva mancare un voto il cui unico match era nel titolo breve. Aggiunto osr:titoloBreve come terza alternativa OR (con lo stesso BOUND() guard) sia nella query principale sia nel supplemento fiducie. Rilievo di Copilot (review PR), verificato reale e corretto; regressione coperta dal test esistente sulle fiducie (stesso percorso di codice) + nuovo test dedicato.
  • fix camera-amendments — fallback ostr non swalloweva più gli errori (review PR #69) — il .catch(() => "") sul fetch dell'indice ostr trasformava un fallimento di rete/HTTP nello stesso vuoto di "nessun emendamento", esattamente il problema di vuoto ingannevole che il blocco looksValid appena sopra escludeva per la scheda principale. Rimosso: ora l'errore propaga con lo status HTTP. Rilievo di Greptile (review PR), verificato reale e corretto.
  • senato-votes — colonna ddl_titleddl_uri era già risolto anche quando puntava a un osr:Documento (es. le risoluzioni di commissione), ma il titolo non era mai esposto in output: per capire il tema di un voto con label generico (es. un ODG che non nomina la regione) serviva una query SPARQL manuale su osr:titoloBreve/osr:titolo. Scoperto verificando la seduta 19-438 del 16/7/2026 (autonomia differenziata, 4 risoluzioni identiche su Liguria/Lombardia/Piemonte/Veneto): solo il voto sul Veneto aveva la regione nel label, gli altri tre erano identificabili solo scavando nel documento collegato. Ora ddl_title è popolato da osr:titoloBreve (fallback osr:titolo) del ddl_uri risolto, senza richiedere a osr:Ddl nel BGP (i Documento restavano esclusi altrimenti); vuoto se ddl_uri è multiplo (testo unificato, ambiguo). +1 test, verificato anche via CLI reale sulla seduta 19-438.
  • fix senato-votes --ddl-uri — fiducia su seduta diversa dal voto forte — la risoluzione delle date del DDL si basava solo sul link forte osr:oggetto/osr:relativoA: quando la fiducia cadeva su una seduta diversa (es. decreto sicurezza 2025 S.1509: pregiudiziale 3/6, fiducia 4/6), la sua data restava fuori dal filtro e la fiducia spariva senza errore. Ora la risoluzione date include anche le fiducie della legislatura il cui numero nel label (extractBillNumber) corrisponde alla osr:fase del DDL cercato. Nessuna regressione sui casi same-day (Piano Casa, DDL 1561) o storici (leg.18). +1 test, wiki senato/fiducia-fallback.md aggiornata. Emerso dal report news-agent 2026-07-17_18-59.md.
  • fix camera-amendments — fallback all'indice ostr sugli atti storici — la scheda atto delle legislature vecchie (es. leg.18) spesso non incorpora più il link diretto getProposteEmendative.aspx (il bottone "Emendamenti" punta al motore di ricerca generico): il tool restituiva 0 righe senza errore pur esistendo gli emendamenti alla fonte (verificato su AC 2402, decreto Covid 2020: 54+53 emendamenti, incluso il Locatelli 1.52 citato dalla stampa). Ora, quando la scheda è valida ma senza link diretto, prova l'indice per-atto apps/emendamenti/ostr/{leg} prima di concludere "nessun emendamento". +1 test, wiki camera/assenti.md aggiornata. Emerso dal report news-agent 2026-07-17_18-59.md.

2026-07-15

  • v0.25.3 — release patch. Migliora l'emptyHint di senato-votes (dettaglio nella voce sotto): da stringa statica a diagnostica data-driven che sonda il grafo al volo sui risultati vuoti per data. Recepiti in review (PR #68, Greptile) due rilievi: la sonda è resiliente (su errore 403/timeout degrada all'hint statico, non trasforma un vuoto legittimo in errore) e usa una sola query (COUNT sedute + votazioni via OPTIONAL) invece di due, dimezzando latenza e richieste all'endpoint. Aggiunta nota wiki OKF docs/lod-wiki/senato/vuoto-votazioni-diagnosi.md (metodo dei tre stati). 43 tool invariati, 170/170 test verdi, tsc pulito.
  • senato-votes — emptyHint dinamico, data-driven (sonda del grafo invece di buchi hardcoded) — l'emptyHint era una stringa statica che su ogni risultato vuoto ripeteva tre blocchi (--ddl-uri, filtri label, buco COVID 10/3–16/4/2020) anche fuori contesto: cercando la fiducia Milleproroghe (26/2/2020, --confidence-vote) l'agente vedeva l'avviso su una finestra che non interseca il 26/2 e il testo su --ddl-uri mai usato. Ora, sui risultati vuoti con vincolo di data, execute() valorizza result.hint (che ha precedenza su MCP+CLI) sondando il grafo al volo: 2 COUNT leggeri (sedute d'Assemblea osr:SedutaAssemblea + votazioni) sull'intervallo distinguono lo stato reale senza sapere in anticipo dov'è il buco — sedute=0 → nessuna seduta; sedute>0, votazioni=0 → fingerprint di un buco della fonte (rileva dinamicamente il gap #36 su qualsiasi data); votazioni>0 → dato pieno, è il filtro (caso 26/2: 21 votazioni ma nessuna fiducia). Senza vincolo di data restano i soli frammenti statici pertinenti. Verificato end-to-end su 5 contesti (leg 18 e 19, singola data e range). Funzione pura buildSenatoVotesEmptyHint estratta e testata (8 test); 170/170 suite verde, tsc pulito. Emerso dal report news-agent 2026-07-15_12-08.md (P0).
  • news-driven analyzer: catalogo anti-duplicati per vicenda parlamentare — creato docs/news-agent/catalog.md con il backfill delle sole sezioni “Notizie analizzate” dei 20 report esistenti, senza conclusioni pregresse. La skill ora deduplica semanticamente per atto + fase/evento + data/seduta (non per URL), considera nuova una fase distinta dello stesso DDL, richiede due raffinamenti Exa prima di un eventuale ritest, motiva i ritest nel report e aggiorna il catalogo a fine run. L’analisi della copertura CLI resta vergine: i report precedenti non sono leggibili, il catalogo è ammesso solo come exclusion list.

2026-07-13

  • v0.25.2 — release patch che consolida il fix keyword-entità HTML (bug ad alto impatto: ricerche Camera silenziosamente vuote). Recepiti due rilievi Copilot sulla PR #64 (già mergiata): (1) htmlEntityKeywordVariants copriva solo le vocali accentate minuscole → una keyword in maiuscolo (CRIMINALITÀ) non generava la variante &Agrave; e, nonostante il match SPARQL sia LCASE-insensitive, falliva sul dato grezzo; aggiunte À/È/É/Ì/Ò/Ù alla mappa e al regex, con test di regressione dedicato; (2) il test di regressione usava limit: 10, fragile rispetto all'ordinamento (19 atti matchano criminalità): alzato a 100. 43 tool invariati, 162/162 test verdi.
  • fix keyword Camera con entità HTMLbills --keyword e bill-progress --uri/--branch C --keyword ora cercano anche la variante HTML-escaped della keyword (criminalitàcriminalit&agrave;). Il LOD Camera salva alcuni rdfs:label/dc:title con entità HTML ma la CLI decodifica l'output: prima una ricerca sul testo visibile (--keyword "criminalità") non trovava atti il cui titolo mostrato contiene criminalità organizzata; funzionavano solo workaround come criminalit o criminalit&agrave;. Aggiunto helper condiviso htmlEntityKeywordVariants, test di regressione su ac19_2696, build/tsc/test completi verdi (161/161).

2026-07-12

  • v0.25.1 — release patch. Un solo fix funzionale dopo la v0.25.0: senato-votes --ddl-uri ora deriva la legislatura dal DDL invece di applicare il default 19 come filtro rigido (#60, PR #61). Prima, passando solo --ddl-uri di un provvedimento di legislatura ≠ 19, il comando dava un falso negativo silenzioso ("Nessuna votazione trovata"): un giornalista che cercava un voto Senato storico (es. il decreto lockdown DL 19/2020, S.1811, leg.18) non trovava nulla e poteva concludere erroneamente che il dato mancasse. Ora la legislatura effettiva è letta da osr:legislatura del DDL (l'URI dati.senato.it/ddl/{N} non la codifica) e usata in tutte le query — allineando il comportamento a votes --bill-code (Camera), che non vincola la legislatura. Aggiunti alla scoperta anche description e esempio --help con un DDL storico, e nota nella skill. Emerso dal run news-driven docs/news-agent/2026-07-11_18-11.md (era l'unico gap reale con impatto; gli altri due rilievi erano assenze source-side già documentate nel wiki, non gap CLI). Contestualmente rimosso dalla CI il job live_integration (sempre rosso dai runner GitHub: gli endpoint LOD bloccano gli IP CI — zero segnale; il testing live resta affidabile in locale). 43 tool invariati, 159/159 test verdi in locale.

2026-07-11

  • fix senato-votes --ddl-uri — legislatura derivata dal DDL (non dal default 19) (#60, branch fix/senato-votes-ddl-legislature). Prima --ddl-uri di un DDL di legislatura ≠ 19, senza --legislature esplicito, dava un falso negativo silenzioso ("Nessuna votazione trovata"): il filtro osr:legislatura cablato al default 19 escludeva le sedute del DDL. L'URI Senato dati.senato.it/ddl/{N} non codifica la legislatura, ma il DDL espone osr:legislatura: ora quando c'è --ddl-uri la legislatura effettiva è derivata dal DDL (una query <ddlUri> osr:legislatura ?leg) e usata in tutte le query, con fallback all'input. Allinea il comportamento a votes --bill-code (Camera) che di default non vincola la legislatura. Ripro: DDL 52988 (S.1811, decreto lockdown DL 19/2020, leg.18) ora torna il voto 18-219-1 anche senza --legislature, con rss_url corretto su /18. +1 test di integrazione. Emerso dal run news-driven docs/news-agent/2026-07-11_18-11.md.

  • v0.25.0 — release minor. Raccoglie tutte le PR mergiate dopo la v0.24.2 (#54–#59). Due nuove capacità utente: (1) speeches filtro temporale --date-from/--date-to + colonna date su entrambe le camere (#54, chiude #37); (2) geografia di nascita normalizzata birth_province/birth_country/birth_region su senators/deputies, regione derivata da SITUAS/ISTAT via opensituas, copertura 100% (#59, chiude #24/#23; corretto l'assunto errato "Senato solo città"). Più fix: bill-progress --branch C ora dà la timeline Camera (#55, chiude #41) e i follow-up di robustezza SPARQL/paginazione (#56/#57/#58). 43 tool invariati, 158/158 test verdi. Dettaglio nelle voci sotto.

  • feat geografia di nascita normalizzata — provincia, nazione, regione su entrambe le camere (#24/#23, branch feat/birth-geography-province-region). Corretto un assunto sbagliato (era anche nel codice e nel wiki): il Senato non espone "solo la città". Ri-verificato con COUNT su tutta la leg.19 (254 senatori): osr:cittaNascita 254/254, osr:provinciaNascita 250/254 (i 4 mancanti sono i nati all'estero), osr:nazioneNascita 254/254 (Italia o stato estero). senators list ora restituisce birth_province, birth_country, birth_region. Regione derivata una volta sola in src/core/province-region.ts (modulo TS bundle-safe) da SITUAS/ISTAT report 64 via CLI opensituas, date-accurate (union snapshot 2010 + 2020 + più recente per le province soppresse/rinominate, + 3 alias per le forme brevi del Senato): copertura 100% (0 province non risolte). Camera (deputies list): la geografia è solo uno slug nell'URI (comune_provincia_regione), non triple — ora decodificata in birth_city/birth_province/birth_country/birth_region con disambiguazione dello slug a 2 parti (Valle d'Aosta / Trentino-Alto Adige comune_regione vs estero comune_stato) e birth_region portata alla stessa forma canonica del Senato (verificato: "Veneto" identico tra camere). birth_province/birth_country restano nella forma nativa di ciascuna fonte (nessun canonicalizzatore province cross-camera). Wiki dati-anagrafici.md/index.md corretti; nota ai gestori Camera (geografia non strutturata) in docs/note-gestori-lod/. +10 test unitari (risorsa provincia→regione + parser slug Camera), 158/158 verdi.

  • follow-up residuale commenti PR #56/#57 (bill-progress): migliorato l'hint del ramo Camera su risultato vuoto (ora segnala che i filtri keyword/intervallo di date o la paginazione limit/offset possono escludere tutte le righe), ordinamento timeline Camera reso stabile per paginazione con ?st come tie-breaker in ORDER BY (non proiettato in SELECT DISTINCT, che ne altererebbe il dedup), escape robusto dei literal SPARQL per keyword (Senato + Camera) con helper dedicato che escapa solo \ " newline CR per la grammatica STRING_LITERAL2 (non JSON.stringify, che emette \uXXXX per i controlli, rifiutato da alcuni parser), e descrizioni dateFrom/dateTo chiarite sui due significati (presentazione Senato vs data stato-iter Camera).

  • CI PR stabilizzata su due livelli: il check richiesto ci ora esegue solo build + typecheck + test deterministici (esclude src/tools/tools.test.ts e src/tools/sparql.test.ts dipendenti da endpoint live). Aggiunto job live_integration su PR con continue-on-error per mantenere visibilità sui test reali Camera/Senato senza bloccare il merge per anti-bot/instabilità esterna.

  • follow-up commenti review su bill-progress: nel ramo Camera (number + branch=C) ora vengono applicati anche i filtri input (keyword, dateFrom/dateTo, limit, offset) invece di essere ignorati nel return anticipato. Aggiunto hint dinamico specifico quando la timeline Camera è vuota (evita messaggi fuorvianti orientati al Senato) e rese neutrali rispetto all'entrypoint le describe() di number/branch (niente grafia --flag, testo valido sia per CLI sia MCP). Inclusi test su paginazione e hint del ramo C.

  • fix bill-progress--number --branch C dà la timeline Camera (#41, branch fix/bill-progress-branch-c-camera). Prima --number 2617 --branch C --legislature 18 restituiva il record Senato di rimando (osr:ramo="C": una riga, phase C.2617, senza date), inutile per chi voleva l'iter Camera. Ora --branch C risolve l'atto Camera ac<leg>_<num> e ne restituisce la timeline completa degli stati (via cameraIterTimeline, stesso path di --uri): per il dl Covid 2020, 8 stati datati (assegnato→…→legge). --branch S invariato (repertorio Senato, stato corrente). Cambio di comportamento del valore --branch C (prima quasi inutile, nessun uso documentato/testato). Framing onesto dell'asimmetria in description/help/skill/wiki (iter-statoIter.md): Camera = timeline stati, Senato = stato corrente (timeline nel feed RSS) — riflette la fonte, non un buco della legislatura. +1 test.

  • feat speeches — filtro --date-from/--date-to + colonna date (#37, branch feat/speeches-date-filter). Prima gli interventi non erano filtrabili né datati nell'output. Camera: la data reale non è sull'intervento (ods:modified è il timestamp del record) ma sulla ocd:discussione che lo raggruppa (dc:date plain AAAAMMGG); verificato che copre sia Aula (stenografico, sezione=assemblea) sia commissione (bollettino), cardinalità 1 per intervento. Il join+FILTER sulle date va dentro la subquery-first (il filtro precede il LIMIT); performante solo col range filter sul soggetto → il filtro data richiede legislature (0.5s con, degrada senza). Senato: osr:dataSeduta (xsd:date ISO) già disponibile, filtro con STR(?ds) (regge sia xsd:date sia xsd:string). Colonna date (YYYY-MM-DD) ora su entrambe le camere — rinominata da session_date per parità (nessun consumer esterno). CLI (--date-from/--date-to), MCP, skill (tools.md + commands.md) e wiki (camera/interventi-data.md nuova) allineati. +2 test (Camera e Senato), 139/139 verdi, tsc pulito.

  • workflow PR quasi-autonomo configurato: aggiunto workflow CI su pull_request/push main (npm ci, npm run build, NODE_OPTIONS="--max-old-space-size=4096" npx tsc --noEmit, npm test -- --run) per gate automatico su PR; lato repository abilitato allow_auto_merge, aggiornato ruleset Copilot review for default branch con review_on_push=true, e impostata branch protection classica su main con status check richiesto ci (strict). Obiettivo: ridurre il ciclo manuale commenta/correggi/committa nelle PR.

  • v0.24.2 — release patch. Raccoglie i fix e i miglioramenti mergiati dopo la 0.24.1 (137/137 test verdi, tsc pulito, 43 tool invariati): (1) fix CLI emptyHint (#49) — l'emptyHint statico dei tool ora arriva anche via CLI (prima cablato solo in formatResult/MCP), così i vuoti "legittimi" (es. gap COVID leg.18) hanno la spiegazione anche a riga di comando; via OpenSpec + withEmptyHint. (2) anti-refuso bill_number blindato (#52) — test di regressione sul Piano Casa (label "DDL n. 1994" → bill_number 1944, dal backfill osr:fase) e wiki fiducia-fallback allineata. (3) senato-vote-detail — dettaglio per tipo di voto (#53) — verificato che il dettaglio nominativo c'è per tutte le modalità; la scelta espressa (Favorevole/Contrario/Astenuto) manca solo su segreta e verifica numero legale (solo presenze). Description del tool, wiki votazione-tipo-semantico e skill aggiornate; segnale roll_call_available derivabile da type, niente campo nuovo. (4) ricetta sede question time aic — la sede (Aula/commissione) è già filtrabile via --type "immediata in assemblea"/"in commissione" (match sul label regolare), documentata in description/esempio + skill. (5) fix agente news-driven-cli-gap-analyzer che si auto-delegava (#50). Nessuna nuova capacità utente → patch.

2026-07-10

  • v0.24.1 — release patch con i fix UX della PR #48 (merge commit 73ea81e, 5 giri di review Copilot/Greptile, 10 commenti gestiti, tutti i thread risolti): which azionabile con esempi incollabili e catalogo testato (src/core/capabilities.ts), help completo su --format/--limit/--offset, messaggi d'uso/no-match su stderr, link repo a ondata, repository/homepage/bugs in package.json. Include anche la raccomandazione README "uso locale consigliato" (push diretto post-#28). 130/130 test verdi. npm + Worker deployati.

  • fix CLI — which azionabile, help completo, link repo a ondata (PR #48, branch fix/cli-discoverability-and-repo-links). Tre fix UX emersi dall'uso reale: (1) which restituiva forme combinate non incollabili ("bill-text links / fetch") senza dire come usarle — ora una voce per comando con esempio pronto da eseguire (dagli examples reali dei tool) e rimando a --help; JSON con campo example; aggiunte capacità mancanti (camera-amendments, audizioni, bill-text fetch come voce propria); query vuota (which " ") → messaggio d'uso + exit 2 invece dell'intero catalogo. (2) --format/--limit/--offset senza descrizione nell'help in ~93 occorrenze (incluso sparql query, definizione separata) — ora --format documenta csv | jsonl ovunque. (3) Riferimenti al vecchio path github.com/aborruso/...ondata in README (git clone, npx skills add), manifest.json, User-Agent camera-amendments, 5 citation wiki; aggiunti repository/homepage/bugs in package.json (la pagina npm non linkava il repo). Il pacchetto npm resta @aborruso/.... 123/123 test verdi.

  • v0.24.0 — PR #47 mergiata (merge commit e0ba02b): fallback bulk AKN completato dopo un lungo ciclo di review (Greptile + 7 giri Copilot, 10 commit, 25 thread tutti risolti). Oltre a quanto già in LOG: colonna sede separata da type (semantica diversa per fonte LOD/AKN), fix di correttezza sulla paginazione quando solo il bucket Assemblea del bulk è troncato, enrichProponents fallisce esplicito su outage sistematico (non più vuoto silenzioso indistinguibile dai file stub), cap limit<=100 su --with-proponents (rischio timeout/rate-limit sul Worker con limit alto), hint distinto per pagina-oltre-la-fine vs vuoto genuino nel fallback AKN. Chiude automaticamente #38 e #30 (issue #45 resta aperta: altri spunti dell'analisi non ancora implementati, es. bill-text con link AKN raw). 123/123 test verdi.

  • feat amendments — fallback bulk AKN GitHub + proponenti (P1 dell'analisi fonti non-LOD; issue #45→#38+#30, branch feat/amendments-akn-fallback). Nuovo src/core/akn.ts (Worker-compatible, fetch nativo): path atto, listing cartelle via endpoint JSON web-UI GitHub (troncamento a 1000 gestito via totalCount, errore esplicito su finestra irraggiungibile), parser AKN. Il tool: (1) espone akn_xml_url su ogni riga LOD (conversione URLTestoXml→raw, flagCommissione decide emend/emendc); (2) con ddlUri e LOD vuoto fa fallback sul bulk (source=akn); (3) --with-proponents estrae primo firmatario e cofirmatari dal testo AKN (docProponent/TLCPerson, doppia variante markup Assemblea/Commissione, preferito showAs col nome completo). Scoperta in corsa: il LOD osr:Emendamento si è riallineato tra il 7 e il 10 lug (Piano Casa 0→799, match esatto col bulk 400+399) — freschezza intermittente, il fallback resta rete di sicurezza; trappola nuova dataPresentazione xsd:string sui DDL recenti (wiki aggiornato, 3 pagine + log). Verificato: Amidei em. 1.30 ddl/56260 (caso wiki, prima solo via browser WAF), Piano Casa con proponenti da entrambe le sedi. 114/114 test verdi (+11), tsc pulito. Skill CLI e MCP aggiornate. Idea futura annotata: estrattore AKN bulk in Rust per analisi offline (docs/future-ideas.md).

  • Analisi fonti non-LOD in docs/analisi-fonti-non-lod.md: le 3 fonti (listasommcomm #33, AKN bulk GitHub #45, RSS per-DDL #13) valutate contro forza/debolezza degli output LOD correnti. Criterio discriminante: il WAF — in un progetto MCP-first una fonte non fetchabile dal Worker vale solo come URL emesso. Priorità: P1 AKN (unica senza WAF, verificata oggi: raw 200 + listing JSON 200) → amendments fallback post-2024 + proponente, bill-text link AKN; P2 RSS → rss_url su bills/bill (già emesso da 3 tool), niente parsing server-side; P3 listasommcomm → solo colonna sommari_json_url, niente fetch (WAF + rate). Commentate le 3 issue con gli esiti.

  • Wiki LOD: recepite (spersonalizzate) le conferme del gestore del repository LOD Camera via scambio email — ontologia OCD ferma al 2009-2011, RDF pubblicato a lotti alla ricezione delle triple, emendamenti assenti dal repository, audizioni non strutturate. Nuova sezione "provenienza" in freschezza-e-autorevolezza.md, note di conferma in camera/assenti.md e camera/audizioni.md.

  • fix votes --confidence-vote — hint "gg-1" su ricerca fiducia vuota (issue #44). La stampa riporta spesso la data dell'approvazione finale, ma la fiducia è tipicamente votata il giorno prima: cercare --confidence-vote true sulla data di stampa dava 0 righe mute. Ora, se il risultato è vuoto e --confidence-vote true con una finestra di date, un hint dinamico suggerisce di riprovare gg-1 (nessun retry automatico). Verificato sul DL Rilancio 2020 (fiducia 8/7, approvazione 9/7). +1 test, 100/100 verdi. Chiusa anche #31 (già risolta da v0.17.1, non ancora chiusa su GitHub): aic --date-from/--date-to combacia già sulla data d'Aula, verificato con la riproduzione originale.

  • v0.23.0 — i 4 punti critici del report news-agent 2026-07-10_11-10.md risolti (nessun tool nuovo, 43 tool; 98/98 test verdi, tsc pulito): (1) bill-signatories non classifica più come Governo i DDL Senato parlamentari da Camera (+ person_uri/html_url deputati via ocd:rif_deputato); (2) senato-votes --keyword trova le fiducie anche per tema (supplemento via titolo DDL); (3) bill guardia instradante "solo-Camera" su URI Senato; (4) bill_number backfillato dalla osr:fase del DDL risolto (corregge anche i refusi della fonte). Wiki: firmatari-iniziativa (seconda trappola + censimento tipoIniziativa), fiducia-fallback (discovery tematica), nuova akn-bulk-data (bulk AKN GitHub senza WAF, issue #45). Skill CLI aggiornata. Dettaglio nelle voci sotto.

  • feat senato-votes — backfill di bill_number dal DDL risolto (punto 4 dell'analisi del report docs/news-agent/2026-07-10_11-10.md). Sui label generici ("Votazione finale") o coi refusi il numero non è estraibile dal testo, ma quando ddl_uri è risolto (link diretto o fallback) il numero è la osr:fase del DDL: una query aggiuntiva (solo se servono backfill, solo DDL singolo, solo fasi S.) popola la colonna. Bonus: sul refuso Piano Casa (label "DDL n. 1994") ora esce il numero GIUSTO 1944, derivato dal DDL verificato e non dal testo. Verificato: 19-376-2 → 1457, 19-434-2 → 1944; lista standard 100 righe in ~4,3s (throttle-dominata). +1 test e assert sul refuso, 98/98 verdi.

  • fix bill — guardia instradante sugli URI Senato (punto 3 dell'analisi del report docs/news-agent/2026-07-10_11-10.md). bill show --uri http://dati.senato.it/ddl/... rispondeva "Nessun atto trovato": un utente/agente lo legge come dato assente, mentre è il tool sbagliato (bill è Camera-only). Ora errore esplicito "solo-Camera" che indirizza a bill-progress --ddl-uri / bill-signatories --bill-uri / bill-text --uri (stesso pattern di amendments "solo-Senato"); description aggiornata anche per i client MCP. +1 test.

  • fix senato-votes --keyword — le fiducie ora emergono anche per tema (punto 2 dell'analisi del report docs/news-agent/2026-07-10_11-10.md). Root cause: il match keyword sul titolo del DDL (v0.20.0) passa in SPARQL da osr:oggetto→osr:relativoA→osr:titolo, ma le fiducie non hanno osr:oggetto?ddlTitolo unbound → escluse dal filtro anche quando il tema è nel titolo del DDL citato per numero nel label (--keyword sicurezza non trovava la fiducia 19-312-1 sul decreto sicurezza). Fix: supplemento fiducie — con --keyword una seconda query recupera le sole fiducie del periodo (set piccolo, ~55 nell'intera leg. 19), risolve il DDL citato nel label via osr:fase leggendone anche osr:titolo, e tiene quelle il cui titolo matcha; righe merged con dedup e riordino, countOnly allineato. Non si applica con --ddl-uri (già coperto), --confidence-vote false, --final-vote true, o offset>0 (eviterebbe righe ripetute per pagina). Residuo documentato: fiducie con refuso nel numero (S.1944 "DDL n. 1994") non recuperabili per keyword (solo Fallback 1, niente propagazione intra-seduta). Verificato: --keyword sicurezza giu 2025 → fiducia 19-312-1 + pregiudiziale, count=2 coerente; regressioni "corte dei conti" e combinazioni --confidence-vote true/false invariate. Wiki senato/fiducia-fallback.md estesa. +1 test, 96/96 verdi, tsc pulito.

  • fix bill-signatories — i DDL Senato di iniziativa parlamentare arrivati dalla Camera non sono più classificati "Governo (proponente)" (punto 1 dell'analisi del report docs/news-agent/2026-07-10_11-10.md). Root cause: l'euristica assumeva atto governativo quando mancava osr:senatore, ma i firmatari deputati (es. ddl/59070, S.1457 Corte dei Conti, Dep. Foti) vivono nel grafo senza osr:senatore e con ocd:rif_deputato. Fix: la query legge anche osr:tipoIniziativa (discriminante esplicito: Parlamentare/Governativa/Popolare/Regionale/CNEL/…) e ocd:rif_deputato; i deputati ora hanno ruolo primo firmatario/cofirmatario corretto più person_uri/html_url (scheda camera.it, prima vuoti); le iniziative non parlamentari e non governative hanno ruolo <tipo> (proponente) invece del falso "Governo". Bug annidato trovato in corsa: flattenBindings rende "" i binding assenti, quindi il fallback senatore ?? deputato non scattava mai → ||. Verificato su 4 casi reali: ddl/59070 (parl-deputati, il bug), ddl/55281 (parl-senatori, regressione), ddl/60233 (governativo, regressione), ddl/24134 (popolare). Wiki senato/firmatari-iniziativa.md estesa con la seconda trappola + censimento tipoIniziativa. +1 test, 95/95 verdi, tsc pulito.

2026-07-09

  • v0.22.0 — senato-votes multi-DDL + fix EPIPE CLI (nessun tool nuovo, 43 tool; 94/94 test verdi, tsc pulito). Campi strutturati per i voti su testi unificati (ddl_count, ambiguous_ddl, ddl_uris_json, ddl_html_urls_json, rss_urls_json) e pipe Unix interrotte (| head) che escono pulite. Dettaglio nelle voci sotto. Rimosso anche il dead code joinMap (rompeva tsc --noEmit). Wiki OKF covid-2020 esteso ad altri due decreti (Rilancio, Agosto).

  • senato-votes — Fase 1 + 2 sul multi-valore: ambiguità esplicita + campi JSON strutturati. Il problema non era nei dati ma nel nostro output: il tool collassava liste reali di DDL/URL in stringhe piatte separate da |, poi le risplittava internamente. Fix minimale senza rompere retrocompatibilità: i campi esistenti (ddl_uri, ddl_html_url, rss_url) restano invariati, ma si aggiungono ddl_count, ambiguous_ddl, ddl_uris_json, ddl_html_urls_json, rss_urls_json. Così il CSV resta leggibile e il JSONL diventa realmente machine-friendly senza dover parseare pipe custom. Verificato sul caso multi-DDL del 2026-06-23 (votazione/19-431-19): ddl_count=5, ambiguous_ddl=true, array JSON con i 5 DDL e i rispettivi URL/RSS. Root cause classificata correttamente: problema di modellazione output del tool, non del LOD.

  • fix CLI EPIPE — pipe Unix interrotte ora escono pulite: usando pipeline brevi tipo italianparliament ... | head -3, Node continuava a scrivere su stdout dopo che head aveva chiuso il pipe → stack trace Error: write EPIPE, rumoroso ma non legato ai dati. Fix minimo in src/cli.ts: handler su process.stdout e process.stderr che intercetta err.code === "EPIPE" e termina con exit(0) invece di rilanciare. Verificato end-to-end dopo build con node dist/cli.js vote-detail show --vote-uri http://dati.camera.it/ocd/votazione.rdf/vs18_382_001 --format jsonl | head -3 → 3 righe, nessuno stack trace, exit code 0. Problema puro di ergonomia CLI/tooling, non della fonte.

2026-07-08

  • v0.21.0 — 3 fix/mitigazioni dai gap del report news-agent docs/news-agent/2026-07-08_12-57.md (nessun tool nuovo, 43 tool; 94/94 test verdi, tsc pulito): (1) bill-progress --number recupera l'intera navetta a più letture (-B/-C); (2) bill-signatories risolve i ministri sui decreti-legge Camera (basta righe con name vuoto); (3) hint di staleness su votes per non restituire un vuoto muto sul dato Camera più recente. Dettaglio nelle voci sotto.

  • fix bill-signatories — decreti-legge/atti governativi Camera: risolti i ministri (basta righe con name vuoto) (gap 4 del report docs/news-agent/2026-07-08_12-57.md). Root cause: sui decreti-legge ocd:primo_firmatario non punta a un deputato ma a un blank node "membro di governo" privo di foaf:firstName; il tool cercava il nome solo sul nodo → 5 righe con name="" e nodeID://… (sembrava un bug/assenza firmatari). Il dato c'è: il nome del ministro è un hop più in là via ocd:rif_persona (→ persona.rdf), il dicastero in ocd:ruolo. Fix: la query Camera segue rif_persona+ruolo; discriminante netto parlamentare vs governativo = presenza di rif_persona. Ora Piano Casa (ac19_2920) torna Piantedosi/Salvini/Giorgetti/Foti/Meloni con role="Governo — <dicastero>", is_primary=false (proponenti multipli, coerente con #29/ramo Senato); regressione atto parlamentare (Ziello ac19_2941) invariata. html_url vuoto per i ministri (URI persona.rdf, non risolvibile senza mandato — limite #14, accettabile). Nuova pagina wiki OKF camera/firmatari-atti-governativi.md + index. +1 test. 94/94 verdi.

  • fix bill-progress --number — navetta a più letture (-B/-C) completa (gap 3 del report docs/news-agent/2026-07-08_12-57.md). Root cause: il filtro sul numero era un match esatto FILTER(STR(?numeroFase) = "1353"), che intercettava solo la prima lettura; le letture successive hanno numeroFase diverso ("1353-B") e cadevano fuori. Peggio: --number 1353-B falliva con ERROR --number: Invalid (validazione Zod ^\d+$). Fix (1 riga): match esatto → REGEX(STR(?numeroFase), "^<n>(-[A-Z])?$"), così --number 1353 --branch S torna S.1353 e S.1353-B. Verificato: separazione carriere (1353 → entrambe le letture), regressione Cura Italia (1766, singola lettura invariata), ramo C (1917 → C.1917 + C.1917-B). Distinto da #41 (bug diverso, --branch C ritorna record Senato).

  • feat: hint di staleness su votes — vuoto recente non più muto (gap 2 del report docs/news-agent/2026-07-08_12-57.md; il voto Camera 7/7/2026 sul reclutamento non era verificabile via CLI, LOD fermo all'1/7). Non è "risolvibile" (il dato manca a monte, ritardo di ~7gg del LOD Camera pubblicato a lotti), ma il vuoto silenzioso è mitigabile. Aggiunto hint? dinamico su ToolResult: votes calcola una nota di freschezza SOLO quando il risultato è vuoto E la finestra di date tocca gli ultimi ~14 giorni (o il futuro prossimo); niente hint su finestre storiche. Sorgente MCP (server.ts, precede l'emptyHint statico) e CLI (emitstderr, così non sporca CSV/JSONL delle pipeline). +1 test (data futura = vuoto+recente stabile; controprova storica). 93/93 verdi. Advocacy freschezza già in corso (canale Synapta + wiki freschezza-e-autorevolezza.md): nessuna issue aperta, si attendono le risposte gestori.

  • v0.20.0 — senato-votes --keyword cerca anche nel titolo del DDL, throttle anti-403 Senato, emptyHint su due gap di dataset (dai gap del report news-agent docs/news-agent/2026-07-08_08-17.md).

    • senato-votes --keyword ora matcha anche il titolo del DDL collegato, non solo il rdfs:label del voto. Root cause: il tema del provvedimento (es. "bilancio", "caccia") spesso non è nel label ("Votazione finale", "Articolo 143") ma nel titolo del DDL (osr:oggetto/osr:relativoA/osr:titolo). Fix: OPTIONAL sul titolo DDL + CONTAINS in OR col label, con BOUND() così le fiducie (prive di osr:oggetto) non fanno fallire l'intero OR. Verificato leg.19: --keyword bilancio ora torna le votazioni della legge di bilancio (DDL 59654) che prima erano invisibili (label-only). Allineato anche il ramo countOnly.
    • Throttle prudenziale sull'endpoint Senato (src/core/client.ts): un burst di ~9 query dirette in pochi minuti ha bloccato l'endpoint con un 403 l'8/7. Aggiunto un throttle sequenziale (≥2s tra l'inizio di due richieste Senato, coda condivisa così protegge anche chiamate concorrenti); solo Senato (la Camera non ha mai mostrato lo stesso comportamento). snQuery diventa async. Il Webmaster (interpellato) non comunica soglie "per ragioni di sicurezza": la soglia è prudenziale.
    • emptyHint anti-confabulazione su due gap di dataset noti. (1) amendments: il dataset osr:Emendamento è fermo al 9 agosto 2024 — nessun DDL con dataPresentazione successiva ha emendamenti collegati; un vuoto su un DDL recente NON è "nessun emendamento" (issue #38). (2) senato-votes: tra il 10 marzo e il 16 aprile 2020 (leg.18, COVID) non risulta ALCUNA votazione d'Assemblea nel LOD pur essendoci le sedute con gli interventi — inclusa la fiducia sul Cura Italia (9/4/2020).
    • Wiki: nuove pagine senato/emendamenti-freschezza.md, senato/votazioni-covid-2020.md, senato/corrispondenza-webmaster.md (log segnalazioni al Webmaster) + index. Nessun tool aggiunto (43 tool). 92/92 test verdi, tsc pulito.
  • run news-driven CLI gap analyzer: nuova verifica fresca in docs/news-agent/2026-07-08_08-17.md su 6 notizie (current 2026, bucket 2025, bucket 2020). Esito sintetico: Camera regge bene anche su storico leg.18 (fiducia dl Covid 2020 + dettaglio individuale); Senato regge bene su leg.19 recente (separazione carriere, dettaglio voto) ma mostra gap su discovery e storico: senato-votes --keyword dipende troppo dal label, il caso Cura Italia 2020 non emerge via senato-votes, e la fiducia Piano Casa 2026 resta con ddl_uri vuoto nel listato base. Emersa anche confusione semantica di bill-progress --number --branch C (ritorna la fase Senato del C.*). Nessun fix applicato in questa run; solo evidenza grounded da comandi reali.

2026-07-07

  • feat: attendance/senato-attendance — presenze/assenze aggregate per legislatura (issue #9). Conteggio aggregato dei voti di un parlamentare su tutte le votazioni della sua legislatura. Camera: query diretta su ocd:voto filtrata per URI mandato (già specifico di legislatura, niente join su rif_leg — evita il gap di dati noto lì, ~50% delle votazioni senza rif_leg popolato). Colonne: favorevole/contrario/astensione/non_ha_votato/ha_votato (scrutinio segreto)/altro/totale. Senato: 5 query per categoria su proprietà inverse osr:favorevole|contrario|astenuto|presenteNonVotante|inCongedoMissione, filtrate per osr:legislatura (l'URI senatore non la contiene, richiede --legislature). Verificato: Meloni (PM) — 37457/37491 non ha votato; Casellati (senatrice attiva tutta la leg.19) — totale 8008 su 8044 votazioni della legislatura (residuo ~0.4%, non spiegato ma non bloccante); Pera (senatore a vita) — totale 5122, coerente con partecipazione parziale. +2 tool (43 tool), +2 test, 92/92 verdi.

  • v0.18.0 — filtri semantici votazioni Camera e Senato (gap #1 e #2 dal report news-agent docs/news-agent/2026-07-07_13-47.md).

    • Camera votes --keyword ora cerca anche in description. Root cause: keywordFilter confrontava solo ?label e ?title (OPTIONAL), ignorando ?description — dove invece vive il testo tematico (es. "Bilancio di previsione dello Stato..."). Risultato: votes list --keyword bilancio restituiva 0 nonostante il DDL 2750 esistesse. Fix: aggiunto ?description al CONTAINS del filtro e reso vincolante (?s dc:description ?description .) nella inner query quando la keyword è attiva. Verificato: --keyword bilancio ora torna DDL 2750/2574/2537; regressione su label-only keyword e confidence+date OK. Limite residuo (dati, non tool): votazioni con description povera (es. "DDL.n. 2920-A") non sono ricercabili per tema — il tema vive nel titolo del decreto (bill-progress).
    • Senato senato-votes: nuovi filtri --keyword, --confidence-vote, --final-vote. Verificato per enumerazione che osr:tipoVotazione è la modalità di voto (elettronica/nominale/segreta), NON il tipo semantico: "Votazione finale" e "questione di fiducia" vivono solo nel rdfs:label. I tre filtri sono quindi label-based (CONTAINS su rdfs:label). Trappola gestita: sfiduciafiducia--confidence-vote true esclude esplicitamente sfiducia (le mozioni di sfiducia). Verificato leg.19: --confidence-vote true → 55 (vs 56 keyword "fiducia" grezzo, 1 sfiducia esclusa), --final-vote true → 246; validazione bool CLI (true/false, errore chiaro altrimenti). Limite dati: il tema del decreto NON è nel label delle votazioni Senato — --keyword caccia torna vuoto (il tema vive nel DDL collegato: usare --ddl-uri + bill-progress). Nuova pagina wiki senato/votazione-tipo-semantico.md.
    • Nessun tool aggiunto (41 tool). Test 90/90 verdi.
  • v0.17.2 — fix vuoto silenzioso dei filtri data Camera (bug #1 dal report news-agent docs/news-agent/2026-07-07_09-03.md). Root cause: FILTER(?date >= "AAAAMMGG") sulla variabile nuda legata a dc:date (literal AAAAMMGG) faceva un confronto numerico su Virtuoso → range errati o 0 righe mute. Es.: votes list --legislature 19 --date-from 2025-02-01 --date-to 2025-02-28 restituiva 0 pur essendoci 436 votazioni, e gennaio ne "catturava" 7378 (conteggi assurdi); riprodotto su leg.18 (marzo 2020: 2 invece di 35). Un giornalista non poteva distinguere «nessun voto» da «query rotta». Fix: avvolti in STR(...) tutti i confronti data Camera in 5 toolvotes, bills, sessions, committee-sessions, audizioni (le date Senato tipizzate ^^xsd:date restano invariate). Verificato: votes leg.19 gen/feb/mar 2025 → 313/436/464 (coerente con STRSTARTS); bills feb 2025 → 60; sessions feb 2025 → 18; votes leg.18 marzo 2020 → 35. Nessun tool aggiunto (41 tool). Test Camera invariati (i 13 fallimenti sono tutti Senato 403, quirk endpoint). Wiki trappole-virtuoso-funzioni-stringa.md generalizzato: la trappola #2 vale anche sulle variabili dc:date nude, non solo sui risultati di SUBSTR/REPLACE.

2026-07-06

  • v0.17.1 — 2 fix da stress-test news-driven (report docs/news-agent/2026-07-06_18-51.md). Nessun tool aggiunto (41 tool). Dettaglio nelle due voci sotto.

  • fix aic --date-from/--date-to: ora combacia anche sulla data d'Aula (question time) (bug #2 dal report news-agent docs/news-agent/2026-07-06_18-51.md). Root cause: il filtro usava solo la data di presentazione (SUBSTR(dc:date,1,8)); i question time sono presentati la vigilia e trattati in Aula il giorno dopo (dc:date="AAAAMMGG-AAAAMMGG", presentazione-modifica), quindi --date-from/--date-to sul giorno d'Aula dava 0 righe. Fix: il filtro combacia se cade nell'intervallo la presentazione oppure la modifica (2° gruppo del composto, estratto con REPLACE); nessun flag nuovo. Verificato: 9/7/2025 → 19 interrogazioni immediata; 25/6/2025 → 33; presentazione singola invariata. Due trappole Virtuoso incontrate e ora documentate (docs/lod-wiki/trappole-virtuoso-funzioni-stringa.md): (1) SUBSTR fuori range aborta la query senza short-circuit dell'&& → estrarre con REPLACE, non SUBSTR(...,10,8); (2) >=/<= sul risultato di SUBSTR/REPLACE fa un confronto numerico che dà 0 righe → forzare con STR(...). +1 test sentinella, 89/89. Aggiornati aic.ts (descrizione), skill CLI+MCP, wiki camera/aic-date.md + nuova pagina trasversale Virtuoso (referenziata da index e da senato/trappole.md).

  • fix senato-votes --ddl-uri: ora include la fiducia collegata (bug #1 dal report news-agent docs/news-agent/2026-07-06_18-51.md). Root cause: il filtro server-side osr:relativoA <ddlUri> escludeva a monte le fiducie (prive di osr:oggetto); i fallback che ricollegano il DDL giravano solo nel percorso per data. Fix: con --ddl-uri si risolvono prima le date delle sedute in cui il DDL è votato (query osr:relativoA), si interroga per quelle date e si applicano i fallback esistenti, poi si post-filtra. Post-filtro selettivo: si tengono i voti collegati fortemente (link diretto osr:relativoA o Fallback 1 via numero nel label) più le sole fiducie; i voti agganciati per sola propagazione-data (Fallback 2) che fiducie non sono (es. una risoluzione votata la stessa seduta) vengono scartati. countOnly allineato alla lista. Verificato Piano Casa (ddl/60233): restituisce pregiudiziale (57/79) + fiducia (106/62/2, 19-434-2), esclusa la risoluzione 19-434-3; DDL con voto finale (ddl/58039) invariato (42 voti). +1 test sentinella live, tsc pulito. Aggiornati description/emptyHint del tool, skill CLI+MCP e docs/lod-wiki/senato/fiducia-fallback.md.

  • v0.17.0 — 5 miglioramenti da stress-test news-driven (report docs/news-agent/2026-07-06_08-10.md, notizia Piano Casa approvato al Senato con fiducia il 1/7/2026). (1) Nuovo helper src/core/current-legislature.ts: risolve la legislatura corrente in modo dinamico dall'endpoint Camera (ultima repubblica_* per dc:date), cache in-memory + fallback 19 con warning su stderr. Sostituisce l'hardcode 19 in senator, committees, committee-sessions, bill-text e nel ramo Camera di bill-progress (si romperebbe alla prossima legislatura). (2) bill-progress --number senza --legislature ora defaulta alla legislatura corrente invece di pescare omonimi storici cross-legislatura (S.1809 → solo leg.19). (3) search find: ranking lato TS — match esatto sul cognome/token finale (batte le sottostringhe: Boschi > Tiraboschi) + preferenza per la legislatura corrente + tie-break Camera; boschi → Maria Elena Boschi leg.19 in cima senza bisogno del nome completo. (4) votes list Camera: due colonne nuove aic_code (es. 1/00586) e aic_link (URL scheda AIC) per mozioni/risoluzioni, estratte da description/title (MOZ 1-586, RIS 6-263, Risoluzione 6_00266); i voti su DDL restano vuoti. Link verificato HTTP 200. (5) senato-votes: Fallback 2 intra-seduta — un voto di fiducia rimasto senza ddl_uri (per refuso della fonte nel numero, es. "DDL n. 1994" per S.1944) eredita il ddl_uri da altri voti della stessa data se non ambiguo; sblocca la fiducia Piano Casa 19-434-2ddl/60233. (6) bill-signatories: gli atti Senato di iniziativa governativa (nessun osr:senatore) ora hanno ruolo Governo (proponente) invece di primo firmatario, senza person_uri/html_url fittizi. Nessun tool aggiunto (41 tool). 87/87 test, tsc pulito. README + skill CLI/MCP aggiornati.

2026-07-05

  • v0.16.1 — direzionamento lato MCP anti-confabulazione (issue #32, checkbox 1). Emerso da uno stress-test di un chatbot terzo (basato su Mistral, che non carica le skill): all'"iter completo della legge sul salario giusto" produceva una confabulazione su scheletro reale — dato vero al ~70% ma con voto finale Camera inventato ("342/180/20"), emendamenti presi da un DDL Senato omonimo sbagliato (59851 invece di 60201/S.1933) e contenuto della legge ricamato. Verificato che i tool restituivano il dato giusto: il difetto era orchestrazione+interpretazione del client. Un agente neutro guidato dalla skill ha ricostruito l'iter corretto → i tool bastano, ma i client senza skill vanno direzionati. Interventi (solo path MCP server.ts, CLI invariata): (1) nuovo campo Tool.emptyHint + messaggio di default anti-confabulazione sul risultato vuoto ("non dedurre né inventare; un vuoto è spesso un mismatch"); (2) descrizioni prescrittive su bill-progress (spina dorsale dell'iter, aggancio DDL Senato per numero non per keyword), senato-votes (caveat fiducia: ddl_uri vuoto → filtra per data + label), votes (voto finale/fiducia per intervallo di date), bills (mismatch lessicale), bill-text (se non recuperi il testo, non descrivere il contenuto). Nessun tool aggiunto/rimosso (41 tool). 87/87 test, tsc pulito. Skill CLI v1.4 / MCP v1.3 già aggiornate con la ricetta "iter completo" e la sezione Grounding.

  • v0.16.0 — filtri demografici su deputies e senators (issue #23: genere, data e luogo di nascita). Nuovi parametri (CLI + MCP): --gender male|female, --born-from/--born-to (YYYY-MM-DD), --birth-place. Camera: genere già sul nodo deputato; nascita sul nodo persona (foaf:Person) agganciato via lo stesso ocd:rif_mandatoCamera (vincolo a foaf:Person per non duplicare le righe), bio:date (YYYYMMDD→YYYY-MM-DD in output) e ocd:rif_luogo. Esposte 2 colonne nuove birth_date+birth_place: birth_place è lo slug comune_provincia_regione (es. messina_messina_sicilia) → il filtro per regione/provincia di nascita è nativo (10 deputate leg.19 nate in Sicilia, coerente col wiki). Senato: i campi c'erano già; aggiunti solo i filtri — dataNascita (YYYY-MM-DD, confronto via STR()), cittaNascita (solo città, no provincia/regione: limite documentato). Quirk: foaf:gender al Senato è letterale tipizzato → FILTER(?gen="F") dà 0, serve FILTER(STR(?gen)="F") (stesso caso di siglaGruppo). +4 test (Camera gender+luogo, range nascita; Senato gender STR, born-from). 87/87 test. Aggiornati reference skill CLI+MCP.

  • skill: nuovo reference condiviso obiettivi-giornalistici.md (in entrambe le skill CLI e MCP) — catalogo di analisi giornalistiche derivabili combinando i tool esistenti, senza tool dedicato. Prima voce: dissidenti/ribelli (voto difforme dalla linea di gruppo), con ricetta vote-detail/senato-vote-detail + jq (Camera e Senato), scelte analitiche e limite di volume. Credito e ringraziamento a openpolis/openparlamento (parlamento19.openpolis.it). Referenziato da entrambe le SKILL.md; bump skill CLI v1.3, MCP v1.2.

  • fix vote-detail — enum --vote-type Camera: AstenutoAstensione (emerso valutando issue #7). Il valore reale di dc:type sui voti Camera è Astensione (verificato: campione 5000 → 4 valori netti Favorevole/Contrario/Astensione/Non ha votato, nessun "In missione"), ma l'enum del tool accettava Astenuto (→ 0 righe muto) e rifiutava Astensione → filtrare gli astenuti era impossibile. Corretto in vote-detail.ts (enum + descrizione) e cli.ts (descrizione + cast). Il Senato non è toccato: senato-vote-detail costruisce i label via osr:astenuto e usa correttamente Astenuto. 83/83 test.

  • issue #7 (filtro dissidenti) chiusa senza nuovo tool — derivabile via pipeline. Verificato che i dissidenti (voto difforme dalla linea di gruppo) sono già calcolabili da vote-detail show (Camera) / senato-vote-detail show (Senato), che espongono già nome+voto+gruppo: una sola pipeline ... --format jsonl | jq (moda del gruppo sui voti espressi → chi diverge). Testato su vs19_641_046 → 1 dissidente (Lorenzo Cesa, M-ALT). Aggregato "più ribelle della legislatura" = problema di volume (iterare su migliaia di votazioni), non di dato. Regola acquisita: niente nuovi tool/flag se il risultato è derivabile combinando i tool esistenti.

  • v0.15.0 — Due nuovi agganci sui DDL: ricerca per numero + commissioni assegnate (emersi da confronto con una scheda atto di riferimento su S.1809). (A) bill-progress list ora accetta --number <n> + --branch S|C (default S): risolve un atto Senato dal suo numero (es. --number 1809 --branch S → S.1809, ddl/59924), colmando il buco per cui si poteva cercare solo per parola chiave nel titolo. osr:numeroFase è tipizzato → FILTER(STR(?n)); il ramo è obbligatorio perché lo stesso numero esiste in entrambi (C.1809 e S.1809). (B) Nuovo tool bill-committees list --bill-uri <ddl/atto> (dual-branch, auto-detect ramo): elenca le commissioni a cui l'atto è assegnato con sede/ruolo (Referente/Consultiva/…), tipo, data e URI organo. Dato già nel LOD ma prima non esposto: Senato osr:assegnazione, Camera ocd:rif_assegnazione. Verificato su S.1809 → 7 commissioni (1ª Referente + 6 Consultive, 30/04/2026) e su C.2822 (I Commissione Referente). Aggiunto &ordf;/&ordm; al decoder HTML (label Senato "1ª (…)"). Registrati in CLI, MCP server.ts e registry; voce which per "commissione/assegnazione". 83/83 test, tsc pulito. 41 tool.

  • "Il cielo del Parlamento" — pagina vetrina live su GitHub Pages (site/ + workflow pages.yml): pagina animata (GSAP + ScrollTrigger, canvas) che racconta CLI/MCP con dati sempre live dal Worker: costellazioni = parlamentari in carica per gruppo (Camera+Senato via group-members/senator-group-members), strada delle legislature 1848→oggi (legislatures), ultime votazioni con contatori (votes), griglia dei 40 strumenti da tools/list (si auto-aggiorna a ogni release). Nessun dato precotto: ogni visita interroga /mcp (JSON-RPC tools/call stateless, parse SSE, cache localStorage 1-24h). Fix worker necessario: la risposta di transport.handleRequest non aveva Access-Control-Allow-Origin (solo il preflight lo aveva) → aggiunto wrap con header CORS, deployato. Deploy Pages via Actions (actions/deploy-pages, path site/). Testata con agent-browser: desktop 1440, mobile 390 (layout compatto senza etichette), barre voti (fix display:block su span inline), ScrollTrigger.refresh() dopo il load asincrono. Click su una stella → scheda ufficiale del parlamentare (camera.it/senato.it, da html_url dei tool membership), cursore pointer + hint nel tooltip; verificato con click sintetici su entrambe le camere. Card votazioni arricchite: le sigle criptiche ("MOZ 1-569") vengono agganciate all'atto AIC reale — una sola query SPARQL VALUES per tutte le card risolve presentatore (gruppo) e incipit del "premesso che"; doppio link per card: testo dell'atto (aic.camera.it; DDL → scheda camera.it) + scheda votazione. Nota deploy: gh run rerun su deploy-pages fallito produce "Multiple artifacts named github-pages" — mai rerun, sempre run nuova (gh workflow run pages.yml).

  • v0.14.1camera-amendments: fallimento diagnostico su Worker + header browser-like. Verifica del Worker live (requisito): SPARQL e audizioni OK; camera-amendments fallisce da Cloudflare — camera.it serve agli IP Worker una pagina anti-bot (~4.5KB senza i marcatori attesi), non un errore, quindi prima tornava "No results" ingannevole. Ora: header Accept/Accept-Language + se la scheda non ha i marcatori di un atto valido → errore chiaro ("fonte non raggiungibile, riprovare da CLI locale"). L'atto senza emendamenti (scheda valida) resta vuoto senza errore. Diagnosi confermata: IP-blocking dei Worker Cloudflare (issue #28). Distinzione verificata: il tool funziona da CLI e dal server MCP locale (dist/index.js stdio, 37/25 confermati) — è solo il Worker remoto a essere bloccato. Chi usa l'MCP installato in locale ha il tool pieno.

  • v0.14.0 — nuovo tool camera-amendments + fix esposizione MCP di audizioni.

  • fix: audizioni mancava da server.ts allTools — era registrato nella CLI (tools/index.ts) ma mai esposto via MCP/Worker: Claude Desktop non aveva il tool audizioni. Emerso allineando il contatore tools: del worker durante la release (diceva 39 "aspirazionale" mentre allTools erano 38). Aggiunto al server MCP → 40 tool coerenti in CLI, registry, worker.

  • Nuovo tool camera-amendments (emendamenti Camera via scraping app HTML): colma il gap #19. Fonte: getProposteEmendative.aspx (non LOD). Pipeline tutta fetch (no browser; Camera senza WAF): scheda atto (actHtmlUrl) → link liste per sede → parsing righe con cheerio/slim (àncore tr.rigaArticoloNormale/tr.normale/idPropostaEmendativa/idPersona). Output per emendamento: sede, articolo, numero, primo firmatario (+person_id), emendamenti identici, link al testo; --count-only → conteggio per sede. Esito escluso di proposito (vuoto nelle liste cumulative; vive nella vista per-seduta → possibile Phase 2). cheerio/slim obbligatorio: cheerio full trascina encoding-sniffer→iconv-lite→require("buffer") che rompe il bundle ESM e gonfia il Worker a 2.8mb (con slim: 484kb). Guardia amendments su URI Camera → rimanda a camera-amendments. Test-sentinella che fissano i conteggi noti (AC 2696: referente 37, Assemblea 25; lista 62 righe) per intercettare cambi di markup. Validato anche su AC 2920 (479 emendamenti referente). MCP + CLI. 83/83 test. Da verificare: raggiungibilità di documenti.camera.it dal Worker Cloudflare (la CLI locale funziona).

  • Emendamenti Camera: assenti dal LOD ma raggiungibili via HTML (indagine agent-browser): verificato che l'app documenti.camera.it/apps/emendamenti/getProposteEmendative.aspx è attiva per leg 19 e pubblica per ogni atto/sede l'elenco completo con art., testo, firmatari ed esito (es. C.2696-A legge elettorale: 25 proposte in Assemblea). Traffico ispezionato (HAR): nessuna API JSON/XHR, solo HTML server-rendered → un tool camera-amendments sarebbe fattibile solo via scraping. URL corretto va preso dal pulsante EMENDAMENTI della scheda atto (URN a mano → lista vuota). Aggiornati issue #19 (commento con esito+strategia) e nota gestori Camera (advocacy: esporre come LOD/JSON). Memoria project_camera_amendments_absent aggiornata.

  • amendments: guardia esplicita su URI non-Senato (anti falso-vuoto): il tool interroga solo l'endpoint Senato; un --ddl-uri della Camera passava il FILTER senza match → CSV vuoto silenzioso, scambiabile per "nessun emendamento". Ma gli emendamenti Camera non esistono come entità nel LOD OCD (issue #19 chiusa, nota camera-assistenza-dati.md punto 1): l'unica traccia è testuale nelle descrizioni delle votazioni. Fix: amendments.ts ora lancia un errore esplicito se ddlUri non contiene dati.senato.it, spiegando che il vuoto non significa assenza e rimandando al tool votes. +1 test offline di regressione (/solo-Senato/). Verificato: URI Camera → errore; URI Senato (ddl/56260) → emendamenti regolari. 80/80 test.

  • Difesa bill_number fantasma su votazioni + segnalazione refuso fonte Senato: emerso dalla run news-driven del 2026-07-05 (docs/news-agent/2026-07-05_09-16.md, gap n.3). Il voto di fiducia Piano Casa 19-434-2 ha rdfs:label = "DDL n. 1994. Votazione questione di fiducia", ma nessun DDL 1994 esiste in leg.19: il provvedimento reale è S.1944 (ddl/60233, osr:numeroFase=1944) — refuso (trasposizione cifre) nel campo testo libero della fonte, non bug CLI. Il voto non ha osr:oggetto → unico appiglio al DDL è la label → il typo rompe l'aggancio per numero (aggancio affidabile = seduta/data: seduta 434 = 2026-07-01 = dataStatoDdl del 60233). Fix difensivo (senato-votes.ts + votes.ts, simmetria Camera): dopo il fallback di risoluzione bill_number→URI atto, se il numero citato non corrisponde ad alcun atto reale della legislatura, bill_number viene azzerato — così non si espone un identificativo non interrogabile (restituirebbe sempre zero); il testo grezzo resta in label/description. Verificato via CLI: 19-434-2 (typo) → bill_number vuoto; 19-432-3 (fiducia DDL 1933, numero valido) → bill_number=1933 + ddl_uri=ddl/60201 risolto. 79/79 test. Refuso annotato nella bozza gestori docs/note-gestori-lod/senato-webmaster.md (punto 1, esempio concreto). Regola di workflow acquisita: ogni problema dati a monte → prima verificare se già annotato nelle note webmaster.

2026-07-04

  • v0.13.1 — fix committee-members (Camera) — bug di completezza grave: emerso dalla run news-driven del 2026-07-04 (docs/news-agent/2026-07-04_19-02.md, gap n.1). Il ramo Camera interrogava solo ocd:ufficioParlamentare (cariche apicali) legato via ocd:rif_deputato, ignorando del tutto (a) i membri effettivi, modellati come ?deputato ocd:membro [ ocd:rif_organo ?organo ; dc:type "Titolare"/"Sostituto" ], e (b) i presidenti/segretari senatori delle bicamerali, legati via ocd:rif_senatore. Effetto: sulla Commissione d'inchiesta Covid (o19_4281) restituiva 3 persone invece di 16 e Giuseppe Conte (Titolare) non compariva; il presidente Marco Lisei (senatore) veniva scartato. Verificato il modello RDF empiricamente su bicamerale (Covid) e permanente (XIII Agricoltura o19_3513) prima di toccare la query. Fix: query a UNION dei due path (ocd:membro + ufficioParlamentare con rif_deputato OR rif_senatore); merge lato TS con priorità ruolo (Presidente>Vicepresidente>Segretario>Capogruppo>Titolare>Sostituto) per collassare a una riga per persona — ma solo con activeOnly=true; con activeOnly=false le righe storiche restano distinte. Fallback ruolo mancante → "Componente". Verificato: Covid 3→16 con Conte e Lisei presenti; Agricoltura 33 membri con breakdown ruoli coerente; --member-uri con URI senatore ora ritorna le cariche (prima nulla); Senato non toccato (0-1 → 24 membri invariati). Copertura completata lato Camera: i senatori membri semplici delle bicamerali vivono solo nel LOD Senato (osr:afferisce), fuori scope di una query per URI-organo Camera. 79/79 test.

2026-07-03

  • v0.13.0 — fix search multi-token + committee-sessions --count-only: due difetti emersi da uso reale su Claude Desktop (connector remoto → il fix arriva all'utente solo col redeploy del Worker). (1) search: il match era su sottostringa contigua del nome anagrafico (rdfs:label/CONCAT(fn,ln)), quindi "Elena Schlein" falliva perché in banca dati è "Elena Ethel Schlein" (il secondo nome spezza la sottostringa) e chiunque abbia un secondo nome era irraggiungibile con "nome cognome". Fix: tokenizzazione su spazi + AND di CONTAINS per token, sia Camera che Senato — l'ordine diventa indifferente (sostituisce anche il doppio-CONCAT del fix #20). Verificato: "Elena Schlein" ✅, "Schlein Elena" ✅, "Ignazio La Russa" (Senato) ✅, regressione "Giorgia Meloni" ✅. Resta scoperto "Elly Schlein": "Elly" è nome d'uso non presente nel LOD (limite fonte, non del codice). (2) committee-sessions: mancava countOnly (a differenza di votes/aic/speeches), così alla domanda "quante audizioni ha svolto la commissione X" il tool scaricava tutte le sedute complete (femminicidio → 76 KB/146 righe) — query pesante che va in timeout dal Worker (IP Cloudflare vs endpoint Camera) mentre da CLI locale passa. Aggiunto --count-only (schema+CLI, esposto via MCP/Worker automaticamente): COUNT leggero, una riga per ramo (chamber,count). Verificato: femminicidio Camera → 146. +1 test di regressione middle-name, 79/79.
  • Relazione + proposta "modellare audizioni e auditi" (Camera) — deliverable strategico di advocacy verso i gestori LOD (Camera/Synapta), in docs/proposta-audizioni-auditi/. Coordinati 3 esperti (SPARQL, ricerca web/Exa, campagne civiche). Tesi: l'audito è già scritto nel dc:title della ocd:discussione (nome+ruolo+ente) ma vive come stringa, mai entità → strutturarlo ha costo ~zero per il publisher. Proposta = 2 mosse di riuso OCD: (A) ripristinare dc:type="Audizione" come in leg.14; (B) popolare ocd:rif_persona (slot vivo su 45.808 discussioni, mai valorizzato sulle audizioni) con entità ocd:soggettoAudito tipizzata + link all'atto. Benchmark: USA "Truth in Testimony" (conflitto dentro il dato), UK oral evidence, UE Integrity Watch/LobbyFacts; hook IT = Registro rappresentanti di interessi Camera + A.C. 2336 (approvata Camera 29/01/2026, ora al Senato). File: 00-relazione.md (sintesi), 01-as-is-to-be-sparql.md, 02-benchmark-web.md, 03-perche-conta-civico.md. Correzione numerica verificata: il "6.636" citato prima era non-distinct — 6.636 = 2×3.318 coppie dibattito–discussione (raddoppio rdf:type); discussioni distinte = 3.311, ~935 nella forma pulita "Audizione di ". Corretto anche in memoria e tasks/todo.md.

2026-07-02

  • v0.12.0 — bill-signatories esteso a Camera (news-agent punto 5): era [SENATO] puro. Scoperto che il LOD Camera espone i firmatari come proprietà dirette dell'attoocd:primo_firmatario (1 deputato) e ocd:altro_firmatario (cofirmatari) — quindi nessun buco di dati, mancava solo il tool. Riscritto come dual-branch sul modello di bill-rapporteurs: ramo rilevato dall'URI, colonne unificate name, role, is_primary, person_uri, html_url. Nome deputato composto da foaf:firstName+foaf:surname (fallback rdfs:label senza suffisso legislatura). Parametro rinominato ddlUribillUri (--bill-uri), con --ddl-uri mantenuto come alias retrocompat. Verificato: C.2696 "Liberi di scegliere" → 28 firmatari (Colosimo prima firmataria + 27 cofirmatari); Senato ddl/25597 invariato. +1 test Camera, 78/78. Skill CLI+MCP allineate. Gap dalla run news-driven del 2026-07-02.
  • v0.11.3 — backoff più lungo sui retry SPARQL (flap Camera): l'endpoint dati.camera.it/sparql "flappa" (finestre di errore di pochi secondi); i 3 tentativi con backoff 500·attempt (~1,5s totali) cadevano spesso tutti nella stessa finestra → errore visibile all'utente. Ora DEFAULT_MAX_RETRIES=5 e backoff esponenziale con tetto a 2s (250→500→1000→2000ms, ~4s totali) in src/core/client.ts, così i buchi transitori vengono assorbiti senza esporre l'errore. Nessuna modifica ai tool. 77/77 test. NB: verificato che il fix decode v0.11.2 è live e corretto sul Worker (curl diretto a /mcp su ac18_2853dell'articolo 13-bis pulito); la grezza vista via l'integrazione claude.ai era connessione stale del client MCP, non il Worker.
  • v0.11.2 — decodeHtml gestisce anche le entità numeriche: il fix v0.11.1 era incompleto. L'helper src/core/decode-html.ts decodificava solo le entità con nome (&rsquo;, &agrave;), lasciando grezze quelle numeriche decimali (&#39;) ed esadecimali (&#x27;), frequenti nei titoli OCD della Camera (es. all&#39;articolo, nell&#39;area). Aggiunta la decodifica dei riferimenti numerici (con guardia sui codepoint non validi), prima dello strip dei tag. Beneficia tutti i tool che usano l'helper (bill-progress, bills, member-bills, audizioni, person-career). Nuovo file src/core/decode-html.test.ts (7 test: named, numeriche dec/hex, strip tag, ^^xsd, codepoint invalido, idempotenza). Verificato end-to-end su ac18_1397 (locale pulito vs Worker 0.11.1 ancora grezzo → motivo del redeploy). 77/77 test.
  • v0.11.1 — decode HTML nei titoli di bill-progress (Camera): il ramo Camera (cameraIterTimeline) restituiva title/status grezzi dall'endpoint OCD, con entità HTML non risolte (es. l&rsquo;assistenza, criminalit&agrave;), a differenza degli altri tool (bills, member-bills, ecc.) che già applicano decodeHtml. Applicato l'helper condiviso src/core/decode-html.ts ai due campi. Verificato che le entità sono single-encoded (&rsquo;/&agrave;), quindi il single-pass è corretto; fix validato sulla stringa esatta del report news-agent, indipendente dall'endpoint (che era intermittente). 70/70 test. Gap emerso dalla run news-driven del 2026-07-02 (docs/news-agent/2026-07-02_13-11.md, punto 5).
  • wiki LOD — freschezza e autorevolezza (docs/lod-wiki/freschezza-e-autorevolezza.md): sondato il segnale di freschezza sui due endpoint (2026-07-02). Camera espone dcterms:modified sui void:Dataset ma congelato a feb 2024 su dataset vivi fino a metà 2026; Senato non ha metadati di freschezza (le 926 triple dcterms:modified sono interni Virtuoso del 2012). Documentata la conseguenza (un "non trovato" su dato recente è ambiguo: non caricato / assente / non avvenuto), la gerarchia di autorevolezza sull'esito (resoconto stenografico → scheda iter → GU) e la distinzione critica "approvato da un ramo" ≠ "legge". Le bozze ai gestori LOD (docs/note-gestori-lod/, non versionata) hanno un punto freschezza aggiunto per ciascuna camera.
  • Nuovo tool audizioni (solo Camera) — estrae le audizioni delle commissioni via SPARQL, cosa finora ritenuta impossibile per la legislatura corrente. Scoperta chiave: le audizioni leg. 19 NON si trovano via dc:type (valorizzato solo leg. 14, 619 dibattiti storici) ma via il titolo della ocd:discussione collegata al dibattito (ocd:dibattito rif_leg → ocd:rif_discussionedc:title con "Audizione di …"): ~3.167 audizioni reali leg. 19 (dato vivo fino a giu 2026), con nome+ruolo dell'audito nel titolo, data, commissione (dal titolo del dibattito), atti collegati e link al bollettino. Filtro server-side con REGEX mirata per escludere i falsi positivi d'agenda ("…allo svolgimento di un'audizione"). Due modalità: leg. 19 (titolo discussione) e leg. 14 (dc:type storico, con data ricavata dal suffisso URI). Colonne: date, committee, title, bill_codes, bill_uris, bulletin_url, discussion_uri, dibattito_uri. CLI+MCP allineati, 70/70 test. Testato: Confindustria, prefetto, femminicidio (180 audizioni). Senato non coperto: osr:Procedura tipo="Audizioni" (1.833 leg.19, flag strutturato) esiste ma il nodo è orfano di data e commissione (unico link entrante = intervento/null, rotto) → segnalabile ai gestori LOD. Nessun link video/YouTube nel LOD (tutti i dc:relation → bollettino testuale). Contesto: primo step della pipeline per ricostruire il lavoro manuale di The Good Lobby (mappatura audizioni leg. 18); step 2-4 (estrazione audito, classificazione categoria, statistiche) previsti come skill+duckdb. Doc aggiornata: README, skill CLI+MCP, wiki LOD (camera/sedute-commissione.md Via A/Via B), tmp/audizioni.md v2, tasks/todo.md.
  • committees esteso a Camera (#6): il tool era [SENATO] puro (osr:Commissione). Aggiunto --chamber (camera/senato/both, default both). Camera: ocd:organo filtrato per legislatura (default 19, le istanze sono per-legislatura), con dc:type esposto come nuova colonna category (COMMISSIONE PERMANENTE/BICAMERALE D'INCHIESTA/GIUNTA/COMITATO/...) e session_count da ocd:seduta+rif_organo. Aggiunta category anche lato Senato (da osr:categoriaCommissione, prima non esposta). Nuova colonna chamber in testa, come già in committee-sessions/committee-members. Retrocompatibile: senza --chamber default both; Senato senza --legislature continua a mostrare tutte le 300 commissioni storiche. +1 test (Camera leg. 19), 70/70. Verificato manualmente: camera/senato/both, legislature diverse, errore chamber invalido, jsonl.
  • Fix sparql tool — falso positivo "Solo query SELECT supportate": bug reale (non un problema di query malformate lato utente), segnalato in tmp/audizioni.md §7.3. validateSelectQuery toglieva i "commenti" con query.replace(/#[^\n]*/g, ""), ma non distingueva un # di commento da un # dentro un IRI (es. <http://www.w3.org/2000/01/rdf-schema#>, terminazione standard di rdfs/rdf/owl/xsd). Su query scritte su una riga sola (tipico da CLI/agente), un PREFIX rdfs: <...#> prima di SELECT faceva sparire tutto il resto della riga — SELECT incluso — dalla stringa validata, con conseguente rifiuto di query perfettamente valide. Fix: nuovo stripComments che traccia se si è dentro <...> e ignora i # in quel caso. +1 test di regressione, 69/69. Le altre voci di §7 di tmp/audizioni.md (timeout su scansioni globali non vincolate a una classe; "nessun risultato" per audizione in Senato) sono limiti reali dell'endpoint pubblico/assenza di dato, non bug del progetto.
  • Fix committee-sessions (verifica delle modifiche del 01/07): due bug trovati testando manualmente tutte le varianti CLI. (1) toTitleCase (pensato per nomi di persona) applicato anche al nome della commissione rompeva i numerali romani e gli apostrofi ("XIV COMMISSIONE (POLITICHE DELL'UNIONE EUROPEA)""Xiv Commissione (politiche Dell'Unione Europea)"), disallineato da committee-members che lascia il label grezzo — rimosso il title-case sul campo committee. (2) --committee-uri Camera senza --committee-name lasciva committee vuoto (asimmetrico rispetto al Senato, che risolve sempre il label); aggiunto fetchCameraOrganoLabel per popolarlo anche in quel caso. Verificato: build, typecheck, 68/68 test, e ri-eseguiti tutti i casi manuali (DDL, committee-uri/committee-name, camera/senato/both, date range, paginazione, errori) senza regressioni.

2026-07-01

  • committee-sessions esteso: "segui una commissione" (Camera+Senato) — il tool era limitato all'iter di un DDL (--ddl-uri obbligatorio, solo Senato). Ora --ddl-uri è opzionale e si possono elencare tutte le sedute di una commissione via --committee-uri o --committee-name (ricerca fuzzy), filtrabili per --chamber (camera/senato/both), --legislature, --date-from/--date-to. Aggiunto supporto Camera (ocd:seduta + ocd:rif_organo + dc:date stringa AAAAMMGG + dc:relation→bollettino), oltre al Senato (osr:SedutaCommissione + osr:dataSeduta xsd:date + osr:tipoSeduta + conteggio interventi). Nuova colonna bulletin_url (Camera) e chamber/ddl_uri per uniformare. Caso d'uso guida: la Commissione parlamentare di inchiesta sul femminicidio (bicamerale) ha composizione esposta da entrambi i rami (24 senatori membri in commissione/4-223), ma sedute/interventi solo in OCD come o19_3941 (181 URI seduta / 157 date distinte fino a giu 2026) — nel Senato LOD 0 sedute per la XIX; ora committee-sessions list --committee-name femminicidio --chamber camera la rende visibile. (Correzione in corso di verifica: i "292" iniziali erano triple rif_bollettino, non sedute distinte.) Decisioni utente: scope Camera in fase 1 (sì); input sia --committee-uri che --committee-name (entrambi); rami separati nell'output (no merge). Bug trovato e fixato in corso: RDFS_LABEL typo (namespace senza label) → 0 risultati; e una edit maldestra aveva invertito l'ordine flatten/query. Wiki LOD: nuove pagine senato/sedute-commissione.md e camera/sedute-commissione.md (proprietà reali verificate + trappole: doppia osr:titoloBreve Senato → MIN(?tb)+GROUP BY; filtro legislatura Camera obbligatorio). CLI+MCP allineati. 68/68 test.
  • v0.9.1 — fix output disomogeneo Camera/Senato in search, people, committee-members: segnalato dall'utente via screenshot VisiData su search find --name Meloni — colonne first_name/last_name/gender/legislature_uri non uniformi tra righe. Causa: pass-through dei dati SPARQL grezzi, e le due camere modellano gli stessi concetti in formati diversi (Camera nomi MAIUSCOLI + gender esteso + legislature_uri come vero URI; Senato Capitalizzato + gender sigla M/F + legislatura come intero nudo). Nuovo helper condiviso src/core/normalize.ts (toTitleCase, normalizeGender) applicato ai tre tool che mescolano righe delle due camere nello stesso output (search.ts, people.ts, committee-members.ts --chamber both); per il Senato legislature_uri ora è un URI sintetico http://dati.senato.it/legislatura/{n}. Non toccati i tool a camera singola (senator.ts/senators.ts/deputy.ts/deputies.ts, decisione utente) né quelli multi-camera ma single-chamber-per-call (rank.ts, bill-rapporteurs.ts, dove non c'è mescolamento nella stessa colonna). +8 unit test su normalize.ts, 68/68.
  • v0.9.0 — bill_number copre gli Ordini del Giorno Camera + fix aic: extractBillNumber (src/core/bill-number.ts) esteso per chiudere il gap residuo Camera dell'#21 (in v0.8.0 solo 4/62 voti del DDL 2920 avevano bill_uri, quasi tutti ODG). Scoperto che gli ODG citano l'atto senza la parola "DDL", con la numerazione ufficiale 9/<atto>/<progressivo> in forma breve ("ODG 9/2920/46") ed estesa ("Ordine del giorno n. 9/1049/3 COGNOME (GRUPPO)"), tollerante al suffisso "E ABB" (e abbinate); aggiunta anche l'alternativa "PDL"/"proposta di legge" accanto a "DDL"/"disegno di legge". Risultato: votes list --bill-code 2920 ora 62/62 con bill_uri; su campione di 6000 voti leg. 19, 1591/1606 ODG risolti (99%) — i residui sono voci "Doc. VIII" (non un DDL, correttamente vuote) o testi unificati a 4 segmenti, lasciati vuoti per non rischiare un'estrazione sbagliata. +5 unit test. Tool aic: --keyword ora matcha a confini di parola (era CONTAINS a sottostringa: "CETA" includeva falsi positivi come "Acetamiprid"/"Cetara"; ora REGEX(...,"\\bKEYWORD\\b","i") su label/title/description, verificato 31→10 righe reali su "CETA" leg. 19); nuovo filtro --type su dc:type con fallback sul label (per la leg. 19 dc:type non distingue "a risposta immediata"/question time da "a risposta orale" — la differenza è solo testuale nel label); fix filtro data: dc:date è composto YYYYMMDD-YYYYMMDD (presentazione-modifica) sul 62% degli aic leg. 19 e rompeva il confronto lessicografico di --date-from/--date-to — ora si confronta solo la data di presentazione via SUBSTR, e l'output mostra entrambe le date quando modificato. Considerato ma scartato un endpoint HTML non documentato dietro documenti.camera.it/apps/votazioni/votazionitutte/ (reverse-engineered con agent-browser, stesso testo del dc:description ma più fragile) — documentato solo nel wiki (docs/lod-wiki/camera/votazioni-ricerca-html.md) come prova indipendente del gap, non integrato. 60/60 test.
  • v0.8.0 — link voto→DDL/atto risolto (#21): i tool senato-votes e votes espongono ora la colonna bill_number (numero provvedimento estratto dal testo: label Senato, dc:description Camera; es. 562-B, 2920-A) e popolano ddl_uri/bill_uri anche quando il legame diretto manca. Senato: le fiducie (prive di osr:oggetto) si risolvono dal numero via osr:fase="S.<num>" (univoco intra-legislatura; verificato 1933→ddl/60201). Camera: i voti senza rif_attoCamera si risolvono sul numero base (2920-A2920) verificando l'esistenza dell'atto via dc:identifier (una query per legislatura, mai URI fabbricati). Estrazione in nuovo core src/core/bill-number.ts (extractBillNumber/billBaseNumber, regex tollerante a DDL n./DDL.n./Disegno di legge n. e suffissi -B/-bis). Fallback in una sola query aggiuntiva (OR-chain, no VALUES batch su Virtuoso), solo per le righe non già risolte che citano un provvedimento. Wiki OKF aggiornata (senato/votazione-ddl-link.md); skill CLI+MCP allineate. +6 unit test, 56/56.
  • fix search — matching Senato nome+cognome (#20): search find --name "Ignazio La Russa" non trovava il senatore (mentre "La Russa" sì). Causa: searchSenato filtrava CONTAINS(?fn, q) || CONTAINS(?ln, q) confrontando la query separatamente con firstName e lastName → un nome completo non matcha nessuno dei due campi. La Camera non aveva il problema (match su rdfs:label intero). Fix: FILTER(CONTAINS(LCASE(CONCAT(?fn," ",?ln)), q) || CONTAINS(LCASE(CONCAT(?ln," ",?fn)), q)) (concat nei due ordini; CONCAT in FILTER funziona sul Virtuoso Senato, BIND no). Verificato: senatore/1275 per "Ignazio La Russa", senatore/29480 per "Roberto Marti"; nessuna regressione. 51/51 test.
  • avvio LOD wiki OKF (docs/lod-wiki/): bundle [OKF](Open Knowledge Format) come fonte di verità dello schema LOD Camera (OCD) / Senato (OSR), pattern "compile don't retrieve" (idea in docs/future-ideas.md). Scaffold (index.md, camera/, senato/, log.md) + prime concept page verificate: camera/assenti.md (emendamenti Camera assenti dal LOD, con query di verifica) e senato/trappole.md (quirk Virtuoso: 403 su curl, no BIND, legislatura integer, matching nomi). Il differenziale sono trappole e assenti verificati.
  • issue: #19 (emendamenti Camera: verificato assenti dal LOD OCD — nessuna classe emendamento tra i 47 tipi RDF; solo testo in dc:description votazioni) chiusa not planned. #20 (fix sopra). #21 (link voto→DDL parziale: fiducie senza osr:oggetto) risolta in v0.8.0 (vedi voce in cima).

2026-06-29

  • v0.7.1 — rifiniture discoverability: bill-rapporteurs ora registrato anche in src/tools/index.ts (tools/toolsByName), allineato a server.ts (era discoverabile solo via CLI/MCP, non da toolsByName); aggiunto people resolve alla lista capability di which/guide (+ termini iter Camera su bill-progress). MCP invariato a 38 tool. 51/51 test. Resta da fare: sync documentazione README/skills con le feature 0.6.0/0.7.0 (issue dedicata).
  • v0.7.0 — risoluzione URI→nome (issue #12). Nuovo tool people: risolve in batch una lista di URI persona misti Camera + Senato ai nomi, con una query per endpoint (FILTER(?s IN (...)); VALUES dà 400 sul Senato), dedup per URI, output uri/first_name/last_name/label/chamber/html_url; URI non risolti restano in output con label vuota. CLI people resolve --uris a,b,c; registrato in MCP (38 tool) e CLI. Inoltre group-members e senator-group-members ora includono deputy_name/senator_name direttamente (prima solo URI nudo → niente più lookup a valle). 51/51 test.
  • v0.6.0 — URL human-readable + iter Camera. bill-progress ora copre la Camera (timeline iter via ocd:rif_statoIter); colonna html_url (scheda istituzionale camera.it/senato.it) su tutti i tool persone e atti/DDL; rss_url (feed RSS iter dettagliato) sui tool DDL Senato; fix path leg19 hardcoded. Nuovo core src/core/html-url.ts (personHtmlUrl/actHtmlUrl/ddlRssUrl). 50/50 test. Issue aperte per i follow-up.
  • Audit html_url leggi + rss_url DDL: centralizzati helper actHtmlUrl (atto Camera + DDL Senato) e ddlRssUrl (feed RSS per-DDL, richiede legislatura) in src/core/html-url.ts. Colmati i gap: member-bills (html_url atto), bill-signatories/bill-rapporteurs (html_url persona, righe sono firmatari/relatori), amendments e senato-votes (ddl_html_url + rss_url, gestione multi-valore a | b), bill-progress Senato (rss_url per riga, schema allineato col ramo Camera). Refactor di bills/bill su actHtmlUrl (fix bug path leg19 hardcoded → ora leg{N}). aic lasciato com'è (URL portale AIC, schema diverso). Verifica agent-browser: did=60201→S.1933, rss 59372 ok. 50/50 test (+5 unit). Feed RSS tracciato in #13.
  • html_url per le persone (PRD docs/prd-human-readable-urls.md): i tool che restituiscono parlamentari espongono ora l'URL della scheda istituzionale accanto all'URI SPARQL. Helper puro src/core/html-url.ts (personHtmlUrl, zero query): deputato …/deputato.rdf/d{ID}_{LEG}camera.it/deputati/elenco/{LEG}-{ID} ; senatore …/senatore/{N}senato.it/…/scheda-attivita?did={N}. Cablato in deputies, senators, search, group-members, senator-group-members, rank, vote-detail, senato-vote-detail (colonna aggiuntiva, nessuna rottura di schema). Verifica end-to-end con agent-browser: 19-307302→Ascari, did=32578→Alfieri, did=32→Casellati. Differiti gov-members/person-career (URI persona.rdf, non risolve) e speeches (schema misto). Unit test pattern in html-url.test.ts; 45/45 test. Scoperti anche: feed RSS per-DDL del Senato con iter dettagliato (issue #13) e gemello S.1822 della legge elettorale.
  • bill-progress esteso alla Camera (parità tool, issue #11): con --uri <atto Camera> il tool restituisce ora la cronologia completa dell'iter (timeline di tutti gli stati attraversati), non solo lo stato corrente come per il Senato. Modello Camera scoperto via SPARQL: atto → ocd:rif_statoIter → {dc:date, dc:title=stato} (DISTINCT obbligatorio per il rdf:type duplicato). Routing per host dell'URI (dati.camera.it → timeline, dati.senato.it/default → lista Senato invariata); stesso schema colonne in entrambi i rami (colonne solo-Senato vuote nel ramo Camera, date normalizzate a YYYY-MM-DD, phase=C.<id>). CLI: bill-progress list --uri …; MCP auto-registrato via inputSchema.shape. Verificato su C.2822 (4 stati: Da assegnare → Assegnato → In corso di esame in Commissione → Concluso l'esame in Commissione) e C.302. Test parità schema in tools.test.ts; 41/41 verdi. Nota: il dataset Senato rispecchia lo stato corrente anche di atti Camera (1 riga), la Camera ne dà la storia (N righe).
  • v0.5.2Perf speeches (Camera, path legislatura): da ~6s a ~1.8s (3-4×). Gli interventi Camera non hanno ocd:rif_leg (la legislatura è solo nel pattern URI); il vecchio FILTER(STRSTARTS(...)) + ORDER BY DESC(?modified) impediva l'uso dell'indice sul soggetto e materializzava/ordinava tutti gli interventi (~126k distinti) prima del LIMIT. Riscritto: (1) STRSTARTS → range filter FILTER(?s >= <…in19_> && ?s < <…in19_z>) che Virtuoso esegue come scansione indicizzata all'indietro fermandosi al LIMIT; (2) ordinamento per DESC(?s) (ID intervento = ordine di creazione, proxy della cronologia reale; ods:modified era un timestamp di modifica del record); (3) subquery-first con GROUP BY ?s nell'interna — la tripla ?s a ocd:intervento è duplicata alla fonte (2× per intervento, COUNT(*)=253354 vs DISTINCT=126677), GROUP BY collassa i doppioni ed è ~2× più veloce di DISTINCT; (4) dedup per uri in TS (modified/relation multi-valore). Path per deputato già selettivo (invariato), Senato invariato. Test speeches da 7570ms a 1440ms; suite 40/40.
  • v0.5.1 — allineamento campi aic / sindacato-ispettivo: su sindacato-ispettivo rinominati tipotype, datadate, numeroidentifier, senatore_urisponsor_uri, legislaturalegislature; aggiunto filtro keyword (cerca in label/tipo/numero) e countOnly. Su aic: legislature_urilegislature (numero estratto dall'URI), date normalizzata da YYYYMMDD a YYYY-MM-DD. Fix TS (comparazione boolean/string su countOnly). countOnly Senato implementato con COUNT(DISTINCT ?s) diretto (Virtuoso non supporta subquery wrap).
  • Nuovo tool senato-groups [SENATO]: elenca i gruppi parlamentari del Senato con sigla (acronym) e numero di componenti distinti (members), filtrabile per legislatura. È il parallelo diretto di groups (Camera). Query SPARQL con COUNT(DISTINCT ?sen) per evitare il doppio conteggio dovuto a senatori con carica interna (Presidente, Tesoriere, ecc.) che hanno due adesioni attive. Output: uri, title, acronym, members, html_url. Testato: XIX leg. → 9 gruppi, FdI 63, PD-IDP 36, Lega 29. CLI: senato-groups list --legislature 19. MCP, skill e README aggiornati.

2026-06-28

  • v0.4.1 (patch, doc-only): case study docs/case-study-salario-giusto.md (fiducia DL "salario giusto", catena verificata 94+61+2+36=193); avvertenza trasversale sui voti di fiducia Senato (ddl_uri vuoto, legame solo nella label) in README + 2 skill; fix src/cli.ts (versione CLI letta da package.json invece dell'hardcoded 0.0.1). Versione allineata 0.4.1 nei 3 punti; tsc/build/worker puliti; 40/40 test.

  • Docs: nuovo case study docs/case-study-salario-giusto.md — inchiesta end-to-end sul voto di fiducia del 2026-06-24 per la conversione del DL "salario giusto" (S.1933 / ddl 60201). Catena verificata: legislatures/governments → bill-progress (keyword) → bill-signatories → bill-rapporteurs → senato-votes → senato-vote-detail (voto per gruppo + filtri assenti) → bill-text links/fetch. Tabella voto per gruppo con totali coerenti (94+61+2+36=193). Linkato dal README accanto al box Corte dei Conti.

  • Docs/avvertenza trasversale: i voti di fiducia al Senato hanno ddl_uri vuoto (il legame col DDL è solo nel testo della label), quindi senato-votes list --ddl-uri <uri> non li restituisce — va filtrato per data seduta (--date-from/--date-to). Le votazioni "finali" trovate per data possono inoltre appartenere a un atto diverso (testo unificato). Nota aggiunta in README "Note sui dati" + sezione Tips della skill CLI + blocco "Who voted how (Senato)" della skill MCP. Emersa verificando il case study (prima la tabella voti mescolava DDL 60201 e 60220 e citava un --confidence-vote inesistente su senato-votes).

  • v0.4.0: nuove capacità da verifica giornalistica. Relatori Senato in bill-rapporteurs (ora Camera+Senato), gruppo di voto in senato-vote-detail (voto per gruppo al Senato), timeline gruppi in person-career, group-members ordinato, sparql usabile senza il sotto-comando query, pulizia label gruppi Camera. README con esempio d'inchiesta e skill allineate. Dettagli nelle voci sotto.

  • Perf votes (Camera): da ~33s a <1s (45×). La query usava FILTER(?rif_leg = …) su una variabile OPTIONAL + ORDER BY DESC(?date) con 17 OPTIONAL: Virtuoso materializzava tutte le votazioni di tutte le legislature prima di ordinare/limitare (timeout del test a 30s). Riscritta subquery-first: l'interna seleziona/filtra/ordina/limita i soli ?s (legislatura come triple vincolante; GROUP BY ?s + MAX(?date) perché alcune votazioni hanno dc:date multiplo), l'esterna aggancia i 17 OPTIONAL solo alle righe risultanti; dedup per uri in TS contro la moltiplicazione delle OPTIONAL multi-valore. Tutti i filtri (keyword/approved/confidence/date/bill-code) spostati nell'interna. Test votes ora verde in 0.5s; suite 40/40.

  • Secondo round dalla stessa verifica giornalistica (gap media/bassa) + esempio d'inchiesta. (1) group-members: aggiunto ORDER BY ?deputy_uri ?start_date → timeline cambi-gruppo in ordine cronologico (prima FDI compariva prima di IV-CR). (2) person-career: nuovo ramo UNION (ocd:siComponeDi) → ora include le righe kind=gruppo (appartenenza ai gruppi con date, label pulita via cleanGroupLabel), output riordinato per categoria+data. (3) sparql: shim argv che inserisce il sotto-comando query quando manca → sparql --endpoint senato --query … funziona (prima mostrava l'help/"Unknown command"); sparql query … resta valido, sparql --help resta help. (4) Help sotto-comandi: NON era un bugbills list --help ecc. mostrano i flag; il falso positivo del report nasceva da zsh (niente word-splitting su $c). Allineamento CLI↔MCP+skill: bill-rapporteurs mancava da entrambe le reference skill (gap pre-esistente) → aggiunto; aggiornate descrizioni di person-career/senato-vote-detail/sparql nelle 2 skill; check automatico: 36/36 tool presenti in CLI+MCP reference. README: nuovo box "Un'inchiesta passo per passo: la riforma della Corte dei Conti" (catena verificata bill-progress→bill-signatories→bill-rapporteurs→senato-votes→senato-vote-detail con voto per gruppo), tabelle aggiornate. tsc --noEmit pulito; 39/40 test (l'unico KO è timeout di rete su votes, non legato alle modifiche).

  • Due gap "alta priorità" emersi da una verifica giornalistica (3 notizie 2025: riforma Corte dei Conti S.1457, legge IA S.1146-B, cambio gruppo Gruppioni). (1) Relatori Senato in bill-rapporteurs: prima solo-Camera; ora il ramo è rilevato dall'URI (dati.senato.it → query osr:relatore, blank node con rdfs:label/tipoRelatore/organo/dataNomina/senatore, mappati sulle stesse colonne Camera). Es. S.1146-B → Rosa/Minasi (commissione 09/07) + Rosa/Minasi/Mazzella f.f. (assemblea 30/07); Camera invariata (ac19_2316). (2) Gruppo in senato-vote-detail: nuova colonna group_label = gruppo del senatore alla data del voto. Query gruppi separata (logica senator-group-members con asOf = data seduta) → mappa JS unita ai risultati: le 5 query di conteggio restano invariate (verificato 19-376-2 → 93/51/5 intatti, group_label 100%). Abilita il voto-per-gruppo al Senato (es. Corte dei Conti: FdI 49 + Lega 25 + FI 12 fav, PD 27 + M5S 21 contr, IV 5 ast). Analisi in tmp/analisi-notizie-2025-verifica-cli.md.

  • Fix label gruppi Camera: rdfs:label/dc:title arrivano troncati alla fonte come NOME (ACRONIMO) (DD.MM.YYYY (parentesi sulla data di inizio mai chiusa, sistematico su tutti i gruppi). Nuovo helper src/core/group-label.ts (cleanGroupLabel) che rimuove la coda (data → resta NOME (ACRONIMO). Applicato a group-members (group_label) e groups (label + title); acronimo estratto dalla label grezza prima della pulizia, dcterms:alternative resta la fonte primaria. Verificato su gr4133 (FDI) e tutti i gruppi leg19.

  • Fix README: due domande "Quante interrogazioni..." (xylella, deputato) corrette in "Quali", perché il comando aic list elenca (non conta). Per il conteggio resta --format jsonl | wc -l.

  • v0.3.3 (doc-only): skill CLI/MCP corrette (flag kebab, formato default CSV, parametri voteUri/ddlUri, valori --rank-by completi); README sezione "Uso con un agente AI (skill)" (install cross-agent npx skills, consiglio CLI+MCP in coppia con la skill). Republish per allineare la README su npm.

  • v0.3.2: fix groups acronimo — ora dal campo dedicato dcterms:alternative (prima puntava a ocd:acronimo inesistente, ripiegando su parsing regex della label). README "Note sui dati" corretta. Spostato l'esempio member-bills da "Riferimento" a "Esempi pratici".

  • v0.3.1: repo reso pubblico e pubblicato su npm come @aborruso/italianparliament-mcp (CLI + MCP installabili con npm i -g). Aggiunta sezione "Installazione" al README (CLI npm / MCP remoto-locale / da sorgente), prepublishOnly in package.json, e step npm in RELEASING.md.

  • Affordance di scoperta CLI stile opensdmx: comando guide (flusso tipico di orchestrazione: scoperta→URI→dettaglio, catene, opzioni trasversali); comando which <capacità> (mappa capacità→comando, es. which "testo ddl" → bill-text); errori enum con valori validi via wrapper runTool che valida l'input Zod prima di execute (prima la CLI chiamava execute senza validare: gli enum errati scivolavano nella query). Solo-CLI, non tool MCP.

  • Nuovo tool person-career [CAMERA] (gap US-22 + US-31): carriera unificata di una persona via il hub persona.rdf/p{id}, che collega tutti i mandati da deputato (per legislatura, join rif_mandatoCamera) e gli incarichi di governo (rif_membroGoverno), più owl:sameAs Wikidata. Risolve doppio incarico parlamento+governo e carriera multi-legislatura. Es. Meloni: 5 mandati (leg15-19) + PM + ministra; Wikidata Q451791. NB: Camera↔Senato non collegabile dai dati (namespace separati, nessun ID condiviso, nessun owl:sameAs sul Senato) → match solo per nome+data nascita (non implementato). Tot tool 36.

  • Nuovo tool committee-sessions [SENATO] (gap US-28): sedute di commissione in cui un DDL è stato trattato, con data, commissione (osr:titoloBreve), tipo seduta e n. interventi. Catena: osr:Intervento → osr:oggetto → osr:relativoA → ddl + osr:Intervento → osr:seduta. Es. ddl/56260 → 32 sedute (Industria/agricoltura 8, Politiche UE 6, Affari Costituzionali 5...). Tot tool 35.

  • Roadmap gap-analysis #1 e #2. (1) --count-only su bills/aic/votes/senato-votes: restituisce solo il totale (colonna count) per confronti senza scaricare le righe. bills/aic via wrap COUNT del SELECT (veloce su dataset piccoli); votes/senato-votes via count minimale sui soli pattern vincolanti (il wrap con gli OPTIONAL su decine di migliaia di righe va in timeout). Verificato: votes leg19 18587 (0.6s), senato-votes leg19 8030, fiducia 70. (2) Nuovo tool group-rank [CAMERA]: classifica i gruppi per AIC/DDL (via gruppo del primo firmatario) con conteggio, membri e media per membro (count_per_member). Es. AIC leg19: AVS 4195/12 = 349.6 per membro vs FDI 2714/108 = 25.1. Tot tool 34. #3 (assenteismo) rinviato: il dato grezzo "Non ha votato" è fuorviante (governo/ministri in testa, nessun tipo "missione").

  • Fix due bug emersi dalla gap-analysis. (1) aic --keyword era accettato dalla CLI ma ignorato (né schema né query lo gestivano): ora filtra davvero su label/titolo/description (COALESCE per i campi opzionali). Verificato: --keyword xylella → 28/28 risultati pertinenti. (2) amendments non aveva filtro per DDL: aggiunto --ddl-uri via osr:oggetto → osr:relativoA (niente BIND, non supportato da Virtuoso) + colonna ddl_uri. Verificato: ddl/56260 → 119 emendamenti.

  • Nuovi tool senato-votes + senato-vote-detail [SENATO]: chiudono il gap n.1 della ripartenza (votazioni Senato). senato-votes lista le votazioni d'Assemblea (osr:Votazione) con esito, contatori, tipo, data seduta (via osr:seduta/osr:dataSeduta, filtro ^^xsd:date) e DDL collegato (osr:oggetto → osr:relativoA); dedup TS per voti su DDL unificati. senato-vote-detail dà il voto del singolo senatore via osr:favorevole/contrario/astenuto/presenteNonVotante/inCongedoMissione (una query per categoria: Virtuoso non supporta VALUES/BIND); nome via foaf. Gruppo non incluso (membership temporale) → incrociare con senator-group-members. Tot tool MCP: 33.

  • Gap analysis (ripartenza) in docs/gap-analysis-2026-06-28/: re-test mirato dei 16 gap residui di aprile + user story openparlamento, metodo a due agenti (giornalista+developer) con chiamate reali. Copertura framework 33 US: da 17/10/6 a 23 OK / 8 PARZIALE / 2 KO. Gap n.1 ora: votazioni Senato (dato presente, osr:Votazione ~63.911 voti leg19, ma nessun tool dedicato — solo via sparql). Bug emersi: aic --keyword accettato ma ignorato; amendments senza filtro DDL. Vecchia analisi marcata archiviata. README: aggiunto openparlamento come riferimento per le esigenze giornalistiche.

  • Nuovo tool bill-text [CAMERA+SENATO]: link diretti al testo di un DDL, con format (html/pdf/urn) e auth (none/browser) pensati per un orchestratore. Il testo integrale NON è nei dati SPARQL: il Senato espone solo l'URN osr:testoPresentato. Registrato in MCP+CLI (bill-text links). Aggiunto anche member-bills al server MCP (era solo in CLI).

  • Nuovo comando CLI locale bill-text fetch (Senato): scarica e converte il testo in markdown. www.senato.it è dietro AWS WAF (fetch diretto → HTTP 202): apre un browser reale via agent-browser, estrae il cookie aws-waf-token + UA, scarica il PDF (singolo testo via scraping del tab "Testi ed emendamenti", oppure --fascicolo per il dossier completo) e converte con lit (liteparse). Opzioni --which, --all, --fascicolo, --out. Pulizia soft-hyphen (&shy;) per ricongiungere le parole sillabate. Token effimero → preso fresco a ogni run.

  • Pattern URL Senato verificati: testo presentato …/Ddliter/testi/{N}_testi.htm, fascicolo …/FascicoloSchedeDDL/ebook/{N}.pdf, singoli testi …/service/PDF/PDFServer/BGT/{idDoc}.pdf; scheda-ddl?did={N} con {N} = numero in dati.senato.it/ddl/{N} (non idDdl). Camera: nessun WAF, pagine fetchabili.

  • Skill italian-parliament-mcp (v1.1) e italian-parliament-cli aggiornate con bill-text. Test unitario per parseTextList. Totale tool MCP: 31.

2026-04-15

  • member-bills [CAMERA+SENATO]: nuovo tool unificato per DDL come primo firmatario. Camera: filtra ocd:primo_firmatario + tipo atto. Senato: pattern INIZ-DDL-{ddl_id}-{id} + osr:primoFirmatario="1" (REGEX [^0-9]{id}$ perché Virtuoso non supporta BIND/STRAFTER nidificato).
  • bill [CAMERA]: aggiunto campo cosignatories con tutti i cofirmatari (dc:contributor) separati da |.
  • deputy [CAMERA]: aggiunti committees, election_list, election_date, election_validated, birth_date, birth_place. Nascita via persona.rdf/p{id}bio:Birth.
  • senator [SENATO]: fix birth_city (via bio:birthbio:placerdfs:label); fix CLI --legislature non passato all'execute (mandato sempre leg 19); fix filtro osr:legislatura con FILTER(STR(...)) (era xsd:integer, non stringa).

2026-04-14

  • Nuovo tool bill-rapporteurs [CAMERA]: relatori di un DDL per commissione con nome, tipo (Relatore/Relatore f.f.), commissione, data inizio esame e URI deputato. Path SPARQL: atto→rif_dibattito→dibattito→rif_discussione→discussione→rif_relatore. Totale tool: 26.
  • Aggiunto campi mandato/elezione a deputy (Camera) e senator (Senato): election_region, election_district (solo Camera), election_type, mandate_start, mandate_end. Camera via ocd:rif_mandatoCamera → ocd:rif_elezione; Senato via osr:mandato filtrato per legislatura.
  • Aggiunto campo description al tool votes (Camera): dc:description contiene il testo reale della votazione (es. "Votazione Fiducia A.C. 2807-A"), mentre label e title sono spesso vuoti. Utile soprattutto con --confidence-vote true.
  • Aggiunto parametro --bill-code al tool votes (Camera): filtra votazioni collegate a un DDL per numero atto (es. --bill-code 2807). Restituisce fiducia, voto finale e ordini del giorno. Combina con --legislature per evitare falsi positivi tra legislature.

2026-04-13

  • Gap analysis giornalista vs MCP: 33 user stories testate con tool reali. 13 OK, 14 PARZIALI, 6 KO. Documenti in docs/gap-analysis-2026-04-13/.
  • Fix group-members: campo start_date conteneva inizio+fine concatenati (20221018-20240916), end_date sempre vuoto. Ora split su - con date separate.
  • Aggiunto filtro region al tool deputies: filtra per circoscrizione/regione con CONTAINS case-insensitive su election_label. Testato: sicilia, lombardia. Circoscrizioni estero: AFRICA, AMERICA, EUROPA.
  • Aggiunto campo description al tool aic (Camera): contiene il testo/oggetto dell'interrogazione, interpellanza o mozione. Il Senato (sindacato-ispettivo) non espone l'oggetto nell'endpoint SPARQL.
  • Tool rank esteso al Senato: 2 nuove dimensioni sindacato-ispettivo e ddl-senato. Aggiunto campo chamber nell'output. Top Senato atti ispettivi: Camusso 786, Rojc 753. Con --order asc: Casellati, Bernini, Durigon con 1 atto. Totale dimensioni rank: 7 (5 Camera + 2 Senato).
  • Nuovo tool committee-members [CAMERA+SENATO]: composizione commissioni con ruoli. Camera: ocd:ufficioParlamentare con carica (PRESIDENTE, VICEPRESIDENTE, SEGRETARIO, CAPOGRUPPO, COMPONENTE). Senato: osr:Afferenza + osr:afferisce con carica (Presidente, Membro, ecc.). Parametro chamber: camera|senato|both, filtrabile per commissione, parlamentare, legislatura, activeOnly. Totale tool: 25.
  • Fix sindacato-ispettivo: campo presentatore e senatore_uri ora sempre popolati (anche senza filtro senatore). Riscrittura query con GROUP BY + MIN() per evitare duplicati da join multi-firmatario. Subquery non supportata da Virtuoso Senato, usato MIN su tutti i campi.
  • Sprint 1 (6 quick wins):
    • keyword su bills, bill-progress, votes: ricerca full-text nei titoli DDL e votazioni. Cerca in title+label (OR).
    • confidenceVote su votes: filtra votazioni di fiducia. 10 fiducie leg19 testate.
    • dateFrom/dateTo su sessions: filtra sedute per data.
    • deputyUri su group-members: storia cambi gruppo di un singolo deputato. Testato con Enrico Costa (Azione→FI).
    • order: asc|desc su rank: ordinamento inverso per trovare i meno attivi. asc mostra ministri con 4 speeches.
    • initiative su bills: filtra per iniziativa (Popolare/Governo/Parlamentare/Regioni). Tutti e 4 testati.

2026-04-12 (aggiornamento 8)

  • Nuovo helper src/core/decode-html.ts: rimuove ^^xsd:type, decodifica entità HTML (&quot;, &rsquo;, &agrave;, ecc.), rimuove tag HTML (<em> ecc.).
  • Applicato a bills su campi label e title. Titoli ora leggibili.
  • Deploy worker aggiornato.

2026-04-12 (aggiornamento 7)

  • Fix vote-detail: aggiunto campo deputy_name (nome leggibile) alla query. Usato rdfs:label con URI completo (Camera rifiuta prefisso rdfs:). Suffisso legislatura rimosso con stripLegLabel.
  • Deploy worker aggiornato.

2026-04-12 (aggiornamento 6)

  • Fix dedup bills: blank node multipli su primo_firmatario governativi → aggiunto FILTER(!isBlank(?sponsor_uri)). DDL governativi ora hanno sponsor_uri vuoto (corretto).
  • Fix dedup sindacato-ispettivo: join su osr:iniziativa produceva N righe per N firmatari → rimosso join quando nessun filtro senatore. senatore_uri/presentatore vuoti nella lista generale, presenti solo quando si filtra per senatore.
  • Deploy worker aggiornato.

2026-04-12 (aggiornamento 5)

  • Fix bug rank: senatori comparivano nella classifica Camera perché legFilter non vincolava ?person a ocd:deputato. Aggiunto il tipo esplicito in entrambi i rami (con e senza legislatura).
  • Deploy worker aggiornato.

2026-04-12 (aggiornamento 4)

  • Aggiunto filtro dateFrom/dateTo (YYYY-MM-DD) a 4 tool: votes, aic, bills, sindacato-ispettivo.
  • Aggiunto ORDER BY DESC(?date) su tutti e 4 i tool — le più recenti escono per prime.
  • Fix: Senato usa xsd:date tipizzato → FILTER con "data"^^xsd:date (non plain string).
  • CLI: aggiunto --date-from/--date-to nei 4 subcommand corrispondenti.
  • Testato: votes leg19 dal 1 apr 2026 → seduta 641 del 9 apr; sindacato-ispettivo dal 1 apr → interrogazione 3-02528 del 10 apr.

2026-04-12 (aggiornamento 3)

  • Nuovo tool sindacato-ispettivo [SENATO]: equivalente Senato degli AIC Camera (interrogazioni, interpellanze, mozioni, risoluzioni).
  • Filtrabile per legislatura, senatore URI, tipo atto.
  • Fix: BIND non supportato su Virtuoso Senato → sostituito con FILTER(?senatore_uri = <URI>).
  • Fix: legislatura come triple pattern diretto (?s osr:legislatura 19) non OPTIONAL+FILTER.

2026-04-12 (aggiornamento 2)

  • Nuovo tool rank [CAMERA]: classifica deputati per attività parlamentare in una sola chiamata.
  • 5 dimensioni: aic-primo-firmatario, aic-cofirmatario, bills-primo-firmatario, bills-cofirmatario, speeches.
  • Evita N batch da 1000 righe: GROUP BY lato SPARQL, risposta diretta top-N.
  • Fix trovato: prefisso PREFIX rdfs: causa errore silenzioso su endpoint Camera — usare URI completo <http://www.w3.org/2000/01/rdf-schema#label>.

2026-04-12 (aggiornamento)

  • Nuovo tool senator-group-members [SENATO]: membri attivi di un gruppo parlamentare del Senato.
  • Default asOf = oggi; opzionale --as-of YYYY-MM-DD, --legislature, --group-uri.
  • Schema Senato differisce dalla Camera: ocd:aderisce (direzione inversa), label gruppo via blank node osr:Denominazione con storico nomi filtrato per data.

2026-04-12

  • Fase 6 batch 3: +4 tool Camera (speeches, aic, vote-detail, group-members). Totale tool: 17/24.
  • Fix vote-detail: la query R usava ocd:voto come proprietà ma non esiste — il valore del voto è in dc:type. Scoperto con query esplorativa sulle proprietà reali del triplo.
  • speeches: dati disponibili solo per legislatura 17, molti campi opzionali vuoti (limite dati upstream Camera).
  • README aggiornato con riferimento al repo upstream italyParlR.
  • README riscritto in italiano per giornalisti parlamentari (tabella comandi, esempi pratici, note sui limiti dati).
  • Verifica completa dei 17 tool, 3 fix applicati:
    • groups: acronimo ora estratto dalla label con regex (era campo vuoto nell'endpoint).
    • roles: proprietà corretta ocd:ruolo invece di dc:type (VICEPRESIDENTE, SEGRETARIO, ecc. ora popolati).
    • sessions: filtro STRSTARTS per escludere bollettini (BF_*), ora solo sedute formali con numero progressivo.
    • speeches: riscritto completamente. rif_leg/dc:date/rif_seduta non esistono; la legislatura è nell'URI (in19_), filtro con STRSTARTS. Ora funziona per tutte le legislature (>1M interventi totali). Colonne: uri, label, deputy_uri, document_url, modified.
    • deputy/senator/bill show: riscritti con query mirate invece di triple RDF grezze. Output ora con campi leggibili (first_name, last_name, gender, photo, ecc.). Deputy usa foaf:firstName/foaf:surname/foaf:gender.
    • governments: riscritta query, ora interroga direttamente ocd:governo con dc:date. Ordinamento cronologico DESC (Meloni→Draghi→Conte II→...). Aggiunto campo start_date.
  • Fase 6 batch 4: +5 tool (gov-members, committees, bill-progress, bill-signatories, amendments). Totale tool: 22.
    • gov-members: membri del governo con nome persona, ruolo (MINISTRO, SOTTOSEGRETARIO, ecc.), date, motivo termine. Cerca per nome.
    • committees: 279 commissioni Senato (permanenti, speciali, d'inchiesta).
    • bill-progress: iter DDL Senato con stato, date, iniziativa, natura. Dati freschi (DDL del 10 aprile 2026).
    • bill-signatories: firmatari DDL Senato con primo firmatario/cofirmatari e link senatore.
    • amendments: 53K emendamenti Senato leg 19, con link al testo.
  • documents Camera (ocd:documento): 0 istanze nell'endpoint. Nessun tipo documento alternativo trovato. Senato ha osr:Documento (48K), implementato.
  • documents Senato: atti del governo, atti UE, relazioni Corte dei Conti, risoluzioni commissioni. Dati freschi (9 aprile 2026).
  • committees: migliorato con filtro legislatura via SedutaCommissione. Con --legislature 19 mostra 12 commissioni attive con conteggio sedute (Affari Costituzionali: 681 sedute). Senza filtro mostra catalogo storico (279).

2026-04-11

  • Commit root: 26 file (Fase 0-4).

  • Fase 6 batch 1: +4 tool Camera (legislatures, groups, sessions, governments). Query portate da italyParlR (clone in tmp/). Type check pulito. Smoke test CLI reali verdi su tutti e 4. Totale tool: 9/24.

  • Fase 6 batch 2: +4 tool dettaglio (deputy, senator, bill property/value; roles Camera con filtri deputy/group/legislature). Totale tool: 13/24.

  • Refactor obbligato: makeHandler in src/index.ts passato da <I> generico a any perché a 13 tool la generic instantiation combinata Zod × MCP SDK × helper mandava tsc in OOM (FATAL heap limit) anche con --max-old-space-size=4096. Con handler non generico il type check torna pulito.

  • Nota su deputy: rimossa la z.refine() sullo schema (produceva ZodEffects senza .shape, incompatibile con registerTool MCP). Validazione ora dentro execute().

  • Repo creato. Fase 0 completata.

  • Struttura: src/core/, src/tools/, entrypoint previsti src/cli.ts, src/index.ts, src/worker.ts.

  • Stack: TypeScript + citty + axios + zod + @modelcontextprotocol/sdk.

  • Scripts package.json clonati da ckan-mcp-server: build (esbuild cli+index), build:worker, build:dxt, pack:skill, deploy.

  • Paradigma vincolante: CLI agent-friendly (non-interattiva, flag+stdin, errori actionable, output machine-readable, no emoji).

  • Naming CLI: italianparliament <resource> <verb>.

  • Fase 1 COMPLETATA: src/core/ con endpoints.ts, prefixes.ts (OCD/OSR copiati da italyParlR), types.ts (SparqlResults, Row), flatten.ts (flattenBindings), client.ts (cdQuery/snQuery con axios, retry, SparqlError), format.ts (toCsv/toJsonl), index.ts barrel.

  • Endpoint: Camera https://dati.camera.it/sparql, Senato http://dati.senato.it/sparql. Timeout 60s, retry 3.

  • Test vitest: 10/10 passati (flatten + format). Type check pulito.

  • Smoke test reale contro Camera SPARQL (SELECT ?s ?label WHERE Person LIMIT 3) → OK end-to-end.

  • Fase 2 COMPLETATA: 5 tool MVP in src/tools/ (deputies, senators, bills, votes, search) + types.ts (Tool interface con name, description, inputSchema Zod, examples, execute) + index.ts (barrel + toolsByName).

  • Ogni tool: schema Zod per input, lista columns per output stabile, execute() che chiama cdQuery/snQuery → flattenBindings → riga rinominata secondo naming coerente (uri invece di s, legislature_uri invece di rif_leg, in_favour/against/abstentions per i voti, ecc.).

  • Fix importanti scoperti durante smoke test:

    • Endpoint Senato aggiornato a HTTPS (il vecchio http://dati.senato.it/sparql redirige e il redirect mangia le {} della query).
    • Formato response: format=application/json + Accept: application/json (il vecchio application/sparql-results+json restituisce 406 su Senato).
    • User-Agent custom obbligatorio su Senato (default axios → 403 CloudFront).
  • Smoke test reale verde su Camera (deputies leg 19), Senato (senators leg 19), search (Camera "meloni"). 10/10 vitest ancora verdi, type check pulito.

  • Fase 3 COMPLETATA: src/cli.ts con citty. Pattern italianparliament <resource> <verb>: deputies list, senators list, bills list, votes list, search find. --help per ogni subcommand include sezione Examples con invocazioni copiabili. --format csv|jsonl su tutti (default csv, machine-readable, no colori/emoji). Errori fail-fast: --limit abc → "Invalid --limit value. Expected a positive integer", argomento required mancante → citty mostra help + errore. Catch top-level gestisce SparqlError con endpoint/status.

  • Test reali verdi: deputies, senators, search, jsonl output, error handling.

  • Fase 4 COMPLETATA: src/index.ts — MCP stdio server con @modelcontextprotocol/sdk. McpServer + StdioServerTransport. Registrazione esplicita (non loop) dei 5 tool via registerTool(name, {description, inputSchema: tool.inputSchema.shape}, handler). Helper makeHandler generic per catturare SparqlError/Error e restituire isError: true. Output in formato JSONL via toJsonl.

  • Nota TS2589: il loop generico su tools[] generava "Type instantiation is excessively deep". Fix: chiamate registerTool esplicite per ogni tool.

  • Test stdio end-to-end: initialize + tools/list → 5 tool con JSON Schema completo da Zod (properties, required, default, enum). tools/call search {name:"meloni",chamber:"camera",limit:2} → 2 righe JSONL da Camera SPARQL reale.

  • Prossimo: Fase 5 — Cloudflare Worker (src/worker.ts con MCP HTTP server clonando pattern da ckan-mcp-server).