# Comandi e accessi — come sono stati raccolti i materiali

Registro operativo aggiornato al 1 agosto 2026. Serve a rifare gli stessi passaggi fra un mese senza ricominciare da capo, e soprattutto a ricordare **dove l'accesso si rompe e come si aggira**.

## Le tre barriere incontrate, e cosa funziona

| Fonte | Cosa succede con `curl` | Cosa funziona |
|---|---|---|
| `www.senato.it` | HTTP 202 con challenge JavaScript («we need to verify that you're not a robot») | browser vero: `agent-browser --headed` con `DISPLAY` verso X410 |
| `www.camera.it`, `documenti.camera.it` | risponde normalmente | `curl` con user-agent da browser |
| `www.garanteprivacy.it` | risponde normalmente | `curl`, oppure Brave Search API per trovare il doc-web giusto |

## La CLI italianparliament

Il repertorio dei dati parlamentari: <https://github.com/ondata/italianparliament-mcp>

Commissioni della Camera, per ricavare l'URI dell'organo:

```bash
italianparliament committees list --chamber camera --legislature 19 | grep -iE "affari costituzionali|giustizia"
# I  Affari costituzionali → http://dati.camera.it/ocd/organo.rdf/o19_3501
# II Giustizia            → http://dati.camera.it/ocd/organo.rdf/o19_3502
```

Componenti di una commissione, con ruolo e date:

```bash
italianparliament committee-members list \
  --committee-uri http://dati.camera.it/ocd/organo.rdf/o19_3501 \
  --chamber camera --legislature 19 --limit 200 --format csv > camera-I-membri.csv
```

Gruppi di appartenenza e anagrafica con circoscrizione di elezione:

```bash
italianparliament group-members list --legislature 19 --limit 900 --format csv > camera-gruppi.csv
italianparliament deputies list      --legislature 19 --limit 900 --format csv > camera-deputati.csv
```

Atti del Governo al Senato:

```bash
italianparliament documents list --legislature 19 --type 'Atto del Governo' --limit 200
```

Sedute di commissione. L'endpoint SPARQL del Senato ha restituito un `403` intorno alle 18 del 1 agosto e ha ripreso a rispondere poco dopo, senza che cambiasse nulla nella richiesta: **è un errore transitorio, la reazione giusta è riprovare**, non cercare un'altra strada. Verificato funzionante alle 19:30 dello stesso giorno, con le sedute aggiornate al 30 luglio.

```bash
italianparliament committee-sessions list --committee-uri http://dati.senato.it/commissione/0-1 \
  --chamber senato --date-from 2026-07-25 --date-to 2026-08-01
```

Limiti noti del LOD, verificati: **il relatore di un Atto del Governo non c'è**, né alla Camera né al Senato — va letto dal resoconto o dalla scheda dell'atto. E **le email dei deputati non esistono**: la Camera non le pubblica né nel LOD né nella scheda personale (verificato anche aprendo la scheda con un browser). Il Senato invece pubblica `nome.cognome@senato.it` nella scheda del senatore.

## Il join con DuckDB

L'elenco `destinatari-camera-ag418.csv` nasce così. Nota `all_varchar=true`: senza, le colonne di date vuote fanno fallire la conversione a `INT64`.

```bash
duckdb -c "
COPY (
WITH m AS (
  SELECT member_uri, member_name, role, 'I Affari costituzionali' AS commissione
  FROM read_csv_auto('camera-I-membri.csv', header=true, all_varchar=true) WHERE end_date IS NULL
  UNION ALL
  SELECT member_uri, member_name, role, 'II Giustizia'
  FROM read_csv_auto('camera-II-membri.csv', header=true, all_varchar=true) WHERE end_date IS NULL
),
g AS (SELECT deputy_uri, group_label,
             row_number() OVER (PARTITION BY deputy_uri ORDER BY start_date DESC) rn
      FROM read_csv_auto('camera-gruppi.csv', header=true, all_varchar=true) WHERE end_date IS NULL),
d AS (SELECT uri, election_label, html_url
      FROM read_csv_auto('camera-deputati.csv', header=true, all_varchar=true))
SELECT m.commissione, m.member_name AS deputato, m.role AS ruolo_commissione,
       g.group_label AS gruppo,
       regexp_extract(d.election_label, 'circoscrizione ([A-ZÀ-Ù0-9 ''\-]+?) nelle', 1) AS circoscrizione,
       d.html_url AS scheda
FROM m LEFT JOIN g ON g.deputy_uri = m.member_uri AND g.rn = 1
       LEFT JOIN d ON d.uri = m.member_uri
) TO 'destinatari-camera-ag418.csv' (HEADER, DELIMITER ',');"
```

## Il Senato dietro il WAF: browser headed via X410

Playbook completo del display: `/mnt/c/varie/msi/wsl/display-x410.md`. In sintesi, in rete NAT `DISPLAY` deve puntare al gateway Windows, non a `127.0.0.1`.

```bash
GW=$(ip route | grep default | awk '{print $3; exit}')
for ip in 127.0.0.1 "$GW"; do
  timeout 2 bash -c "echo > /dev/tcp/$ip/6000" 2>/dev/null && { export DISPLAY=$ip:0.0; break; }
done
echo "$DISPLAY"   # es. 172.19.0.1:0.0

agent-browser close --all
agent-browser open "https://www.senato.it/leggi-e-documenti/attivita-non-legislative/procedure?documentoId=56098" \
  --headed --profile ~/.agent-browser-profiles/senato \
  --args "--enable-unsafe-swiftshader,--ignore-gpu-blocklist"
agent-browser read > pagina.txt
```

`close --all` va sempre per primo: se il daemon è già vivo, `--headed` e `--profile` vengono ignorati e riparte headless.

Per estrarre i link dei resoconti dalla pagina di trattazione:

```bash
agent-browser eval "Array.from(document.querySelectorAll('a')) \
  .filter(a => /Resoconto/i.test(a.textContent)) \
  .map(a => a.textContent.trim() + ' || ' + a.href).join('\n')"
```

I resoconti hanno una struttura a parti: la pagina base mostra solo l'indice, il testo sta in `&part=doc_dc` e gli allegati — dove si trova il **testo del parere approvato** — in `&part=doc_dc-allegato_a`.

```
https://www.senato.it/show-doc?leg=19&tipodoc=SommComm&id=1518018&idoggetto=0                        # indice
https://www.senato.it/show-doc?leg=19&tipodoc=SommComm&id=1518018&idoggetto=0&part=doc_dc            # resoconto
https://www.senato.it/show-doc?leg=19&tipodoc=SommComm&id=1518018&idoggetto=0&part=doc_dc-allegato_a # parere
```

## La Camera con curl

La scheda di un Atto del Governo alla Camera ha lo stesso numero del Senato e contiene assegnazioni, termini, relatori, sedute, audizioni e **memorie depositate**:

```bash
curl -sL -A "Mozilla/5.0" "https://www.camera.it/leg19/682?atto=418&idLegislatura=19&tab=&tipoAtto=atto" -o cam418.html
```

Estrazione dei link ai PDF delle memorie:

```bash
python3 -c "
import re, html
t = open('cam418.html', encoding='utf-8', errors='ignore').read()
for m in re.finditer(r'<a[^>]*href=\"([^\"]+)\"[^>]*>(.*?)</a>', t, re.S):
    txt = html.unescape(re.sub(r'<[^>]+>', '', m.group(2))).strip()
    if 'Memoria' in txt: print(txt, '||', m.group(1))
"
```

## Estrazione del testo dai PDF

```bash
liteparse parse memoria.pdf --format markdown --image-mode off -o memoria.md
```

In alternativa `kreuzberg extract --content-format markdown <file>`, e `markitdown` come ripiego.

## Ricerca web

Brave, con la chiave già in ambiente. È servita per trovare il numero corretto del provvedimento del Garante, che sul sito non è indicizzato in modo evidente:

```bash
curl -s -H "X-Subscription-Token: $BRAVE_API_KEY" -H "Accept: application/json" \
  "https://api.search.brave.com/res/v1/web/search?q=<query urlencoded>&count=10&country=it&search_lang=it" \
  | python3 -c "import sys,json;[print('-',r['title'],'\n ',r['url']) for r in json.load(sys.stdin)['web']['results']]"
```

Exa via MCP funziona ma restituisce risposte molto grandi, che vanno lette da file invece che a schermo.

## NotebookLM da riga di comando

Il notebook di lavoro è «Protezione dei dati personali in ambito penale: Direttiva 2016/680», id `2d0fadcc-63ba-4ae4-ba16-cb6dca965dda`.

```bash
export DISPLAY=172.19.0.1:0.0        # il login apre un browser: senza X410 fallisce
notebooklm login                     # va lanciato in un terminale interattivo: chiede INVIO
notebooklm auth check --test --json  # deve dare status ok E token_fetch true

notebooklm list --json
notebooklm source list -n <notebook-id> --json
notebooklm source add ./file.pdf -n <notebook-id> --json
notebooklm source delete <source-id> -n <notebook-id> -y
notebooklm ask "domanda" -n <notebook-id> -s <source-id> -s <source-id>
```

Tre cose imparate a spese nostre:

- se `auth check` dà cookie presenti ma `token_fetch: false`, **il problema è la versione della CLI, non il login**: con la 0.3.4 l'autenticazione falliva comunque, `uv tool upgrade notebooklm-py` alla 0.7.3 l'ha risolta senza rifare nulla;
- gli URL con query string lunghe di `documenti.camera.it` **non vengono letti da NotebookLM**: i bollettini vanno scaricati e caricati come file di testo. I tentativi falliti restano nel notebook come fonti in stato `error` e vanno cancellati a mano;
- il notebook ha un **limite di 50 fonti** legato al piano: raggiunto quello, ogni nuovo caricamento fallisce con «Invalid argument».

## Alert di calendario

Le scadenze del procedimento — parere della Camera, adozione definitiva entro il 10 ottobre 2026 — si annotano con `gwsb` sul calendario gmail.com, evento a orario, fuso `Europe/Rome`.
