probes
Data license: CC BY-SA 4.0 (records) · Apache 2.0 (tools) · Data source: Loxyn-Korela/fault-atlas
78 rows
This data as json, CSV (advanced)
corpus_id 3
| id ▼ | form_id | form_name | file | kind | corpus_id | written | run | result_count | result_of | result_date | matches_excerpt | note | code |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | Diversity of document templates form-017 | Diversity of document templates | probes/2026-08/line-017.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-017.py <dir with the 272 .xml> | 272 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 17 (form-017). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-017.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco # ── 17 · diversité des gabarits ──────────────────────────────────────────── @sonde(17, "gabarit du document (article-type JATS)") def d17(x): a = re.findall(r'<article\b[^>]*article-type="([^"]+)"', x) s = re.findall(r'<sub-article\b[^>]*article-type="([^"]+)"', x) return sorted(set(a + s)) | |
| 2 | Variable length and chunking form-018 | Variable length and chunking | probes/2026-08/line-018.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-018.py <dir with the 272 .xml> | 243 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 18 (form-018). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-018.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco CORPS = lambda x: re.search(r'(?s)<body\b.*?</body>', x) def _txt(x): """texte brut d'un XML JATS, balises retirées""" return re.sub(r'<[^>]+>', ' ', x) @sonde(18, "document trop long pour un seul morceau (2 000 car.)") def d18(x): m = CORPS(x) if not m: return [] t = re.sub(r'\s+', ' ', _txt(m.group(0))).strip() n = -(-len(t) // 2000) return [f"{len(t)} car. → {n} morceaux"] if n > 1 else [] | |
| 3 | Layout furniture (headers, footers) form-020 | Layout furniture (headers, footers) | probes/2026-08/line-020.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-020.py <dir with the 272 .xml> | 181 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 20 (form-020). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-020.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _txt(x): return re.sub(r'<[^>]+>', ' ', x) @sonde(20, "fait porté par la mise en page (ligne de tableau / liste de définitions)") def d20(x): n_tr = len(re.findall(r'<tr\b', x)) n_def = len(re.findall(r'<def-item\b', x)) if n_tr + n_def == 0: return [] ex = re.findall(r'<tr\b[^>]*>(.*?)</tr>', x, re.S) e = re.sub(r'\s+', ' ', _txt(ex[0]))[:60] if ex else '' return [f"tr×{n_tr} def-item×{n_def} | {e}"] | |
| 4 | Multiple spellings of one entity form-021 | Multiple spellings of one entity | probes/2026-08/line-021.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-021.py <dir with the 272 .xml> | 209 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 21 (form-021). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-021.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco # ── 21 · GRAPHIES MULTIPLES : « Forme Longue (ACRO) », puis « ACRO » tout seul # ⚠️ 1re version CORRIGÉE : elle rendait « By extracting tumor volume (TV) » — les # initiales étaient cherchées EN SOUS-CHAÎNE, donc l'empan de la forme longue # débordait. On exige maintenant une correspondance EXACTE, et on prend le plus # court segment qui la donne. Hors correspondance exacte : refus, pas de devinette. GLOSE = re.compile(r"((?:[\wÀ-ÿ'’\-]+ ){1,8})\(([A-Z][A-Za-z0-9]{1,6})\)") def _corps(x): m = re.search(r'(?s)<body\b.*?</body>', x) return m.group(0) if m else '' def _forme_longue(avant, acro): lettres = ''.join(c for c in acro if c.isalpha()).lower() mots = [m for m in avant.split() if m] for depart in range(len(mots) - 1, -1, -1): # du plus COURT au plus long seg = mots[depart:] ini = ''.join(p[0] for w in seg for p in w.split('-') if p).lower() if ini == lettres: return ' '.join(seg) return None def _txt(x): return re.sub(r'<[^>]+>', ' ', x) @sonde(21, "graphies multiples d'une entité (forme longue + acronyme réutilisé)") def d21(x): t = _txt(_corps(x)) or _txt(x) out = [] for avant, acro in GLOSE.findall(t): longue = _forme_longue(avant, acro) if not longue or len(acro) < 2: continue # l'acronyme doit être RÉUTILISÉ seul : sinon ce n'est pas une 2e graphie if len(re.findall(… | |
| 5 | Pronominal references form-022 | Pronominal references | probes/2026-08/line-022.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-022.py <dir with the 272 .xml> | 212 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 22 (form-022). Question: does a sentence open on a bare pronoun (It / They / These / Those / This) whose referent must be resolved? Rewritten 2026-09-09 as a standalone probe from the August 2026 campaign script (the original was an inline loop, one counter per line, same regexes). A carrier here is a document with a bare-pronoun sentence opening. A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-022.py <dir with the 272 .xml> Result (re-run 2026-09-09): 212/272 documents open at least one sentence on a bare pronoun, 3,260 occurrences; 49/272 also carry an explicit lexical reprise ("the latter", "these authors"). Same figures as the excerpt. """ import re, collections SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def corps(x): m = re.search(r'(?s)<body\b.*?</body>', x) return m.group(0) if m else '' def txt(x): return re.sub(r'\s+', ' ', re.sub(r'<[^>]+>', ' ', x)) REP_EN = re.compile(r'\b(the latter|the former|this group|these authors|the same ' r'(?:group|authors|patients?)|said (?:company|study))\b', re.I) PRON_SUJ = re.compile(r'(?<=[.;] )(It|They|These|Those|This)\b') @sonde(22, "pronominal reprise: a sentence opens on a bare pronoun") def d22(x): t = txt(corps(x) or x) pro = PRON_SUJ.findall(t) rep = REP_EN.findall(t) if not pro: return [] out = [f"pronoun-subject x{len(pro)}"] if rep: out.append("lexical reprise: " + " | ".join(sorted(set(rep))[:3])) return out | |
| 6 | Registry identifiers form-025 | Registry identifiers | probes/2026-08/line-025.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-025.py <dir with the 272 .xml> | 31 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 25 (form-025). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-025.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco # ── 25 — identifiant d'immatriculation d'organisation (clé de jointure) ────── @sonde(25, "identifiant de registre d'organisation (ROR/GRID/ISNI)") def d25(x): v = re.findall(r'<institution-id[^>]*institution-id-type="([^"]+)"[^>]*>([^<]+)<', x) return [f"{t}:{val.strip()}" for t, val in v][:6] | |
| 7 | Relative dates form-036 | Relative dates | probes/2026-08/line-036.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-036.py <dir with the 272 .xml> | 64 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 36 (form-036). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-036.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco # ── 36 · dates relatives ─────────────────────────────────────────────────── REL = (r'(?i)\b((?:the\s+)?(?:previous|preceding|following|next|last|past|coming)\s+' r'(?:year|month|week|decade|quarter|season|fiscal year|study period)s?' r'|(?:one|two|three|four|five|\d{1,2})\s+(?:years?|months?|weeks?|decades?)\s+(?:ago|earlier|later|before|after)' r'|(?:l\'|la\s+)?(?:année|annee)\s+(?:précédente|precedente|dernière|derniere|suivante)' r'|to\s?day|as of (?:today|writing)|at present|nowadays)\b') def _txt(x): x = re.sub(r'(?s)<(front|back|ref-list)\b.*?</\1>', ' ', x) return re.sub(r'<[^>]+>', ' ', x) @sonde(36, "date relative (à résoudre contre la date du document)") def d36(x): return sorted(set(m.strip() for m in re.findall(REL, _txt(x))))[:6] | |
| 8 | Exact and near duplicates form-038 | Exact and near duplicates | probes/2026-08/line-038.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-038.py <dir with the 272 .xml> | 0 | 272 | 2026-09-09 | 1 | Measured absent: the probe finds no carrier among the 272 files. An absence is a result, dated. | """Fault Atlas probe — catalogue line 38 (form-038). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-038.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco @sonde(38, "doublon exact dans le fonds") def d38(x): return [] # se mesure sur le FONDS, pas sur un document — voir doublons() |
| 9 | Successive versions of a document form-039 | Successive versions of a document | probes/2026-08/line-039.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-039.py <dir with the 272 .xml> | 39 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 39 (form-039). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-039.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco ETATS = {'retracted-article', 'retraction-forward', 'corrected-article', 'correction-forward', 'final-edited-article', 'updated-article', 'preprint'} @sonde(39, "versions successives du même document") def d39(x): """⚠️ RESSERRÉE le 08/08 après audit (143/272 → 39/272). Deux fautes de la 1re version : <article-version>1</article-version> (120 documents) n'est PAS une succession, et <pub-history> est présent dans 272/272 — une sonde à 100 % est une alarme de méthode. On ne garde que les liens désignant un AUTRE ÉTAT DU MÊME TEXTE : « companion » et « commentary-article » sont d'autres documents.""" r = sorted(set(re.findall(r'related-article-type="([^"]+)"', x)) & ETATS) v = sorted({f"version={s.strip()}" for s in re.findall(r'<article-version[^>]*>([^<]*)', x) if s.strip() and s.strip() != '1'}) return (r + v)[:5] | |
| 10 | Cited documents absent from the corpus form-040 | Cited documents absent from the corpus | probes/2026-08/line-040.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-040.py <dir with the 272 .xml> | 0 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 40 (form-040). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-040.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco @sonde(40, "renvoi interne pendant (xref sans cible dans le document)") def d40(x): ids = set(re.findall(r'\bid="([^"]+)"', x)) pend = [] for m in re.finditer(r'<xref\b([^>]*)>', x): at = m.group(1) r = re.search(r'rid="([^"]+)"', at) if not r: continue for cible in r.group(1).split(): if cible not in ids: pend.append(cible) return sorted(set(pend))[:6] | |
| 11 | Truncated or unreadable documents form-041 | Truncated or unreadable documents | probes/2026-08/line-041.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-041.py <dir with the 272 .xml> | 0 | 272 | 2026-09-09 | 1 | Measured absent: the probe finds no carrier among the 272 files. An absence is a result, dated. | """Fault Atlas probe — catalogue line 41 (form-041). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-041.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco # ── 41 · DOCUMENT TRONQUÉ : le texte intégral n'est pas là # ⚠️ 1re version RETIRÉE : « <body> de moins de 300 mots » sonnait 29 fois, TOUTES # dans la strate `retract`. Un avis de rétractation COURT n'est pas un document # tronqué — c'est un genre bref. Une sonde concentrée à 100 % sur une strate est # une alarme de méthode. Restent ici les seules marques de troncature RÉELLES : # corps absent, renvoi interne dont la cible manque, bloc déclaré indisponible. @sonde(41, "document tronqué (corps absent, cible interne manquante, bloc indisponible)") def d41(x): marques = [] if '<body' not in x: marques.append('AUCUN <body> — front-matter seul') ids = set(re.findall(r'\bid="([^"]+)"', x)) rids = set() for r in re.findall(r'<xref[^>]*\brid="([^"]+)"', x): rids.update(r.split()) pendants = sorted(rids - ids) if pendants: marques.append(f'renvoi interne SANS cible : {pendants[:3]}') if re.search(r'(?s)<table-wrap\b(?:(?!</table-wrap>).)*</table-wrap>', x): sans = [t for t in re.findall(r'(?s)<table-wrap\b.*?</table-wrap>', x) if '<table' not in t] if sans: marques.append('tableau livré sans contenu textuel') if re.search(r'(?i)\[(?:text|figure|image|table)s? (?:omitted|not available|unavailable)\]', x): marques.append('bloc déclaré manquant') return marques |
| 12 | Names damaged by scanning form-042 | Names damaged by scanning | probes/2026-08/line-042.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-042.py <dir with the 272 .xml> | 1 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 42 (form-042). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-042.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco # ── 42 — nom abîmé par le scan (OCR) ──────────────────────────────────────── # ⚠️ CORRIGÉE le 08/08 : la 1re version écrivait `<(?:…|institution)[^>]*>`, qui # attrapait AUSSI <institution-id> — et rendait « chiffre dans le nom » sur des # URL ROR (https://ror.org/02z1vqm45). 22/272 étaient donc 0 vrai positif. # Elle rendait aussi « lettre détachée » sur le « e » conjonctif portugais. @sonde(42, "nom d'auteur abîmé par le scan (OCR)") def d42(x): noms = re.findall(r'<(surname|given-names)(?:\s[^>]*)?>([^<]{2,60})<', x) ab = [] for balise, n in noms: if '�' in n or '�' in n or re.search(r'Ã[©¨¢«¤ -¿]', n): ab.append(f"mojibake:{balise}:{n}") elif re.search(r'[A-Za-zÀ-ÿ][01]|[01][A-Za-zÀ-ÿ]', n): ab.append(f"chiffre-dans-le-nom:{balise}:{n}") elif re.search(r'(?<=[a-zà-ÿ]) [a-zà-ÿ](?![a-zà-ÿ])', n): ab.append(f"lettre-detachee:{balise}:{n}") return sorted(set(ab))[:6] | |
| 13 | Tables with multi-level headers form-043 | Tables with multi-level headers | probes/2026-08/line-043.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-043.py <dir with the 272 .xml> | 73 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 43 (form-043). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-043.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco # ── 43 · tableaux à en-têtes multiples ───────────────────────────────────── @sonde(43, "tableau à en-têtes sur plusieurs niveaux") def d43(x): span = len(re.findall(r'<th[^>]*(?:col|row)span="([2-9]|\d\d)"', x)) lignes_th = len(re.findall(r'(?s)<thead\b.*?</thead>', x)) multi = 0 for th in re.findall(r'(?s)<thead\b.*?</thead>', x): if len(re.findall(r'<tr\b', th)) > 1: multi += 1 if span or multi: return [f"th colspan/rowspan ×{span}", f"thead à >1 ligne ×{multi}/{lignes_th}"] return [] | |
| 14 | Several languages in one corpus form-044 | Several languages in one corpus | probes/2026-08/line-044.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-044.py <dir with the 272 .xml> | 27 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 44 (form-044). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-044.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco @sonde(44, "même contenu en DEUX langues dans le même document") def d44(x): tt = re.findall(r'<trans-title[^>]*>', x) ta = re.findall(r'<trans-abstract[^>]*>', x) return ([f"trans-title ×{len(tt)}"] if tt else []) + ([f"trans-abstract ×{len(ta)}"] if ta else []) | |
| 15 | Incremental ingestion form-046 | Incremental ingestion | probes/2026-08/line-046.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-046.py <dir with the 272 .xml> | 0 | 272 | 2026-09-09 | 1 | Measured absent: the probe finds no carrier among the 272 files. An absence is a result, dated. | """Fault Atlas probe — catalogue line 46 (form-046). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-046.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco @sonde(46, "ingestion incrémentale (propriété du pipeline)") def d46(x): return [] # rien à chercher dans un fichier : le défaut n'y vit pas |
| 16 | Period typographic conventions form-064 | Period typographic conventions | probes/2026-08/line-064-arginine-titles.py | python | corpus-pubmed-arginine-pre1990-3642 | 2026-09-09 | python3 tools/run_probe.py probes/2026-08/line-064-arginine-titles.py <corpus .md file> | 234 | 3642 | 2026-09-09 | 0 | The August note says 184; the rule it used was not written down. This probe (title carries no lower-case letter) gives 234, almost all from the 1960s. The probe's figure is the one to cite; the excerpt keeps its own. | """Fault Atlas probe — catalogue line 64 (form-064), origin observation. Corpus: PubMed abstracts on arginine, published before 1990-01-01, harvested 2026-07-29 by efetch (3,642 records; see corpora/pubmed-arginine-pre1990-3642.json). The corpus file is a Markdown list: one record per "## n. PMID x · year" heading, the title in bold on the next line. Question: how many titles are written in full capitals (a dated convention)? Run: python3 tools/run_probe.py probes/2026-08/line-064-arginine-titles.py <corpus .md file> Result: 234 of 3,642 titles carry no lower-case letter (232 of them from the 1960s). The August 2026 note counted 184 with a rule that was not preserved; the figure below is the one this probe gives, and it is the one to cite from now on. """ import re CORPUS_PROBE = True LINE = 64 TITLE = "title in full capitals (dated typographic convention)" REC = re.compile(r'^## \d+\. PMID (\d+) · (\d{4})\n\*\*(.*?)\*\*', re.M) def population(text): return len(REC.findall(text)) def probe(text): out = [] for pmid, year, title in REC.findall(text): if re.search(r'[A-Z]', title) and not re.search(r'[a-z]', title): out.append(f"PMID {pmid} ({year}): {title}") return out |
| 17 | Period typographic conventions form-064 | Period typographic conventions | probes/2026-08/line-064-jats-headings.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-09-09 | python3 tools/run_probe.py probes/2026-08/line-064-jats-headings.py <dir with the 272 .xml> | 0 | 272 | 2026-09-09 | 1 | The zero is reproduced: no article title in full capitals among the 272. The excerpt's secondary counts (70/272 headings, 30/272 words) came from a rule not kept; this probe's rules give 36 and 36 (see its header). Stated, not adjusted. | """Fault Atlas probe — catalogue line 64 (form-064), absence check on the 272 JATS files. Question: does the dated convention (title in full capitals) survive in recent Europe PMC articles? Three counts per document: A. the article title itself carries no lower-case letter B. at least one section heading (<title>) of four characters or more is in full capitals C. at least one word of four letters or more in the article title is in full capitals Run: python3 tools/run_probe.py probes/2026-08/line-064-jats-headings.py <dir with the 272 .xml> Result (re-run 2026-09-09): A = 0/272 — the convention is absent from the modern corpus. The record's excerpt also quotes 70/272 for B and 30/272 for C, measured 2026-08-08 with a rule that was not preserved; this probe gives 36 and 36. Both figures are kept, the discrepancy is stated, not hidden. A carrier here is a document with A. """ import re SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _txt(x): return re.sub(r'\s+', ' ', re.sub(r'<[^>]+>', ' ', x)).strip() def counts(x): m = re.search(r'(?s)<article-title[^>]*>(.*?)</article-title>', x) t = _txt(m.group(1)) if m else '' a = bool(t) and not re.search(r'[a-z]', t) heads = [_txt(h) for h in re.findall(r'(?s)<title[^>]*>(.*?)</title>', x)] b = [h for h in heads if len(h) >= 4 and re.search(r'[A-Z]', h) and not re.search(r'[a-z]', h)] c = [w for w in re.findall(r'[A-Za-z]{4,}', t) if w.isupper()] return a, b, c @sonde(64, "article title in full capitals (dated convention)") def d64(x): a, b, c = counts(x) return [f"title in full capitals"] if a else [] |
| 18 | Multi-column layout form-066 | Multi-column layout | probes/2026-08/line-066.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-066.py <dir with the 272 .xml> | 2 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 66 (form-066). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-066.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco # ── 66 · MULTI-COLONNES : reste-t-il la moindre information de mise en page ? @sonde(66, "mise en page multi-colonnes (info de colonnes HORS tableau)") def d66(x): y = re.sub(r'(?s)<table-wrap\b.*?</table-wrap>', ' ', x) y = re.sub(r'(?s)<table\b.*?</table>', ' ', y) return sorted(set(re.findall( r'<(?:column|multicol|layout|column-break)\b[^>]*>' r'|\b(?:two|three|multi)[- ]columns?\b' r'|column-count\s*[:=]' r'|\bcolumn[- ]?(?:layout|order|width)\b', y, re.I)))[:4] | |
| 19 | Bilingual document form-067 | Bilingual document | probes/2026-08/line-067.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-067.py <dir with the 272 .xml> | 27 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 67 (form-067). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-067.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco # ── 67 — document bilingue : la MÊME chose dite deux fois ─────────────────── @sonde(67, "document bilingue (traduction redondante déclarée)") def d67(x): tr = re.findall(r'<trans-(abstract|title)\b[^>]*?xml:lang="([^"]+)"', x) if not tr: return [] b = re.search(r'<article\b[^>]*xml:lang="([^"]+)"', x) return [f"{b.group(1) if b else '?'}→{lg} ({k})" for k, lg in tr][:6] | |
| 20 | The fact is in the figure form-068 | The fact is in the figure | probes/2026-08/line-068.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-068.py <dir with the 272 .xml> | 198 | 272 | 2026-09-09 | 0 | The figure quoted in the excerpt was counted on 2026-08-08 by a finer, hand-run measurement whose code was not kept; this probe is the one that was kept, and its figure is the one that can be re-run. Both stand, dated. | """Fault Atlas probe — catalogue line 68 (form-068). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-068.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco @sonde(68, "le fait est dans la figure (image liée, hors XML)") def d68(x): g = re.findall(r'<graphic[^>]*xlink:href="([^"]+)"', x) return g[:5] if g else [] |
| 21 | Several documents in one file form-069 | Several documents in one file | probes/2026-08/line-069.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-069.py <dir with the 272 .xml> | 30 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 69 (form-069). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-069.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco # ── 69 · plusieurs documents dans un fichier ─────────────────────────────── @sonde(69, "plusieurs documents dans un même fichier") def d69(x): s = re.findall(r'<sub-article\b[^>]*>', x) r = re.findall(r'<response\b[^>]*>', x) if len(s) + len(r) == 0: return [] ty = re.findall(r'<sub-article\b[^>]*article-type="([^"]+)"', x) return [f"sub-article ×{len(s)} response ×{len(r)}", "types=" + ",".join(sorted(set(ty))[:4])] | |
| 22 | Bibliographic references form-070 | Bibliographic references | probes/2026-08/line-070.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-070.py <dir with the 272 .xml> | 251 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 70 (form-070). Question: does the document carry a final reference block, and citation calls in its body? Rewritten 2026-09-09 as a standalone probe from the August 2026 campaign script (the original was an inline loop, one counter per line, same regexes). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-070.py <dir with the 272 .xml> Result (re-run 2026-09-09): 251/272 carry a final <ref> block (10,912 references, median 36, max 450); 247/272 carry <xref ref-type="bibr"> calls (17,210); 13/272 carry an author-year citation in the running text. Same figures as the excerpt. A carrier here is a document with a reference block. """ import re, collections SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def corps(x): m = re.search(r'(?s)<body\b.*?</body>', x) return m.group(0) if m else '' def txt(x): return re.sub(r'\s+', ' ', re.sub(r'<[^>]+>', ' ', x)) CIT_LIGNE = re.compile(r'\((?:[A-Z][A-Za-z\'’-]+(?: et al\.?| and [A-Z][A-Za-z-]+)?,? ' r'(?:19|20)\d\d[a-z]?)\)') @sonde(70, "bibliographic references: final block, xref calls, inline author-year") def d70(x): nref = len(re.findall(r'<ref\b', x)) nxref = len(re.findall(r'<xref[^>]*ref-type="bibr"', x)) ninline = len(CIT_LIGNE.findall(txt(corps(x) or x))) if not nref: return [] return [f"ref x{nref}", f"xref bibr x{nxref}"] + ([f"inline author-year x{ninline}"] if ninline else []) | |
| 23 | Multiple dates on one document form-071 | Multiple dates on one document | probes/2026-08/line-071.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-071.py <dir with the 272 .xml> | 111 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 71 (form-071). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-071.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco @sonde(71, "dates multiples du document") def d71(x): d = re.findall(r'<date date-type="([^"]+)"|<pub-date[^>]*pub-type="([^"]+)"', x) v = sorted({a or b for a, b in d}) return v if len(v) > 1 else [] | |
| 24 | Redundancy between text and table form-072 | Redundancy between text and table | probes/2026-08/line-072.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-072.py <dir with the 272 .xml> | 95 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 72 (form-072). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-072.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco CORPS = lambda x: re.search(r'(?s)<body\b.*?</body>', x) def _txt(x): """texte brut d'un XML JATS, balises retirées""" return re.sub(r'<[^>]+>', ' ', x) @sonde(72, "même valeur dite dans le texte ET dans un tableau") def d72(x): """⚠️ RESSERRÉE DEUX FOIS le 08/08. Tous les nombres → « 10 », « 12 », « 13 » : du hasard. Décimales seules (107/272) → « 0.3 » tombait sur une borne d'IC ici et un temps de trajet là. Seuil retenu : ≥ 3 chiffres significatifs → 95/272, cas inspectés à la main, redondances réelles.""" tw = re.findall(r'(?s)<table-wrap\b.*?</table-wrap>', x) if not tw: return [] cell = set() for t in tw: cell |= {v for v in re.findall(r'<td[^>]*>\s*(?:<p[^>]*>\s*)?(\d+[.,]\d+)', t) if len(re.sub(r'\D', '', v)) >= 3} if not cell: return [] corps = re.sub(r'(?s)<table-wrap\b.*?</table-wrap>', ' ', x) m = CORPS(corps) if not m: return [] txt = _txt(m.group(0)) dans = sorted({c for c in cell if re.search(r'(?<![\d.,])' + re.escape(c) + r'(?![\d.,])', txt)}) return [f"{len(dans)} valeurs communes : {dans[:5]}"] if dans else [] | |
| 25 | Values with quantified uncertainty form-073 | Values with quantified uncertainty | probes/2026-08/line-073.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-073.py <dir with the 272 .xml> | 69 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 73 (form-073). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-073.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _txt(x): """texte brut d'un XML JATS, balises retirées""" return re.sub(r'<[^>]+>', ' ', x) @sonde(73, "valeurs avec incertitude quantifiée") def d73(x): return sorted(set(re.findall(r'\d+[\.,]?\d*\s*(?:±|\+/-|\(95\s*%\s*CI)', _txt(x))))[:5] | |
| 26 | Multiple citation conventions form-075 | Multiple citation conventions | probes/2026-08/line-075.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-075.py <dir with the 272 .xml> | 13 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 75 (form-075). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-075.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _txt(x): """texte brut d'un XML JATS, balises retirées""" return re.sub(r'<[^>]+>', ' ', x) @sonde(75, "conventions de citation multiples") def d75(x): t = _txt(x) f = {'auteur-année': len(re.findall(r'\([A-Z][a-zà-ÿ]+(?: et al\.?)?,? \d{4}\)', t)), 'numérique': len(re.findall(r'\[\d{1,3}(?:[,–-]\d{1,3})*\]', t)), 'exposant': len(re.findall(r'<sup>\d{1,3}(?:[,–-]\d{1,3})*</sup>', x))} p = {k: v for k, v in f.items() if v > 2} return [f"{k}×{v}" for k, v in p.items()] if len(p) > 1 else [] | |
| 27 | Anonymised entities form-076 | Anonymised entities | probes/2026-08/line-076.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-076.py <dir with the 272 .xml> | 18 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 76 (form-076). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-076.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _txt(x): """texte brut d'un XML JATS, balises retirées""" return re.sub(r'<[^>]+>', ' ', x) @sonde(76, "entités anonymisées") def d76(x): return sorted(set(re.findall(r'(?i)\b(?:patient|case|subject|participant)s?\s+(?:no\.?\s*)?\d{1,3}\b', _txt(x))))[:6] | |
| 28 | Local and ambiguous date formats form-077 | Local and ambiguous date formats | probes/2026-08/line-077.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-077.py <dir with the 272 .xml> | 4 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 77 (form-077). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-077.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco # ── 77 · DATE LOCALE AMBIGUË : jj-mm-aa dans le TEXTE, sans indication # ⚠️ 1re version CORRIGÉE : « \d{2,4} » en 3e groupe attrapait « 7.5-20 % », « 5/10/20 # years », « version 2.3.28 », « v1.6.20 » — 11 documents porteurs dont 7 de bruit. # On EXIGE une année à 4 chiffres et le MÊME séparateur deux fois. Prix payé, dit : # l'année à 2 chiffres (« 31-05-77 » du corpus 1977) n'est plus vue. DATE_NUM = re.compile(r'(?<![\w/.\-])(\d{1,2})([-/.])(\d{1,2})\2((?:19|20)\d{2})(?![\d/.\-])') def _corps(x): m = re.search(r'(?s)<body\b.*?</body>', x) return m.group(0) if m else '' def _txt(x): return re.sub(r'<[^>]+>', ' ', x) @sonde(77, "date numérique ambiguë dans le corps du texte") def d77(x): out = [] for a, s, b, an in DATE_NUM.findall(_txt(_corps(x))): if 1 <= int(a) <= 12 and 1 <= int(b) <= 12: # les DEUX lisibles comme mois out.append(f"{a}{s}{b}{s}{an}") return sorted(set(out))[:5] | |
| 29 | Scientific notation inside names form-079 | Scientific notation inside names | probes/2026-08/line-079.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-079.py <dir with the 272 .xml> | 163 | 272 | 2026-09-09 | 0 | The figure quoted in the excerpt was counted on 2026-08-08 by a finer, hand-run measurement whose code was not kept; this probe is the one that was kept, and its figure is the one that can be re-run. Both stand, dated. | """Fault Atlas probe — catalogue line 79 (form-079). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-079.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco @sonde(79, "notation scientifique dans les noms") def d79(x): return sorted(set(re.findall(r'\b[A-Z][a-z]?(?:<sup>\d?[+-]</sup>|\d+[+-])', x)))[:5] |
| 30 | Relative quantified relations form-080 | Relative quantified relations | probes/2026-08/line-080.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-080.py <dir with the 272 .xml> | 33 | 272 | 2026-09-09 | 0 | The figure quoted in the excerpt was counted on 2026-08-08 by a finer, hand-run measurement whose code was not kept; this probe is the one that was kept, and its figure is the one that can be re-run. Both stand, dated. | """Fault Atlas probe — catalogue line 80 (form-080). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-080.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _txt(x): """texte brut d'un XML JATS, balises retirées""" return re.sub(r'<[^>]+>', ' ', x) @sonde(80, "relations quantifiées relatives") def d80(x): return sorted(set(re.findall( r'(?i)\b(\d+[\.,]?\d*[- ]?fold|\d+[\.,]?\d*\s*times (?:higher|lower|greater)|increased by \d+)', _txt(x))))[:5] |
| 31 | Landscape-oriented table form-082 | Landscape-oriented table | probes/2026-08/line-082.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-082.py <dir with the 272 .xml> | 0 | 272 | 2026-09-09 | 1 | Measured absent: the probe finds no carrier among the 272 files. An absence is a result, dated. | """Fault Atlas probe — catalogue line 82 (form-082). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-082.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco # ── 82 — tableau en orientation paysage (propriété de mise en page) ───────── @sonde(82, "tableau en orientation PAYSAGE") def d82(x): o = re.findall(r'<(table-wrap|fig)\b[^>]*orientation="([^"]+)"', x) return [f"{b}:{v}" for b, v in o if v.lower() != 'portrait'][:6] |
| 32 | The table carries its own definitions form-083 | The table carries its own definitions | probes/2026-08/line-083.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-083.py <dir with the 272 .xml> | 47 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 83 (form-083). Question: does a table carry its own definitions in its foot (<table-wrap-foot> with "XX = ..." or "XX: ...")? Rewritten 2026-09-09 as a standalone probe from the August 2026 campaign script (the original was an inline loop, one counter per line, same regexes). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-083.py <dir with the 272 .xml> Result (re-run 2026-09-09): 180/272 carry a table, 128/272 a table foot, 47/272 define acronyms in a table foot. Same figures as the excerpt. A carrier here is a document whose table foot defines its acronyms. """ import re, collections SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def corps(x): m = re.search(r'(?s)<body\b.*?</body>', x) return m.group(0) if m else '' def txt(x): return re.sub(r'\s+', ' ', re.sub(r'<[^>]+>', ' ', x)) @sonde(83, "the table carries its definitions in its foot") def d83(x): pieds = re.findall(r'(?s)<table-wrap-foot\b.*?</table-wrap-foot>', x) out = [] for p in pieds: m = re.search(r'\b[A-Z]{2,6}\b\s*[=:]', txt(p)) if m: out.append(txt(p)[max(0, m.start()-10):m.start()+60].strip()) return out[:5] | |
| 33 | Non-atomic cells form-084 | Non-atomic cells | probes/2026-08/line-084.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-084.py <dir with the 272 .xml> | 70 | 272 | 2026-09-09 | 0 | The figure quoted in the excerpt was counted on 2026-08-08 by a finer, hand-run measurement whose code was not kept; this probe is the one that was kept, and its figure is the one that can be re-run. Both stand, dated. | """Fault Atlas probe — catalogue line 84 (form-084). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-084.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco @sonde(84, "cellules non atomiques") def d84(x): c = re.findall(r'<td[^>]*>([^<]{2,40})</td>', x) nn = [v for v in c if re.match(r'^\s*[\d.,]+\s*[(±]\s*[\d.,]', v) or ' / ' in v] return nn[:5] |
| 34 | Non-standard decimal separator form-085 | Non-standard decimal separator | probes/2026-08/line-085.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-085.py <dir with the 272 .xml> | 54 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 85 (form-085). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-085.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _txt(x): """texte brut d'un XML JATS, balises retirées""" return re.sub(r'<[^>]+>', ' ', x) @sonde(85, "séparateur décimal — LES DEUX dans le même document") def d85(x): t = _txt(x) virg = re.findall(r'\b\d+,\d\b', t); pt = re.findall(r'\b\d+\.\d\b', t) return [f"virgule: {virg[:3]}", f"point: {pt[:3]}"] if virg and pt else [] | |
| 35 | One compound, several names form-086 | One compound, several names | probes/2026-08/line-086.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-086.py <dir with the 272 .xml> | 20 | 272 | 2026-09-09 | 0 | The figure quoted in the excerpt was counted on 2026-08-08 by a finer, hand-run measurement whose code was not kept; this probe is the one that was kept, and its figure is the one that can be re-run. Both stand, dated. | """Fault Atlas probe — catalogue line 86 (form-086). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-086.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _txt(x): """texte brut d'un XML JATS, balises retirées""" return re.sub(r'<[^>]+>', ' ', x) @sonde(86, "un composé, plusieurs noms") def d86(x): t = _txt(x) return sorted(set(re.findall(r'\b[A-Za-z][A-Za-z0-9-]{3,}\s*\((?:also known as|aka|CAS|[A-Z]{2,6}-?\d{2,6})\)', t)))[:4] |
| 36 | Mixed units and conventions form-087 | Mixed units and conventions | probes/2026-08/line-087.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-087.py <dir with the 272 .xml> | 62 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 87 (form-087). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-087.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _corps(x): m = re.search(r'(?s)<body\b[^>]*>(.*?)</body>', x) return m.group(1) if m else '' # ── 87 · unités et conventions mixtes ────────────────────────────────────── FAMILLES = { 'masse' : r'\b\d+[\.,]?\d*\s*(?:µg|mcg|ug|mg|kg|ng|pg)\b', 'volume' : r'\b\d+[\.,]?\d*\s*(?:mL|ml|µL|uL|cc|dL)\b', 'molaire': r'\b\d+[\.,]?\d*\s*(?:mol|mmol|µmol|nmol|M|mM|µM|nM)\b', 'pourcent': r'\d+[\.,]?\d*\s*%', 'duree' : r'\b\d+[\.,]?\d*\s*(?:s|min|h|hours?|minutes?|seconds?|days?|weeks?)\b', 'temp' : r'\b−?-?\d+[\.,]?\d*\s*°\s*C\b', } def _txt(x): x = re.sub(r'(?s)<(front|back|ref-list)\b.*?</\1>', ' ', x) return re.sub(r'<[^>]+>', ' ', x) @sonde(87, "unités mixtes : ≥3 familles de mesure dans le même corps") def d87(x): t = _txt(_corps(x)) trouv = {k: len(re.findall(p, t)) for k, p in FAMILLES.items()} pres = {k: v for k, v in trouv.items() if v >= 2} return [f"{k}×{v}" for k, v in sorted(pres.items(), key=lambda kv: -kv[1])] if len(pres) >= 3 else [] | |
| 37 | Collective entity form-088 | Collective entity | probes/2026-08/line-088.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-088.py <dir with the 272 .xml> | 71 | 272 | 2026-09-09 | 0 | The figure quoted in the excerpt was counted on 2026-08-08 by a finer, hand-run measurement whose code was not kept; this probe is the one that was kept, and its figure is the one that can be re-run. Both stand, dated. | """Fault Atlas probe — catalogue line 88 (form-088). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-088.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _txt(x): """texte brut d'un XML JATS, balises retirées""" return re.sub(r'<[^>]+>', ' ', x) @sonde(88, "entité collective") def d88(x): """⚠️ RESSERRÉE le 08/08 : la 1re version captait « Neural Network » et « control group » — des termes techniques, pas des collectifs d'auteurs. On exige maintenant que le collectif figure comme AUTEUR ou soit cité comme tel.""" dans_contrib = re.findall(r'<collab[^>]*>([^<]{4,80})</collab>', x) nomme = re.findall( r'\b((?:[A-Z][A-Za-z]{2,}\s){1,3}(?:Consortium|Collaboration|Study Group|Investigators|Trial Group|Working Group))\b', _txt(x)) exclus = re.compile(r'(?i)neural|control|treatment|placebo|experimental|intervention') return sorted(set(v.strip() for v in dans_contrib + nomme if not exclus.search(v)))[:4] |
| 38 | Complex attributions and affiliations form-089 | Complex attributions and affiliations | probes/2026-08/line-089.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-089.py <dir with the 272 .xml> | 119 | 272 | 2026-09-09 | 0 | The figure quoted in the excerpt was counted on 2026-08-08 by a finer, hand-run measurement whose code was not kept; this probe is the one that was kept, and its figure is the one that can be re-run. Both stand, dated. | """Fault Atlas probe — catalogue line 89 (form-089). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-089.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco @sonde(89, "attributions et affiliations complexes") def d89(x): a = re.findall(r'<aff\b', x); c = re.findall(r'<contrib\b', x) return [f"{len(c)} auteurs · {len(a)} affiliations"] if len(a) > 3 and len(c) > 3 else [] |
| 39 | Text-to-figure reference by label form-090 | Text-to-figure reference by label | probes/2026-08/line-090.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-090.py <dir with the 272 .xml> | 186 | 272 | 2026-09-09 | 0 | The figure quoted in the excerpt was counted on 2026-08-08 by a finer, hand-run measurement whose code was not kept; this probe is the one that was kept, and its figure is the one that can be re-run. Both stand, dated. | """Fault Atlas probe — catalogue line 90 (form-090). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-090.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco @sonde(90, "renvoi texte ↔ figure par étiquette") def d90(x): return sorted(set(re.findall(r'<xref[^>]*ref-type="fig"[^>]*>([^<]{1,14})</xref>', x)))[:5] |
| 40 | Dense relative chronology form-091 | Dense relative chronology | probes/2026-08/line-091.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-091.py <dir with the 272 .xml> | 3 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 91 (form-091). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-091.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco CORPS = lambda x: re.search(r'(?s)<body\b.*?</body>', x) REL = re.compile(r'(?i)\b(' r'\d+\s*(?:min|minutes?|hours?|days?|weeks?|months?|years?)\s+(?:later|earlier|after|before|from)' r'|(?:one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|\d{1,3})\s+' r'(?:minutes?|hours?|days?|weeks?|months?|years?)\s+(?:later|earlier|previously|after|before)' r'|on day \d{1,2}|at (?:day|week|month) \d{1,2}' r'|the (?:next|following|previous) (?:day|week|month|year|morning|evening)' r'|(?:shortly|soon|immediately) (?:after|before)' r'|thereafter|subsequently|from the onset|since the onset|prior to (?:admission|onset)' r')\b') def _txt(x): """texte brut d'un XML JATS, balises retirées""" return re.sub(r'<[^>]+>', ' ', x) @sonde(91, "chronologie RELATIVE dense (≥8 repères, presque pas de dates)") def d91(x): m = CORPS(x) if not m: return [] t = _txt(m.group(0)) rel = REL.findall(t) abs_ = re.findall(r'\b(?:19|20)\d{2}\b|\b\d{1,2}\s+(?:January|February|March|April|May|June|July|August|September|October|November|December)\b', t) return [f"{len(rel)} repères relatifs / {len(abs_)} dates absolues : {sorted(set(rel))[:4]}"] \ if len(rel) >= 8 and len(rel) > len(abs_) else [] | |
| 41 | Drop cap form-092 | Drop cap | probes/2026-08/line-092.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-092.py <dir with the 272 .xml> | 60 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 92 (form-092). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-092.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _corps(x): m = re.search(r'<body\b[^>]*>(.*?)</body>', x, re.S) return m.group(1) if m else '' def _txt(x): return re.sub(r'<[^>]+>', ' ', x) @sonde(92, "lettrine — capitale isolée collée au mot suivant") def d92(x): t = re.sub(r'\s+', ' ', _txt(_corps(x))) return sorted(set(re.findall(r'(?<![A-Za-z])([A-Z]) ([A-Z]{3,})\b', t)))[:5] | |
| 42 | Content added by digitisation form-093 | Content added by digitisation | probes/2026-08/line-093.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-093.py <dir with the 272 .xml> | 25 | 272 | 2026-09-09 | 0 | The regex matches 25 documents; the excerpt's 0/272 is a human reading of those matches (publisher lines and one physics sentence, not digitisation stamps). Raw matches and reviewed zero both kept. | """Fault Atlas probe — catalogue line 93 (form-093). Question: does the text carry a stamp added by a digitisation chain ("Downloaded from", "Electronic Library Service", "scanned by"...)? Rewritten 2026-09-09 as a standalone probe from the August 2026 campaign script (the original was an inline loop, one counter per line, same regexes). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-093.py <dir with the 272 .xml> Result (re-run 2026-09-09): the regex matches 25/272 documents. The excerpt says 0/272 after reading the matches by hand: modern "Downloaded from" / "Published online by" are publisher lines, not digitisation stamps, and "scanned by the bare plate" (phys-PMC13257740) is a physics sentence. The probe gives the raw matches; the zero is a human reading of them. Both kept. """ import re, collections SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def corps(x): m = re.search(r'(?s)<body\b.*?</body>', x) return m.group(0) if m else '' def txt(x): return re.sub(r'\s+', ' ', re.sub(r'<[^>]+>', ' ', x)) NUM = re.compile(r'(Electronic Library Service|Published online by|Downloaded from ' r'https?://|Provided by the [A-Z]|scanned by|This content downloaded)', re.I) @sonde(93, "content added by the digitisation chain (raw regex matches)") def d93(x): return sorted(set(NUM.findall(txt(x))))[:4] |
| 43 | The three PDF regimes (native, OCR layer, pure image) form-097 | The three PDF regimes (native, OCR layer, pure image) | probes/2026-08/line-097.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-097.py <dir with the 272 .xml> | 0 | 272 | 2026-09-09 | 1 | Measured absent: the probe finds no carrier among the 272 files. An absence is a result, dated. | """Fault Atlas probe — catalogue line 97 (form-097). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-097.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco # ── 97 · RÉGIMES DE PDF : il faut un flux PDF. Y en a-t-il un seul ? @sonde(97, "régime de PDF (natif / OCR posé / image pure)") def d97(x): return ['flux PDF présent'] if x.lstrip().startswith('%PDF') else [] |
| 44 | Sample size written in words form-100 | Sample size written in words | probes/2026-08/line-100.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-100.py <dir with the 272 .xml> | 80 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 100 (form-100). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-100.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _txt(x): """texte brut d'un XML JATS, balises retirées""" return re.sub(r'<[^>]+>', ' ', x) @sonde(100, "effectif écrit en toutes lettres") def d100(x): return sorted(set(re.findall( r'(?i)\b(one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|twenty|thirty|forty|fifty)[\s-]?' r'(?:\w+\s)?(?:patients?|subjects?|participants?|cases?|animals?|samples?)\b', _txt(x))))[:5] | |
| 45 | Sample size is computed, not stated form-101 | Sample size is computed, not stated | probes/2026-08/line-101.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-101.py <dir with the 272 .xml> | 25 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 101 (form-101). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-101.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco MOTS_SUJETS = r'(?:patients?|participants?|subjects?|cases?|women|men|children|individuals)' def _txt(x): """texte brut d'un XML JATS, balises retirées""" return re.sub(r'<[^>]+>', ' ', x) # ── 101 — l'effectif ne se lit nulle part : a = b + c ─────────────────────── def effectifs(x): t = _txt(x) v = re.findall(rf'(?i)\b(\d{{1,5}})\s+(?:\w+\s+){{0,2}}{MOTS_SUJETS}\b', t) v += re.findall(rf'(?i){MOTS_SUJETS}\s*[\(:=]?\s*n\s*=\s*(\d{{1,5}})', t) return sorted({int(n) for n in v if 1 < int(n) < 100000}) @sonde(101, "effectif obtenu par addition (a = b + c), écrit nulle part") def d101(x): v = effectifs(x) if len(v) < 3: return [] t = [f"{a}={b}+{c}" for a in v for i, b in enumerate(v) for c in v[i + 1:] if b + c == a and b != a and c != a] return sorted(set(t))[:5] | |
| 46 | Furniture language differs from body language form-102 | Furniture language differs from body language | probes/2026-08/line-102.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-102.py <dir with the 272 .xml> | 53 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 102 (form-102). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-102.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco @sonde(102, "langue du mobilier ≠ langue du corps") def d102(x): l = sorted(set(re.findall(r'xml:lang="([^"]+)"', x))) return l if len(l) > 1 else [] | |
| 47 | No apparatus at all form-104 | No apparatus at all | probes/2026-08/line-104.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-104.py <dir with the 272 .xml> | 25 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 104 (form-104). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-104.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _corps(x): m = re.search(r'(?s)<body\b[^>]*>(.*?)</body>', x) return m.group(1) if m else '' # ── 104 · absence totale d'appareil ──────────────────────────────────────── @sonde(104, "absence d'appareil : ni résumé, ni mots-clés, ni section nommée") def d104(x): res = bool(re.search(r'<abstract\b', x)) kwd = bool(re.search(r'<kwd\b', x)) corps = _corps(x) titres = re.findall(r'<title>([^<]{2,60})</title>', corps) manque = [n for n, v in (('résumé', res), ('mots-clés', kwd), ('titre de section', bool(titres))) if not v] return [f"manque: {', '.join(manque)}"] if len(manque) == 3 and corps else [] | |
| 48 | Signature mark of a gathering form-106 | Signature mark of a gathering | probes/2026-08/line-106.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-106.py <dir with the 272 .xml> | 0 | 272 | 2026-09-09 | 1 | Measured absent: the probe finds no carrier among the 272 files. An absence is a result, dated. | """Fault Atlas probe — catalogue line 106 (form-106). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-106.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco CORPS = lambda x: re.search(r'(?s)<body\b.*?</body>', x) @sonde(106, "signature de cahier (chiffre nu isolé, artefact d'imprimerie)") def d106(x): """⚠️ CORRIGÉE le 08/08 après audit. La 1re version trouvait 1/272 — et l'inspection a montré que ces « chiffres nus » étaient des CELLULES DE TABLEAU (<td><p>4</p></td> dans nonEN-PMC13267755). On retire tableaux, listes et figures : il reste 0/272. La signature de cahier est un artefact d'IMPRIMERIE, elle n'atteint pas le XML.""" m = CORPS(x) if not m: return [] b = re.sub(r'(?s)<table-wrap\b.*?</table-wrap>', ' ', m.group(0)) b = re.sub(r'(?s)<(?:list|fig|disp-formula)\b.*?</(?:list|fig|disp-formula)>', ' ', b) p = re.findall(r'<p[^>]*>\s*(\d{1,3})\s*</p>', b) return [f"paragraphe = chiffre nu : {p[:5]}"] if p else [] |
| 49 | Several DOIs per document form-107 | Several DOIs per document | probes/2026-08/line-107.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-107.py <dir with the 272 .xml> | 27 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 107 (form-107). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-107.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco @sonde(107, "plusieurs DOI") def d107(x): d = re.findall(r'<article-id[^>]*pub-id-type="doi"[^>]*>([^<]+)', x) return d if len(d) > 1 else [] | |
| 50 | Partial ORCID form-108 | Partial ORCID | probes/2026-08/line-108.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-108.py <dir with the 272 .xml> | 64 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 108 (form-108). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-108.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco @sonde(108, "ORCID") def d108(x): return sorted(set(re.findall(r'orcid\.org/([0-9X\-]{10,})', x)))[:6] | |
| 51 | Structured abstract form-109 | Structured abstract | probes/2026-08/line-109.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-109.py <dir with the 272 .xml> | 106 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 109 (form-109). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-109.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _resume(x): m = re.search(r'<abstract\b[^>]*>(.*?)</abstract>', x, re.S) return m.group(1) if m else '' @sonde(109, "résumé structuré (rubriques nommées dans l'abstract)") def d109(x): a = _resume(x) ti = [re.sub(r'<[^>]+>', '', t).strip() for t in re.findall(r'<sec\b[^>]*>\s*<title>(.*?)</title>', a, re.S)] ti = [t for t in ti if t] return ti[:6] if len(ti) >= 2 else [] | |
| 52 | Pre-registration identifier form-110 | Pre-registration identifier | probes/2026-08/line-110.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-110.py <dir with the 272 .xml> | 19 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 110 (form-110). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-110.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco @sonde(110, "registre d'essai") def d110(x): return sorted(set(re.findall(r'\b(NCT\d{8}|ISRCTN\d{8}|EUCTR\d{4}-\d{6}-\d{2})\b', x)))[:4] | |
| 53 | Supplementary material cited and absent form-113 | Supplementary material cited and absent | probes/2026-08/line-113.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-113.py <dir with the 272 .xml> | 1 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 113 (form-113). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-113.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco ATTACHE = re.compile(r'<(?:inline-)?supplementary-material\b|<media\b[^>]*xlink:href=') # ── 113 · MATÉRIEL SUPPLÉMENTAIRE CITÉ, mais AUCUN fichier attaché # ⚠️ 1re version CORRIGÉE deux fois. (a) elle ne comptait comme « attaché » que # xlink:href porté par <supplementary-material> — or le fichier pend souvent d'un # <media> imbriqué : 115/272 faux porteurs. (b) « supplementary material » nu, dans # la formule de disponibilité des données, n'est PAS la citation d'un objet : on # exige désormais un objet NOMMÉ (« Supplementary Fig. S1 », « S1 Table »). CITE_SUP = re.compile( r'\b(?:[Ss]upplementary|[Ss]upplemental|[Ss]upporting)\s+' r'(?:[Tt]able|[Ff]igure|[Ff]ig\.?|[Ff]ile|[Dd]ataset|[Dd]ata|[Mm]aterial|' r'[Ii]nformation|[Mm]ovie|[Vv]ideo|[Aa]ppendix|[Nn]ote|[Mm]ethod)s?\s*(?:S\s?\d+|\d+)\b' r'|\bS\d+\s+(?:Table|Fig(?:ure)?|File|Appendix|Text|Data|Movie|Video)\b' r'|\b(?:Table|Fig(?:ure)?)\s*S\d+\b') def _txt(x): return re.sub(r'<[^>]+>', ' ', x) @sonde(113, "objet supplémentaire NOMMÉ, cité, sans aucun fichier attaché") def d113(x): cites = sorted({re.sub(r'\s+', ' ', m.group(0)).strip() for m in CITE_SUP.finditer(_txt(x))}) return cites[:5] if cites and not ATTACHE.search(x) else [] | |
| 54 | Author declarations form-114 | Author declarations | probes/2026-08/line-114.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-114.py <dir with the 272 .xml> | 190 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 114 (form-114). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-114.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco # ── 114 — déclarations SUR LES AUTEURS (et non sur le sujet) ──────────────── TITRES_114 = (r'(?i)>\s*((?:competing|conflicts? of|conflict of) interests?|' r'authors?.{0,3} contributions?|contributorship statement|' r'data availability(?: statement)?|credit authorship[^<]{0,30}|' r'declarations? of (?:competing )?interests?)\s*<') @sonde(114, "déclarations sur les auteurs (COI, contributions, données)") def d114(x): v = re.findall(r'<fn[^>]*fn-type="(COI-statement|financial-disclosure|con)"', x) v += re.findall(r'<role[^>]*vocab="credit"[^>]*>([^<]{2,40})<', x) v += re.findall(TITRES_114, x) return sorted(set(s.strip() for s in v))[:6] | |
| 55 | Licence on page 1 form-115 | Licence on page 1 | probes/2026-08/line-115.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-115.py <dir with the 272 .xml> | 272 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 115 (form-115). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-115.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _corps(x): m = re.search(r'(?s)<body\b[^>]*>(.*?)</body>', x) return m.group(1) if m else '' # ── 115 · licence en page 1 ──────────────────────────────────────────────── LIC = (r'(?i)(creative commons|open access article|licensed under|' r'permits unrestricted use|http://creativecommons\.org|https://creativecommons\.org)') def _txt(x): x = re.sub(r'(?s)<(front|back|ref-list)\b.*?</\1>', ' ', x) return re.sub(r'<[^>]+>', ' ', x) @sonde(115, "paragraphe de licence — dans <permissions> et/ou dans le corps") def d115(x): perm = bool(re.search(r'<permissions\b', x)) lic = bool(re.search(r'<license\b', x)) dans_corps = sorted(set(m.lower() for m in re.findall(LIC, _txt(_corps(x)))))[:3] if not (perm or lic or dans_corps): return [] return [f"permissions={perm} license={lic}", f"dans le corps: {dans_corps or 'non'}"] | |
| 56 | Crossmark badge (retraction status) form-117 | Crossmark badge (retraction status) | probes/2026-08/line-117.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-117.py <dir with the 272 .xml> | 35 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 117 (form-117). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-117.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco @sonde(117, "article rétracté (marqué dans le document)") def d117(x): return sorted(set(re.findall( r'(?i)<article-title[^>]*>([^<]{0,90}retract[^<]{0,60})</article-title>' r'|(?i)>(\s*retracted:?[^<]{0,60})<', x)[0] if False else [m for m in re.findall(r'(?i)(retraction of|this article has been retracted|<related-article[^>]*retracted[^>]*>)', x)]))[:3] | |
| 57 | Unicode traps in native PDF form-118 | Unicode traps in native PDF | probes/2026-08/line-118.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-118.py <dir with the 272 .xml> | 9 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 118 (form-118). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-118.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco INV = '' @sonde(118, "caractères invisibles") def d118(x): return [f"{c!r} ×{x.count(c)}" for c in INV if c in x] | |
| 58 | Coloured hyperlinks carrying a type form-119 | Coloured hyperlinks carrying a type | probes/2026-08/line-119.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-119.py <dir with the 272 .xml> | 0 | 272 | 2026-09-09 | 1 | Measured absent: the probe finds no carrier among the 272 files. An absence is a result, dated. | """Fault Atlas probe — catalogue line 119 (form-119). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-119.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco @sonde(119, "hyperlien dont la COULEUR porte le type") def d119(x): """⚠️ CORRIGÉE le 08/08 après audit. La 1re version trouvait 24/272 — c'étaient des style="background-color:#ccc" sur des CELLULES, pas sur des liens. On n'interroge plus que les liens eux-mêmes : 0/272. En JATS le type est porté par ref-type / ext-link-type (24 158 et 18 156 occurrences), donc EXPLICITE. La couleur est un fait de RENDU (PDF, HTML), absent de la source.""" return sorted(set(re.findall(r'<(?:ext-link|xref)[^>]*(?:style|color)="[^"]*"', x)))[:4] |
| 59 | Running head with dotted initials form-120 | Running head with dotted initials | probes/2026-08/line-120.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-120.py <dir with the 272 .xml> | 1 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 120 (form-120). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-120.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco @sonde(120, "running head / titre alternatif à initiales pointées") def d120(x): alt = re.findall(r'<alt-title\b[^>]*>(.*?)</alt-title>', x, re.S) alt += re.findall(r'<subtitle\b[^>]*>(.*?)</subtitle>', x, re.S) out = [] for a in alt: a = re.sub(r'\s+', ' ', re.sub(r'<[^>]+>', '', a)).strip() if re.search(r'\b[A-Z]\.\s?[A-Z]?\.?\s?[A-Z][a-zà-ÿ]+', a): out.append(a[:70]) return out[:4] | |
| 60 | Access format of the document (PDF, HTML, XML) form-121 | Access format of the document (PDF, HTML, XML) | probes/2026-08/line-121.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-121.py <dir with the 272 .xml> | 272 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 121 (form-121). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-121.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco # ── 121 · FORMAT D'ACCÈS : quelle forme du document tient-on réellement ? @sonde(121, "format d'accès au document (XML intégral / front seul / PDF)") def d121(x): if x.lstrip().startswith('%PDF'): return ['PDF'] if '<body' in x and '<sec' in x: return ['XML JATS intégral (body + sections)'] if '<body' in x: return ['XML JATS · body sans sections'] if '<article' in x: return ['XML JATS · front seul'] return ['format non reconnu'] | |
| 61 | Numbered display equation form-122 | Numbered display equation | probes/2026-08/line-122.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-122.py <dir with the 272 .xml> | 33 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 122 (form-122). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-122.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco # ── 122 — équation numérotée en display, objet citable ────────────────────── @sonde(122, "équation numérotée en display (objet citable)") def d122(x): v = re.findall(r'(?s)<disp-formula\b.{0,6000}?</disp-formula>', x) lab = [m.group(1) for m in (re.search(r'<label>([^<]{1,12})</label>', b) for b in v) if m] return lab[:6] | |
| 62 | Pseudo-code with double numbering form-126 | Pseudo-code with double numbering | probes/2026-08/line-126.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-126.py <dir with the 272 .xml> | 17 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 126 (form-126). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-126.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _corps(x): m = re.search(r'(?s)<body\b[^>]*>(.*?)</body>', x) return m.group(1) if m else '' def _txt(x): x = re.sub(r'(?s)<(front|back|ref-list)\b.*?</\1>', ' ', x) return re.sub(r'<[^>]+>', ' ', x) # ── 126 · pseudo-code à double numérotation ──────────────────────────────── @sonde(126, "bloc d'algorithme / pseudo-code numéroté") def d126(x): t = _txt(_corps(x)) algo = re.findall(r'(?i)\bAlgorithm\s+\d+\b', t) mots = re.findall(r'(?im)^\s*\d{1,2}:\s|\b(?:Require|Ensure|end for|end while|end procedure)\b', t) balise = re.findall(r'<(?:preformat|code)\b[^>]*>', x) if algo or (mots and balise): return [f"Algorithm N ×{len(algo)}", f"mots-clés pseudo-code ×{len(mots)}", f"<preformat|code> ×{len(balise)}"] return [] | |
| 63 | Acronym defined by typography alone form-128 | Acronym defined by typography alone | probes/2026-08/line-128.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-128.py <dir with the 272 .xml> | 240 | 272 | 2026-09-09 | 0 | The excerpt's 214/272 used a stricter rule (three uses or more, stop list) that was not kept in code; this probe gives 240. Both kept. | """Fault Atlas probe — catalogue line 128 (form-128). Question: is an acronym used repeatedly without ever being introduced as "long form (ACRONYM)", or introduced only by bare apposition ("NADPH, an important antioxidant")? Rewritten 2026-09-09 as a standalone probe from the August 2026 campaign script (the original was an inline loop, one counter per line, same regexes). A carrier here is a document with at least one never-glossed acronym. A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-128.py <dir with the 272 .xml> Result (re-run 2026-09-09): 240/272 use at least one all-capitals acronym twice or more without a parenthetical gloss (4,259 orphan acronyms); 143/272 carry a bare apposition. The excerpt quotes 214/272 with a stricter rule (three uses or more, and a stop list of common words) that was not kept in code. Both kept. """ import re, collections SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def corps(x): m = re.search(r'(?s)<body\b.*?</body>', x) return m.group(0) if m else '' def txt(x): return re.sub(r'\s+', ' ', re.sub(r'<[^>]+>', ' ', x)) ACRO = re.compile(r'\b([A-Z][A-Za-z0-9]{1,5})\b') APPO = re.compile(r'\b([A-Z][A-Za-z0-9]{1,5}), (?:an?|the) [a-z]') @sonde(128, "acronym never glossed by (ACRONYM); bare apposition") def d128(x): t = txt(corps(x) or x) glosed = set(re.findall(r'\(([A-Z][A-Za-z0-9]{1,5})\)', t)) counts = collections.Counter(ACRO.findall(t)) orphans = sorted(a for a, n in counts.items() if n >= 2 and a not in glosed and a.isupper() and len(a) >= 2) if not orphans: return [] out = [f"never glossed: {', '.join(orphans[:6])}"] ap = APPO.findall(t) if ap: out.append("bare apposition: " + ", ".join(sorted(set(ap))[:4])) re… |
| 64 | Repository cover page prepended to the article form-129 | Repository cover page prepended to the article | probes/2026-08/line-129.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-129.py <dir with the 272 .xml> | 0 | 272 | 2026-09-09 | 1 | Measured absent: the probe finds no carrier among the 272 files. An absence is a result, dated. | """Fault Atlas probe — catalogue line 129 (form-129). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-129.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _txt(x): """texte brut d'un XML JATS, balises retirées""" return re.sub(r'<[^>]+>', ' ', x) @sonde(129, "couverture de dépôt ajoutée devant l'article") def d129(x): """⚠️ CORRIGÉE le 08/08 après audit. La 1re version trouvait 53/272 en captant « distributed under the terms of the Creative Commons » — une mention de LICENCE, pas une page de garde. Sur les marqueurs réels d'un dépôt : 0/272.""" return sorted(set(re.findall( r'(?i)(to cite this version|hal id\s*:|archive ouverte|submitted on \d{1,2} ' r'[a-z]{3}|this is a pdf file of an unedited manuscript)', _txt(x))))[:4] |
| 65 | Two conflicting paginations form-130 | Two conflicting paginations | probes/2026-08/line-130.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-130.py <dir with the 272 .xml> | 102 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 130 (form-130). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-130.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco @sonde(130, "deux paginations en conflit") def d130(x): fp = re.findall(r'<fpage[^>]*>([^<]+)', x); el = re.findall(r'<elocation-id[^>]*>([^<]+)', x) return [f"fpage={fp[0]} + elocation={el[0]}"] if fp and el else [] | |
| 66 | Display mathematics inside the abstract form-131 | Display mathematics inside the abstract | probes/2026-08/line-131.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-131.py <dir with the 272 .xml> | 0 | 272 | 2026-09-09 | 1 | Measured absent: the probe finds no carrier among the 272 files. An absence is a result, dated. | """Fault Atlas probe — catalogue line 131 (form-131). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-131.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _resume(x): m = re.search(r'<abstract\b[^>]*>(.*?)</abstract>', x, re.S) return m.group(1) if m else '' @sonde(131, "mathématiques en display DANS le résumé") def d131(x): a = _resume(x) d = re.findall(r'<disp-formula\b[^>]*>(.*?)</disp-formula>', a, re.S) if not d: return [] tex = re.findall(r'<tex-math[^>]*>(.*?)</tex-math>', d[0], re.S) return [f"disp-formula×{len(d)} | {(tex[0] if tex else re.sub(r'<[^>]+>','',d[0]))[:60]}"] |
| 67 | Clickable zone differs from semantic unit form-132 | Clickable zone differs from semantic unit | probes/2026-08/line-132.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-132.py <dir with the 272 .xml> | 21 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 132 (form-132). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-132.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco # ── 132 · ZONE CLIQUABLE ≠ UNITÉ SÉMANTIQUE : l'année reste HORS du lien # ⚠️ 1re version CORRIGÉE : elle cherchait « <xref>Nom</xref> (année) » — l'annexe # décrit ce sens-là (« le lien entoure Staiger and Stock et laisse (1997) dehors »). # Mesuré : 0/272. C'est le sens INVERSE qui existe en JATS — le lien n'entoure que # l'ANNÉE, et le nom d'auteur reste dehors. Même défaut, même conséquence sur # l'ancrage : la zone active n'est pas l'unité sémantique. COUPEE = re.compile(r"([A-ZÀ-Þ][\w.'’\-]*(?:\s+(?:et\s+al\.?|and|&|[A-ZÀ-Þ][\w.'’\-]*)){0,4})" r"\s*<xref[^>]*ref-type=\"bibr\"[^>]*>\s*\(?\s*((?:19|20)\d{2}[a-z]?)\s*\)?\s*</xref>") @sonde(132, "zone cliquable qui coupe la citation (nom hors du <xref>)") def d132(x): return [f"« {a.strip()} » DEHORS + <xref>{b}</xref>" for a, b in COUPEE.findall(x)][:5] | |
| 68 | Clickable zone differs from semantic unit form-132 | Clickable zone differs from semantic unit | probes/2026-08/line-132-counterform.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-132-counterform.py <dir with the 272 .xml> | 16 | 272 | 2026-09-09 | 1 | Counter-form of line 132: the clickable span carries the WHOLE citation, year included. 16/272, as quoted in the excerpt. | """Fault Atlas probe — catalogue line 132, COUNTER-FORM (form-132): the link carries the WHOLE citation. Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-132-counterform.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco # ── 132 · ZONE CLIQUABLE ≠ UNITÉ SÉMANTIQUE : l'année reste HORS du lien # ⚠️ 1re version CORRIGÉE : elle cherchait « <xref>Nom</xref> (année) » — l'annexe # décrit ce sens-là (« le lien entoure Staiger and Stock et laisse (1997) dehors »). # Mesuré : 0/272. C'est le sens INVERSE qui existe en JATS — le lien n'entoure que # l'ANNÉE, et le nom d'auteur reste dehors. Même défaut, même conséquence sur # l'ancrage : la zone active n'est pas l'unité sémantique. COUPEE = re.compile(r"([A-ZÀ-Þ][\w.'’\-]*(?:\s+(?:et\s+al\.?|and|&|[A-ZÀ-Þ][\w.'’\-]*)){0,4})" r"\s*<xref[^>]*ref-type=\"bibr\"[^>]*>\s*\(?\s*((?:19|20)\d{2}[a-z]?)\s*\)?\s*</xref>") ENTIERE = re.compile(r'<xref[^>]*ref-type="bibr"[^>]*>([^<]*(?:19|20)\d{2}[^<]*)</xref>') @sonde(1320, "contre-forme : la zone cliquable porte la citation ENTIÈRE") def d1320(x): return [f"<xref>{a.strip()}</xref>" for a in ENTIERE.findall(x) if not COUPEE.search(x)][:3] |
| 69 | HTML fragments the text form-133 | HTML fragments the text | probes/2026-08/line-133.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-133.py <dir with the 272 .xml> | 38 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 133 (form-133). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-133.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _txt(x): """texte brut d'un XML JATS, balises retirées""" return re.sub(r'<[^>]+>', ' ', x) # ── 133 — le balisage fragmente ET DOUBLE le texte ────────────────────────── # ⚠️ CORRIGÉE le 08/08 : la 1re version cherchait <annotation> TeX — 0 partout. # Le doublement réel de ce corpus passe par <alternatives> : la MÊME formule y # est écrite deux fois, une fois en <tex-math>, une fois en MathML (ou en image). @sonde(133, "le balisage FRAGMENTE le symbole (MathML)") def d133(x): """Ce que mesure la sonde : un symbole contigu à la lecture devient, une fois les balises retirées bêtement, plusieurs morceaux séparés par des espaces.""" ex = [] for b in re.findall(r'(?s)<(?:mml:)?math\b.{0,4000}?</(?:mml:)?math>', x): nu = re.sub(r'\s+', ' ', _txt(b)).strip() # lecture naïve vrai = ''.join(re.findall(r'<(?:mml:)?m[ino]\b[^>]*>([^<]*)<', b)) # lecture assemblée if vrai and nu != vrai and len(nu.split()) > 1: ex.append(f"{len(b)}o : naïf {nu[:34]!r} ≠ assemblé {vrai[:34]!r}") if len(ex) >= 3: break return ex | |
| 70 | Page 1 is not representative form-134 | Page 1 is not representative | probes/2026-08/line-134.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-134.py <dir with the 272 .xml> | 196 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 134 (form-134). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-134.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco # ── 134 · page 1 non représentative ──────────────────────────────────────── @sonde(134, "notion de PAGE dans le XML (préalable à « page 1 non représentative »)") def d134(x): p = re.findall(r'<(?:page|page-break|pb)\b[^>]*>', x) return [f"marqueurs de page ×{len(p)}"] if p else [] | |
| 71 | Translated verbatim form-135 | Translated verbatim | probes/2026-08/line-135.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-135.py <dir with the 272 .xml> | 12 | 272 | 2026-09-09 | 1 | 12 carriers = 11 documents with a block quotation + 1 with a translation marker (PMC13234180, "our translation"), the two figures the excerpt quotes. | """Fault Atlas probe — catalogue line 135 (form-135). Question: does the document carry a block quotation (<disp-quote>), and a marker that it is a translation ("our translation", "translated by")? Rewritten 2026-09-09 as a standalone probe from the August 2026 campaign script (the original was an inline loop, one counter per line, same regexes). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-135.py <dir with the 272 .xml> Result (re-run 2026-09-09): 11/272 carry a block quotation (102 blocks); 1/272 carries a translation marker (phys-PMC13234180, "our translation"). Same figures as the excerpt. A carrier here is a document with a block quotation or a translation marker (11 + 1 = 12). """ import re, collections SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def corps(x): m = re.search(r'(?s)<body\b.*?</body>', x) return m.group(0) if m else '' def txt(x): return re.sub(r'\s+', ' ', re.sub(r'<[^>]+>', ' ', x)) TRAD = re.compile(r'(?i)\b(translated (?:by|from)|our translation|translation (?:by|from|of) the authors?|\(translation\))\b') @sonde(135, "verbatim quotation, possibly translated") def d135(x): n = len(re.findall(r'<disp-quote\b', x)) m = TRAD.findall(txt(x)) out = [f"disp-quote x{n}"] if n else [] if m: out.append("translation marker: " + ", ".join(sorted(set(m)))) return out |
| 72 | Citation with internal locator form-139 | Citation with internal locator | probes/2026-08/line-139.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-139.py <dir with the 272 .xml> | 71 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 139 (form-139). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-139.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _txt(x): """texte brut d'un XML JATS, balises retirées""" return re.sub(r'<[^>]+>', ' ', x) @sonde(139, "citation avec localisateur interne") def d139(x): return sorted(set(re.findall(r'(?i)\((?:see\s+)?(?:p\.|pp\.|page|table|fig\.?)\s*\d+[^\)]{0,12}\)', _txt(x))))[:5] | |
| 73 | Editorial status printed in the document form-140 | Editorial status printed in the document | probes/2026-08/line-140.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-140.py <dir with the 272 .xml> | 6 | 272 | 2026-09-09 | 0 | The figure quoted in the excerpt was counted on 2026-08-08 by a finer, hand-run measurement whose code was not kept; this probe is the one that was kept, and its figure is the one that can be re-run. Both stand, dated. | """Fault Atlas probe — catalogue line 140 (form-140). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-140.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco def _txt(x): """texte brut d'un XML JATS, balises retirées""" return re.sub(r'<[^>]+>', ' ', x) @sonde(140, "statut éditorial imprimé (preprint, non relu)") def d140(x): """⚠️ CORRIGÉE le 08/08 : la 1re version captait « pmc-prop-preprint », une métadonnée PMC présente dans 272/272 articles. Une sonde à 100 % est une alarme de méthode, pas une découverte. On retire d'abord le bloc custom-meta.""" corps = re.sub(r'(?s)<custom-meta-group.*?</custom-meta-group>', ' ', x) corps = re.sub(r'(?s)<processing-meta.*?</processing-meta>', ' ', corps) return sorted(set(re.findall( r'(?i)\b(this is a preprint|not (?:been )?peer[- ]reviewed|author manuscript|' r'accepted manuscript|posted as a preprint)\b', _txt(corps))))[:4] |
| 74 | Multiple abstracts from different registries form-141 | Multiple abstracts from different registries | probes/2026-08/line-141.py | python | corpus-europe-pmc-jats-2023-2026 | 2026-08-08 | python3 tools/run_probe.py probes/2026-08/line-141.py <dir with the 272 .xml> | 57 | 272 | 2026-09-09 | 1 | """Fault Atlas probe — catalogue line 141 (form-141). Extracted verbatim from the August 2026 observation campaign (Loxyn SAS, Lyon). A probe answers one question on one JATS XML file: "is this fault here, and where?" It repairs nothing and decides nothing. Returns a list of excerpts, empty if absent. Run on the reproducible corpus (see corpora/europe-pmc-jats-2023-2026-272.json): python3 tools/run_probe.py probes/2026-08/line-141.py <dir with the 272 .xml> """ import re, collections, unicodedata SONDES = {} def sonde(ligne, nom): def deco(f): SONDES[ligne] = (nom, f); return f return deco @sonde(141, "résumés multiples") def d141(x): a = re.findall(r'<abstract\b[^>]*>', x) return a if len(a) > 1 else [] | |
| 75 | Legal basis encoded as an empty identifier URI form-182 | Legal basis encoded as an empty identifier URI | probes/2026-09/form-182-empty-celex-uri.sparql | sparql | corpus-2026-09-eurlex | 2026-09-09 | curl -G https://publications.europa.eu/webapi/rdf/sparql -H 'Accept: text/csv' --data-urlencode query@probes/2026-09/form-182-empty-celex-uri.sparql | 43 | 2026-09-09 | 1 | Re-run live: 43 acts, the same list as the frozen lot A. | # Fault Atlas probe — form-182: legal basis encoded as an empty CELEX URI. # Endpoint: https://publications.europa.eu/webapi/rdf/sparql (public, no key) # Population: the whole Cellar graph as served on the day of the run. # Written 2026-09-09 from the lot A observation (Alexandrie, frozen N-Triples); # re-run live 2026-09-09 by the Fault Atlas maintainer: 43 acts, same list. # # curl -G https://publications.europa.eu/webapi/rdf/sparql \ # -H 'Accept: text/csv' --data-urlencode query@probes/2026-09/form-182-empty-celex-uri.sparql # # Expected: 43 rows. Widening the predicate (second query, commented) shows the same # empty URI as object of 14 predicates, 8,164 triples in all — form-182 in lot A is # the tip of it. PREFIX cdm: <http://publications.europa.eu/ontology/cdm#> SELECT ?act WHERE { ?act cdm:resource_legal_based_on_resource_legal <http://publications.europa.eu/resource/celex/> . } ORDER BY ?act # --- wider count, run separately: # SELECT ?p (COUNT(*) AS ?n) WHERE { ?s ?p <http://publications.europa.eu/resource/celex/> . } GROUP BY ?p | |
| 76 | Half of the in-force acts have no structured text in the official dump form-183 | Half of the in-force acts have no structured text in the official dump | probes/2026-09/form-183-dump-coverage.py | python | corpus-2026-09-eurlex | 2026-09-09 | python3 probes/2026-09/form-183-dump-coverage.py <dir holding the three dump .zip> | 27792 | 58686 | 2026-09-09 | 1 | Re-run on the 2026-09-06 archives (stream read, nothing extracted): 58,686 works; 30,047 with EN Formex; 29,892 with FR; 27,792 without any (47 %); 22,293 .tif in the EN archive. Same figures. | #!/usr/bin/env python3 """Fault Atlas probe — form-183: half of the in-force acts have no structured text in the official dump. Input: the three archives of the Publications Office data dump for legal acts in force (sector 3), as served on 2026-09-06 behind EU Login (https://datadump.publications.europa.eu/, "LEG" collection): LEG_MTD_20260906_01_00.zip metadata, one tree_non_inferred.rdf per work LEG_EN_FMX_20260906_01_00.zip Formex XML sections, English LEG_FR_FMX_20260906_01_00.zip Formex XML sections, French Nothing is extracted: the archives are read as streams, their content is not rewritten. Adapted from the collector's inventory script (Alexandrie, Loxyn, 2026-09-09). Run: python3 probes/2026-09/form-183-dump-coverage.py <dir holding the three .zip> Result on the 2026-09-06 dump: 58,686 works in the metadata, 30,047 with at least one EN Formex file, 29,892 with FR, 27,792 (47 %) with no Formex in either language. On the EN side the archive carries 22,293 .tif files: old acts scanned, never converted. The dump is complete for metadata and half complete for text. """ import sys, os, re, zipfile, collections def main(dump_dir): zips = sorted(x for x in os.listdir(dump_dir) if x.endswith(".zip")) mtd = [z for z in zips if "_MTD_" in z] fmx = {z.split("_")[1].lower(): z for z in zips if "_FMX_" in z} if not mtd or not fmx: sys.exit("need one *_MTD_*.zip and at least one *_FMX_*.zip in " + dump_dir) works_with_text = {} tif = collections.Counter() for lang, name in fmx.items(): z = zipfile.ZipFile(os.path.join(dump_dir, name)) per_work = collections.Counter() for n in z.namelist(): if "/" not in n: continue per_work[n.split("/")[0]] += 1 if n.lower().endswith(".tif"): tif[lang] += 1 works_with_text[lang] = set(per_work) z.close() z = zipfile.ZipFile(os.path.join(dump_dir, mtd[0])) works = {n.split("/")[0] for n in z.namelist() if n.endsw… |
| 77 | Sentinel date in an official record, contradicted by the identifier's own year form-184 | Sentinel date in an official record, contradicted by the identifier's own year | probes/2026-09/form-184-sentinel-date.sparql | sparql | corpus-2026-09-eurlex | 2026-09-09 | curl -G https://publications.europa.eu/webapi/rdf/sparql -H 'Accept: text/csv' --data-urlencode query@probes/2026-09/form-184-sentinel-date.sparql | 7 | 2026-09-09 | 1 | Re-run live, restricted to works taking part in repeals/amends/based_on: 7, the same seven CELEX. | # Fault Atlas probe — form-184: sentinel date 1003-03-03 in cdm:work_date_document. # Endpoint: https://publications.europa.eu/webapi/rdf/sparql (public, no key) # Written 2026-09-09 from the lot A observation (7 works among the 265,172 resources # of the three relations repeals/amends/based_on); re-run live 2026-09-09: # whole Cellar: 351 works carry the sentinel, 80 of them have a CELEX; # restricted to works that take part in one of the three relations: 7, the same # seven CELEX as lot A (51966FC0402, 51984PC0606, 51988EC1677, 51993EC1858, # 51995EC1429, 51995EC1591, 51997EC0936). # # curl -G https://publications.europa.eu/webapi/rdf/sparql \ # -H 'Accept: text/csv' --data-urlencode query@probes/2026-09/form-184-sentinel-date.sparql PREFIX cdm: <http://publications.europa.eu/ontology/cdm#> SELECT ?work ?celex WHERE { ?work cdm:work_date_document ?date . FILTER(STR(?date) = "1003-03-03") ?work cdm:resource_legal_id_celex ?celex . { ?work cdm:resource_legal_repeals_resource_legal ?x } UNION { ?x cdm:resource_legal_repeals_resource_legal ?work } UNION { ?work cdm:resource_legal_amends_resource_legal ?x } UNION { ?x cdm:resource_legal_amends_resource_legal ?work } UNION { ?work cdm:resource_legal_based_on_resource_legal ?x } UNION { ?x cdm:resource_legal_based_on_resource_legal ?work } } GROUP BY ?work ?celex ORDER BY ?celex # --- whole-Cellar counts, run separately: # SELECT (COUNT(DISTINCT ?work) AS ?n) WHERE { ?work cdm:work_date_document ?d . FILTER(STR(?d) = "1003-03-03") } # SELECT ?work ?celex WHERE { ?work cdm:work_date_document ?d . FILTER(STR(?d) = "1003-03-03") ?work cdm:resource_legal_id_celex ?celex } ORDER BY ?celex | |
| 78 | Text format served depends on the act's age (404 in one format, 200 in another) form-185 | Text format served depends on the act's age (404 in one format, 200 in another) | probes/2026-09/form-185-format-by-era.sh | http | corpus-2026-09-eurlex | 2026-09-09 | sh probes/2026-09/form-185-format-by-era.sh | 2 | 3 | 2026-09-09 | 1 | Re-run live on three acts: 2026 act served in xhtml+xml only, 1985 act in text/html only, 1968 act in no text format at all. | #!/bin/sh # Fault Atlas probe — form-185: the text format Cellar serves depends on the act's age. # Public REST resolution by CELEX, content negotiation on Accept. No key. # Written 2026-09-09 from lot B (Alexandrie); re-run live 2026-09-09: # 32026D1970 (2026): xhtml+xml 200, text/html 404 # 31985L0374 (1985): xhtml+xml 404, text/html 200 # 31968R5759 (1968): 404 in both, and in Formex — no text served at all # A client that asks for one format only will call half the registry "without text". for celex in 32026D1970 31985L0374 31968R5759; do for accept in "application/xhtml+xml" "text/html" "application/xml;type=fmx4"; do printf '%s | %-28s | ' "$celex" "$accept" curl -s -o /dev/null -L -w '%{http_code} %{content_type}\n' \ -H "Accept: $accept" -H "Accept-Language: eng" \ "http://publications.europa.eu/resource/celex/$celex" done done |
Advanced export
JSON shape: default, array, newline-delimited, object
CREATE TABLE probes(id integer primary key, form_id text references forms(id), form_name text, file text, kind text, corpus_id text, written text, run text, result_count int, result_of int, result_date text, matches_excerpt int, note text, code text);