A Containment Check Passes Because the List It Checks Is Empty
TLDR
A containment check designed to filter out horizontal line segments inside paragraph text was silently failing, inserting unwanted<hr> divider rules in extracted HTML output. The math (containsBbox(region, segment)) was correct, but the divider classifier ran before paragraph text regions were generated. Moving the divider detection call to execute after text classification resolved the defect immediately without changing a single line of detection logic.
| Pipeline Execution Order | Containment Query Target | Query Result for Paragraph Lines | Extracted Output |
|---|---|---|---|
| Dividers BEFORE Text | regions (Contains only tables/images) | False (Evaluated as "Unclaimed") | Spurious <hr> inside paragraphs |
| Dividers AFTER Text | regions (Contains paragraphs & headings) | True (Correctly identified as contained) | Clean paragraphs without <hr> noise |
Technical defect analysis
Horizontal line segments inside paragraph blocks were being misclassified as standalone section dividers:
<!-- DEFECTIVE OUTPUT: Spurious divider inserted mid-paragraph -->
<p>Unexpectedly, such degradation is not caused by overfitting,</p>
<hr />
<p>and adding more layers leads to higher training error.</p>
The faulty execution order
// DEFECTIVE IMPLEMENTATION: Dividers evaluated before text regions exist
export function classifyPageMonolith(segments, textItems) {
const regions = [];
// Step 1: Detect tables regions.push(...detectLatticeTables(segments));
// Step 2: Detect dividers (FAILS: regions array contains NO text blocks yet!) const dividers = segments.hLines.filter(seg => { const isContained = regions.some(r => isBBoxContained(r.bbox, seg)); return !isContained; // Always returns true for lines inside paragraphs! }); regions.push(...dividers);
// Step 3: Classify text blocks (Paragraphs created TOO LATE) regions.push(...classifyTextBlocks(textItems));
return regions; }
Because regions contained zero paragraph entries at Step 2, every horizontal segment inside a paragraph passed the check as "unclaimed", generating a spurious divider.
Remediation: re-ordering pipeline execution
Re-order the orchestrator sequence so text classification runs before divider detection:
// REFACTORED: Correct Ordering in Pipeline Orchestrator
export function classifyPagePipeline(segments, textItems) {
const regions = [];
// Step 1: Detect tables and images regions.push(...detectLatticeTables(segments)); regions.push(...detectImages(textItems));
// Step 2: Classify text blocks (Paragraphs, Headings, Lists) FIRST const textBlocks = classifyTextBlocks(textItems); regions.push(...textBlocks);
// Step 3: Detect dividers AFTER text regions exist in regions // Dependency: Requires textBlocks to be populated in regions const dividers = segments.hLines.filter(seg => { const isContained = regions.some(r => isBBoxContained(r.bbox, seg)); return !isContained; // Correctly identifies lines inside paragraph boxes! }); regions.push(...dividers);
return regions; }
Rule of thumb: Ensure shared state collection arrays are fully populated by upstream producers before executing downstream containment queries.