Repository navigation
fix(validate,catalog): URL con query-string e pattern_probes min_bytes - #435
Merged
Merged
Conversation
Gabrymi93
added a commit
that referenced
this pull request
Oct 26, 2026
Cherry-pick da #435: serve a far girare lint/mypy/pytest su questa PR. La CI installa duckdb 2.0.0 (il pin è >=1.5.3) i cui stubs tipizzano duckdb.sql() come Optional: mypy falliva su _validate_base.py con 'union-attr', e con mypy rosso pytest viene saltato. Il venv locale ha duckdb 1.5.6, quindi il problema non è riproducibile in locale: verificato sul log CI (Downloading duckdb-2.0.0).
Gabrymi93
added a commit
that referenced
this pull request
Oct 26, 2026
#429) * feat(catalog): referenced-item drift — verifica upstream vs inventario Per ogni dataset con upstream refs, controlla che gli item referenziati esistano ancora nell'inventario della fonte. Se ne sparisce uno → trigger referenced_item_missing nel verdict (label REFERENCED_MISSING). Match per kind: - dataflow: item_id == flow ID - ckan_dataset: item_name == dataset name (gestisce placeholder {year}) - ckan_resource: distribution_url contiene resource ID - url: prefix match sul path statico (rimuove {year} e query) - script_type: keyword matching (il tipo e' un ID logico, non un suffisso diretto — es. 'ricorsi-pervenuti-class' matcha 'ricorsi-pervenuti-per-classificazione' via keywords) Verificato su GA: 11/11 upstream matchati, 0 missing, verdict STABLE. +11 test (match per kind, placeholder, missing, mixed). * fix(catalog): drift su URL a query-string — match discriminante e stato unknown Il matcher `url` troncava l'URL a '?' e faceva prefix match sul path: per le fonti che servono il dato via query-string il prefisso statico e' tutto il portale. Due conseguenze, entrambe misurate sull'inventario reale di MEF analisi_stat (fonte di dichiarazioni-italia): - falso positivo: 5 dei 7 dataset dichiarati REFERENCED_MISSING pur essendo sani (inventario homepage, URL del dataset su .../public/index.php); - falso negativo: con l'inventario dell'albero, qualunque export parte da index.php → 'base in url' sempre vero, il drift non viene mai rilevato. Ora: - match sull'URL completo con i placeholder {year} come wildcard, con fallback sul solo path quando la query e' un parametro di serializzazione (ECB ?format=csvdata) → la query non e' piu' ne' identita' per forza ne' rumore; - esito a tre stati: matched / missing / unknown. 'unknown' = l'inventario non copre l'area del ref (o il pattern matcha tutto l'inventario, quindi non discrimina): niente falso allarme, niente silenzio. - area coperta (→ missing) se il prefisso statico del ref esiste nell'inventario oppure se il ref condivide con alcuni item un prefisso che entra nel path ma non con tutti. Verifiche (inventari reali dei repo del workspace, old vs new): giustizia-amministrativa 11/0 → 11/0/0 eurostat 34/0 → 34/0/0 oecd-benchmark 11/0 → 11/0/0 appalti-pubblici 12/0 → 12/0/0 sistema-bancario 4/0 → 4/0/0 (ECB: fix del falso missing da query) open-mur 3/10 → 3/10/0 (invariato) indice-pa 0/9 → 0/8/1 (drift preservato, 1 ref non coperto → unknown) opencivitas 3/1 → 1/0/3 (2 'matched' vacui + 1 missing non concludibile) MEF: inventario homepage 2 matched / 0 missing / 5 unknown (era 5 falsi missing); albero con vista rimossa → missing rilevato; albero completo → 7/0/0 5 test nuovi su query-string, serializzazione, match vacuo e area non coperta. * fix(ci): guard su duckdb.sql() None (stubs duckdb 2.0) Cherry-pick da #435: serve a far girare lint/mypy/pytest su questa PR. La CI installa duckdb 2.0.0 (il pin è >=1.5.3) i cui stubs tipizzano duckdb.sql() come Optional: mypy falliva su _validate_base.py con 'union-attr', e con mypy rosso pytest viene saltato. Il venv locale ha duckdb 1.5.6, quindi il problema non è riproducibile in locale: verificato sul log CI (Downloading duckdb-2.0.0). * fix(catalog): script_type a tre stati — un match parziale non e' un match Review #429 (finding Medium). Il matcher a keyword (`>=2 token`) era troppo largo: un token generico bastava a restare 'matched' anche dopo la rimozione dell'intera famiglia referenziata. Prova avversariale sull'inventario reale openga (436 item): ref ricorsi-pervenuti-class → 155 match (ricorsi 248, pervenuti 93, class 124) rimossi tutti i package 'pervenuti' → restava 'matched' (via ricorsi+class) ref ricorsi-tipo-decisione → 93 match rimossi tutti i 'tipo-di-decisione' → restava 'matched' (via ricorsi+tipo) Ora `_script_type_state` ha tre esiti, coerenti con il matcher URL: - matched: un item contiene TUTTI i token del ref; - missing: nessun item li contiene tutti ma almeno un token compare ancora (la famiglia c'e', la variante no) — o ref mono-token assente (la famiglia stessa e' sparita, caso motivante 'package *-sentenze'); - unknown: ref multi-token di cui nessun token compare nell'inventario. Verifiche: GA normale 11/11 matched (invariato); rimozione di 'pervenuti', 'tipo-di-decisione', 'sentenze', 'sentenze-brevi', 'ricorsi-definiti' → tutti missing (prima 2/11 restavano matched). Nessuna regressione sugli altri repo: sistema-bancario 4/4, eurostat 34/34, oecd 11/11, appalti 12/12, open-mur 3 matched/10 missing, indice-pa 8/1, opencivitas 1/3 — identici a prima. Review #429 (finding Medium) — test di integrazione mancanti: nuovo TestReferencedItemsIntegration in test_catalog_build.py (4 test): campo referenced_items nell'e2e, caso missing con trigger/label/score/next_action, label non riscritta quando non era STABLE, coesistenza drift + new_content. 3 test unit nuovi sul matcher script_type (multi-token, famiglia rimossa, vocabolario assente).
Due difetti emersi osservando il portale MEF analisi_stat (fonte del repo dichiarazioni-italia) con il catalog engine per-repo. 1. validate_tabular_group troncava l'URL a '?' prima del probe: per le fonti che servono il dato via query-string (index.php?tree=...&export=3) validava la pagina HTML del portale, non il file → reachable/readiness sbagliati. Ora l'URL intero va al probe; il path senza query serve solo a indovinare il formato. 2. probe_pattern considerava disponibile qualsiasi 2xx: MEF risponde 200 con 0 byte per l'anno non ancora pubblicato, quindi new_content_available scattava a ogni run. Nuovo campo opzionale min_bytes per entry (default 0, retro-compatibile) + validazione in [tool.observatory]. Test: 4 nuovi (query-string nel validate, min_bytes su probe e pass-through), aggiornati i fake che mockavano l'URL troncato. Verificato sulla fonte reale: vista 2025 → reachable, colonne lette; 2025 IRAP (0 byte) → unavailable con min_bytes=200, 2024 → available. Closes #433
Gabrymi93
force-pushed
the
fix/query-string-validate-pattern-probes
branch
from
October 26, 2026 22:40
59e4cc7 to
fbf8553
Compare
Review #435 (finding Low). Da quando l'URL intero (query inclusa) va al probe, la query finisce nei parquet tracciati: data/pipeline/validated.parquet qui e out/so/validated.parquet nei repo dati. Oggi i parametri osservati sono format/d/t, nessuna credenziale — ma una fonte con URL firmati o tokenizzati verrebbe persistita in chiaro. Nota esplicita nel runbook: la redazione dei parametri sensibili va fatta prima di onboardare una fonte di quel tipo.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Sintesi
Due difetti piccoli ma silenziosi nel catalog engine, emersi osservando il portale MEF
analisi_stat(fonte del repodichiarazioni-italia) e ricondotti a un caso minimo riproducibile. Entrambi falsano il segnale senza errori: il catalogo di una fonte risulta "validato" o "nuovo contenuto" quando non lo è.Contesto collegato
Closes #433
Cosa cambia
docs/runbook.md)1.
validatetroncava l'URL a?scripts/collectors/_validate_base.pyfacevaurl = url_raw.split("?")[0]prima del probe. Per le fonti che servono il dato via query-string —index.php?tree=2025CIVATOT0201&export=3— validava la pagina HTML del portale invece del file:is_csvfalso, probe sul target sbagliato,reachable/readinesssbagliati.Ora l'URL intero va al probe; il path senza query serve solo a indovinare il formato.
2.
pattern_probes: 200 vuoto contava come disponibileavailable = 200 <= status < 300. MEF risponde 200 con 0 byte per l'anno non ancora pubblicato, quindinew_content_availablesarebbe scattato a ogni build, per sempre: il segnale "nuovo contenuto" diventava rumore.Nuovo campo opzionale
min_bytesper entry (default0, retro-compatibile), conbelow_min_bytes: truenel risultato per il debug e validazione in[tool.observatory](intero ≥ 0, errore leggibile).Documentazione
pattern_probesnon era documentato da nessuna parte: aggiunta una sottosezione indocs/runbook.md(catalog per-repo, sintassi,min_bytes) con l'esempio MEF.Checklist
Se modifichi script o MCP
pytest tests/passaruff check .passamypy scripts/ so_mcp/passaVerifica
Test aggiunti (8):
validatecon URL a query-string: l'URL integro arriva al probe e resta inresult["url"]index.php?...&export=3): è il campoformata decidere il probeprobe_pattern: 2xx con 0 byte +min_bytes=200→available=False,below_min_bytes=Trueprobe_pattern: 2xx sopra soglia →available=True, nessunbelow_min_bytesrun_pattern_probes:min_bytesdell'entry arriva al probe (e valore non valido → 0)config:pattern_probes.min_bytesrifiutato se non intero ≥ 0 (2 test: negativo, stringa)Aggiornati i fake che mockavano l'URL troncato (
MEF_URL_NO_QUERY), che documentavano il comportamento precedente.Verifica manuale sulla fonte reale:
validatesuindex.php?tree=2025CIVATOT0201&export=3reachable=True,result["url"]con query integra, probe sul file reale (200). Lo sniff dello schema resta debole (num_columns=1, delim,) per il preambolo di 9 righe: limite dichiarato, rimandato a #434probe_patternsutree=2025DIRTOT020001(0 byte)available=True❌ · conmin_bytes=200→available=False✅probe_patternsutree=2024DIRTOT020001(dato presente)available=True, 5381 byte ✅Note
csv.Sniffersceglie,invece di;→ 1 colonna). Serve un hint esplicito (skip_lines/delimiter) nell'extraction mode di html collector: extraction mode per portali ad albero con export a query-string (MEF analisi_stat) #434 — dettagli e misure nel commento su html collector: extraction mode per portali ad albero con export a query-string (MEF analisi_stat) #434.min_bytes=0mantiene il comportamento attuale per le fonti già in produzione: nessun cambio per i cataloghi esistenti.content_lengthè ora valorizzato anche sotto soglia (2xx) invece diNone: cambio semantico minore, lo schema lo consente.docs/runbook.md, redazione dei parametri sensibili tracciata in validate: redigere i parametri sensibili delle query-string negli artifact tracciati #436.