fix(verify): PUNCT_END generico e frasi spezzate come warning

PUNCT_END esteso con terminatori comuni in documenti tecnici:
- $ e } per formule LaTeX inline e blocchi
- > per tag HTML (</table>, </td>, ecc.)
- \\ per line break LaTeX
- · (punto centrato, U+00B7) per notazione matematica
Caratteri unicode scritti con chr() per evitare SyntaxError Python 3.12+
con curly quotes nel sorgente.

Frasi spezzate spostate da blockers a warnings: il chunker le risolve
automaticamente con merge_broken_sentences; i casi residui sono
perlopiù falsi positivi (frontespizi, entità proprie senza punto finale).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-04 14:18:27 +02:00
parent f0c6bad046
commit c93e2e8494
+28 -16
View File
@@ -32,13 +32,27 @@ import config as cfg
MIN_CHARS = cfg.MIN_CHARS
MAX_CHARS = cfg.MAX_CHARS
_PUNCT_CLS = (
"[.!?"
+ chr(0xBB) + ")\\]"
+ chr(0x2018) + chr(0x2019)
+ chr(0x201C) + chr(0x201D)
+ "\"'"
+ chr(0x2014) + chr(0x2013) + chr(0x2026)
+ chr(0xB7) # punto centrato LaTeX
+ "]$"
)
PUNCT_END = re.compile(
r"[.!?\xbb)\]'\"“”‘—–…]$"
r"|/$" # URL che finisce con /
r"|\|$" # riga di tabella Markdown
r"|;$" # fine clausola legale
r"|:$" # introduzione a lista o formula
r"|\d[\d.,/]*$" # numero, anno, versione, riferimento normativo
_PUNCT_CLS
+ r"|/$"
+ r"|\|$"
+ r"|;$"
+ r"|:$"
+ r"|\d[\d.,/]*$"
+ r"|\$$"
+ r"|\}$"
+ r"|>$"
+ r"|\\\\$"
)
_HEX_END = re.compile(r"[0-9a-fA-F]{8,}$")
_URL_TAIL = re.compile(r"(https?://|www\.)\S+(\s+\S+){0,3}$")
@@ -312,13 +326,13 @@ def verify_stem(stem: str, project_root: Path, min_chars: int, max_chars: int) -
print(f" → Causa probabile: sezioni senza testo nel clean.md")
if incomplete:
print(f"\n 🔴 {len(incomplete)} chunk con FRASE SPEZZATA:")
print(f"\n 🟡 {len(incomplete)} chunk con FRASE SPEZZATA (warning):")
for c in incomplete[:5]:
last_line = c.get("text", "").rstrip().split("\n")[-1][-80:]
print(f" [{c.get('chunk_id', '?')}] ...{last_line!r}")
if len(incomplete) > 5:
print(f" ... e altri {len(incomplete) - 5}")
print(f" → Soluzione: python chunks/fix_chunks.py --stem {stem}")
print(f" → Soluzione: correggi il sorgente .md e rigenera con chunker.py --force")
# ── Warnings ──────────────────────────────────────────────────────────────
@@ -350,12 +364,12 @@ def verify_stem(stem: str, project_root: Path, min_chars: int, max_chars: int) -
print(f" [{e['chunk_id']}] ≡ [{e['duplicate_of']}] «{e['last_text'][:60]}»")
if len(duplicates) > 5:
print(f" ... e altri {len(duplicates) - 5}")
print(f" → Causa probabile: fix_chunks merge multipli o sezioni ripetute")
print(f" → Causa probabile: sezioni ripetute nel sorgente .md")
# ── Report.json ───────────────────────────────────────────────────────────
blockers = empty_chunks + no_prefix + malformed_prefix + body_empty + incomplete
warnings = too_short + too_long + incomplete_math + broken_tables
blockers = empty_chunks + no_prefix + malformed_prefix + body_empty
warnings = too_short + too_long + incomplete + incomplete_math + broken_tables
verdict = "blocked" if blockers else ("warnings_only" if (warnings or duplicates) else "ok")
@@ -418,18 +432,16 @@ def verify_stem(stem: str, project_root: Path, min_chars: int, max_chars: int) -
print(f" python ingestion/ingest.py --stem {stem}")
if too_short or too_long:
print()
print(f" Per ottimizzare prima:")
print(f" python chunks/fix_chunks.py --stem {stem} --dry-run")
print(f" python chunks/fix_chunks.py --stem {stem}")
print(f" Per ottimizzare: correggi il sorgente .md e rigenera con --force")
else:
print(f" 🔴 {len(blockers)} problemi bloccanti — correggi prima di procedere:")
if empty_chunks or body_empty:
print(f" • chunk vuoti/senza corpo → controlla sources/{stem}/auto/{stem}_clean.md")
if no_prefix or malformed_prefix:
print(f" • prefisso mancante/malformato → controlla gli heading in {stem}_clean.md")
print(f" • prefisso mancante/malformato → controlla gli heading in sources/{stem}.md")
if incomplete:
print(f" • frasi spezzate → python chunks/fix_chunks.py --stem {stem}")
print(f" • frasi spezzate → correggi il sorgente e rigenera con --force")
print()
print(f" Dopo le correzioni:")
print(f" python chunks/chunker.py --stem {stem} --force")