diff --git a/LOG.md b/LOG.md index 0dccca4..dd1602a 100644 --- a/LOG.md +++ b/LOG.md @@ -4,6 +4,12 @@ ## 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. diff --git a/manifest.json b/manifest.json index 70e8d56..e83e79e 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "dxt_version": "0.1", "name": "italianparliament-mcp", - "version": "0.26.2", + "version": "0.27.0", "display_name": "Italian Parliament MCP", "description": "Query Italian Parliament open data (Camera dei Deputati + Senato della Repubblica)", "long_description": "MCP server for the Italian Parliament SPARQL endpoints (dati.camera.it, dati.senato.it). Tools for deputies, senators, bills, votes, speeches, parliamentary groups, government members, oversight acts, committees, rankings, and direct links to bill texts. Designed for journalists, researchers and parliamentary analysts.", diff --git a/package.json b/package.json index 567e1b0..87ce8bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@aborruso/italianparliament-mcp", - "version": "0.26.2", + "version": "0.27.0", "description": "CLI and MCP server for querying Italian Parliament SPARQL endpoints (Camera dei Deputati and Senato della Repubblica)", "main": "dist/index.js", "type": "module", diff --git a/skills/italian-parliament-cli/references/commands.md b/skills/italian-parliament-cli/references/commands.md index 8d2a98c..c8c7364 100644 --- a/skills/italian-parliament-cli/references/commands.md +++ b/skills/italian-parliament-cli/references/commands.md @@ -316,12 +316,14 @@ italianparliament sessions list --legislature 19 ``` ### `committees list` (Camera + Senato) -Commissioni parlamentari con categoria (permanente/speciale/inchiesta/comitato) e numero di sedute. +Commissioni e giunte parlamentari con categoria (permanente/speciale/inchiesta/comitato/giunta) e numero di sedute. ```bash italianparliament committees list --legislature 19 italianparliament committees list --chamber camera --legislature 19 italianparliament committees list --chamber senato +italianparliament committees list --chamber senato --legislature 19 --format jsonl | jq 'select(.category=="Giunte")' ``` +Al Senato le **giunte** (Regolamento, Elezioni e immunità parlamentari, …) hanno `session_count` **0**: nel LOD non risultano loro sedute, pur essendo organi attivi. Alcuni organi con molte sedute hanno invece i campi di testo **vuoti**: sono URI che la fonte referenzia senza descriverli, e il nome non è ricavabile dal dato — non dedurlo dall'URI. --- diff --git a/src/core/client.ts b/src/core/client.ts index fe43631..ef25069 100644 --- a/src/core/client.ts +++ b/src/core/client.ts @@ -61,7 +61,7 @@ async function sparqlRequest( method: "GET", headers: { Accept: "application/json", - "User-Agent": "italianparliament-mcp/0.26.2", + "User-Agent": "italianparliament-mcp/0.27.0", }, signal: controller.signal, }); diff --git a/src/server.ts b/src/server.ts index a67f8fd..1705c7c 100644 --- a/src/server.ts +++ b/src/server.ts @@ -97,7 +97,7 @@ function makeHandler(tool: Tool) { export function createServer(): McpServer { return new McpServer({ name: "italianparliament-mcp", - version: "0.26.2", + version: "0.27.0", }); } diff --git a/src/worker.ts b/src/worker.ts index 4d2d960..93b494b 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -10,7 +10,7 @@ export default { return new Response( JSON.stringify({ name: "italianparliament-mcp", - version: "0.26.2", + version: "0.27.0", description: "MCP server for querying Italian Parliament SPARQL endpoints (Camera + Senato)", mcp_endpoint: "/mcp",