difensore-civico-ti-scrivo: new Category I, geo-blocking (PA sites unreachable from abroad via IP-geolocation restrictions), with the supporting norms inreferences/normativa.md(CAD art. 9 and art. 53, D.Lgs. 36/2006 art. 8); trigger description updatedtypst-cards: nuovo principio "Gender in the copy" - ruoli e titoli prendono il genere di chi li ricopre ("una Presidente", non "un Presidente"); per i gruppi va valutata la doppia forma ("bambine e bambini") e la scelta va lasciata all'utente. Aggiunto il controllo alla Phase 4
-
openalex: nuove sezioni su ricerca per campo, sintassi disearch=e semantica, tutte misurate contro un articolo bersaglio. La ricerca per titolo perde i lavori che parlano di un paese senza nominarlo nel titolo:title_and_abstract.searchunito aauthorships.institutions.country_codedà insiemi piccoli abbastanza da leggersi per intero, che è quello che serve quando il ranking non aiuta. Aggiunto il test di richiamo su un lavoro noto (cercalo per DOI nei risultati e guarda a che posto sta) come passo della validazione: è quello che ha fatto emergere un articolo rimasto al diciannovesimo posto su 468 e mai letto. La sintassifilter=campo.search:è deprecata da OpenAlex ma non ha sostituto per lo scoping su un campo, e resta l'unica strada: annotato. Misurato anche il resto:corpus=allporta il 2024 da 10,8 a 28,0 milioni di lavori, un filtro.searchsi paga come ricerca (0,001) e non come filtro (0,0001),meta.cost_usddice il costo reale di ogni chiamata, e la semantica rifiutafrom_publication_dateelencando nell'errore i filtri che accetta. Due punti in cui la documentazione non regge alla prova: dàper_pagemassimo 100 mentre l'API accetta 200 e lo dichiara nel proprio messaggio d'errore, e dice che la semantica accetta quasi tutti i filtri mentre la whitelist vera sta nell'errore. Corretta la ricetta 1, che passavatitle.searchcome parametro a sé (non valido) e senzaapi_key -
cup-cig: new referencereferences/openbdap-cli.mdforopenbdap-pp-cli, a CLI that covers source #2 only. It collapses the MOP branch —dossier <CUP>works out which regional partitions to query and returns project, payments, tenders with CIG, bidders, cost plan and owners in one call, where the reference documents five hand-built OData requests.mopandcercareturn both ids per dataset (package for the CSV dump, XML resource for OData), which retires the two-ids trap, andcolonnemaps readable names to the mangled filter ids -
cup-cig:allineais a prerequisite forcup,cig,dossierandoperetoo — they fetch live rows but read the local catalogue to know which dataset to query. Against an empty archive all four answertrovati: 0and exit 0, told apart from a real miss only by anotafield, which is the one misreading this skill keeps warning about. Note for the record:--homedoes not relocate the archive, only--dbdoes, so a fresh-home test silently keeps reading the populated one -
cup-cig: verified the one claim that justifies the tool — the API answers"__count" : "0"on a filter that matches rows, whilecontaandopere --solo-conteggioboth answer 13489 for ANAS, and raw pagination counts 13489 -
cup-cig: three traps survive the CLI and are documented as such.righedefaults to--limite 50and returns exactly 50 rows with no warning where the true count is 347, so it must be paired withcontaor--tutte --limite 0;scaricapasses the dump through as latin-1 (àas the single byte0xE0) while the OData path is clean UTF-8;cigwithout--regionecosts 24s against 2s, measured with--no-cache— not serial requests, which already run six at a time, but their number:garandpgafor 21 regions, two calls each -
cup-cig: the decimals trap no longer holds. The reference claimed a comma inprgagainst a dot ingarandsal; today it is a dot in every family, on the same/datastore/dump/endpoint the reference documents — 866.216 numeric fields with a dot and none with a comma over 66.633 rows of the nationalprg, and the six Sicilian regional files agree. Checked with plaincurltoo, so it is not the CLI normalising anything. A staleREPLACE(col, ',', '.')is a no-op, which is the bad kind of wrong: it hides theCASTit was protecting. What does vary, and was not documented, is the quoting: numeric fields are quoted in the nationalprgand bare in the regional files -
cup-cig:dossierreturns six of the seven MOP families — nolocalizzazione— so it answers everything about a work except where it is.mopdoes map the seventh,--famiglia localizzazioneworks; it is the flag's help text that lists six values, and--regionehides the row because Localizzazione is national. Howdossierpicks the region is not what the output suggests: it matches a region name insideDescrizione Titolare, soREGIONE AUTONOMA VALLE D'AOSTAresolves andCOMUNE DI PALERMOdoes not —regionecomes backnulland it falls back to querying every partition. It never reads the Localizzazione dataset, so anullregion says nothing about where the work is -
cup-cig:compatibilitywas 520 characters against the 500 the spec allows, and had been for a while. The macOScoreutilshint moved into the body next to the othercig-fetch.shrequirements, where it is more useful anyway: the field is now 473. No other skill in the repo is over its frontmatter limits -
cup-cig: the portal'sRICERCA PER CUPform returns an Excel with a Dettaglio Indicatori sheet that has no counterpart in the open data. Of the 29 datasets matchingindicatorin the catalogue, all are budget Note Integrative, none is MOP — so that sheet, and the 300-CUP cap that comes with it, is the one reason left to use the browser -
cup-cig: the CLI author shipped fixes for everything the report raised, same day, and the reference is realigned to them. Verified on a build of that day's HEAD:dossierhas alocalizzazionesection withCodice ISTAT Comunealready concatenated and takes the region fromCodice Regioneinstead of guessing it from the owner's name — a CUP of the Comune di Palermo now resolves to Sicilia and the call drops from 34,7s to 9,4s, because the guess used to fail and it queried all 21 partitions;righecarriesmeta.notawith the real total when a result touches the limit (troncato a --limite 50 di 347 righe) andresultsstays an array;archivio_vuoto: trueseparates an unsynced archive from a genuine miss, and--homenow moves the archive;scaricaconverts to UTF-8, byte-identical toiconv -f ISO-8859-1, with--rawfor the portal's own bytes;cigreads the schema once per family and runs twelve at a time, 18,8s, and 4,1s with--rate-limit 0 -
cup-cig: on the one suggestion turned down, the author is right and the reasoning is worth keeping: an exit code other than zero reads as a failed command, and an unsynced archive is not a failure — the command ran and answered. Hence the boolean field rather than an exit status -
cup-cig: the reference now states the date it was checked against, because a build older than that differs on four points and every one of them fails silently. The public library copy was still being published when this was written
cup-cig: tested the OpenCoesione API with the credentials now available in the environment. They are plain HTTP Basic and buy throughput, not access: wrong ones give401, which is what tells a credentials problem apart from a quota one. Anonymous cannot sustain one request per second — 24 of 70 got through, first429on the twelfth; authenticated the same pace ran 70 out of 70. A burst cap sits well below the per-minute quota either way, so requests must be paced rather than batchedcup-cig: the reference had the throttling failure mode backwards. It said a throttled reply is a JSON body with adetailkey and not an HTTP error, hence the advice to branch on.detail. It is429, anonymous and authenticated alike, so the rule is to branch on the HTTP status before parsing:.count // 0on a throttled body reads as "project not found" and hands back a false negative on a project that is there. A genuine miss is200withcount: 0. No script does OpenCoesione lookups, so nothing but prose needed fixingcup-cig: new trap, the mirror image of one already documented — filter values are slugs and an unknown one answers500, not an empty result (territorio=siciliafails,territorio=sicilia-regionereturns 118.846 projects). A wrong filter name fails silently and looks like data; a wrong filter value fails loudly. Slugs come from thetemi,nature,territoriandprogrammiendpointscup-cig: confirmed unchanged —limitandper_pageare ignored (limit=3downloads 26 MB wherepage_size=3downloads 278 KB),search=andq=are accepted and ignored and return the whole catalogue's countcup-cig: the router line inSKILL.mdsaid "REST API without authentication". True, but it is the only line loaded by default, and as written it gave an agent no reason to open the reference looking for credentials it does not know exist. Reworded, and the optional env vars added tocompatibility. RealignedPLAN.md, which still carried the12 req/min/60 req/minfigures dropped from the references in v0.13
typst-cards: newreferences/charts.md— cards can now embed real data charts, drawn with gribouille (grammar of graphics for Typst, same author as the full-bleed pattern already credited). Verified on gribouille 0.6.0 + typst 0.14.2typst-cards: the sub reference pins only what is card-specific and delegates the API surface to the upstreamllms.txt/.llms.md, regenerated on every release — a copy of the ~50-geom list here would rot within a releasetypst-cards:plot(width:)rejects ratios, so the usable width is arithmetic the writer must do — 6.4in margined, 6.05in full-bleed on a 1:1 card. The plot is one rigid unbreakable block, so it overflows onto an extra page in the margined pattern and clips silently in the full-bleed onetypst-cards: brand mapping istheme-minimal(ink:, paper:), withpaper: rgb(0,0,0,0)when the chart sits on a colour band; fonts are inherited from the deck's#set text. Default tick labels read small at 1080px — raise them withtext: element-text(size: 13pt)typst-cards: newreferences/design-md.md— the skill now reads a DESIGN.md as the source of the theme instead of guessing a palette. Two version numbers not to confuse: the format is atversion: alpha, the@google/design.mdtooling has its own release linetypst-cards: the two conversions that bite when a DESIGN.md meets a card.fontSizepx→pt is ×0.75, but a literal translation makes a screen-sized scale look timid on 1080px — keep the ratios between levels, raise the absolute values. And CSSlineHeightis the whole line box while Typstleadingis the gap added on top, so(L−1)collides: subtract ~0.75 (verified, 87px pitch against the 88px prescribed bylineHeight: 1.1)typst-cards:{token.references}must be resolved before reachingtheme.typ, and named CSS colours must be converted to hex —rgb()rejects both loudly.oklch()is native in typst 0.14.2. Fonts are the quiet failure: a DESIGN.md names brand families that are typically absent, and Typst falls back with a warning, not an errortypst-cards:componentsand elevation largely do not map onto a static card (no hover state, shadows read poorly at 1080px) — stated as a boundary so the mapping stays honest instead of inventing one for text inputstypst-cards: three silent failure modes found by compiling, not by reading docs —geom-textdraws nothing whenlabelmaps to a number instead of a string, horizontal bars needcoord-flip()rather than swapped aesthetics (swapping renders axes with no bars), and category order is alphabetical unless fixed withscale-discrete(limits:)cup-cigv0.14: fixed the dangling pointer inbdap-mop.md— two references toreferences/bdap-mop-datasets.md, a file that does not exist because the content was inlined into the same file. Found independently by three reviewers, which is what made it credible. Also removed the merge scar it left behind: two consecutive identical##headings and a paragraph declaring itself "companion to the BDAP section" while sitting inside that very section. The second half of the file now opens with an explicit Reference heading saying it is material to consult, not to readcup-cig: theHEAD→ 404 quirk on ItaliaDomani is real and was nearly deleted by mistake. A first check — one request per file — returned 200 on all four, which looked like proof the documented behaviour was gone. Repeating it five times per file showed the truth: the response is not deterministic.PNRR_ProgettiandPNRR_Localizzazioneanswer 404 most of the time with the occasional 200;PNRR_GareandPNRR_Soggettianswer 200 every time. The CDN edge nodes are not configured alike, so which node serves the request decides the answercup-cig: the instruction «never probe these URLs withcurl -I» therefore stays, with the non-determinism now documented — a 404 onHEADproves neither that the file is missing nor that it exists. Added the methodological corollary: a single probe per file is not a measurement here. Confirmed separately thatRangeis genuinely ignored (a ranged request returns200, not206)cup-cig: reworded the Akamai block as conditional on the caller's network rather than as a property of the server — from consumer networks a plainGETworks with no headers at all, from datacenter egress it needsUser-AgentandRangetogether, and from some egress (GitHub Actions on Azure) nothing works. Stated the consequence: never conclude anything about this source from a single networkcup-cig: ported v0.11 into the repo. The skill had been developed for months in the installed copy at~/.agents/skills/cup-cig/, which is not a git clone, so v0.1 → v0.11 existed only there, unversioned and unbackedcup-cig: decision tree rewritten — OpenCUP is now the mandatory entry point (resolves 3.457 of 3.481 CUPs measured), and itsDESC_TIPO_COPERTURA/DESC_STRUMENTOfields are read as a routing hint to pick the monitoring system (PNRR → ReGiS, cohesion → OpenCoesione, national → BDAP MOP). The CIG branch now splits on what you need, not on the shape of the codecup-cig: theZprefix no longer identifies below-threshold CIGs — it was the convention until 31/12/2023 only; measured at 96.5% ofsmartcigrecords in December 2023 and 0% from January 2024 onwardscup-cig: eight new reference files —opencup.md,bdap-mop.md,anac-datasets.md,anac-lifecycle.md,anac-ocds.md,anac-pvl.md,opencoesione.md,regis-italiadomani.md;scp-mit-api.mdreplaced byscp-mit.md(SCP-MIT is empty from 2024 on, superseded by ANAC PVL via theanac-plCLI)cup-cig: dropped the oldscripts/cig-fetch.sh— the browser-scraping route to ANAC dettaglio-gara is superseded by the ANAC bulk datasets and CLIs. Recoverable from history atbf37822if ever neededcup-cigv0.12: newscripts/cig-fetch.sh, a rewrite of the dropped one — batch input from file or arguments, deduplication, one retry, idempotent skip of valid existing files,--force,--stdout. The download is accepted only if.bando.CIGmatches the request, so a wrong file is never left on disk. Translated from Italian on the way in;Risultati della Ricercastays as is, it is ANAC page textcup-cig:cig-fetch.shreturns 20 top-level sections in one call — what would otherwise take four or five bulk datasets — andbando.CUPmakes it a CIG→CUP resolver. Measured: ~10s per CIG, and 3 CIGs also ~10s total since the browser session is reused; a non-existent CIG costs ~56s (two attempts) and reports the misleadingstarting the search; SmartCIG yield ~6 populated sections against ~10 for ordinary CIGscup-cig: newscripts/test-cig-fetch.sh— offline contract test driving the script with a fakeagent-browser, no network, ~1s. Run it after touching the fetchercup-cigv0.13: stripped the one-off measurements out ofSKILL.mdand all nine reference files. A skill carries the concepts needed to use a source well, not a technical datasheet of it — the test for a number is does it change a decision?. Kept: operational limits (the 10.000totcountcap, 12 req/min, the bare 50 rows that signal a silent truncation, exit codes, timeouts), the 31/12/2023 regime cutoff, break-even thresholds, and every ISTAT/CUP/CIG code used as a worked example. Dropped: file sizes in MB, row counts, distinct-value counts, percentages, and the provenance of each samplecup-cig: the worst offenders were figures hard-coded from a single ad-hoc measurement — «99,3% of CUPs resolve (3.457 of 3.481)», «49 of 100 CUPs appear in MOP», «250 of 257 pairs coincide». They freeze one sample as if it were a property of the source, and they age silently. Each was rewritten as the claim it was evidence for, keeping the operational consequence (a miss in MOP is expected, not an anomaly; the two CIG↔CUP bridges disagree, use the union)cup-cig: three measurements were load-bearing and were kept as claims rather than cut — theZprefix appearing on no record from January 2024 (what makes "never infer the regime from the code shape" absolute), multi-municipality CUPs spanning dozens of territories, and the API gateway serialising per credential so parallel calls buy nothingcup-cig: added two operational facts the mirror's README had and the skill lacked — the cost of a query is the number of round-trips rather than the bytes, and wildcards are refused over plain HTTP (_manifest.jsonis the way to enumerate the files). Fixed a broken reference toreferences/scp-mit-api.md, deleted in the v0.11 port, now pointing at the WADLcup-cig: three CUP↔CIG sources exist (ANACcup, BDAP MOP Gare, ReGiSPNRR_Gare) and they do not fully agree — use the union when completeness matters
typst-cards: new full-bleed layout pattern —margin: 0+ aslide()helper that adds the padding back viainset, with rail, page counter and URL drawn throughplace(out of flow, so content cannot push them). Enables backgrounds, colour bands and images reaching every edge, which the margined pattern cannot do. Newreferences/slides-fullbleed-starter.typ(5 cards, 1:1); the two existing starters are unchanged. New "Two layout patterns" section with the rule for choosing. Adapted from Mickaël Canouil's post on Typst LinkedIn carousels (credited in SKILL.md); geometry kept in inches —7.5in × 144ppi= exactly 1080px, while his 21cm square gives 1191pxtypst-cards: PDF output alongside PNG — a LinkedIn carousel is uploaded as a single PDF document post, sotypst compile slides.typ output/deck.pdfis now part of the flow; frontmatterdescriptionupdated accordingly; optional ImageMagick one-liner for an animated GIF previewtypst-cards: new pitfall —block(width: 100%, height: 100%, inset: ...)clips silently on overflow (one page, text cut at the edge, no warning, exit 0); the same content withoutheight: 100%spills into 6 real pages. Diagnostic recipe + page-count check added to Phase 4. Verified on typst 0.14.2typst-cards: new pitfall — insidebody,width: 100%is the padded column, not the page, so "full-bleed" decoration drawn there comes out framed; theslide()helper takes ableedargument for content that must be measured against the pagetypst-cards: font availability documented as two tiers — onlyLibertinus Serif,New Computer Modern(+ Math) andDejaVu Sans Monoare embedded in the binary (typst fonts --ignore-system-fonts);DejaVu Sans, previously the skill's primary body font, is system-only and can silently fall back elsewhere. Stated plainly: there is no embedded sans-seriftypst-cards: neutral placeholder palette intheme-starter.typ— the old default was recognisably GitHub (#0d1117/#58a6ff) and looked like a decision already made, so it got shipped unchanged. Now neutral greys for surfaces and text plus a single accent marked as the first value to replace, withACC/ACC-Ldocumented as the same hue at two lightnesses (not two brand colours). Every pairing measured: 16.5 / 15.9 / 6.6 / 8.4 / 5.8 / 7.0 : 1, all above 4.5:1, and the numbers are in the file. All three starters recompiled and reviewedtypst-cards: fix visible bug inslides-16x9-starter.typ—[short\ndescription]rendered asshortndescription, since a backslash escapes the next character and a line break is a trailing backslash at end of line. Compiled cleanly, output was wrong. New pitfall documentedtypst-cards: new pitfall sub-case — smart quotes never reach string values, so#helper("l'x")keeps a straight apostrophe while#helper[l'x]and plain markup get’; interpolating the string into content does not rescue it. Silent, no error, and a deck mixing prose with helper calls ends up with two kinds of apostrophe. Surfaced by the first real deck built with the new pattern; mechanism isolated with a 5-case compiletypst-cards: fix brokencprecipe — all three slide starters importedtheme-starter.typwhile Phase 3 tells you to copy the theme totheme.typ, so following the documented recipe failed with "file not found". Imports now point attheme.typ; verified end-to-end on all three starters (5 / 3 / 5 pages + PDF). Reported by the Copilot review on PR #27typst-cards:#set document(title:, author:)for PDF metadata,#set text(lang: "it")for Italian hyphenation, and deck-level guidance (5–7 cards, cover → content → CTA, one inverted card, one visual idea per deck)
scrivi-chiaro-pa: new skill — write, rewrite, or review Italian PA content in plain language, based on the Designers Italia Guida al linguaggio della Pubblica Amministrazione (CC-BY 4.0). Three modes (draft / rewrite / review), tone-of-voice scenarios, final glossary pass + checklist. References: style/grammar/numbers, structure/usability/SEO/accessibility, tone of voice (9 scenarios), social/newsletter/images/editorial management, A–Z glossary (~90 terms in two tables). Content harvested from the published "bozza" on docs.italia.it (suggerimenti + tono di voce sections exist only there) and from theitalia/writing-toolkitrepo (glossary RST sources)
datawrapper: document arrow markers in locator maps (new Datawrapper feature). Tested end-to-end via pure API: arrows are writable (line + flow types, triangle/lines heads, bidirectional, gradient, taper, curve). Full JSON schema + per-field table added toreferences/locator-map.mddatawrapper: fix wrong marker storage inreferences/locator-map.md— there is NO/markersendpoint (404); points, areas and arrows are all stored in the chart data viaPUT /v3/charts/<ID>/dataas{"markers":[...]}(full replace each PUT). Corrected point (needsiconblock) and area (colors in top-levelproperties, GeoJSON-style keys) examplesdatawrapper: document counterintuitivecurve.anglebehaviour — values near 0 produce huge looping arcs; gentle curves need larger magnitudes that scale with arrow length (|angle| ≈ km/2.5); recommend tuning in GUI then reading back via API (verified by hand-edit round-trip)
typst-cards: refactor starter templates to native page-level footer —#set page(footer: footer-block(URL, dark: ...))with Typst'scounter(page).display()/.final().first(); replaces fragile#v(1fr) + #ctr(n,total)last-row pattern that produced silent ghost slides on dense content; newfooter-blockhelper in theme; new pitfall entry; sub-case on literal helper params under special-chars table; smoke-tested both starters (5+3 PNGs, footer fully visible)typst-cards: add "Footer/edge content never clips" design rule — footer, counter, source URL must be fully visible; fix via larger page bottom margin or smaller footer font; must-check in Phase 4typst-cards: expand "Special characters in content mode" pitfall — table covering$,//,_,*,@,<,~(replaces the previous single bullet on<); typical contexts where these characters break compilation with "unclosed delimiter" errorstypst-cards: tighten Phase 4 — explicit read → evaluate → report+propose → wait flow; AI must look at PNGs (not just check existence), evaluate against design principles, propose concrete fixes, wait for user decision before iteratingtypst-cards: new skill — generates PNG images for online communication (social posts, carousels, infographics) using Typst; brand-materials interview, supported formats (1:1, 4:5, 9:16, 16:9, 1.91:1), three reference starters (theme-starter.typ,slides-1x1-starter.typ,slides-16x9-starter.typ), known pitfalls
openalex: strengthen API key security — added prominent SECURITY callout at top of SKILL.md; expanded Common Pitfalls rule to cover all output contexts (text responses, echoed commands, logs), not just verification
evals/openalex: addrun_evals.sh— runs prompts viaclaude -p, saves outputs toruns/YYYY-MM-DD/, auto-checks 4 pitfall patterns (relevance_sort, title.search scope, multi-line curl, API key printed)openalex: never print API key to verify presence — use[[ -n "${OPENALEX_API_KEY:-}" ]]; added to Quick Start and Common Pitfalls in SKILL.mdevals/openalex: add 20 synthetic test prompts (test-09–test-28) targeting 11 known pitfall zones (relevance_sort_no_search, title_search_standalone, entity_name_filter, sequential_batch, nested_select, missing_api_key, default_per_page, multi_line_curl, null_csv_field, pdf_fallback_chain, wrong_search_scope); add corresponding pitfall checks tochecks.md
-
open-data-quality: ZIP-wrapped CSV support — if resource declared as CSV is actually a ZIP, extract largest CSV to /tmp and proceed with full analysis; report MINORzip_wrapped_csvinstead of BLOCKER; uses Pythonzipfile(no DuckDB/Polars ZIP support); score 40→86 on Liguria air quality dataset -
open-data-quality: fix_extras_value— also check top-level package fields; some harvesters (dati.gov.it from regional portals) promote holder_name/identifier to top-level instead of extras; fixes false-positive MAJORs; score 83→97 on test dataset -
open-data-quality: add qualitative assessment section to SKILL.md — 9 LLM-only checks (title discoverability, title↔description, description↔content, content↔update frequency, dataset usefulness); runs after scripts, requires data content; Good/Acceptable/Poor rating; added to report template -
open-data-quality: addoutlier_valuescheck (phase3_content) — IQR method on numeric columns (≥100 rows); severity MINOR, -2 pts; no fix suggestion (signal only); new fixtureoutlier_values.csv; 36/36 tests pass -
open-data-quality: addduplicate_rowscheck (phase3_content) — detects exact duplicate rows via DuckDBSELECT DISTINCT *; severity MAJOR, -3 pts on data content quality; new fixtureduplicate_rows.csv; 35/35 tests pass
-
open-data-quality: fix IT holder label —dcatapit:datasetHolder→dct:rightsHolderin code and all reference docs (confirmed from real dati.gov.it data) -
open-data-quality: remove deprecateddcatapit:datasetHolderrow from profiles table;dct:rightsHoldernow marked M for IT -
open-data-quality: addportal_field_aliases.json— JSON vocabulary mapping standard DCAT-AP field names to portal-specific CKAN extras keys; UK profile mapsissued/modified→dcat_issued/dcat_modified -
open-data-quality: fix UK date detection —metadata_validator.pynow usesFIELD_ALIASESfallback for date fields per profile -
open-data-quality: add 3 new tests —test_it_holder_present_ok,test_it_holder_missing_flagged,test_uk_dcat_prefixed_dates_accepted; 34/34 pass -
open-data-quality: added pytest test suite — 25 tests across phase0–3 + CLI integration; fixtures inscripts/tests/fixtures/;pytestadded as dev dependency inpyproject.toml -
open-data-quality: fix fuzzy check — skip datetime/timestamp columns stored as VARCHAR (e.g.2025-03-14T00:00:00was triggering false positive near-duplicate alert) -
open-data-quality: file type detection in phase 0 — detect ZIP, HTML/XML, JSON, PDF, OLE2/Excel, UTF-16 via magic bytes/content sniffing; report specific type (e.g. "File is a ZIP archive") instead of generic binary/separator error -
open-data-quality: fuzzy false positive fix — addlevenshtein/max_len < 10%ratio filter + raise JW threshold to 0.95 + minimum length > 5; eliminates NORD-ESTNORD-OVEST, MINISTERO DELLA DIFESASALUTE type false positives while preserving real typos (D'INTERESSE~DI INTERESSE caught at 4% ratio) -
open-data-quality: fix encoding false positive — normalizeutf_8→utf-8before comparison; was marking valid UTF-8 files as MAJOR issue -
open-data-quality: split fuzzy check — trailing/leading whitespace now reported separately; fuzzy comparison works on trimmed values to avoid spurious matches -
open-data-quality: fix #11 — placeholder message now shows actual values found (e.g.NA) instead of full catalogn/a, n.d., -…; SQL useslist_distinct(list(...))to collect found values per column -
open-data-quality: non-UTF8 encoding no longer a BLOCKER —charset_normalizerdetects encoding, file converted to UTF-8 temp copy, full analysis runs; MAJOR finding reported (tested on Comune di Palermo CP1250 dataset: 33→73/100) -
open-data-quality: added fuzzy near-duplicate category check viajaro_winkler_similarity > 0.92(DuckDB built-in) — found real issues in Palermo dataset -
open-data-quality: CRLF line endings no longer flagged (RFC 4180 prescribes CRLF) -
open-data-quality: replacedchardetwithcharset_normalizerfor more accurate encoding detection -
open-data-quality: added developer notes toCONTRIBUTING.md(WSL/uvx cache, PYTHONUTF8, DuckDB lenient parsing) -
evals/open-data-quality/fixtures/palermo-edifici-pubblici-cp1250.csv: archived as encoding test fixture -
open-data-quality: fixed false BLOCKER on valid CSVs with quoted newlines in headers — retry withstrict_mode=false; added_lenientflag +_rcsv()helper across all DuckDB queries (score 29→86 on Copertino dataset) -
open-data-quality: SKILL.md rewritten —uvx odq-csv/odq-ckanis now the single primary path; all bash inline phases removed (no double maintenance) -
SKILL.md reduced from ~450 to ~130 lines
-
evals/checks.mdupdated to validateuvx+ package usage
- Added
open-data-qualityskill: CSV validator (odq-csv) and CKAN/DCAT-AP metadata validator (odq-ckan) - Created missing
__init__.pyfor package discovery - Translated
scripts/README.mdfrom Italian to English - Tested end-to-end on real dati.gov.it datasets (bilancio, popolazione)
- Created eval suite:
evals/open-data-quality/(8 prompts, 15 checks)
openalexSKILL.md v0.2: added filter syntax (OR/NOT/ranges), batch pipe lookup, two-step entity lookup,group_by/sample/seedparams,per-page=200default, error handling with backoff, endpoint costs table
- Saved reference documents in
docs/: testing-agent-skills-with-evals.md and agent-skills-specification.md - Added "Core Idea" section to
PRD.md(skill creation as an inclusive, non-technical activity) - Created
docs/prd.mdwith Alessio Cimarelli's eval proposal - Created
evals/structure with_template/and first eval foropenalex - First complete eval run on
openalex: score 78/100 (7/9 checks), 7 improvements to the skill- single-line curl required (warning at top of SKILL.md)
title.searchinsidefilter=, not standalonedisplay_nameas title (Output Format section)api_keyexplicitly documented as pitfall- Europe PMC fallback documented as recipe #6
- Definition of Done added to SKILL.md
- Updated
README.md: catalog with Eval column, Evals section, fix skill data-quality-csv → openalex - Created project
CLAUDE.md