fix(document-processors): correct cascade escalation metric + review nits

Address round-1 review on PR #914:
- Attribute the OCR hop in a fast->structured->ocr inline cascade to
  from_tier="structured" (not a second "fast" escalation), so
  astrolabe_document_escalation_total per-tier counts stay accurate.
- Add test_inline_fast_structured_ocr_cascade pinning that two-hop path and the
  metric attribution.
- Note in classify_from_text that its doc-level control ratio is over full_text
  (all pages), not the sampled subset classify_pdf uses.
- Clarify that corrupt_glyphs never lands in the suppressed-escalation counter.
- Dedupe the glyph-corrupt test string into tests/fixtures/glyph_corruption.py.
- Use pytest.approx for the control-char-ratio zero checks (SonarCloud S1244).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-06-16 20:17:56 +02:00
co-authored by Claude Opus 4.8
parent cf7209cd85
commit d5286e39d6
6 changed files with 65 additions and 18 deletions
@@ -247,6 +247,12 @@ class ProcessorRegistry:
result, content, settings, record=True, filename=filename
)
# The tier whose output produced the current ``classification`` -- used as
# ``from_tier`` for a subsequent OCR hop so a fast->structured->ocr cascade
# is attributed correctly (the OCR hop is from ``structured``, not a second
# ``fast`` escalation).
from_tier = "fast"
# Escalate a poor fast extraction up the ladder (fast -> structured -> ocr),
# mirroring the external per-tier path so both modes behave identically. A
# glyph-corrupt layer (the extractor leaked raw glyph codes -- the
@@ -293,6 +299,7 @@ class ProcessorRegistry:
)
if structured_result.success:
result = structured_result
from_tier = "structured"
classification = self._classify_result(
result, content, settings, record=False, filename=filename
)
@@ -331,10 +338,11 @@ class ProcessorRegistry:
if classification.total_chars == 0
else "low_confidence"
)
record_document_escalation("fast", "ocr", reason)
record_document_escalation(from_tier, "ocr", reason)
logger.info(
"Escalating %s fast->ocr (reason=%s)",
"Escalating %s %s->ocr (reason=%s)",
filename or "<bytes>",
from_tier,
reason,
)
ocr_result = await self._run_processor(