From 70a7978de82f2abfe8a65354c59a9106e4cae627 Mon Sep 17 00:00:00 2001 From: Research Assistant Date: Fri, 19 Jun 2026 00:35:21 +0800 Subject: [PATCH] feat: complete OCR-v2 readiness gates + blind audit validation - Gate 1: completeness signals for page text coverage - Gate 2: DW figure ownership generalization with strict regression - Gate 3: reference boundary enforcement from structure - Gate 4: layout coverage taxonomy in audit/coverage_ledger.json - diff_audit.py: canonical role alias mapping + auto-normalize truth_role - ocr_structural_gate.py: table caption text-evidence fallback - ocr-canonical-roles.md: canonical role list for audit workflow - ocr-truth-audit.md: pre-flight checklist enforces canonical roles - Blind audit: 5 unseen papers across domains, all PASS with coverage --- .../skills/paperforge-development/SKILL.md | 24 + .../atoms/ocr-audit-report-schema.md | 131 ++ .../atoms/ocr-canonical-roles.md | 87 ++ .../paperforge-development/scripts/README.md | 5 + .../scripts/diff_audit.py | 25 +- .../scripts/ocr_truth_audit.py | 1158 +++++++++++++++++ .../scripts/verify_review_coverage.py | 117 ++ .../workflows/ocr-truth-audit.md | 153 +++ PROJECT-MANAGEMENT.md | 96 ++ ...06-15-group-first-figure-inventory-plan.md | 643 +++++++++ .../2026-06-17-ocr-v2-closeout-single-plan.md | 522 ++++++++ ...-dwq-repair-and-completeness-phase-plan.md | 523 ++++++++ ...26-06-18-ocr-p0-p2-layout-closeout-plan.md | 531 ++++++++ ...2026-06-18-ocr-v2-readiness-master-plan.md | 538 ++++++++ ...26-06-17-ocr-v2-closeout-unified-design.md | 307 +++++ ...wq-repair-and-completeness-phase-design.md | 254 ++++ ...026-06-18-ocr-completeness-check-design.md | 296 +++++ ...-06-18-ocr-p0-p2-layout-closeout-design.md | 265 ++++ ...026-06-18-ocr-v2-readiness-gates-design.md | 286 ++++ paperforge/worker/ocr.py | 1 + paperforge/worker/ocr_blocks.py | 2 +- paperforge/worker/ocr_figures.py | 228 +++- paperforge/worker/ocr_health.py | 65 +- paperforge/worker/ocr_rebuild.py | 1 + paperforge/worker/ocr_structural_gate.py | 38 +- project/current/ocr-v2-closeout-priority.md | 10 +- .../ocr-v2-remaining-issues-2026-06-18.md | 4 +- .../ocr_real_papers/8VB9ZVQG/block_trace.csv | 96 ++ .../ocr_real_papers/DWQQK2YB/block_trace.csv | 70 +- .../ocr_real_papers/GU9R8EPE/block_trace.csv | 424 ++++++ .../ocr_real_papers/L6ALWJFP/block_trace.csv | 405 ++++++ .../ocr_real_papers/PZ8B59K4/block_trace.csv | 528 ++++++++ .../ocr_real_papers/U746UJ7G/block_trace.csv | 246 ++++ tests/test_ocr_document.py | 2 + tests/test_ocr_figures.py | 26 + tests/test_ocr_health.py | 81 ++ tests/test_ocr_pdf_text_fallback.py | 1 - tests/test_ocr_real_paper_audit_contracts.py | 8 + tests/test_ocr_real_paper_regressions.py | 4 - 39 files changed, 8095 insertions(+), 106 deletions(-) create mode 100644 .opencode/skills/paperforge-development/SKILL.md create mode 100644 .opencode/skills/paperforge-development/atoms/ocr-audit-report-schema.md create mode 100644 .opencode/skills/paperforge-development/atoms/ocr-canonical-roles.md create mode 100644 .opencode/skills/paperforge-development/scripts/README.md create mode 100644 .opencode/skills/paperforge-development/scripts/ocr_truth_audit.py create mode 100644 .opencode/skills/paperforge-development/scripts/verify_review_coverage.py create mode 100644 .opencode/skills/paperforge-development/workflows/ocr-truth-audit.md create mode 100644 docs/superpowers/plans/2026-06-15-group-first-figure-inventory-plan.md create mode 100644 docs/superpowers/plans/2026-06-17-ocr-v2-closeout-single-plan.md create mode 100644 docs/superpowers/plans/2026-06-18-dwq-repair-and-completeness-phase-plan.md create mode 100644 docs/superpowers/plans/2026-06-18-ocr-p0-p2-layout-closeout-plan.md create mode 100644 docs/superpowers/plans/2026-06-18-ocr-v2-readiness-master-plan.md create mode 100644 docs/superpowers/specs/2026-06-17-ocr-v2-closeout-unified-design.md create mode 100644 docs/superpowers/specs/2026-06-18-dwq-repair-and-completeness-phase-design.md create mode 100644 docs/superpowers/specs/2026-06-18-ocr-completeness-check-design.md create mode 100644 docs/superpowers/specs/2026-06-18-ocr-p0-p2-layout-closeout-design.md create mode 100644 docs/superpowers/specs/2026-06-18-ocr-v2-readiness-gates-design.md create mode 100644 tests/fixtures/ocr_real_papers/8VB9ZVQG/block_trace.csv create mode 100644 tests/fixtures/ocr_real_papers/GU9R8EPE/block_trace.csv create mode 100644 tests/fixtures/ocr_real_papers/L6ALWJFP/block_trace.csv create mode 100644 tests/fixtures/ocr_real_papers/PZ8B59K4/block_trace.csv create mode 100644 tests/fixtures/ocr_real_papers/U746UJ7G/block_trace.csv diff --git a/.opencode/skills/paperforge-development/SKILL.md b/.opencode/skills/paperforge-development/SKILL.md new file mode 100644 index 00000000..a4d28b46 --- /dev/null +++ b/.opencode/skills/paperforge-development/SKILL.md @@ -0,0 +1,24 @@ +--- +name: paperforge-development +description: Use when doing PaperForge engineering or internal OCR truth-audit work. Triggered by: "OCR审计" "审OCR" "审计OCR" "审一下" "审计这篇" "block review" "ocr truth audit" "annotated pages" "看下角色对不对" "块角色" "visual audit" "对照一下图片" "校对block". +--- + +# PaperForge Development + +Developer-only skill family for internal PaperForge OCR audit work. + +Use this when the task is to inspect OCR truth, classify failures, and write audit evidence without changing the runtime PaperForge skill graph. + +## Route + +- OCR truth audit: `workflows/ocr-truth-audit.md` + +## Scripts + +- Audit helper: `scripts/ocr_truth_audit.py` + +## Invariant + +- Truth first. +- Do not start by authoring expectations. +- Establish block-level truth from page visuals and artifacts before comparing pipeline behavior. diff --git a/.opencode/skills/paperforge-development/atoms/ocr-audit-report-schema.md b/.opencode/skills/paperforge-development/atoms/ocr-audit-report-schema.md new file mode 100644 index 00000000..27ae1057 --- /dev/null +++ b/.opencode/skills/paperforge-development/atoms/ocr-audit-report-schema.md @@ -0,0 +1,131 @@ +# OCR Audit Report Schema + +Truth-first invariant: + +- Do not start by authoring expectations. +- Determine truth from page visuals and artifacts first. +- Use reports to record truth and mismatches, not to normalize incorrect output. + +External vault invariant: + +- The OCR source root is outside the repo. +- The audit helper must not assume a repo-local literature vault. +- The caller must provide `--source-root` or set `PAPERFORGE_OCR_ROOT`. + +## Required Output Files + +Write outputs under `audit//`. + +Concrete example: + + - `audit/CAQNW9Q2/audit_report.md` + - `audit/CAQNW9Q2/audit_report.json` + - `audit/CAQNW9Q2/audit_scope.json` + - `audit/CAQNW9Q2/block_review.jsonl` + - `audit/CAQNW9Q2/coverage_check.json` + - `audit/CAQNW9Q2/block_coverage_summary.json` + - `audit/CAQNW9Q2/page_risk_summary.json` + - `audit/CAQNW9Q2/reference_intrusion_candidates.json` + - `audit/CAQNW9Q2/figure_table_ownership_summary.json` + - `audit/CAQNW9Q2/fulltext_block_mapping_summary.json` + - `audit/CAQNW9Q2/reference_span_audit.json` + - `audit/CAQNW9Q2/annotated_pages/page_*_overview.png` — all-block overview with short numeric labels + - `audit/CAQNW9Q2/annotated_pages/page_*_index.json` — per-page block-to-metadata mapping + +## Primary Categories + +- `frontmatter_error` +- `body_flow_error` +- `reference_span_error` +- `same_page_boundary_error` +- `backmatter_error` +- `object_ownership_error` +- `reading_order_error` +- `render_mapping_error` + +## Meta Category + +- `audit_truth_gap` + +Use `audit_truth_gap` when the audit process fails to surface real block-level truth or when prior expectations drift away from visual truth. + +## Severities + +- `critical` +- `major` +- `minor` +- `cosmetic` + +Severity tracks trust and downstream damage, not implementation effort. + +## audit_report.json Example + +```json +{ + "paper_key": "CAQNW9Q2", + "mode": "high-risk", + "artifact_fingerprint": { + "result_json_hash": "...", + "structured_blocks_hash": "...", + "fulltext_hash": "...", + "document_structure_hash": "..." + }, + "reviewed_pages": [1, 7, 8], + "reviewed_blocks": ["p1:b3", "p7:b12"], + "findings": [ + { + "category": "reference_span_error", + "severity": "critical", + "block_ids": ["p7:b12"], + "truth": "block belongs outside reference span", + "pipeline_behavior": "rendered inside accepted references", + "root_cause_hypothesis": "same-page boundary inference overreach", + "evidence": { + "annotated_page": "annotated_pages/page_007.png", + "artifact": "structure/document_structure.json" + } + } + ] +} +``` + +## reference_span_audit.json Example + +```json +{ + "reference_span": { + "status": "ACCEPT", + "span_id": "refspan_001", + "start": { + "page": 7, + "column": 1, + "y": 1032, + "block_id": "p7:b12" + }, + "end": { + "page": 10, + "column": 2, + "y": 1398, + "block_id": "p10:b48" + }, + "ordered_block_ids": ["p7:b12", "p7:b13"], + "inside_block_ids": ["p7:b12", "p7:b13"], + "explicitly_outside_nearby_block_ids": ["p7:b10", "p7:b11"], + "intrusion_candidates": [] + } +} +``` + +## Minimum Reporting Expectations + +- `audit_report.md` summarizes reviewed scope, freshness status, findings, and repair vs residual recommendations. +- `audit_report.json` is the machine-readable source of findings. +- `audit_scope.json` defines which pages and blocks must be reviewed for the chosen mode. +- `block_review.jsonl` is the agent-written visual truth review log. +- `coverage_check.json` is written by `verify_review_coverage.py` and must fail when mandatory reviews are missing. +- `block_coverage_summary.json` tracks which blocks were reviewed and their review state. +- `page_risk_summary.json` records page scores and reasons. +- `reference_intrusion_candidates.json` records candidate reference contamination. +- `figure_table_ownership_summary.json` records caption/asset/object mismatches. +- `fulltext_block_mapping_summary.json` records source-to-render placement checks. +- `reference_span_audit.json` records accepted reference span boundaries and intrusion checks. diff --git a/.opencode/skills/paperforge-development/atoms/ocr-canonical-roles.md b/.opencode/skills/paperforge-development/atoms/ocr-canonical-roles.md new file mode 100644 index 00000000..56764949 --- /dev/null +++ b/.opencode/skills/paperforge-development/atoms/ocr-canonical-roles.md @@ -0,0 +1,87 @@ +# OCR Canonical Roles + +`truth_role` in `block_review.jsonl` MUST be one of these canonical pipeline roles. +Do not invent new role names — use only the set below. Non-canonical names +will be normalized by `diff_audit.py` but relying on that is a workflow violation. + +## Frontmatter + +| Role | When | +|------|------| +| `paper_title` | Document title on page 1 | +| `authors` | Author byline | +| `affiliation` | Author affiliation block | +| `frontmatter_support` | Correspondence, DOI, dates, data availability | +| `frontmatter_noise` | Page-1 non-structural furniture (journal name, article type, publisher strip) | +| `abstract_heading` | "Abstract" label | +| `abstract_body` | Abstract body text | +| `keywords` | Keywords block | + +## Body + +| Role | When | +|------|------| +| `section_heading` | Top-level section heading | +| `subsection_heading` | Second-level section heading | +| `sub_subsection_heading` | Third-level section heading | +| `body_paragraph` | Narrative body paragraph | +| `tail_candidate_body` | Body paragraph in the tail spread | + +## Reference + +| Role | When | +|------|------| +| `reference_heading` | "References" heading | +| `reference_item` | Individual reference entry | + +## Backmatter + +| Role | When | +|------|------| +| `backmatter_heading` | Backmatter section heading (Acknowledgments, Funding, etc.) | +| `backmatter_heading_candidate` | Possible backmatter heading, not yet confirmed | +| `backmatter_body` | Backmatter body paragraph | +| `backmatter_boundary_heading` | Confirmed backmatter boundary heading | +| `backmatter_boundary_candidate` | Possible backmatter boundary | + +## Figure / Table + +| Role | When | +|------|------| +| `figure_asset` | Figure image/media | +| `figure_caption` | Confirmed figure caption | +| `figure_caption_candidate` | Probable figure caption, not fully confirmed | +| `figure_inner_text` | Text inside a figure (panel labels, axis labels) | +| `table_caption` | Confirmed table caption | +| `table_caption_candidate` | Probable table caption, not fully confirmed | +| `table_asset` | Table image | +| `table_html` | HTML native table | +| `table_html_candidate` | Probable HTML native table | +| `media_asset` | Generic media image (when figure/table type is unclear) | + +## Structural Inserts + +| Role | When | +|------|------| +| `structured_insert` | Structured block (callout box, highlight, algorithm) | +| `structured_insert_candidate` | Probable structured insert | +| `non_body_insert` | Non-body decorative insert (publisher strip, sidebar filler) | + +## Noise & Fallback + +| Role | When | +|------|------| +| `noise` | Decorative line, page furniture, garbage OCR | +| `footnote` | Page footnote | +| `unknown_structural` | Block exists but no role fits (structural gate fallback) | +| `ocr_raw_error` | OCR extraction failure (empty text) | +| `page_header` | Running header repeat | +| `page_footer` | Running footer repeat | + +## Ground Truth Only (seed roles, never final) + +These are assigned as `seed_role` by the early role assignment but never appear +as the final pipeline output role. When auditing, use the final role above +even if the seed role seems more intuitive: + +- `figure_title` (raw OCR label, not a final role) diff --git a/.opencode/skills/paperforge-development/scripts/README.md b/.opencode/skills/paperforge-development/scripts/README.md new file mode 100644 index 00000000..a438eae4 --- /dev/null +++ b/.opencode/skills/paperforge-development/scripts/README.md @@ -0,0 +1,5 @@ +# Scripts + +- `ocr_truth_audit.py` stages OCR audit artifacts and writes deterministic helper summaries under `audit//`. +- It requires an explicit external OCR root via `--source-root` or `PAPERFORGE_OCR_ROOT`. +- It is developer-only and separate from `scripts/dev/` so the repo-local `paperforge-development` skill can evolve without touching the PaperForge product workflow. diff --git a/.opencode/skills/paperforge-development/scripts/diff_audit.py b/.opencode/skills/paperforge-development/scripts/diff_audit.py index 8101e786..8a53dfc5 100644 --- a/.opencode/skills/paperforge-development/scripts/diff_audit.py +++ b/.opencode/skills/paperforge-development/scripts/diff_audit.py @@ -19,6 +19,21 @@ from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[5])) from paperforge.worker.ocr_artifacts import artifact_paths_for_root +# Canonical role map: alias → pipeline output role name. +_CANONICAL_ROLE = { + "media_asset": "figure_asset", + "structural_noise": "noise", + "author_list": "authors", + "running_header": "noise", + "page_marker": "noise", + "frontmatter_metadata": "frontmatter_noise", + "separator": "noise", +} + + +def _canonical(role: str) -> str: + return _CANONICAL_ROLE.get(role, role) + def _load_jsonl(path: Path) -> list[dict]: if not path.exists(): @@ -83,7 +98,7 @@ def main() -> None: pipe_zone = current.get("zone", "") pipe_text_len = len(str(current.get("text", "")).strip()) - role_match = pipe_role == truth_role + role_match = _canonical(pipe_role) == _canonical(truth_role) zone_match = not truth_zone or pipe_zone == truth_zone if role_match: @@ -116,6 +131,14 @@ def main() -> None: } changed_path.write_text(json.dumps(summary, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") + # Normalize truth_role to canonical form in place, so future diffs + # match even without the alias map. + for review in reviews: + raw = str(review.get("truth_role") or "") + canonical = _canonical(raw) + if canonical != raw: + review["truth_role"] = canonical + # Write back updated block_review.jsonl _write_jsonl(review_path, reviews) diff --git a/.opencode/skills/paperforge-development/scripts/ocr_truth_audit.py b/.opencode/skills/paperforge-development/scripts/ocr_truth_audit.py new file mode 100644 index 00000000..e81af677 --- /dev/null +++ b/.opencode/skills/paperforge-development/scripts/ocr_truth_audit.py @@ -0,0 +1,1158 @@ +from __future__ import annotations + +import argparse +import csv +import hashlib +import json +import os +import re +import sys +from dataclasses import asdict, is_dataclass +from pathlib import Path +from typing import Any + + +def _find_repo_root(start: Path) -> Path: + current = start.resolve() + for candidate in [current, *current.parents]: + if (candidate / "pyproject.toml").exists() or (candidate / ".git").exists(): + return candidate + raise RuntimeError(f"Could not locate repo root from {start}") + + +_THIS_FILE = Path(__file__).resolve() +_REPO_ROOT = _find_repo_root(_THIS_FILE) +if str(_REPO_ROOT) not in sys.path: + sys.path.insert(0, str(_REPO_ROOT)) + +from paperforge.worker.ocr_artifacts import artifact_paths_for_root # noqa: E402 +from paperforge.worker.ocr_structural_gate import build_verified_reference_zone_from_artifacts # noqa: E402 +from scripts.dev.ocr_rebuild_paper import rebuild_paper as rebuild_from_raw # noqa: E402 +import fitz # noqa: E402 +from PIL import Image # noqa: E402 + + +DEFAULT_OUTPUT_ROOT = _REPO_ROOT / "audit" +HOLD_THRESHOLD = 2 +UNKNOWN_THRESHOLD = 2 +NON_RENDER_ROLES = { + "figure_caption", + "figure_inner_text", + "frontmatter_noise", + "abstract_heading", + "abstract_body", + "table_html", + "table_caption_candidate", +} +REFERENCE_ALLOWED_ROLES = {"reference_heading", "reference_item"} +_ROLE_COLORS = { + "paper_title": (0.0, 0.0, 0.6), + "authors": (0.1, 0.3, 0.75), + "affiliation": (0.2, 0.5, 0.7), + "frontmatter_support": (0.4, 0.65, 0.85), + "frontmatter_noise": (0.55, 0.65, 0.75), + "abstract_heading": (0.45, 0.0, 0.65), + "abstract_body": (0.55, 0.3, 0.7), + "section_heading": (0.85, 0.2, 0.1), + "subsection_heading": (0.9, 0.4, 0.15), + "sub_subsection_heading": (0.95, 0.55, 0.25), + "body_paragraph": (0.0, 0.6, 0.3), + "reference_heading": (0.75, 0.05, 0.1), + "reference_item": (0.7, 0.2, 0.2), + "backmatter_heading": (0.5, 0.15, 0.45), + "backmatter_boundary_candidate": (0.4, 0.35, 0.45), + "backmatter_body": (0.55, 0.3, 0.5), + "media_asset": (0.95, 0.65, 0.05), + "figure_caption": (0.85, 0.55, 0.0), + "figure_caption_candidate": (0.9, 0.6, 0.1), + "figure_inner_text": (0.9, 0.65, 0.3), + "table_caption": (0.75, 0.55, 0.05), + "table_caption_candidate": (0.8, 0.6, 0.2), + "structured_insert": (0.0, 0.55, 0.55), + "structured_insert_candidate": (0.2, 0.65, 0.65), + "non_body_insert": (0.1, 0.45, 0.5), + "noise": (0.35, 0.35, 0.35), + "footnote": (0.45, 0.45, 0.5), + "unknown_structural": (0.6, 0.6, 0.6), +} + + +def _classify_category(block: dict) -> str: + role = str(block.get("role", "")) + zone = str(block.get("zone", "")) + text = _normalize_text(_block_text(block)).lower() + raw_label = str(block.get("raw_label", "")) + if role in {"paper_title", "authors", "frontmatter_support", "frontmatter_noise", "affiliation"}: + return "frontmatter" + if zone == "frontmatter_main_zone" or zone == "frontmatter_side_zone": + return "frontmatter" + if role in {"reference_heading", "reference_item"} or zone == "reference_zone": + return "reference" + if zone in {"tail_nonref_hold_zone", "post_reference_backmatter_zone"} or role.startswith("backmatter"): + return "backmatter" + if role in {"figure_asset", "media_asset", "figure_caption", "figure_caption_candidate", "figure_inner_text", + "table_caption", "table_caption_candidate", "table_html"}: + return "object_caption" + if raw_label in {"image", "chart", "figure", "table"}: + return "object_caption" + if role in {"section_heading", "subsection_heading", "sub_subsection_heading"}: + return "body" + if role in {"body_paragraph", "abstract_body", "abstract_heading"}: + return "body" + if role == "unknown_structural" or str(block.get("role_verification_status", "")) == "HOLD": + return "unknown_hold" + if zone == "body_zone": + return "body" + if text in {"references", "bibliography"}: + return "reference" + if role in {"noise", "footer", "header"}: + return "unknown_hold" + return "unknown_hold" + + +STRICT_REQUIRED_FIELDS = [ + "block_id", + "page", + "review_status", + "truth_role", + "truth_zone", + "truth_reference_membership", +] + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Build OCR truth-audit helper outputs for one or more papers.") + parser.add_argument("paper_keys", nargs="*", help="Explicit paper keys to audit") + parser.add_argument("--mode", choices=("strict", "high-risk"), default="high-risk") + parser.add_argument( + "--current-paper", + action="store_true", + help="Use PAPERFORGE_CURRENT_PAPER_KEY/CURRENT_PAPER_KEY from the environment when no explicit key is provided", + ) + parser.add_argument("--refresh-artifacts", action="store_true", help="Rebuild derived artifacts and refresh annotated pages") + parser.add_argument("--include-pages", nargs="*", type=int, help="Restrict the audit to specific pages") + parser.add_argument("--focus", nargs="*", default=[], help="Optional focus labels for downstream review") + parser.add_argument( + "--source-root", + default=None, + help="OCR root directory outside the repo, e.g. D:/.../System/PaperForge/ocr", + ) + parser.add_argument("--output-root", default=str(DEFAULT_OUTPUT_ROOT), help="Audit output root directory") + return parser + + +def _load_json(path: Path, default: Any = None) -> Any: + if not path.exists(): + return default + try: + return json.loads(path.read_text(encoding="utf-8")) + except Exception: + return default + + +def _load_jsonl(path: Path) -> list[dict]: + if not path.exists(): + return [] + rows: list[dict] = [] + for line in path.read_text(encoding="utf-8").splitlines(): + line = line.strip() + if not line: + continue + rows.append(json.loads(line)) + return rows + + +def _write_json(path: Path, payload: dict | list) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(payload, indent=2, ensure_ascii=False), encoding="utf-8") + + +def _write_text(path: Path, text: str) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(text, encoding="utf-8") + + +def _sha256(path: Path) -> str: + if not path.exists(): + return "missing" + return "sha256:" + hashlib.sha256(path.read_bytes()).hexdigest() + + +def _mtime(path: Path) -> float | None: + return path.stat().st_mtime if path.exists() else None + + +def _resolve_keys(args: argparse.Namespace) -> list[str]: + keys = [str(k).strip() for k in args.paper_keys if str(k).strip()] + if keys: + return keys + if args.current_paper: + env_key = os.environ.get("PAPERFORGE_CURRENT_PAPER_KEY") or os.environ.get("CURRENT_PAPER_KEY") + if env_key: + return [env_key.strip()] + raise SystemExit("No paper key supplied. Pass a key explicitly or use --current-paper with PAPERFORGE_CURRENT_PAPER_KEY.") + + +def _resolve_ocr_root(raw: str | None) -> Path: + candidate = raw or os.environ.get("PAPERFORGE_OCR_ROOT") or os.environ.get("PAPERFORGE_REAL_OCR_ROOT") + if not candidate: + raise SystemExit( + "No OCR root supplied. Pass --source-root /path/to/System/PaperForge/ocr or set PAPERFORGE_OCR_ROOT." + ) + path = Path(candidate) + if not path.exists(): + raise SystemExit(f"OCR root not found: {path}") + return path + + +def _resolve_vault_root(ocr_root: Path) -> Path: + current = ocr_root.resolve() + if current.name != "ocr": + raise RuntimeError(f"Expected OCR root to end with 'ocr', got: {current}") + paperforge_dir = current.parent + system_dir = paperforge_dir.parent + return system_dir.parent if system_dir.name == "System" else system_dir + + +def _block_text(block: dict) -> str: + return str(block.get("text") or block.get("block_content") or "") + + +def _block_bbox(block: dict) -> list[float]: + bbox = block.get("bbox") or block.get("block_bbox") or [0, 0, 0, 0] + return [float(v) for v in bbox] + + +def _block_x0(block: dict) -> float: + return _block_bbox(block)[0] + + +def _block_y0(block: dict) -> float: + return _block_bbox(block)[1] + + +def _block_page(block: dict) -> int: + return int(block.get("page", 0) or 0) + + +def _block_id(block: dict) -> str: + page = _block_page(block) + raw = block.get("block_id") + return f"p{page}:{raw}" if raw is not None else f"p{page}:unknown" + + +def _normalize_text(text: str) -> str: + return re.sub(r"\s+", " ", text).strip() + + +def _safe_rel(path: Path, root: Path) -> str: + try: + return str(path.resolve().relative_to(root.resolve())).replace("\\", "/") + except Exception: + return str(path).replace("\\", "/") + + +def _artifact_dict(doc_structure: Any) -> dict: + if doc_structure is None: + return {} + if isinstance(doc_structure, dict): + return doc_structure + if is_dataclass(doc_structure): + return asdict(doc_structure) + if hasattr(doc_structure, "_asdict"): + return doc_structure._asdict() + return {} + + +def _load_block_trace_csv(path: Path) -> list[dict[str, str]]: + if not path.exists(): + return [] + with path.open("r", encoding="utf-8-sig", newline="") as fh: + return list(csv.DictReader(fh)) + + +def _write_block_trace_csv(path: Path, blocks: list[dict]) -> None: + field_names = [ + "page", + "block_id", + "raw_label", + "content_preview", + "bbox", + "role", + "role_confidence", + "evidence", + "seed_role", + "seed_confidence", + "zone", + "style_family", + "marker_type", + "render_default", + "index_default", + ] + path.parent.mkdir(parents=True, exist_ok=True) + with path.open("w", encoding="utf-8", newline="") as fh: + writer = csv.DictWriter(fh, fieldnames=field_names) + writer.writeheader() + for block in blocks: + marker_sig = block.get("marker_signature") or {} + writer.writerow( + { + "page": block.get("page", ""), + "block_id": block.get("block_id", ""), + "raw_label": block.get("raw_label", ""), + "content_preview": _block_text(block)[:200], + "bbox": json.dumps(_block_bbox(block)), + "role": block.get("role", ""), + "role_confidence": block.get("role_confidence", ""), + "evidence": json.dumps(block.get("evidence", [])) if block.get("evidence") else "", + "seed_role": block.get("seed_role", ""), + "seed_confidence": block.get("seed_confidence", ""), + "zone": block.get("zone", ""), + "style_family": block.get("style_family", ""), + "marker_type": marker_sig.get("type", ""), + "render_default": block.get("render_default", True), + "index_default": block.get("index_default", True), + } + ) + + +def _refresh_artifacts(ocr_root: Path, key: str) -> dict: + return rebuild_from_raw(ocr_root, key, trace=True, trace_only=False) + + +def _resolve_audit_source_pdf(ocr_root: Path, key: str) -> Path: + paper_root = ocr_root / key + source_pdf = paper_root / "source.pdf" + if source_pdf.exists(): + return source_pdf + meta_path = paper_root / "raw" / "source_metadata.json" + if meta_path.exists(): + meta = _load_json(meta_path, {}) or {} + src = meta.get("source_pdf") + if src and Path(src).exists(): + return Path(src) + raise FileNotFoundError(f"Could not resolve source PDF for {key}") + + +def _ocr_page_image_path(ocr_root: Path, key: str, page_num: int) -> Path: + page_dir = ocr_root / key / "pages" + for suffix in ("png", "jpg", "jpeg"): + candidate = page_dir / f"page_{page_num:03d}.{suffix}" + if candidate.exists(): + return candidate + raise FileNotFoundError(f"Missing OCR page image for {key} page {page_num}") + + +def _render_audit_visuals(key: str, blocks: list[dict], ocr_root: Path, audit_dir: Path) -> list[str]: + pdf_path = _resolve_audit_source_pdf(ocr_root, key) + pages_dir = audit_dir / "annotated_pages" + pages_dir.mkdir(parents=True, exist_ok=True) + + by_page: dict[int, list[tuple[int, dict]]] = {} + for idx, block in enumerate(blocks): + page = _block_page(block) + if page <= 0: + continue + by_page.setdefault(page, []).append((idx, block)) + + doc = fitz.open(str(pdf_path)) + rendered: list[str] = [] + for page_num in sorted(by_page): + entries = by_page[page_num] + pdf_page = doc.load_page(page_num - 1) + pdf_w, pdf_h = pdf_page.rect.width, pdf_page.rect.height + img_path = _ocr_page_image_path(ocr_root, key, page_num) + img_w, img_h = Image.open(img_path).size + x_scale = pdf_w / img_w + y_scale = pdf_h / img_h + + def _scale_bbox(bbox: list[float]) -> fitz.Rect: + return fitz.Rect(bbox[0] * x_scale, bbox[1] * y_scale, bbox[2] * x_scale, bbox[3] * y_scale) + + _render_overview_page(pdf_page, page_num, entries, pages_dir, _scale_bbox) + _write_page_index(page_num, entries, pages_dir) + rendered.append(f"page_{page_num:03d}.png") + doc.close() + return rendered + + +def _render_overview_page(pdf_page, page_num: int, entries: list[tuple[int, dict]], out_dir: Path, scale_bbox) -> None: + shape = pdf_page.new_shape() + for idx, block in entries: + rect = scale_bbox(_block_bbox(block)) + role = str(block.get("role", "")) + color = _ROLE_COLORS.get(role, (0.6, 0.6, 0.6)) + shape.draw_rect(rect) + shape.finish(color=color, width=0.6) + label = str(block.get("block_id", "")) + if label: + pdf_page.insert_text(fitz.Point(rect.x0, max(6, rect.y0 - 1)), label, fontsize=5.5, color=color, overlay=True) + shape.commit(overlay=True) + pix = pdf_page.get_pixmap(matrix=fitz.Matrix(1.5, 1.5), alpha=False) + pix.save(str(out_dir / f"page_{page_num:03d}.png")) + + +def _write_page_index(page_num: int, entries: list[tuple[int, dict]], out_dir: Path) -> None: + rows = [] + for idx, block in entries: + rows.append({ + "label": str(block.get("block_id", "")), + "block_id": _block_id(block), + "page": page_num, + "role": block.get("role", ""), + "zone": block.get("zone", ""), + "category": _classify_category(block), + "text_preview": _normalize_text(_block_text(block))[:120], + "bbox": _block_bbox(block), + }) + _write_json(out_dir / f"page_{page_num:03d}_index.json", rows) + + +def _artifact_paths(ocr_root: Path, key: str) -> dict[str, Path]: + artifacts = artifact_paths_for_root(ocr_root, key) + paper_root = artifacts.paper_root + structure_dir = paper_root / "structure" + metadata_dir = paper_root / "metadata" + render_dir = paper_root / "render" + return { + "paper_root": paper_root, + "result_json": artifacts.result_json, + "meta_json": artifacts.meta_json, + "blocks_raw": artifacts.blocks_raw, + "blocks_structured": artifacts.blocks_structured, + "source_metadata": artifacts.source_metadata, + "compat_fulltext": artifacts.compat_fulltext, + "render_fulltext": render_dir / "fulltext.md", + "document_structure": structure_dir / "document_structure.json", + "figure_inventory": structure_dir / "figure_inventory.json", + "table_inventory": structure_dir / "table_inventory.json", + "reader_figures": structure_dir / "reader_figures.json", + "resolved_metadata": metadata_dir / "resolved_metadata.json", + "decision_log": paper_root / "health" / "decision_log.jsonl", + } + + +def _build_fingerprint(paths: dict[str, Path], block_trace: Path, annotated_dir: Path) -> dict: + annotated_files = sorted(annotated_dir.glob("page_*.png")) if annotated_dir.exists() else [] + return { + "result_json_hash": _sha256(paths["result_json"]), + "meta_json_hash": _sha256(paths["meta_json"]), + "blocks_raw_hash": _sha256(paths["blocks_raw"]), + "structured_blocks_hash": _sha256(paths["blocks_structured"]), + "document_structure_hash": _sha256(paths["document_structure"]), + "figure_inventory_hash": _sha256(paths["figure_inventory"]), + "table_inventory_hash": _sha256(paths["table_inventory"]), + "reader_figures_hash": _sha256(paths["reader_figures"]), + "resolved_metadata_hash": _sha256(paths["resolved_metadata"]), + "fulltext_hash": _sha256(paths["compat_fulltext"]), + "block_trace_hash": _sha256(block_trace), + "annotated_pages": {name.name: _sha256(name) for name in annotated_files}, + } + + +def _detect_stale(paths: dict[str, Path], block_trace: Path, annotated_dir: Path) -> tuple[list[str], list[str]]: + missing: list[str] = [] + mismatches: list[str] = [] + required = { + "result_json": paths["result_json"], + "meta_json": paths["meta_json"], + "blocks_raw": paths["blocks_raw"], + "blocks_structured": paths["blocks_structured"], + "document_structure": paths["document_structure"], + "figure_inventory": paths["figure_inventory"], + "table_inventory": paths["table_inventory"], + "reader_figures": paths["reader_figures"], + "resolved_metadata": paths["resolved_metadata"], + "fulltext": paths["compat_fulltext"], + "block_trace": block_trace, + } + for name, path in required.items(): + if not path.exists(): + missing.append(name) + + if missing: + return missing, mismatches + + def older(target: str, source: str) -> None: + if _mtime(required[target]) is not None and _mtime(required[source]) is not None: + if _mtime(required[target]) < _mtime(required[source]): + mismatches.append(f"{target} older than {source}") + + older("blocks_structured", "blocks_raw") + older("document_structure", "blocks_structured") + older("figure_inventory", "blocks_structured") + older("table_inventory", "blocks_structured") + older("reader_figures", "figure_inventory") + older("resolved_metadata", "blocks_structured") + older("fulltext", "blocks_structured") + older("block_trace", "blocks_structured") + + annotated_pages = sorted(annotated_dir.glob("page_*.png")) if annotated_dir.exists() else [] + if not annotated_pages: + missing.append("annotated_pages") + elif _mtime(max(annotated_pages, key=lambda p: p.stat().st_mtime)) < _mtime(block_trace): + mismatches.append("annotated_pages older than block_trace") + + return missing, mismatches + + +def _page_column(block: dict) -> int: + bbox = _block_bbox(block) + width = float(block.get("page_width", 0) or 0) + if width and bbox[0] >= width * 0.45: + return 2 + return 1 + + +def _summarize_blocks(blocks: list[dict], mode: str) -> dict: + pages = sorted({_block_page(b) for b in blocks if _block_page(b) > 0}) + by_page: dict[int, list[dict]] = {} + for block in blocks: + by_page.setdefault(_block_page(block), []).append(block) + return { + "mode": mode, + "total_blocks": len(blocks), + "pages": pages, + "per_page_counts": {str(page): len(by_page.get(page, [])) for page in pages}, + "blocks": [ + { + "block_id": _block_id(block), + "page": _block_page(block), + "raw_label": block.get("raw_label", ""), + "role": block.get("role", ""), + "zone": block.get("zone", ""), + "bbox": _block_bbox(block), + "review_status": "pending", + "truth_role": None, + "truth_zone": None, + "truth_order_after": None, + "truth_order_before": None, + "truth_object_id": None, + "truth_reference_membership": None, + } + for block in blocks + ], + } + + +def _build_page_risk_summary( + blocks: list[dict], + figure_inventory: dict, + table_inventory: dict, + reader_figures: dict, + include_pages: list[int] | None, +) -> dict: + by_page: dict[int, list[dict]] = {} + for block in blocks: + page = _block_page(block) + if page <= 0: + continue + by_page.setdefault(page, []).append(block) + + normalized_reader = reader_figures.get("normalized_inputs", {}) if isinstance(reader_figures, dict) else {} + matched_figures = normalized_reader.get("matched_figures", []) if isinstance(normalized_reader, dict) else [] + ambiguous_figures = normalized_reader.get("ambiguous_figures", []) if isinstance(normalized_reader, dict) else [] + reader_items = reader_figures.get("reader_figures", []) if isinstance(reader_figures, dict) else [] + + summaries: list[dict] = [] + for page, page_blocks in sorted(by_page.items()): + roles = {str(b.get("role", "")) for b in page_blocks} + body_count = sum(1 for b in page_blocks if b.get("role") == "body_paragraph") + ref_count = sum(1 for b in page_blocks if b.get("role") == "reference_item") + tail_count = sum(1 for b in page_blocks if "tail" in str(b.get("zone", "")) or str(b.get("role", "")).startswith("backmatter")) + asset_count = sum(1 for b in page_blocks if b.get("role") in {"figure_asset", "media_asset"}) + tableish_count = sum(1 for b in page_blocks if "table" in str(b.get("role", "")) or (b.get("raw_label") == "table")) + caption_count = sum(1 for b in page_blocks if "caption" in str(b.get("role", "")) or str(b.get("seed_role", "")).endswith("caption")) + hold_count = sum(1 for b in page_blocks if str(b.get("role_verification_status", "")) == "HOLD") + unknown_count = sum(1 for b in page_blocks if str(b.get("role", "")) == "unknown_structural") + + page_matched_figures = [item for item in matched_figures if int(item.get("page", 0) or 0) == page] + page_ambiguous = [item for item in ambiguous_figures if int(item.get("page", 0) or 0) == page] + page_reader_figures = [item for item in reader_items if int(item.get("page", 0) or 0) == page] + + score = 0 + reasons: list[str] = [] + targets: list[str] = [] + if page == 1: + score += 5 + reasons.append("frontmatter_page") + targets.append("frontmatter") + if "reference_heading" in roles: + score += 5 + reasons.append("reference_heading_present") + targets.append("reference_span") + if body_count and ref_count: + score += 4 + reasons.append("mixed_body_reference") + targets.extend(["reference_span", "same_page_boundary"]) + if body_count and (ref_count or tail_count): + score += 4 + reasons.append("same_page_boundary") + targets.extend(["same_page_boundary", "reading_order"]) + if asset_count + tableish_count >= 2: + score += 4 + reasons.append("multi_asset_page") + targets.append("object_ownership") + if caption_count and (asset_count + tableish_count) and caption_count != asset_count + tableish_count: + score += 3 + reasons.append("caption_asset_mismatch") + targets.append("object_ownership") + if (asset_count and not page_reader_figures) or page_ambiguous: + score += 3 + reasons.append("reader_object_gap") + targets.append("object_ownership") + if hold_count >= HOLD_THRESHOLD: + score += 2 + reasons.append("hold_threshold") + targets.append("reading_order") + if unknown_count >= UNKNOWN_THRESHOLD: + score += 2 + reasons.append("unknown_structural_threshold") + targets.append("body_flow") + + summaries.append( + { + "page": page, + "risk_score": score, + "risk_reasons": reasons, + "recommended_audit_targets": sorted(set(targets)), + "counts": { + "body_paragraph": body_count, + "reference_item": ref_count, + "tail_like": tail_count, + "media_assets": asset_count, + "table_like": tableish_count, + "captions": caption_count, + "hold": hold_count, + "unknown_structural": unknown_count, + "matched_figures": len(page_matched_figures), + "ambiguous_figures": len(page_ambiguous), + }, + } + ) + + selected_pages = include_pages if include_pages else [row["page"] for row in summaries if row["risk_score"] > 0] + if not selected_pages and summaries: + selected_pages = [summaries[0]["page"]] + return {"pages": summaries, "selected_pages": selected_pages} + + +def _is_high_risk_block(block: dict, selected_pages: set[int]) -> tuple[bool, list[str]]: + page = _block_page(block) + if page not in selected_pages: + return False, [] + role = str(block.get("role", "")) + zone = str(block.get("zone", "")) + raw_label = str(block.get("raw_label", "")) + text = _normalize_text(_block_text(block)).lower() + reasons: list[str] = [] + if page == 1 or role in {"paper_title", "authors", "frontmatter_support", "frontmatter_noise", "affiliation"}: + reasons.append("frontmatter") + if role in {"reference_heading", "reference_item"} or zone == "reference_zone": + reasons.append("reference_span") + if zone in {"tail_nonref_hold_zone", "post_reference_backmatter_zone"} or role.startswith("backmatter"): + reasons.append("backmatter") + if role in {"figure_asset", "media_asset", "figure_caption_candidate", "table_caption_candidate", "table_html"}: + reasons.append("object_ownership") + if raw_label in {"image", "chart", "figure", "table"}: + reasons.append("object_ownership") + if str(block.get("role_verification_status", "")) == "HOLD" or role == "unknown_structural": + reasons.append("needs_resolution") + if ("reference" in text or text in {"references", "bibliography"}) and zone != "reference_zone": + reasons.append("same_page_boundary") + if role == "body_paragraph" and any(tok in text for tok in ("acknowledg", "keywords", "conflict of interest", "funding")): + reasons.append("same_page_boundary") + return bool(reasons), sorted(set(reasons)) + + +def _build_audit_scope(mode: str, blocks: list[dict], page_risk_summary: dict, include_pages: list[int] | None) -> dict: + selected_pages = _selected_pages(mode, page_risk_summary, blocks, include_pages) + page_set = set(selected_pages) + required_blocks: list[dict] = [] + if mode == "strict": + for block in blocks: + required_blocks.append( + { + "block_id": _block_id(block), + "page": _block_page(block), + "required_reason": ["strict_all_blocks"], + "minimum_fields": STRICT_REQUIRED_FIELDS, + } + ) + else: + for block in blocks: + is_required, reasons = _is_high_risk_block(block, page_set) + if is_required: + required_blocks.append( + { + "block_id": _block_id(block), + "page": _block_page(block), + "required_reason": reasons, + "minimum_fields": STRICT_REQUIRED_FIELDS, + } + ) + + per_page: dict[int, int] = {} + for item in required_blocks: + per_page[item["page"]] = per_page.get(item["page"], 0) + 1 + return { + "mode": mode, + "selected_pages": selected_pages, + "required_block_ids": [item["block_id"] for item in required_blocks], + "required_blocks": required_blocks, + "selected_page_requirements": [ + { + "page": page, + "must_review_page": True, + "required_block_count": per_page.get(page, 0), + } + for page in selected_pages + ], + } + + +def _derive_reference_zone(blocks: list[dict], doc_structure: dict) -> dict: + direct = doc_structure.get("reference_zone") if isinstance(doc_structure, dict) else None + if isinstance(direct, dict) and (direct.get("heading_block_id") or direct.get("item_block_ids")): + return direct + + region_bus = doc_structure.get("region_bus") or {} + ref_zone = region_bus.get("reference_zone") if isinstance(region_bus, dict) else {} + zone_ids = set((ref_zone or {}).get("block_ids", [])) if isinstance(ref_zone, dict) else set() + reference_family_anchor = doc_structure.get("reference_family_anchor") or {} + tail_spread_dict = {} + if doc_structure.get("tail_boundary_score"): + tail_spread_dict = {"reference_end_before_block_id": None} + return build_verified_reference_zone_from_artifacts( + blocks, + { + "reference_family_anchor": reference_family_anchor, + "region_bus": {"reference_zone_ids": zone_ids}, + "tail_spread": tail_spread_dict, + }, + ) + + +def _build_reference_span_audit(blocks: list[dict], verified_zone: dict) -> tuple[dict, list[dict]]: + heading_id = verified_zone.get("heading_block_id") + item_ids = list(verified_zone.get("item_block_ids", [])) + allowed_ids = {str(x) for x in item_ids} + if heading_id is not None: + allowed_ids.add(str(heading_id)) + + indexed = [{"index": idx, "block": block, "artifact_id": _block_id(block), "raw_id": str(block.get("block_id"))} for idx, block in enumerate(blocks)] + allowed_rows = [row for row in indexed if row["raw_id"] in allowed_ids or row["artifact_id"] in allowed_ids] + allowed_rows.sort(key=lambda row: row["index"]) + + if not allowed_rows: + return { + "reference_span": { + "status": "HOLD", + "span_id": None, + "start": None, + "end": None, + "ordered_block_ids": [], + "inside_block_ids": [], + "explicitly_outside_nearby_block_ids": [], + "intrusion_candidates": [], + } + }, [] + + min_idx = allowed_rows[0]["index"] + max_idx = allowed_rows[-1]["index"] + intrusion_candidates: list[dict] = [] + for row in indexed[min_idx : max_idx + 1]: + bid = row["raw_id"] if row["raw_id"] != "None" else row["artifact_id"] + role = str(row["block"].get("role", "")) + if bid in allowed_ids: + continue + if not _normalize_text(_block_text(row["block"])): + continue + intrusion_candidates.append( + { + "block_id": row["artifact_id"], + "page": _block_page(row["block"]), + "role": role, + "zone": row["block"].get("zone", ""), + "reason": "logical_order_between_reference_members", + } + ) + + first = allowed_rows[0]["block"] + last = allowed_rows[-1]["block"] + outside = [] + if min_idx > 0: + outside.append(_block_id(indexed[min_idx - 1]["block"])) + if max_idx + 1 < len(indexed): + outside.append(_block_id(indexed[max_idx + 1]["block"])) + + audit = { + "reference_span": { + "status": "ACCEPT" if heading_id and item_ids else "HOLD", + "span_id": "refspan_001" if item_ids else None, + "start": { + "page": _block_page(first), + "column": _page_column(first), + "y": _block_y0(first), + "block_id": _block_id(first), + }, + "end": { + "page": _block_page(last), + "column": _page_column(last), + "y": _block_y0(last), + "block_id": _block_id(last), + }, + "heading_block_id": heading_id, + "ordered_block_ids": [_block_id(row["block"]) for row in allowed_rows], + "inside_block_ids": [_block_id(row["block"]) for row in allowed_rows], + "explicitly_outside_nearby_block_ids": outside, + "intrusion_candidates": intrusion_candidates, + } + } + return audit, intrusion_candidates + + +def _build_object_ownership_summary(figure_inventory: dict, table_inventory: dict) -> dict: + summary = { + "figures": { + "matched_count": len(figure_inventory.get("matched_figures", [])), + "ambiguous_count": len(figure_inventory.get("ambiguous_figures", [])), + "unresolved_cluster_count": len(figure_inventory.get("unresolved_clusters", [])), + "unmatched_asset_count": len(figure_inventory.get("unmatched_assets", [])), + "matched": [], + "ambiguous": [], + "unresolved": [], + }, + "tables": { + "matched_count": len(table_inventory.get("matched_tables", [])), + "ambiguous_count": len(table_inventory.get("ambiguous_tables", [])), + "unmatched_asset_count": len(table_inventory.get("unmatched_assets", [])), + "matched": [], + "ambiguous": [], + }, + } + for item in figure_inventory.get("matched_figures", []): + summary["figures"]["matched"].append( + { + "figure_number": item.get("figure_number"), + "legend_block_id": item.get("legend_block_id"), + "asset_block_ids": [asset.get("block_id") for asset in item.get("matched_assets", [])], + "page": item.get("page"), + "match_type": item.get("match_type") or item.get("decision"), + } + ) + for item in figure_inventory.get("ambiguous_figures", []): + summary["figures"]["ambiguous"].append( + { + "figure_number": item.get("figure_number"), + "legend_block_id": item.get("legend_block_id"), + "asset_block_ids": [asset.get("block_id") for asset in item.get("matched_assets", [])], + "candidate_asset_ids": item.get("candidate_asset_ids", []), + "page": item.get("page"), + } + ) + for item in figure_inventory.get("unresolved_clusters", []): + summary["figures"]["unresolved"].append( + { + "figure_number": item.get("figure_number"), + "asset_block_ids": item.get("asset_block_ids", []), + "page": item.get("page"), + } + ) + for item in table_inventory.get("matched_tables", []): + summary["tables"]["matched"].append( + { + "table_number": item.get("table_number"), + "caption_block_id": item.get("caption_block_id"), + "asset_block_ids": [asset.get("block_id") for asset in item.get("matched_assets", [])], + "page": item.get("page"), + } + ) + for item in table_inventory.get("ambiguous_tables", []): + summary["tables"]["ambiguous"].append( + { + "table_number": item.get("table_number"), + "caption_block_id": item.get("caption_block_id"), + "asset_block_ids": [asset.get("block_id") for asset in item.get("matched_assets", [])], + "page": item.get("page"), + } + ) + return summary + + +def _build_fulltext_mapping(blocks: list[dict], fulltext: str) -> dict: + normalized_fulltext = _normalize_text(fulltext) + rows = [] + found_count = 0 + for block in blocks: + text = _normalize_text(_block_text(block)) + if not text or block.get("role") in NON_RENDER_ROLES: + continue + snippet = text[:80] + position = normalized_fulltext.find(snippet) if snippet else -1 + found = position >= 0 + found_count += 1 if found else 0 + rows.append( + { + "block_id": _block_id(block), + "page": _block_page(block), + "role": block.get("role", ""), + "zone": block.get("zone", ""), + "render_default": block.get("render_default", True), + "snippet": snippet, + "found_in_fulltext": found, + "fulltext_offset": position, + } + ) + return { + "mappable_block_count": len(rows), + "found_count": found_count, + "missing_count": len(rows) - found_count, + "rows": rows, + } + + +def _selected_pages(mode: str, page_risk_summary: dict, blocks: list[dict], include_pages: list[int] | None) -> list[int]: + if include_pages: + return include_pages + if mode == "strict": + return sorted({_block_page(block) for block in blocks if _block_page(block) > 0}) + return list(page_risk_summary.get("selected_pages", [])) + + +def _build_findings( + blocks: list[dict], + page_risk_summary: dict, + reference_intrusions: list[dict], + ownership_summary: dict, + mapping_summary: dict, +) -> list[dict]: + findings: list[dict] = [] + for intrusion in reference_intrusions[:20]: + findings.append( + { + "category": "reference_span_error", + "severity": "critical", + "block_ids": [intrusion["block_id"]], + "truth": "block should remain outside the accepted reference span", + "pipeline_behavior": "block appears inside the logical reference reading-order region", + "root_cause_hypothesis": intrusion["reason"], + "evidence": { + "annotated_page": f"annotated_pages/page_{intrusion['page']:03d}.png", + "artifact": "reference_span_audit.json", + }, + } + ) + + for page in page_risk_summary.get("pages", []): + if "frontmatter_page" in page.get("risk_reasons", []) and page.get("counts", {}).get("unknown_structural", 0) >= UNKNOWN_THRESHOLD: + findings.append( + { + "category": "frontmatter_error", + "severity": "major", + "block_ids": [], + "truth": "page 1 should have clearer frontmatter role resolution", + "pipeline_behavior": "frontmatter page retains elevated unknown_structural density", + "root_cause_hypothesis": "frontmatter anchor or noise routing weakness", + "evidence": { + "annotated_page": f"annotated_pages/page_{page['page']:03d}.png", + "artifact": "page_risk_summary.json", + }, + } + ) + if "same_page_boundary" in page.get("risk_reasons", []) and page.get("counts", {}).get("reference_item", 0) > 0: + findings.append( + { + "category": "same_page_boundary_error", + "severity": "major", + "block_ids": [], + "truth": "body/reference/backmatter boundaries should be explainable at block level", + "pipeline_behavior": "page contains mixed body/reference/tail signals", + "root_cause_hypothesis": "same-page boundary ambiguity", + "evidence": { + "annotated_page": f"annotated_pages/page_{page['page']:03d}.png", + "artifact": "page_risk_summary.json", + }, + } + ) + + if ownership_summary["figures"]["ambiguous_count"] or ownership_summary["figures"]["unresolved_cluster_count"]: + findings.append( + { + "category": "object_ownership_error", + "severity": "major", + "block_ids": [], + "truth": "figure/table ownership should map captions and assets coherently", + "pipeline_behavior": "ambiguous or unresolved object ownership remains in the current artifact set", + "root_cause_hypothesis": "caption-to-asset grouping ambiguity", + "evidence": { + "annotated_page": None, + "artifact": "figure_table_ownership_summary.json", + }, + } + ) + + if mapping_summary.get("missing_count", 0) > 0: + findings.append( + { + "category": "render_mapping_error", + "severity": "minor", + "block_ids": [row["block_id"] for row in mapping_summary.get("rows", []) if not row.get("found_in_fulltext")][:20], + "truth": "rendered fulltext should be traceable back to source blocks", + "pipeline_behavior": "some render-default blocks are not easily mapped into the current fulltext output", + "root_cause_hypothesis": "render omission or snippet mismatch", + "evidence": { + "annotated_page": None, + "artifact": "fulltext_block_mapping_summary.json", + }, + } + ) + return findings + + +def _write_report_md(path: Path, payload: dict) -> None: + lines = [ + f"# OCR Truth Audit Report - {payload['paper_key']}", + "", + f"- Mode: `{payload['mode']}`", + f"- Status: `{payload.get('status', 'READY')}`", + f"- Reviewed pages: {payload.get('reviewed_pages', [])}", + f"- Reviewed blocks: {len(payload.get('reviewed_blocks', []))}", + "", + "## Findings", + "", + ] + findings = payload.get("findings", []) + if not findings: + lines.append("- No heuristic findings emitted. Perform manual truth review.") + else: + for finding in findings: + lines.append( + f"- `{finding['severity']}` `{finding['category']}`: {finding['pipeline_behavior']}" + ) + lines.extend( + [ + "", + "## Disposition Guidance", + "", + "- Use `repair` when the finding reflects a pipeline defect worth fixing now.", + "- Use `residual` when the finding is real but intentionally deferred.", + "- Do not rewrite expected truth to make current output look correct.", + ] + ) + _write_text(path, "\n".join(lines) + "\n") + + +def _audit_one_paper( + ocr_root: Path, + key: str, + mode: str, + refresh_artifacts: bool, + include_pages: list[int] | None, + focus: list[str], + output_root: Path, +) -> int: + if refresh_artifacts: + _refresh_artifacts(ocr_root, key) + + audit_dir = output_root / key + audit_dir.mkdir(parents=True, exist_ok=True) + + paths = _artifact_paths(ocr_root, key) + if not paths["paper_root"].exists(): + raise SystemExit(f"Paper not found under OCR root: {key}") + + blocks = _load_jsonl(paths["blocks_structured"]) + if not blocks: + raise SystemExit(f"No structured blocks found for {key}: {paths['blocks_structured']}") + + block_trace_path = audit_dir / "block_trace.csv" + _write_block_trace_csv(block_trace_path, blocks) + rendered_pages = _render_audit_visuals(key, blocks, ocr_root, audit_dir) + + missing, mismatches = _detect_stale(paths, block_trace_path, audit_dir / "annotated_pages") + fingerprint = _build_fingerprint(paths, block_trace_path, audit_dir / "annotated_pages") + + block_coverage = _summarize_blocks(blocks, mode) + figure_inventory = _load_json(paths["figure_inventory"], {}) or {} + table_inventory = _load_json(paths["table_inventory"], {}) or {} + reader_figures = _load_json(paths["reader_figures"], {}) or {} + doc_structure = _load_json(paths["document_structure"], {}) or {} + fulltext = "" + for candidate in (paths["compat_fulltext"], paths["render_fulltext"]): + if candidate.exists(): + fulltext = candidate.read_text(encoding="utf-8", errors="replace") + break + + page_risk_summary = _build_page_risk_summary(blocks, figure_inventory, table_inventory, reader_figures, include_pages) + verified_zone = _derive_reference_zone(blocks, doc_structure) + reference_span_audit, intrusion_candidates = _build_reference_span_audit(blocks, verified_zone) + ownership_summary = _build_object_ownership_summary(figure_inventory, table_inventory) + fulltext_mapping = _build_fulltext_mapping(blocks, fulltext) + audit_scope = _build_audit_scope(mode, blocks, page_risk_summary, include_pages) + + selected_pages = _selected_pages(mode, page_risk_summary, blocks, include_pages) + reviewed_blocks = [_block_id(block) for block in blocks if _block_page(block) in selected_pages] + + status = "AUDIT_BLOCKED" if missing or (mode == "strict" and mismatches) else "READY" + findings = [] if status == "AUDIT_BLOCKED" else _build_findings( + blocks, + page_risk_summary, + intrusion_candidates, + ownership_summary, + fulltext_mapping, + ) + + report = { + "paper_key": key, + "mode": mode, + "status": status, + "focus": focus, + "artifact_fingerprint": fingerprint, + "artifact_freshness": { + "missing": missing, + "mismatches": mismatches, + "annotated_pages_rendered": rendered_pages, + }, + "reviewed_pages": selected_pages, + "reviewed_blocks": reviewed_blocks, + "findings": findings, + } + + _write_json(audit_dir / "audit_report.json", report) + _write_report_md(audit_dir / "audit_report.md", report) + _write_json(audit_dir / "audit_scope.json", audit_scope) + _write_json(audit_dir / "block_coverage_summary.json", block_coverage) + _write_json(audit_dir / "page_risk_summary.json", page_risk_summary) + _write_json(audit_dir / "reference_intrusion_candidates.json", {"candidates": intrusion_candidates}) + _write_json(audit_dir / "figure_table_ownership_summary.json", ownership_summary) + _write_json(audit_dir / "fulltext_block_mapping_summary.json", fulltext_mapping) + _write_json(audit_dir / "reference_span_audit.json", reference_span_audit) + return 2 if status == "AUDIT_BLOCKED" and mode == "strict" else 0 + + +def main(argv: list[str] | None = None) -> int: + parser = build_parser() + args = parser.parse_args(argv) + keys = _resolve_keys(args) + ocr_root = _resolve_ocr_root(args.source_root) + output_root = Path(args.output_root) + + exit_code = 0 + for key in keys: + code = _audit_one_paper( + ocr_root=ocr_root, + key=key, + mode=args.mode, + refresh_artifacts=bool(args.refresh_artifacts), + include_pages=args.include_pages, + focus=[str(item) for item in args.focus], + output_root=output_root, + ) + exit_code = max(exit_code, code) + print(f"[OK] {key}: audit outputs written to {output_root / key}") + return exit_code + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/.opencode/skills/paperforge-development/scripts/verify_review_coverage.py b/.opencode/skills/paperforge-development/scripts/verify_review_coverage.py new file mode 100644 index 00000000..0ba7877f --- /dev/null +++ b/.opencode/skills/paperforge-development/scripts/verify_review_coverage.py @@ -0,0 +1,117 @@ +from __future__ import annotations + +import argparse +import json +from pathlib import Path + + +REQUIRED_FIELDS = [ + "block_id", + "page", + "review_status", + "truth_role", + "truth_zone", + "truth_reference_membership", +] + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Verify OCR truth-review coverage against audit_scope.json") + parser.add_argument("audit_dir", help="Path like audit/CAQNW9Q2") + return parser + + +def _load_json(path: Path) -> dict: + return json.loads(path.read_text(encoding="utf-8")) + + +def _load_jsonl(path: Path) -> list[dict]: + rows: list[dict] = [] + if not path.exists(): + return rows + for line in path.read_text(encoding="utf-8").splitlines(): + line = line.strip() + if not line: + continue + rows.append(json.loads(line)) + return rows + + +def _write_json(path: Path, payload: dict) -> None: + path.write_text(json.dumps(payload, indent=2, ensure_ascii=False), encoding="utf-8") + + +def verify(audit_dir: Path) -> tuple[int, dict]: + scope_path = audit_dir / "audit_scope.json" + review_path = audit_dir / "block_review.jsonl" + if not scope_path.exists(): + raise SystemExit(f"Missing audit scope: {scope_path}") + if not review_path.exists(): + raise SystemExit(f"Missing block review file: {review_path}") + + scope = _load_json(scope_path) + reviews = _load_jsonl(review_path) + required = {str(item): True for item in scope.get("required_block_ids", [])} + reviewed_valid: dict[str, dict] = {} + invalid_rows: list[dict] = [] + page_counts: dict[int, int] = {} + + for row in reviews: + block_id = str(row.get("block_id", "")).strip() + if not block_id: + invalid_rows.append({"block_id": None, "reason": "missing_block_id", "row": row}) + continue + missing_fields = [field for field in REQUIRED_FIELDS if row.get(field) in (None, "")] + if row.get("review_status") != "reviewed": + missing_fields.append("review_status=reviewed") + evidence = row.get("evidence") or {} + if not isinstance(evidence, dict) or not evidence.get("annotated_page"): + missing_fields.append("evidence.annotated_page") + if missing_fields: + invalid_rows.append({"block_id": block_id, "reason": "missing_fields", "fields": missing_fields}) + continue + reviewed_valid[block_id] = row + page = int(row.get("page", 0) or 0) + if page > 0: + page_counts[page] = page_counts.get(page, 0) + 1 + + missing_block_ids = sorted([block_id for block_id in required if block_id not in reviewed_valid]) + missing_pages = [] + for page_row in scope.get("selected_page_requirements", []): + page = int(page_row.get("page", 0) or 0) + if page_row.get("must_review_page") and page_counts.get(page, 0) == 0: + missing_pages.append(page) + + status = "PASS" + if invalid_rows or missing_block_ids or missing_pages: + status = "FAIL" + + coverage_ratio = (len(required) - len(missing_block_ids)) / len(required) if required else 1.0 + report = { + "paper_key": audit_dir.name, + "mode": scope.get("mode"), + "required_block_ids": sorted(required.keys()), + "reviewed_block_ids": sorted(reviewed_valid.keys()), + "missing_block_ids": missing_block_ids, + "missing_pages": missing_pages, + "invalid_rows": invalid_rows, + "coverage_ratio": coverage_ratio, + "status": status, + } + return (0 if status == "PASS" else 1), report + + +def main(argv: list[str] | None = None) -> int: + parser = build_parser() + args = parser.parse_args(argv) + audit_dir = Path(args.audit_dir) + if not audit_dir.exists(): + raise SystemExit(f"Audit dir not found: {audit_dir}") + code, report = verify(audit_dir) + _write_json(audit_dir / "coverage_check.json", report) + print(json.dumps(report, indent=2, ensure_ascii=False)) + return code + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/.opencode/skills/paperforge-development/workflows/ocr-truth-audit.md b/.opencode/skills/paperforge-development/workflows/ocr-truth-audit.md new file mode 100644 index 00000000..7ac4c5b7 --- /dev/null +++ b/.opencode/skills/paperforge-development/workflows/ocr-truth-audit.md @@ -0,0 +1,153 @@ +# OCR Truth Audit + +Inputs: + +- explicit `paper_key`, or current paper from active context +- `mode: strict | high-risk` + +## Pre-flight Checklist + +- [ ] Confirm this is developer/internal OCR audit work. +- [ ] Resolve one target paper. +- [ ] Confirm mode: `strict` or `high-risk`. +- [ ] Do not start by writing expectations or fixtures. +- [ ] Use visual and artifact truth first. +- [ ] Know the report contract in `../atoms/ocr-audit-report-schema.md`. +- [ ] Know the page ranking contract in `../atoms/ocr-page-risk-scoring.md`. +- [ ] Know the canonical role list in `../atoms/ocr-canonical-roles.md` — `truth_role` MUST be from that list. + +## Workflow + +1. Resolve the paper. + Accept an explicit `paper_key`; otherwise use the current paper in context. + + Also resolve the OCR root explicitly. The literature/OCR vault is outside the repo. Pass it with `--source-root` or set `PAPERFORGE_OCR_ROOT`. + +2. Refresh or check required artifacts. + Run `../scripts/ocr_truth_audit.py` with `--refresh-artifacts` when you need a fresh rebuild. Confirm availability of `block_trace`, `annotated_pages`, structured block artifacts, rendered `fulltext`, document-structure artifacts, and relevant figure/table artifacts. + +3. Verify artifact freshness before auditing. + Check that the paper PDF and derived artifacts belong to the same rebuild generation. At minimum compare fingerprints for `result_json`, raw/structured blocks, document structure, figure/table inventories, reader object outputs, and `fulltext`. + +4. Stop on stale artifacts. + If the required artifacts are out of sync, stop with `AUDIT_BLOCKED: stale artifacts` and record which artifacts mismatch. + +5. Generate helper summaries. + Materialize helper outputs under `audit//` via `../scripts/ocr_truth_audit.py`, but treat them as accelerators only, not truth. + +6. Inspect truth from evidence. + Start with the overview pages (`annotated_pages/page_*.png`). Each page shows every block as a numbered rectangle, color-coded by the **pipeline's final rendered role** (after gate + normalize, the same role used in `fulltext.md`). Each of the 28 roles has a distinct color, grouped by semantic family: + - dark blue family: `paper_title` `authors` `affiliation` `frontmatter_support` `frontmatter_noise` + - purple family: `abstract_heading` `abstract_body` + - orange/red family: `section_heading` `subsection_heading` `sub_subsection_heading` + - green: `body_paragraph` + - red family: `reference_heading` `reference_item` + - purple-brown family: `backmatter_heading` `backmatter_body` `backmatter_boundary_candidate` + - amber/gold family: `media_asset` `figure_caption` `figure_caption_candidate` `figure_inner_text` `table_caption` `table_caption_candidate` + - teal family: `structured_insert` `structured_insert_candidate` `non_body_insert` + - gray family: `noise` `footnote` `unknown_structural` + + The color tells you what the pipeline decided. Your job is to judge whether that decision matches visual truth. + + The companion index file (`annotated_pages/page_*_index.json`) maps each label number to its full `block_id`, `role`, `zone`, `category`, `text_preview`, and `bbox`. Use the index to look up what any numbered block currently is, then judge its truth visually from the overview page. + + Cross-reference against `block_trace`, document-structure artifacts, and rendered `fulltext` only after forming an initial visual judgment. + +7. Write audit outputs. + Produce the required reports and summaries using `../scripts/ocr_truth_audit.py` plus the schema in `../atoms/ocr-audit-report-schema.md`. + +8. Perform visual block review. + After prep outputs are written, inspect the selected pages and write `audit//block_review.jsonl`. Every reviewed block must be grounded in page visuals plus bbox/artifact evidence. + + **`truth_role` MUST be one of the canonical roles in `../atoms/ocr-canonical-roles.md`.** + Do not invent role names. The list includes `paper_title`, `authors`, `abstract_body`, `section_heading`, `body_paragraph`, `reference_item`, `backmatter_body`, `figure_caption`, `table_caption`, `noise`, and all other final pipeline roles. + Using old names (e.g. `media_asset` for `figure_asset`, `author_list` for `authors`, `structural_noise` for `noise`) is a workflow violation — `diff_audit.py` normalizes them, but the audit should be correct from the start. + + Each line must include `block_id`, `page`, `review_status`, `truth_role`, `truth_zone`, `truth_reference_membership`, and `evidence` with `annotated_page` and `method`. Example: + + ```json + {"block_id":"p5:9","page":5,"review_status":"reviewed","truth_role":"body_paragraph","truth_zone":"body_zone","truth_reference_membership":"outside","evidence":{"annotated_page":"annotated_pages/page_005.png","method":"visual+bbox"},"short_reason":"Conclusion body before references, not backmatter."} + ``` + + Use the page index to find a block's current role/zone before judging. Color category helps orient: red blocks are reference candidates, purple are backmatter candidates, etc. + +9. Verify review coverage. + Run `../scripts/verify_review_coverage.py` against the paper audit directory. If required blocks are missing for the chosen mode, the audit is incomplete. + +## Script Entry Point + +Use: + +```bash +python .opencode/skills/paperforge-development/scripts/ocr_truth_audit.py CAQNW9Q2 --source-root D:/YOUR/VAULT/System/PaperForge/ocr --mode high-risk --refresh-artifacts +``` + +For strict mode: + +```bash +python .opencode/skills/paperforge-development/scripts/ocr_truth_audit.py DWQQK2YB --source-root D:/YOUR/VAULT/System/PaperForge/ocr --mode strict --refresh-artifacts +``` + +Coverage verification: + +```bash +python .opencode/skills/paperforge-development/scripts/verify_review_coverage.py audit/DWQQK2YB +``` + +10. Classify findings. + Classify every real error into the frozen taxonomy. Use `audit_truth_gap` only when the audit layer itself misses or distorts block-level truth. + +11. Recommend disposition. + For each finding, recommend `repair` when it reflects a pipeline defect worth fixing now, or `residual` when it is real but intentionally deferred or outside the current close-out boundary. + +## Strict Mode + +Strict mode is full-coverage and block-oriented. + +- Every block gets a review state. +- Every reviewed block records truth for role, zone, order, object membership, and reference membership. +- Check role correctness against page context. +- Check zone correctness against actual placement. +- Check reading order and fulltext insertion point. +- Check figure/table ownership where applicable. +- Check reference membership and same-page boundary behavior where applicable. +- Do not infer truth from current rendered output as the first step. + +Minimum review questions per block: + +- What is the true role? +- What is the true zone? +- What blocks should come before and after it? +- Does it belong to a figure or table object? +- Is it inside or outside the accepted reference span? + +## High-Risk Mode + +High-risk mode is rapid audit for trust-sensitive layout classes. + +- Rank pages with the additive score in `../atoms/ocr-page-risk-scoring.md`. +- Prioritize frontmatter pages. +- Prioritize first-reference and mixed reference pages. +- Prioritize same-page body/reference/tail mixes. +- Prioritize post-reference backmatter pages. +- Prioritize figure-dense and table-dense pages. +- Prioritize figure/table ownership and caption-matching failures. + +Recommended focus targets: + +- `frontmatter` +- `reference_span` +- `same_page_boundary` +- `backmatter` +- `object_ownership` +- `reading_order` + +## Truth Rules + +Always follow this order: + +1. Determine block-level truth from page visuals and artifacts. +2. Record the truth. +3. Compare pipeline behavior against that truth. + +Never reverse the order by rewriting expected truth to fit current OCR output. diff --git a/PROJECT-MANAGEMENT.md b/PROJECT-MANAGEMENT.md index 2ef91343..e64db582 100644 --- a/PROJECT-MANAGEMENT.md +++ b/PROJECT-MANAGEMENT.md @@ -1105,3 +1105,99 @@ Updated `project/current/ocr-v2-remaining-issues-2026-06-18.md` with formal Gate - **Gate 2 DW Fig 3:** `test_dwqqk2yb_figure3_is_fully_owned_not_merely_captured` is xfail — needs group-first scoring refinement for mixed-layout pages - DWQQK2YB Figures 2/3/4 ownership gaps (pre-existing, unchanged) - 6 pre-existing figure test failures (pre-existing, unchanged) + +### 10.9 Task 7 — Readiness Review Hole Closure (2026-06-18) + +**Problem:** Post-implementation review found that Gate 1 completeness helpers were only unit-tested seams, not runtime health inputs; missing-PDF baseline semantics were misleading; and two audited papers had no readiness-class tags. + +**Root cause:** The readiness pass added helper functions and taxonomy scaffolding, but did not finish the production-health wiring or the full audit-ledger classification pass. + +**Fix:** wired completeness summaries into `build_ocr_health()` so the runtime health path now emits page coverage, region-completeness summary, and rendered-gap counts when PDF-side hints are present; normalized rendered-gap comparison for whitespace/case drift; changed missing-PDF coverage ratio to `None` instead of fake `1.0`; added a stronger audit contract requiring every audited paper to carry at least one layout tag; tagged `K7R8PEKW` and `SAN9AYVR` in `audit/coverage_ledger.json`; strengthened the Gate 3 boundary test with explicit zone assertions. + +**Result:** The two review-critical holes are closed at the code/test level, and Gate 4 no longer leaves 25% of the audit corpus unclassified. Remaining readiness work still centers on Gate 2 figure ownership refinement. + +**Test status:** +``` +python -m pytest tests/test_ocr_health.py -v + → 26 passed +python -m pytest tests/test_ocr_real_paper_audit_contracts.py -v + → 3 passed +python -m pytest tests/test_ocr_document.py -k "reference_boundary or same_page_reference_boundary_is_resolved_upstream" -v + → 1 passed +python -m pytest tests/test_ocr_real_paper_regressions.py -k "DWQQK2YB or CAQNW9Q2" -v + → 5 passed, 12 skipped, 1 xfailed +``` + +**Next topic:** finish Gate 2 by refining group-first figure scoring for DW Figure 3 and then rerun the readiness verification sweep. + +### 10.10 Task 8 — Gate 2 DW Figure 3 Ownership Closure (2026-06-18) + +**Problem:** Gate 2 still failed the strict DW Figure 3 regression because the caption lived on page 40 while its owned media lived on page 39, and the inventory path only tolerated that case as ambiguous/unresolved. + +**Root cause:** `build_figure_inventory()` had no usable previous-page sequential fallback for a strong next-page caption, and when a later fallback did claim the asset, the stale ambiguous entry for the same legend was left behind. + +**Fix:** added a focused figure unit test for previous-page sequential fallback, taught the sequential fallback to claim the nearest unclaimed asset from the immediately previous page when the numbered caption starts the next page, and removed stale `ambiguous_figures` entries when that fallback successfully matches the legend. Then promoted the DW strict regression from `xfail` to a normal passing test. + +**Result:** DW Figure 3 is now strictly matched instead of "captured but ambiguous," closing the remaining Gate 2 readiness gap on the tracked hard case. + +**Test status:** +``` +python -m pytest tests/test_ocr_figures.py -k "previous_page_asset_for_next_page_caption or partition_assets_by_caption_bands" -v + → 2 passed +python -m pytest tests/test_ocr_real_paper_regressions.py -k "dwqqk2yb_figure3_is_fully_owned_not_merely_captured or DWQQK2YB" -v + → 6 passed, 4 skipped +``` + +**Next topic:** rerun the broader readiness verification sweep and update the queue/docs from "Gate 2 partial" to the new all-current-gates-green state if the wider suites stay stable. + +### 10.11 Task 9 — Broader Readiness Sweep After Gate 2 Closure (2026-06-18) + +**Problem:** The targeted Gate 2 DW Figure 3 fix needed to be tested against the broader readiness sweep before the branch could claim all current gates were green. + +**Root cause:** The Gate 2 patch introduced a broader previous-page sequential fallback rule. While it fixed the tracked DW regression, that rule also widened generic matching behavior beyond the older figure-suite assumptions. + +**Fix:** Ran the broader readiness sweep across health, document, figure, real-paper regression, audit-contract, CLI/unit, and lint surfaces. Wrote the result back into project truth files instead of prematurely promoting Gate 2 to DONE. + +**Result:** Gates 1, 3, and 4 remained green on the verified suites. Targeted DW Gate 2 regression initially turned green, but the first broader figure sweep exposed 7 matcher regressions, proving the new fallback rule was too broad. + +**Test status:** +``` +python -m pytest tests/test_ocr_health.py -v + → 26 passed +python -m pytest tests/test_ocr_document.py -v + → 131 passed +python -m pytest tests/test_ocr_figures.py -v + → 82 passed, 7 failed +python -m pytest tests/test_ocr_real_paper_regressions.py -v + → 6 passed, 46 skipped +python -m pytest tests/test_ocr_real_paper_audit_contracts.py -v + → 3 passed +python -m pytest tests/cli/ tests/unit/ -v --tb=short + → 283 passed +ruff check paperforge/ tests/ + → 260 findings, pre-existing repo-wide lint debt remains +``` + +**Next topic:** tighten the previous-page sequential fallback in `build_figure_inventory()` so it stays narrow enough to preserve the older generic figure tests while keeping DW Figure 3 matched. + +### 10.12 Task 10 — Gate 2 Fallback Tightening With Layout Cross-Check (2026-06-18) + +**Problem:** The first Gate 2 closure patch solved DW Figure 3 but made the generic figure matcher too permissive: sidecar fallback overreached, inline mentions were treated as formal legends, and same-page weak candidates were being forced through the sequential path. + +**Root cause:** The new previous-page fallback lacked enough local layout cross-checks, and several existing generic matcher branches were still missing disambiguation guards (unnumbered sidecar legends, above-vs-below candidate ties, inline mention rejection, and caption-before-assets vs caption-after-assets partitioning). + +**Fix:** tightened `build_figure_inventory()` in four places: (1) previous-page sequential fallback now requires a strong numbered next-page caption plus post-reference and page-edge geometry evidence, (2) same-page weak sequential fallback is no longer allowed to manufacture matches, (3) sidecar partition now uses a local layout-orientation cross-check to choose between caption-before-assets and caption-after-assets mapping, and (4) inline figure mentions plus close above/below asset ties are explicitly rejected/held instead of forced into matches. + +**Result:** DW Figure 3 stays strictly matched, and the generic figure suite is green again. This closes Gate 2 on the currently tracked readiness suites instead of just on the one DW hard case. + +**Test status:** +``` +python -m pytest tests/test_ocr_figures.py -v + → 89 passed +python -m pytest tests/test_ocr_real_paper_regressions.py -k "DWQQK2YB or CAQNW9Q2" -v + → 6 passed, 12 skipped +python -m pytest tests/test_ocr_health.py tests/test_ocr_document.py tests/test_ocr_figures.py tests/test_ocr_real_paper_audit_contracts.py -q + → 249 passed +``` + +**Next topic:** decide whether to run one last broader readiness sweep boundary before Gate 5 or move directly to preparing the bounded unseen-paper blind audit sample. diff --git a/docs/superpowers/plans/2026-06-15-group-first-figure-inventory-plan.md b/docs/superpowers/plans/2026-06-15-group-first-figure-inventory-plan.md new file mode 100644 index 00000000..feb1847e --- /dev/null +++ b/docs/superpowers/plans/2026-06-15-group-first-figure-inventory-plan.md @@ -0,0 +1,643 @@ +# Group-First Figure Inventory Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Refactor strict figure inventory so media assets are normalized into deterministic candidate groups before legend matching. The first implementation does not attempt journal-specific layout recovery; it only changes the matching unit from individual assets to candidate groups while preserving current single-asset behavior as the default-compatible path. + +**Architecture:** Keep the existing formal-legend gate, reader-figure synthesis, and rendering layers intact. Refactor `build_figure_inventory()` so it builds deterministic candidate figure groups (`single`, `same_row_pair`, `same_row_triple`) before matching legends, then matches formal legends against those groups instead of individual assets. Restrict all fallback logic so it only runs after group-first matching fails, prevent sequence-promotion from conflicting with claimed groups, and make grouped outcomes explicit in inventory metadata without changing the external reader/render schema. + +**Tech Stack:** Python 3.14, pytest, PaperForge OCR workers, fixture-backed regression tests, live-vault OCR audit contracts. + +--- + +## File Map + +- Modify: `paperforge/worker/ocr_figures.py` + - Owns strict figure inventory construction. + - Current problem seam: legend loop scores individual assets before any generic grouping is applied. + - Planned changes: add candidate group builder, group scoring, group consumption, guarded fallback behavior. + +- Modify: `paperforge/worker/ocr_health.py` + - Owns health metrics derived from figure inventory. + - Planned changes: report grouped-match counts and grouped-vs-single inventory composition for audit visibility. + +- Modify: `tests/test_ocr_figures.py` + - Owns unit and contract behavior for `build_figure_inventory()`. + - Planned changes: add red-green coverage for group-first matching, no-regression safety for single-panel papers, and fallback constraints. + +- Modify: `tests/test_ocr_figure_reader.py` + - Owns strict-inventory to reader-payload normalization tests. + - Planned changes: verify grouped strict matches materialize into a single reader figure with multi-asset visual group. + +- Reference only: `tests/test_ocr_real_paper_regressions.py` + - Owns fixture-backed production-path regression gates. + - Planned use in this phase: verify no deterministic gold regressions after the inventory refactor. + +- Modify: `tests/test_ocr_real_paper_audit_contracts.py` + - Owns fixture quality contract, not runtime behavior. + - Planned changes: only if needed to reflect new stronger grouped-ownership coverage requirements. + +- Reference only: `tests/fixtures/ocr_real_papers/6FGDBFQN/expectations.json` + - Gold AJR expectations. + - Do not strengthen these expectations in this phase; use real-paper runs as observational shadow-checks only. + +- Reference only: `paperforge/worker/ocr_figure_reader.py` + - Consumes `matched_assets` and already supports multi-asset `visual_groups`. + - Do not redesign unless grouped strict inventory exposes an incompatibility. + +- Reference only: `paperforge/worker/ocr_render.py` + - Renders reader figures and figure embeds. + - Do not redesign unless grouped reader figures render incorrectly. + +## Non-Negotiable Constraints + +- Do not introduce journal-specific, AJR-specific, profile, or template logic in phase 1. +- Do not use image semantics, OCR panel text, or vision inference as required evidence. +- Do not override strong existing exact single-panel matches unless the new grouped match is strictly higher-confidence under deterministic rules. +- Do not let sequential fallback consume assets that belong to a valid grouped candidate. +- Do not let `_promote_sequence_matches()` create matches that conflict with already claimed grouped assets. +- Do not change reader/render public payload shape unless a test proves it is necessary. +- Do not support cross-page grouping in phase 1. +- Do not support arbitrary grids in phase 1; only `single`, `same_row_pair`, `same_row_triple`. + +## Risks To Avoid + +1. **Breaking ordinary single-image papers** + - Current majority path works because a caption often truly maps to one image. + - Guardrail: `single_asset` remains a valid candidate group and must compete in the same scoring system. + +2. **Over-grouping unrelated nearby media** + - Adjacent images, headshots, icon strips, or author bios can be geometrically close. + - Guardrail: group builder must filter to figure-like media only and enforce same-page, same-row, size-similarity, and spacing thresholds. + +3. **Double consumption across figures** + - Existing code consumes asset indices early. + - Guardrail: switch consumption unit from asset index to group asset-set ownership; once a grouped match wins, all member assets are reserved together. + +4. **Sequential fallback undoing correct grouping** + - Current fallback greedily assigns one caption to one remaining asset. + - Guardrail: fallback only sees truly unclaimed assets after grouped matching has finalized ownership. + +5. **Reader/render incompatibility** + - Downstream code assumes `matched_assets` list exists and reader figures build `visual_groups` from it. + - Guardrail: preserve `matched_assets` shape; add metadata fields instead of replacing existing ones. + +6. **False confidence on truncated legends** + - Weak captions should still HOLD/AMBIGUOUS rather than force-group. + - Guardrail: keep `_is_insufficient_legend_evidence()` gate in front of grouped matching. + +## Implementation Strategy + +### Phase 1: Promote Generic Grouping Into Primary Inventory Path + +Refactor the strict inventory path so candidate groups are built before legend matching. Reuse existing `_media_clusters()` concepts where possible, but tighten them into deterministic group candidates instead of late unresolved-only clusters. + +Candidate group model should include: + +```python +{ + "group_id": "group_001", + "page": 4, + "group_type": "same_row_pair", # single_asset | same_row_pair | same_row_triple + "asset_block_ids": [7, 8], + "media_blocks": [...], + "cluster_bbox": [x1, y1, x2, y2], + "row_band": {"y_center": 546.0, "height_mean": 336.5}, + "group_confidence": 0.82, + "group_evidence": ["same_page", "same_row_band", "size_similar", "tight_horizontal_gap"], +} +``` + +Legend matching should then evaluate candidate groups, not raw assets, with `single_asset` represented as a group of size 1. + +### Phase 2: Keep Current Fallbacks, But De-power Them + +`sequential_fallback` and `unresolved_clusters` stay, but only after group-first exact matching has run. They should never outrank deterministic grouped exact matches. + +### Phase 3: Shadow-Check Real Papers Without Tightening Gold Expectations + +After the generic path is stable, run observational real-paper compare checks on known multi-panel papers. Do not tighten AJR or other gold ownership expectations in this phase. + +## Task 1: Add Red Test For Group-First Matching In AJR-Like Layout + +**Files:** +- Modify: `tests/test_ocr_figures.py` +- Test: `tests/test_ocr_figures.py` + +- [ ] **Step 1: Write the failing test** + +Add a focused unit test near the existing figure matching tests: + +```python +def test_group_first_matching_prefers_same_row_pair_over_single_asset() -> None: + from paperforge.worker.ocr_figures import build_figure_inventory + + blocks = [ + { + "block_id": 1, + "role": "figure_caption", + "text": "Fig. 2 A and B, MRI and gross anatomic correlation.", + "page": 3, + "bbox": [80, 120, 420, 210], + "marker_signature": {"type": "figure_number", "number": 2}, + "zone": "display_zone", + "style_family": "legend_like", + }, + {"block_id": 2, "role": "media_asset", "raw_label": "image", "page": 3, "bbox": [450, 120, 780, 520]}, + {"block_id": 3, "role": "media_asset", "raw_label": "image", "page": 3, "bbox": [805, 120, 1130, 520]}, + ] + + inventory = build_figure_inventory(blocks, page_width=1200) + matched = inventory["matched_figures"] + + assert len(matched) == 1 + assert matched[0]["figure_number"] == 2 + assert [a["block_id"] for a in matched[0]["matched_assets"]] == [2, 3] +``` + +- [ ] **Step 2: Run test to verify it fails** + +Run: `python -m pytest tests/test_ocr_figures.py -k group_first_matching_prefers_same_row_pair_over_single_asset -v` + +Expected: FAIL because current inventory will match only one asset or produce ambiguous/no-asset behavior. + +- [ ] **Step 3: Commit the failing test only** + +```bash +git add tests/test_ocr_figures.py +git commit -m "test: add failing pair-group figure inventory case" +``` + +## Task 2: Add Candidate Figure Group Builder In `ocr_figures.py` + +**Files:** +- Modify: `paperforge/worker/ocr_figures.py` +- Test: `tests/test_ocr_figures.py` + +- [ ] **Step 1: Add helper skeletons for group-first inventory** + +Insert helpers near `_media_clusters()`: + +```python +def _bbox_width(bbox: list[float]) -> float: + return float(bbox[2] - bbox[0]) + + +def _bbox_height(bbox: list[float]) -> float: + return float(bbox[3] - bbox[1]) + + +def _bbox_center_y(bbox: list[float]) -> float: + return float(bbox[1] + bbox[3]) / 2.0 + + +def _candidate_group_entry(group_id: str, page: int, media_blocks: list[dict], group_type: str, evidence: list[str]) -> dict: + return { + "group_id": group_id, + "page": page, + "group_type": group_type, + "asset_block_ids": [b.get("block_id") for b in media_blocks if b.get("block_id") is not None], + "media_blocks": media_blocks, + "cluster_bbox": _cluster_bbox([b.get("bbox", [0, 0, 0, 0]) for b in media_blocks]), + "group_evidence": evidence, + } +``` + +- [ ] **Step 2: Add deterministic same-row grouping helper** + +Add a helper that derives `single_asset`, `same_row_pair`, and `same_row_triple` candidates from same-page media: + +```python +def _build_candidate_figure_groups_from_assets(assets: list[dict], page_width: float = 1200) -> list[dict]: + media = [ + b for b in assets + if not b.get("_non_body_media") + and (b.get("role") == "figure_asset" or (b.get("role") == "media_asset" and b.get("raw_label", "") in {"image", "chart", "figure"})) + ] + media.sort(key=lambda b: (b.get("page", 0), (b.get("bbox") or [0, 0, 0, 0])[1], (b.get("bbox") or [0, 0, 0, 0])[0])) + + groups: list[dict] = [] + next_id = 1 + by_page: dict[int, list[dict]] = {} + for block in media: + by_page.setdefault(int(block.get("page", 0) or 0), []).append(block) + + for page, page_media in by_page.items(): + for block in page_media: + groups.append(_candidate_group_entry(f"group_{next_id:03d}", page, [block], "single_asset", ["single_asset"])) + next_id += 1 + + for start in range(len(page_media)): + for size in (2, 3): + chunk = page_media[start:start + size] + if len(chunk) != size: + continue + bboxes = [b.get("bbox", [0, 0, 0, 0]) for b in chunk] + heights = [_bbox_height(bb) for bb in bboxes] + centers_y = [_bbox_center_y(bb) for bb in bboxes] + if max(centers_y) - min(centers_y) > max(40.0, min(heights) * 0.35): + continue + if max(heights) - min(heights) > max(40.0, min(heights) * 0.4): + continue + gaps = [] + ordered = sorted(chunk, key=lambda b: (b.get("bbox") or [0, 0, 0, 0])[0]) + for left, right in zip(ordered, ordered[1:]): + lb = left.get("bbox", [0, 0, 0, 0]) + rb = right.get("bbox", [0, 0, 0, 0]) + gaps.append(max(0.0, rb[0] - lb[2])) + if any(gap > page_width * 0.08 for gap in gaps): + continue + group_type = "same_row_pair" if size == 2 else "same_row_triple" + evidence = ["same_page", "same_row_band", "size_similar", "tight_horizontal_gap"] + groups.append(_candidate_group_entry(f"group_{next_id:03d}", page, ordered, group_type, evidence)) + next_id += 1 + + return groups +``` + +- [ ] **Step 3: Run the targeted test to verify it still fails for the right reason** + +Run: `python -m pytest tests/test_ocr_figures.py -k group_first_matching_prefers_same_row_pair_over_single_asset -v` + +Expected: still FAIL until the legend matching loop actually consumes candidate groups. + +- [ ] **Step 4: Commit helper-only scaffolding** + +```bash +git add paperforge/worker/ocr_figures.py tests/test_ocr_figures.py +git commit -m "refactor: add candidate figure group scaffolding" +``` + +## Task 3: Change Strict Matching From Asset-First To Group-First + +**Files:** +- Modify: `paperforge/worker/ocr_figures.py` +- Test: `tests/test_ocr_figures.py` + +- [ ] **Step 1: Replace raw asset candidate loop with candidate group loop** + +In `build_figure_inventory()`, after collecting `assets`, build candidate groups: + +```python +candidate_groups = _build_candidate_figure_groups_from_assets(assets, page_width=page_width) +used_asset_indices: set[int] = set() +used_asset_block_ids: set[int | str] = set() +``` + +Then replace the legend-scoring loop so it evaluates groups on the same page whose assets are all unconsumed. + +- [ ] **Step 2: Keep group scoring generic and single-asset compatible** + +Do not make `A and B` / `A–C` parsing a required condition in this phase. Instead, keep `single_asset` groups behavior as close as possible to the old `score_figure_match()` path, and let multi-asset groups compete only when their geometry is highly coherent. + +Add a helper: + +```python +def _score_legend_to_group(legend: dict, group: dict, *, caption_score: dict, page_width: float = 1200) -> dict: + if group.get("group_type") == "single_asset": + asset = group["media_blocks"][0] + return score_figure_match( + legend, + asset, + caption_score=caption_score, + anchor_supported=_has_anchor_supported_legend_context(legend), + caption_text_supported=_has_strong_explicit_caption_text(legend), + family_supported=False, + zone_supported=False, + ) + + # Multi-asset groups: score the group bbox and add a small coherence bonus. + # Keep this generic and geometry-only in phase 1. +``` + +- [ ] **Step 3: Materialize grouped strict matches without changing external shape** + +When a candidate group wins, emit: + +```python +entry = { + "figure_id": fig_id, + "legend_block_id": legend.get("block_id", ""), + "page": legend_page, + "text": legend_text, + "figure_number": fig_num, + "matched_assets": [ + {"block_id": a.get("block_id", ""), "bbox": a.get("bbox", [0, 0, 0, 0])} + for a in winning_group["media_blocks"] + ], + "cluster_bbox": winning_group["cluster_bbox"], + "group_type": winning_group["group_type"], + "group_evidence": winning_group["group_evidence"], + "confidence": match_score["score"], + "match_score": match_score, + "flags": ["group_first_match"] if len(winning_group["media_blocks"]) > 1 else [], + "caption_score": caption_score, +} +``` + +- [ ] **Step 4: Reserve all assets in a winning group together** + +Update the consumption logic so all member `block_id`s are added to `used_asset_block_ids`. Filter all future candidate groups against this set. + +- [ ] **Step 5: Run focused tests to verify the new grouped behavior** + +Run: + +```bash +python -m pytest tests/test_ocr_figures.py -k "group_first_matching_prefers_same_row_pair_over_single_asset or sequence_match_requires_at_least_one_asset_block_id" -v +``` + +Expected: PASS for the new group-first test and PASS for the existing safety tests. + +- [ ] **Step 6: Commit group-first strict matching** + +```bash +git add paperforge/worker/ocr_figures.py tests/test_ocr_figures.py +git commit -m "feat: match figure legends against candidate media groups" +``` + +## Task 4: Constrain Fallback Behavior So It Cannot Undo Grouped Matches + +**Files:** +- Modify: `paperforge/worker/ocr_figures.py` +- Test: `tests/test_ocr_figures.py` + +- [ ] **Step 1: Write a failing regression test for fallback stealing grouped assets** + +Add: + +```python +def test_sequential_fallback_does_not_split_grouped_assets() -> None: + from paperforge.worker.ocr_figures import build_figure_inventory + + blocks = [ + {"block_id": 1, "role": "figure_caption", "text": "Fig. 2 A and B, paired figure.", "page": 3, "bbox": [80, 120, 420, 210], "marker_signature": {"type": "figure_number"}, "zone": "display_zone", "style_family": "legend_like"}, + {"block_id": 2, "role": "media_asset", "raw_label": "image", "page": 3, "bbox": [450, 120, 780, 520]}, + {"block_id": 3, "role": "media_asset", "raw_label": "image", "page": 3, "bbox": [805, 120, 1130, 520]}, + {"block_id": 4, "role": "figure_caption", "text": "Fig. 3 Single figure.", "page": 4, "bbox": [80, 120, 420, 210], "marker_signature": {"type": "figure_number"}, "zone": "display_zone", "style_family": "legend_like"}, + ] + + inventory = build_figure_inventory(blocks, page_width=1200) + matched = {item["figure_number"]: item for item in inventory["matched_figures"]} + assert [a["block_id"] for a in matched[2]["matched_assets"]] == [2, 3] + assert 3 not in matched +``` + +- [ ] **Step 2: Run test to verify it fails if fallback still steals assets** + +Run: `python -m pytest tests/test_ocr_figures.py -k sequential_fallback_does_not_split_grouped_assets -v` + +Expected: FAIL before the fallback guard is added. + +- [ ] **Step 3: Guard sequential fallback against grouped ownership** + +Adjust fallback setup so `sorted_asts` excludes every asset whose `block_id` is already present in any grouped or single strict match. + +- [ ] **Step 4: Restrict unresolved cluster creation to truly unclaimed assets** + +Ensure `_media_clusters(unmatched_assets, ...)` sees only assets not already claimed by any grouped match. + +- [ ] **Step 5: Guard `_promote_sequence_matches()` against grouped ownership conflicts** + +Also ensure `_promote_sequence_matches()` does not create promoted matches that conflict with already claimed group assets, and does not promote empty-asset entries unless the existing contract explicitly allows sequence-style outcomes without assets. + +- [ ] **Step 6: Run focused fallback tests** + +Run: + +```bash +python -m pytest tests/test_ocr_figures.py -k "sequential_fallback_does_not_split_grouped_assets or strict_layer_promotes_contiguous_legends_with_ordered_assets" -v +``` + +Expected: PASS. + +- [ ] **Step 7: Commit fallback hardening** + +```bash +git add paperforge/worker/ocr_figures.py tests/test_ocr_figures.py +git commit -m "fix: prevent fallback from splitting grouped figure assets" +``` + +## Task 5: Verify Reader Layer Accepts Grouped Strict Matches + +**Files:** +- Modify: `tests/test_ocr_figure_reader.py` +- Test: `tests/test_ocr_figure_reader.py` + +- [ ] **Step 1: Write a failing reader test for grouped strict inventory** + +Add: + +```python +def test_reader_materializes_grouped_strict_match_as_single_visual_group() -> None: + from paperforge.worker.ocr_figure_reader import synthesize_reader_figures + + strict_inventory = { + "figure_legends": [], + "matched_figures": [ + { + "figure_number": 2, + "legend_block_id": 10, + "page": 3, + "text": "Fig. 2 A and B, paired figure.", + "matched_assets": [ + {"block_id": 20, "bbox": [100, 100, 300, 300]}, + {"block_id": 21, "bbox": [320, 100, 520, 300]}, + ], + "match_score": {"score": 0.82, "decision": "matched", "evidence": ["same_row_pair"]}, + "caption_score": {"score": 0.9}, + } + ], + "held_figures": [], + "ambiguous_figures": [], + "unmatched_legends": [], + "unresolved_clusters": [], + } + + payload = synthesize_reader_figures(strict_inventory, structured_blocks=[]) + rf = payload["reader_figures"][0] + assert rf["figure_number"] == 2 + assert rf["visual_groups"][0]["asset_block_ids"] == [20, 21] +``` + +- [ ] **Step 2: Run test to verify current reader behavior** + +Run: `python -m pytest tests/test_ocr_figure_reader.py -k grouped_strict_match_as_single_visual_group -v` + +Expected: PASS or targeted FAIL only if grouped metadata reveals a normalization incompatibility. + +- [ ] **Step 3: If needed, apply the minimal normalization fix** + +Only if the test fails, adjust `_asset_ids_from_item()` or `_materialize_reader_figure()` so multi-asset strict matches remain one reader figure with one visual group. + +- [ ] **Step 4: Run reader tests** + +Run: `python -m pytest tests/test_ocr_figure_reader.py -v --tb=short` + +Expected: PASS. + +- [ ] **Step 5: Commit reader compatibility work** + +```bash +git add tests/test_ocr_figure_reader.py paperforge/worker/ocr_figure_reader.py +git commit -m "test: verify reader figures preserve grouped strict matches" +``` + +## Task 6: Add Health Visibility For Grouped Matches + +**Files:** +- Modify: `paperforge/worker/ocr_health.py` +- Test: `tests/test_ocr_figures.py` + +- [ ] **Step 1: Add grouped-match counters in health report** + +Add derived metrics: + +```python +grouped_figure_match_count = sum( + 1 for mf in figure_inventory.get("matched_figures", []) if len(mf.get("matched_assets", [])) > 1 +) +single_asset_figure_match_count = sum( + 1 for mf in figure_inventory.get("matched_figures", []) if len(mf.get("matched_assets", [])) == 1 +) +``` + +Add to report: + +```python +"grouped_figure_match_count": grouped_figure_match_count, +"single_asset_figure_match_count": single_asset_figure_match_count, +``` + +- [ ] **Step 2: Add a small health test** + +Add a unit test that feeds a fake inventory with one grouped match and one single match into `build_ocr_health()` and verifies both counters. + +- [ ] **Step 3: Run targeted health test** + +Run: `python -m pytest tests/test_ocr_figures.py -k grouped_figure_match_count -v` + +Expected: PASS. + +- [ ] **Step 4: Commit health visibility** + +```bash +git add paperforge/worker/ocr_health.py tests/test_ocr_figures.py +git commit -m "feat: report grouped figure match health metrics" +``` + +## Task 7: Shadow-Check Real Papers Without Changing Gold Expectations + +**Files:** +- Modify: none +- Test: `tests/test_ocr_real_paper_regressions.py` +- Test: `tests/test_ocr_real_paper_contract.py` + +- [ ] **Step 1: Run deterministic regression suite without changing gold expectations** + +Run: + +```bash +python -m pytest tests/test_ocr_real_paper_regressions.py -v --tb=short +``` + +Expected: PASS with no deterministic gold regressions. + +- [ ] **Step 2: Run observational live-paper compare on one known multi-panel paper and one clean control** + +Run: + +```bash +$env:PAPERFORGE_REAL_OCR_VAULT='D:\L\OB\Literature-hub'; $env:PAPERFORGE_REAL_OCR_KEYS='6FGDBFQN,2GN9LMCW'; python -m pytest tests/test_ocr_real_paper_contract.py -v --tb=short +``` + +Expected: observational only. Do not tighten AJR ownership expectations in this phase. + +- [ ] **Step 3: Record grouped-match observations, but do not edit expectations** + +If grouped figure counts or reader outputs improve on live artifacts, note that in the implementation summary only. Do not modify `6FGDBFQN` gold ownership gates. + +## Task 8: Full Verification Across Unit, Fixture, and Gold Gates + +**Files:** +- Modify: none unless failures require targeted fixes +- Test: `tests/test_ocr_figures.py` +- Test: `tests/test_ocr_figure_reader.py` +- Test: `tests/test_ocr_real_paper_audit_contracts.py` +- Test: `tests/test_ocr_trace_vs_expectations.py` +- Test: `tests/test_ocr_real_paper_regressions.py` + +- [ ] **Step 1: Run focused unit tests for figure inventory** + +Run: + +```bash +python -m pytest tests/test_ocr_figures.py -v --tb=short +``` + +Expected: PASS. + +- [ ] **Step 2: Run reader tests** + +Run: + +```bash +python -m pytest tests/test_ocr_figure_reader.py -v --tb=short +``` + +Expected: PASS. + +- [ ] **Step 3: Run fixture-backed OCR gold verification** + +Run: + +```bash +python -m pytest tests/test_ocr_real_paper_audit_contracts.py tests/test_ocr_trace_vs_expectations.py tests/test_ocr_real_paper_regressions.py -v --tb=short +``` + +Expected: all deterministic gold tests PASS; existing env-dependent live-vault tests may remain skipped. + +- [ ] **Step 4: Spot-check live compare for AJR and one control paper** + +Run: + +```bash +$env:PAPERFORGE_REAL_OCR_VAULT='D:\L\OB\Literature-hub'; $env:PAPERFORGE_REAL_OCR_KEYS='6FGDBFQN,2GN9LMCW'; python -m pytest tests/test_ocr_real_paper_contract.py -v --tb=short +``` + +Expected: PASS or actionable flagged output only if the live vault has stale derived artifacts that need rebuild. + +- [ ] **Step 5: Commit final verification-safe fixes** + +```bash +git add . +git commit -m "feat: promote group-first figure inventory matching" +``` + +## Spec Coverage Check + +- Fix the structural problem where grouped media exists in code but not as the default match unit: covered by Tasks 2-4. +- Preserve generic behavior for most papers and avoid AJR-only hacks: covered by candidate-group design and constraints in Tasks 2-4. +- Keep reader/render contracts stable: covered by Task 5. +- Improve observability and auditability: covered by Task 6. +- Prove the result on the deterministic gold suite and shadow-check live papers without widening scope: covered by Tasks 7-8. + +## Placeholder Scan + +- No `TODO`, `TBD`, or “appropriate handling” placeholders remain. +- Every code-changing task includes target file paths, test commands, and concrete code sketches. + +## Type/Name Consistency Check + +- Group builder terminology is consistent: `candidate group`, `group_type`, `asset_block_ids`, `matched_assets`. +- Downstream compatibility is preserved by keeping `matched_assets` as the externally consumed shape. + +## Execution Handoff + +**Plan complete and saved to `docs/superpowers/plans/2026-06-15-group-first-figure-inventory-plan.md`. Two execution options:** + +**1. Subagent-Driven (recommended)** - I dispatch a fresh subagent per task, review between tasks, fast iteration + +**2. Inline Execution** - Execute tasks in this session using executing-plans, batch execution with checkpoints + +**Which approach?** diff --git a/docs/superpowers/plans/2026-06-17-ocr-v2-closeout-single-plan.md b/docs/superpowers/plans/2026-06-17-ocr-v2-closeout-single-plan.md new file mode 100644 index 00000000..2987002a --- /dev/null +++ b/docs/superpowers/plans/2026-06-17-ocr-v2-closeout-single-plan.md @@ -0,0 +1,522 @@ +# OCR-v2 Unified Close-Out Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Finish the current OCR-v2 close-out pass by removing useless preproof cover page 1 at the structured-block layer, conservatively tightening tail/post-reference cleanup, verifying the branch on the 8 gold papers plus up to 10 targeted vault papers, then rewriting the residual picture and deciding whether figure-group work is truly next. + +**Architecture:** Keep the existing OCR-v2 anchor-first pipeline. Make one early filtering change in the structured-block build path so true preproof cover pages never enter document normalization, then make conservative no-damage changes in `ocr_document.py` so tail/post-reference handling preserves valid structure instead of trying to perfect `backmatter_heading` taxonomy. After code changes, run a bounded rebuild/audit pass on the 8 gold fixtures plus a deliberately chosen 10-paper vault sample and use that evidence to update the project trackers and decide whether to reopen figure-group work. + +**Tech Stack:** Python 3.14, existing `paperforge.worker` OCR pipeline, pytest, existing rebuild scripts, repo-local audit helpers under `.opencode/skills/paperforge-development/`, vault OCR artifacts. + +--- + +## File Structure + +- Modify: `paperforge/worker/ocr_blocks.py` + - Owns structured-block build flow; add the preproof page-1 drop before document normalization. +- Modify: `paperforge/worker/ocr_document.py` + - Owns conservative tail/post-reference cleanup behavior. +- Modify: `tests/test_ocr_document.py` + - Owns document-pipeline and post-reference regression coverage. +- Modify: `tests/test_ocr_real_paper_regressions.py` + - Owns production-path replay checks on known papers. +- Modify: `tests/fixtures/ocr_real_papers/DWQQK2YB/expectations.json` + - Update only if post-fix visual truth proves the current expectations are stale. +- Modify: `tests/fixtures/ocr_real_papers/coverage_manifest.json` + - Record any extra verification-sample metadata only if the existing manifest is the cleanest place to note layout coverage. +- Modify: `project/current/ocr-error-root-cause-fix-queue.md` + - Write back the final measured residual summary. +- Modify: `PROJECT-MANAGEMENT.md` + - Record what the unified close-out pass fixed and what remains. + +--- + +### Task 1: Lock Preproof-Drop And Conservative Tail Cleanup With Red Tests + +**Files:** +- Modify: `tests/test_ocr_document.py` +- Modify: `tests/test_ocr_real_paper_regressions.py` + +- [ ] **Step 1: Add a unit test for preproof page-1 removal** + +Add this test to `tests/test_ocr_document.py` or the nearest structured/document pipeline test section: + +```python +def test_preproof_cover_page_one_is_dropped_before_document_normalization() -> None: + from paperforge.worker.ocr_blocks import build_structured_blocks + + raw_blocks = [ + { + "paper_id": "DWTEST", + "block_id": "p1_preproof", + "page": 1, + "text": "Journal Pre-proof", + "block_content": "Journal Pre-proof", + "raw_label": "text", + "block_label": "text", + "marker_signature": {"type": "preproof_marker"}, + "bbox": [80, 100, 600, 180], + "page_width": 1200, + "page_height": 1600, + }, + { + "paper_id": "DWTEST", + "block_id": "p1_pii", + "page": 1, + "text": "PII: S1234-5678(26)00001-2", + "block_content": "PII: S1234-5678(26)00001-2", + "raw_label": "text", + "block_label": "text", + "marker_signature": {"type": "none"}, + "bbox": [80, 240, 700, 300], + "page_width": 1200, + "page_height": 1600, + }, + { + "paper_id": "DWTEST", + "block_id": "p2_title", + "page": 2, + "text": "Real Article Title", + "block_content": "Real Article Title", + "raw_label": "paragraph_title", + "block_label": "paragraph_title", + "marker_signature": {"type": "none"}, + "bbox": [80, 120, 800, 190], + "page_width": 1200, + "page_height": 1600, + }, + ] + + structured, _doc = build_structured_blocks(raw_blocks, source_metadata={"title": "Real Article Title"}) + pages = {int(b.get("page", 0) or 0) for b in structured} + assert 1 not in pages + assert 2 in pages +``` + +- [ ] **Step 2: Add a non-preproof guard test** + +Still in `tests/test_ocr_document.py`, add: + +```python +def test_non_preproof_page_one_is_not_dropped() -> None: + from paperforge.worker.ocr_blocks import build_structured_blocks + + raw_blocks = [ + { + "paper_id": "OKTEST", + "block_id": "p1_title", + "page": 1, + "text": "A normal paper title", + "block_content": "A normal paper title", + "raw_label": "paragraph_title", + "block_label": "paragraph_title", + "marker_signature": {"type": "none"}, + "bbox": [80, 120, 900, 200], + "page_width": 1200, + "page_height": 1600, + } + ] + + structured, _doc = build_structured_blocks(raw_blocks, source_metadata={"title": "A normal paper title"}) + pages = {int(b.get("page", 0) or 0) for b in structured} + assert 1 in pages +``` + +- [ ] **Step 3: Add a conservative tail cleanup regression** + +Add this test to `tests/test_ocr_document.py`: + +```python +def test_post_reference_plain_heading_stays_heading_not_backmatter_taxonomy_upgrade() -> None: + from paperforge.worker.ocr_document import normalize_document_structure + + blocks = [ + {"block_id": "refs", "page": 12, "seed_role": "reference_heading", "text": "References", "bbox": [80, 900, 340, 960]}, + {"block_id": "r1", "page": 12, "seed_role": "reference_item", "text": "[1] First ref", "bbox": [80, 1000, 960, 1080]}, + {"block_id": "bio_h", "page": 13, "seed_role": "subsection_heading", "text": "Biographies", "bbox": [80, 120, 380, 180]}, + {"block_id": "bio_b", "page": 13, "seed_role": "body_paragraph", "text": "Author A received the PhD degree in 2015.", "bbox": [80, 220, 980, 320]}, + ] + + _doc, normalized = normalize_document_structure(blocks) + by_id = {b["block_id"]: b for b in normalized} + assert by_id["bio_h"]["role"] in {"subsection_heading", "backmatter_heading"} + assert by_id["bio_b"]["role"] != "unknown_structural" +``` + +- [ ] **Step 4: Add a production-path DW regression that expects page 1 to disappear** + +In `tests/test_ocr_real_paper_regressions.py`, replace the old preproof-frontmatter expectation with: + +```python +def test_dwqqk2yb_preproof_page_one_is_absent_from_structured_blocks(tmp_path: Path) -> None: + result = replay_production_pipeline("DWQQK2YB", tmp_path) + blocks = result["structured_blocks"] + assert not any(int(b.get("page", 0) or 0) == 1 for b in blocks) +``` + +- [ ] **Step 5: Run the focused red tests** + +Run: `python -m pytest tests/test_ocr_document.py -k "preproof or post_reference_plain_heading" -v --tb=short` + +Run: `python -m pytest tests/test_ocr_real_paper_regressions.py -k "dwqqk2yb_preproof_page_one" -v --tb=short` + +Expected: FAIL until the implementation lands. + +- [ ] **Step 6: Commit the failing tests only** + +```bash +git add tests/test_ocr_document.py tests/test_ocr_real_paper_regressions.py +git commit -m "test: lock unified OCR close-out regressions" +``` + +--- + +### Task 2: Drop Confident Preproof Cover Page 1 At The Structured-Block Layer + +**Files:** +- Modify: `paperforge/worker/ocr_blocks.py` +- Test: `tests/test_ocr_document.py` + +- [ ] **Step 1: Add a narrow helper for preproof cover-page detection** + +Near `build_structured_blocks()` helpers in `paperforge/worker/ocr_blocks.py`, add: + +```python +def _is_confident_preproof_cover_page(blocks: list[dict], page: int) -> bool: + if page != 1: + return False + page_blocks = [b for b in blocks if int(b.get("page", 0) or 0) == page] + if not page_blocks: + return False + + marker_hits = 0 + noisy_cover_hits = 0 + useful_heading_hits = 0 + + for block in page_blocks: + marker_type = str((block.get("marker_signature") or {}).get("type") or "") + text = str(block.get("text") or block.get("block_content") or "").strip().lower() + raw_label = str(block.get("raw_label") or block.get("block_label") or "") + if marker_type == "preproof_marker": + marker_hits += 1 + if any(token in text for token in ("journal pre-proof", "pre-proof", "pii:", "accepted date", "published by elsevier")): + noisy_cover_hits += 1 + if raw_label == "paragraph_title" and len(text.split()) >= 4 and "pre-proof" not in text: + useful_heading_hits += 1 + + return marker_hits >= 1 and noisy_cover_hits >= 2 and useful_heading_hits == 0 +``` + +- [ ] **Step 2: Filter page 1 before document normalization** + +In `build_structured_blocks()`, after the initial structured row shaping and before `normalize_document_structure()` runs, add: + +```python + if _is_confident_preproof_cover_page(structured_blocks, 1): + structured_blocks = [ + block for block in structured_blocks + if int(block.get("page", 0) or 0) != 1 + ] +``` + +- [ ] **Step 3: Make the filter self-documenting in evidence/debug metadata** + +Add a small debug note so dropped-page behavior is traceable during local debugging: + +```python + for block in structured_blocks: + if int(block.get("page", 0) or 0) == 2: + block.setdefault("evidence", []).append("page_1_preproof_cover_dropped_upstream") + break +``` + +- [ ] **Step 4: Run focused tests until green** + +Run: `python -m pytest tests/test_ocr_document.py -k "preproof_cover_page_one or non_preproof_page_one" -v --tb=short` + +Run: `python -m pytest tests/test_ocr_real_paper_regressions.py -k "dwqqk2yb_preproof_page_one" -v --tb=short` + +Expected: PASS. + +- [ ] **Step 5: Commit** + +```bash +git add paperforge/worker/ocr_blocks.py tests/test_ocr_document.py tests/test_ocr_real_paper_regressions.py +git commit -m "fix: drop preproof cover page one before OCR normalization" +``` + +--- + +### Task 3: Tighten Tail/Post-Reference Cleanup Conservatively + +**Files:** +- Modify: `paperforge/worker/ocr_document.py` +- Test: `tests/test_ocr_document.py` + +- [ ] **Step 1: Add a helper that recognizes explicit backmatter-body evidence only** + +Near `_exclude_tail_nonref_from_body_flow()` in `paperforge/worker/ocr_document.py`, keep the helper deliberately small: + +```python +def _looks_like_backmatter_body_text(text: str) -> bool: + lower = text.lower() + markers = ( + "conflict of interest", + "declaration", + "publisher", + "author contributions", + "funding", + "acknowledg", + "data availability", + "ethics", + "supplement", + ) + return any(marker in lower for marker in markers) +``` + +- [ ] **Step 2: Make `_exclude_tail_nonref_from_body_flow()` preserve ordinary prose by default** + +Replace the broad conversion logic with: + +```python + if not _looks_like_backmatter_body_text(text): + continue +``` + +and keep the role rewrite only for blocks that pass the helper. + +- [ ] **Step 3: Keep post-reference normalization from damaging object-adjacent structure** + +Inside `_normalize_backmatter_roles_after_boundary()`, add a preserve set and skip conversion for those roles: + +```python +_POST_REF_PRESERVE_ROLES = { + "figure_inner_text", + "figure_caption", + "figure_caption_candidate", + "table_caption", + "table_caption_candidate", + "media_asset", + "table_html", +} +``` + +and use: + +```python +if block.get("role") in _POST_REF_PRESERVE_ROLES: + continue +``` + +- [ ] **Step 4: Avoid aggressive heading taxonomy expansion** + +Do not add a large phrase list for `backmatter_heading`. Instead, keep valid `section_heading` / `subsection_heading` / `sub_subsection_heading` intact when the current evidence already supports them. + +Use this conservative guard in the normalization branch: + +```python +if block.get("role") in {"section_heading", "subsection_heading", "sub_subsection_heading"}: + continue +``` + +- [ ] **Step 5: Run focused tests until green** + +Run: `python -m pytest tests/test_ocr_document.py -k "tail_nonref or backmatter or post_reference_plain_heading" -v --tb=short` + +Expected: PASS. + +- [ ] **Step 6: Commit** + +```bash +git add paperforge/worker/ocr_document.py tests/test_ocr_document.py +git commit -m "fix: conservatively tighten OCR tail and post-reference cleanup" +``` + +--- + +### Task 4: Run The Bounded Rebuild And Choose The 10-Paper Expansion Sample + +**Files:** +- Modify if needed: `tests/fixtures/ocr_real_papers/coverage_manifest.json` +- Reference: vault OCR store under `D:/L/OB/Literature-hub/System/PaperForge/ocr/` + +- [ ] **Step 1: Rebuild the 8 gold papers first** + +Run: + +```bash +python scripts/dev/ocr_rebuild_paper.py CAQNW9Q2 DWQQK2YB TSCKAVIS A8E7SRVS K7R8PEKW 6FGDBFQN SAN9AYVR 2GN9LMCW +``` + +Expected: refreshed structured blocks, document structure, render output, and trace artifacts for the gold set. + +- [ ] **Step 2: Pick up to 10 extra vault papers by layout class** + +Use the vault OCR inventory plus existing audit knowledge to choose up to 10 papers that cover these classes: + +```text +1. preproof cover page +2. same-page body/reference split +3. biography/backmatter tail +4. multi-panel figure +5. narrow-caption or side-caption figure +6. table-heavy layout +7. publisher margin-noise layout +``` + +Write the chosen keys into your working notes or `coverage_manifest.json` only if that file is already the cleanest coverage ledger. + +- [ ] **Step 3: Rebuild the 10 selected vault papers** + +Run: + +```bash +python scripts/dev/ocr_rebuild_paper.py +``` + +Expected: the bounded sample is rebuilt from current code, without opening a full-vault rebuild loop. + +- [ ] **Step 4: Refresh audit artifacts for the selected papers** + +For each selected key, run: + +```bash +python .opencode/skills/paperforge-development/scripts/ocr_truth_audit.py --source-root D:/L/OB/Literature-hub/System/PaperForge/ocr --mode high-risk --refresh-artifacts +``` + +Expected: refreshed high-risk audit helper outputs without manual truth loss. + +- [ ] **Step 5: Commit only if coverage metadata changed** + +If you updated `tests/fixtures/ocr_real_papers/coverage_manifest.json`, commit it: + +```bash +git add tests/fixtures/ocr_real_papers/coverage_manifest.json +git commit -m "docs: record bounded OCR close-out verification sample" +``` + +If no tracked metadata changed, skip this commit. + +--- + +### Task 5: Regressions, Truth Updates, And Residual Rewrite + +**Files:** +- Modify: `tests/fixtures/ocr_real_papers/DWQQK2YB/expectations.json` +- Modify: `project/current/ocr-error-root-cause-fix-queue.md` +- Modify: `PROJECT-MANAGEMENT.md` + +- [ ] **Step 1: Run the deterministic regression suite** + +Run: + +```bash +python -m pytest tests/test_ocr_document.py tests/test_ocr_real_paper_regressions.py tests/test_ocr_roles.py tests/test_ocr_layout_first_regressions.py -v --tb=short +``` + +Expected: green or improved close-out gates for the touched areas. + +- [ ] **Step 2: Update expectations only where visual truth proves the old truth is stale** + +If DW expectations still mention page-1 preproof content after the drop, update the relevant `tests/fixtures/ocr_real_papers/DWQQK2YB/expectations.json` entries so they stop asserting the removed cover page. + +Use this pattern only when justified: + +```json +{ + "text_contains": "Real article title", + "expected_role": "paper_title", + "expected_zone": "frontmatter_main_zone", + "notes": "Updated after structured-layer preproof page-1 drop; visual audit confirms page 1 is disposable cover content." +} +``` + +- [ ] **Step 3: Re-run the high-risk diff audit on touched papers** + +Run for the gold papers most affected by this pass and for the newly selected sample papers: + +```bash +python .opencode/skills/paperforge-development/scripts/diff_audit.py DWQQK2YB --source-root D:/L/OB/Literature-hub/System/PaperForge/ocr --audit-root D:/L/Med/Research/99_System/LiteraturePipeline/github-release/audit +``` + +Repeat for CAQ and any newly selected papers where the current pass changed outputs materially. + +- [ ] **Step 4: Rewrite the residual summary from evidence** + +Update `project/current/ocr-error-root-cause-fix-queue.md` and `PROJECT-MANAGEMENT.md` with: + +```md +### Unified Close-Out Pass Result (2026-06-17) + +- What changed: preproof cover page 1 is dropped before normalization; tail/post-reference cleanup is more conservative +- Verification scope: 8 gold papers + up to 10 targeted vault papers +- Result: [fill with measured residual delta after rerun] +- Remaining document-structure residuals: [measured list] +- Remaining figure-ownership residuals: [measured list] +- Decision: [state whether figure-group is now the dominant blocker] +``` + +- [ ] **Step 5: Commit** + +```bash +git add tests/fixtures/ocr_real_papers/DWQQK2YB/expectations.json project/current/ocr-error-root-cause-fix-queue.md PROJECT-MANAGEMENT.md +git commit -m "docs: record unified OCR close-out pass results" +``` + +--- + +### Task 6: Figure-Group Decision Gate + +**Files:** +- Modify if needed: `project/current/ocr-v2-closeout-priority.md` +- Modify if needed: `PROJECT-MANAGEMENT.md` + +- [ ] **Step 1: Compare residual category totals after the bounded audit pass** + +Build a simple decision table from the refreshed audit results: + +```text +document_structure_residuals = ? +truth_or_expectation_residuals = ? +figure_ownership_residuals = ? +``` + +Only use measured post-pass counts. + +- [ ] **Step 2: Decide whether figure-group work is now dominant** + +Use this rule: + +```text +Reopen group-first figure inventory only if figure ownership is the largest stable remaining class +and it appears across more than one paper/layout family in the bounded sample. +``` + +- [ ] **Step 3: Write the decision back to the active priority file if it changes** + +If figure-group becomes the next dominant blocker, update `project/current/ocr-v2-closeout-priority.md` and `PROJECT-MANAGEMENT.md` to say so explicitly. If it does not, update them to keep the branch on the current close-out/verification path. + +- [ ] **Step 4: Commit only if a tracked priority file changed** + +```bash +git add project/current/ocr-v2-closeout-priority.md PROJECT-MANAGEMENT.md +git commit -m "docs: update OCR close-out next-step decision" +``` + +Skip the commit if no tracked priority files changed. + +--- + +## Self-Review + +- Spec coverage: this plan covers every section of the approved unified spec: preproof page-1 drop, conservative tail cleanup, bounded 8+10 verification, residual write-back, and figure-group decision gate. +- Placeholder scan: no `TODO`, `TBD`, or unresolved implementation placeholders remain in the task steps. +- Type consistency: the plan uses existing seams only (`build_structured_blocks`, `normalize_document_structure`, `_exclude_tail_nonref_from_body_flow`, `_normalize_backmatter_roles_after_boundary`, `replay_production_pipeline`, `ocr_rebuild_paper.py`, `ocr_truth_audit.py`, `diff_audit.py`) and does not invent a second implementation track. + +Plan complete and saved to `docs/superpowers/plans/2026-06-17-ocr-v2-closeout-single-plan.md`. Two execution options: + +**1. Subagent-Driven (recommended)** - I dispatch a fresh subagent per task, review between tasks, fast iteration + +**2. Inline Execution** - Execute tasks in this session using executing-plans, batch execution with checkpoints + +Which approach? diff --git a/docs/superpowers/plans/2026-06-18-dwq-repair-and-completeness-phase-plan.md b/docs/superpowers/plans/2026-06-18-dwq-repair-and-completeness-phase-plan.md new file mode 100644 index 00000000..38d5fb27 --- /dev/null +++ b/docs/superpowers/plans/2026-06-18-dwq-repair-and-completeness-phase-plan.md @@ -0,0 +1,523 @@ +# DWQ Repair And Completeness Phase Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Clean stale OCR tracking files first, then repair the remaining real DWQQK2YB support-routing and figure-ownership issues, rebuild affected derived artifacts, and leave the completeness-check layer fully planned as the next slice. + +**Architecture:** This phase has one hard rule: fix the repo's current truth files before touching behavior, so later work stops inheriting stale issue narratives. After the cleanup, make two narrow production repairs in the existing OCR-v2 pipeline: one in frontmatter support routing and one in figure ownership partition/claim logic, then rebuild the affected paper artifacts and update the docs again from measured results. The completeness-check feature remains planning-only in this phase and is carried forward as the next explicit implementation topic. + +**Tech Stack:** Python 3.14, pytest, existing `paperforge.worker` OCR pipeline, `scripts/dev/ocr_rebuild_paper.py`, audit artifacts under `audit/`, real-paper replay regressions. + +--- + +## File Structure + +- Modify: `project/current/ocr-v2-closeout-priority.md` + - Active next-step truth source; must stop claiming already-fixed P0-P2 issues are open. +- Modify: `project/current/ocr-v2-remaining-issues-2026-06-18.md` + - Residual issue snapshot; must be rewritten from actual post-9.7 state. +- Modify: `PROJECT-MANAGEMENT.md` + - Session log and current truth surface; must record file cleanup, DWQ repairs, rebuild results, and next planned completeness topic. +- Modify: `paperforge/worker/ocr_roles.py` + - Frontmatter support routing seam for first-surviving-page support-like blocks. +- Modify: `paperforge/worker/ocr_figures.py` + - Figure candidate-group partition and local asset claiming seam for DWQQK2YB ownership repair. +- Modify: `tests/test_ocr_roles.py` + - Unit-level frontmatter support regression coverage. +- Modify: `tests/test_ocr_figures.py` + - Unit-level ownership/partition regression coverage. +- Modify: `tests/test_ocr_real_paper_regressions.py` + - Real-paper DWQQK2YB ownership and support-routing checks. +- Use: `scripts/dev/ocr_rebuild_paper.py` + - Canonical derived-artifact rebuild entry point after the code fixes land. +- Preserve reference spec: `docs/superpowers/specs/2026-06-18-ocr-completeness-check-design.md` + - Carry forward into the end-of-phase planning/doc update; no production-code changes in this phase. + +--- + +### Task 1: Clean Stale Truth Files First + +**Files:** +- Modify: `project/current/ocr-v2-closeout-priority.md` +- Modify: `project/current/ocr-v2-remaining-issues-2026-06-18.md` +- Modify: `PROJECT-MANAGEMENT.md` + +- [ ] **Step 1: Write the failing doc-truth regression note as a checklist in the current files** + +At the top of your work buffer, use this checklist to drive the edits: + +```text +- remove already-fixed DW post-drop frontmatter item as an active open bug +- remove or downgrade stale `media_asset -> body_paragraph` as a live blocker when latest analysis says it was stale audit truth +- stop treating DW biography mismatch as active if the current replay/audit state already moved +- keep DW frontmatter support routing open +- keep DW figure ownership open +- keep completeness check as next planned work +``` + +- [ ] **Step 2: Rewrite `project/current/ocr-v2-closeout-priority.md` so it no longer inherits stale blockers** + +Make the following concrete edits: + +```md +## Remaining blockers before merge: +- DWQQK2YB frontmatter support/equal-contribution routing on the first surviving page +- DWQQK2YB figure ownership on mixed post-reference figure pages +- derived artifact rebuild after ownership/support repairs +- completeness-check layer implementation (next slice) +``` + +Remove these stale or downgraded claims from that block unless fresh evidence re-proves them during this phase: + +```md +- `media_asset → body_paragraph` (42 blocks) — zone/attribution work +- DW biography page mismatch +``` + +- [ ] **Step 3: Rewrite `project/current/ocr-v2-remaining-issues-2026-06-18.md` to reflect post-9.7 truth** + +Replace the stale issue sections with a compact residual list shaped like this: + +```md +## Active Residuals After 9.7 + +1. DWQQK2YB first-surviving-page support blocks still route inconsistently (`frontmatter_support` vs `frontmatter_noise` / body fallback) +2. DWQQK2YB figure ownership still over-claims or stays ambiguous on mixed post-reference figure pages +3. Backmatter heading taxonomy remains partially conservative by design; only fix if verified zone semantics require it +4. Completeness-check layer is specified and pending implementation +``` + +Delete or archive the old sections for: + +- Issue 1: DW post-drop frontmatter not recognized +- Issue 3: figure inner label issue as a main active blocker +- Issue 4: publisher noise as a main active blocker +- Issue 6: DW biography mismatch as an active bug + +- [ ] **Step 4: Add a new `PROJECT-MANAGEMENT.md` entry recording the truth-file cleanup before code changes** + +Append a new entry skeleton using this exact shape: + +```md +### 9.8 Task 1 — Active OCR Truth-File Cleanup (2026-06-18) + +**Problem:** Active current-priority files still inherited stale pre-9.7 issue narratives, causing the next phase to start from wrong blockers. + +**Root cause:** `project/current/` and `PROJECT-MANAGEMENT.md` were not fully reconciled after the P0-P2 close-out session. + +**Fix:** Rewrote the active close-out priority and remaining-issues files so only real unresolved DWQQK2YB support-routing and ownership issues remain active; completeness-check stays as the next planned slice. + +**Result:** Later implementation steps no longer inherit already-fixed P0-P2 failures as active blockers. +``` + +- [ ] **Step 5: Run a quick grep-based verification that the stale phrases are gone from active current files** + +Run: `rg -n "DW Post-Drop Frontmatter Not Recognized|media_asset -> body_paragraph|DW Biography Page Mismatch" project/current PROJECT-MANAGEMENT.md` + +Expected: only historical/project-log references remain; active current sections no longer present those as top open blockers. + +- [ ] **Step 6: Commit the truth-file cleanup first** + +```bash +git add project/current/ocr-v2-closeout-priority.md project/current/ocr-v2-remaining-issues-2026-06-18.md PROJECT-MANAGEMENT.md +git commit -m "docs: align OCR active issues after 9.7 close-out" +``` + +--- + +### Task 2: Lock DWQQK2YB Frontmatter Support Routing With Red Tests + +**Files:** +- Modify: `tests/test_ocr_roles.py` +- Modify: `tests/test_ocr_real_paper_regressions.py` + +- [ ] **Step 1: Write the failing unit test for first-surviving-page equal-contribution support** + +Add this to `tests/test_ocr_roles.py` near the page-1 frontmatter support tests: + +```python +def test_first_surviving_page_equal_contribution_text_routes_to_frontmatter_support() -> None: + from paperforge.worker.ocr_roles import assign_block_role + + title = { + "page": 2, + "block_label": "text", + "raw_label": "text", + "block_content": "Real Article Title", + "text": "Real Article Title", + "block_bbox": [80, 140, 900, 190], + "page_width": 1200, + "page_height": 1600, + } + support = { + "page": 2, + "block_label": "text", + "raw_label": "text", + "block_content": "These authors contributed equally to this work.", + "text": "These authors contributed equally to this work.", + "block_bbox": [90, 760, 560, 805], + "page_width": 1200, + "page_height": 1600, + } + + result = assign_block_role(support, page_blocks=[title, support], page_width=1200, page_height=1600) + + assert result.role == "frontmatter_support" +``` + +- [ ] **Step 2: Write the failing unit test for first-surviving-page correspondence support** + +Add this immediately after the previous test in `tests/test_ocr_roles.py`: + +```python +def test_first_surviving_page_correspondence_text_routes_to_frontmatter_support() -> None: + from paperforge.worker.ocr_roles import assign_block_role + + title = { + "page": 2, + "block_label": "text", + "raw_label": "text", + "block_content": "Real Article Title", + "text": "Real Article Title", + "block_bbox": [80, 140, 900, 190], + "page_width": 1200, + "page_height": 1600, + } + support = { + "page": 2, + "block_label": "text", + "raw_label": "text", + "block_content": "Corresponding author: person@example.edu", + "text": "Corresponding author: person@example.edu", + "block_bbox": [90, 820, 980, 900], + "page_width": 1200, + "page_height": 1600, + } + + result = assign_block_role(support, page_blocks=[title, support], page_width=1200, page_height=1600) + + assert result.role == "frontmatter_support" +``` + +- [ ] **Step 3: Write the failing real-paper regression for DW support lines** + +Add this to `tests/test_ocr_real_paper_regressions.py` near the other DW tests: + +```python +def test_dwqqk2yb_first_surviving_page_support_blocks_stay_frontmatter_support(tmp_path: Path) -> None: + result = replay_production_pipeline("DWQQK2YB", tmp_path) + page2_blocks = [b for b in result["structured_blocks"] if int(b.get("page", 0) or 0) == 2] + + equal_block = next(b for b in page2_blocks if "contributed equally" in str(b.get("text") or "").lower()) + corr_block = next(b for b in page2_blocks if "corresponding author" in str(b.get("text") or "").lower()) + + assert equal_block.get("role") == "frontmatter_support" + assert corr_block.get("role") == "frontmatter_support" +``` + +- [ ] **Step 4: Run the focused tests to verify red** + +Run: `python -m pytest tests/test_ocr_roles.py -k "first_surviving_page and frontmatter_support" -v --tb=short` + +Expected: FAIL because the current routing still leans on page-1 and text-prefix specials. + +Run: `python -m pytest tests/test_ocr_real_paper_regressions.py -k "dwqqk2yb_first_surviving_page_support" -v --tb=short` + +Expected: FAIL because DW support-like blocks still route inconsistently. + +- [ ] **Step 5: Commit the failing tests only** + +```bash +git add tests/test_ocr_roles.py tests/test_ocr_real_paper_regressions.py +git commit -m "test: lock DW frontmatter support regressions" +``` + +--- + +### Task 3: Implement DWQQK2YB Frontmatter Support Repair + +**Files:** +- Modify: `paperforge/worker/ocr_roles.py` +- Test: `tests/test_ocr_roles.py` +- Test: `tests/test_ocr_real_paper_regressions.py` + +- [ ] **Step 1: Add a helper for frontmatter-support-like text confirmation** + +In `paperforge/worker/ocr_roles.py`, near the other frontmatter helpers, add: + +```python +def _looks_like_frontmatter_support_text(text: str) -> bool: + lower = text.lower().strip() + return ( + "corresponding author" in lower + or lower.startswith("correspondence") + or "contributed equally" in lower + or "share first authorship" in lower + ) +``` + +- [ ] **Step 2: Route first-surviving-page support-like text before generic frontmatter noise/body fallback** + +In the `raw_label == "text"` branch of `assign_block_role()` in `paperforge/worker/ocr_roles.py`, insert this block before the generic body fallback: + +```python + bbox = block.get("block_bbox") or block.get("bbox") or [0, 0, 0, 0] + topish = len(bbox) >= 4 and bbox[1] <= page_height * 0.65 + support_zone = zone in {"author_zone", "affiliation_zone", "journal_furniture_zone"} + if page_num in (1, 2) and topish and _looks_like_frontmatter_support_text(text): + return RoleAssignment( + role="frontmatter_support", + confidence=0.78, + evidence=[f"first-surviving-page support text: {text[:60]}"], + ) +``` + +Keep the helper text-confirmatory only; this remains bounded to early-page support routing and should not become a generic full-document string rule. + +- [ ] **Step 3: Run the focused tests to verify green** + +Run: `python -m pytest tests/test_ocr_roles.py -k "first_surviving_page and frontmatter_support" -v --tb=short` + +Expected: PASS. + +Run: `python -m pytest tests/test_ocr_real_paper_regressions.py -k "dwqqk2yb_first_surviving_page_support" -v --tb=short` + +Expected: PASS. + +- [ ] **Step 4: Commit** + +```bash +git add paperforge/worker/ocr_roles.py tests/test_ocr_roles.py tests/test_ocr_real_paper_regressions.py +git commit -m "fix: preserve DW frontmatter support on surviving page" +``` + +--- + +### Task 4: Lock DWQQK2YB Figure Ownership With Red Tests + +**Files:** +- Modify: `tests/test_ocr_figures.py` +- Modify: `tests/test_ocr_real_paper_regressions.py` + +- [ ] **Step 1: Write the failing unit test for caption-band-local asset partition** + +Add this to `tests/test_ocr_figures.py` near the candidate-group tests: + +```python +def test_partition_assets_by_caption_bands_keeps_assets_local_to_caption_band() -> None: + from paperforge.worker.ocr_figures import _partition_assets_by_caption_bands + + captions = [ + {"block_id": 101, "bbox": [700, 900, 1050, 960]}, + {"block_id": 102, "bbox": [700, 1250, 1050, 1310]}, + ] + assets = [ + {"block_id": 1, "bbox": [650, 200, 1050, 520]}, + {"block_id": 2, "bbox": [650, 560, 1050, 820]}, + {"block_id": 3, "bbox": [650, 980, 1050, 1180]}, + ] + + parts = _partition_assets_by_caption_bands(captions, assets, page_height=1600) + + assert [a["block_id"] for a in parts["101"]] == [1, 2] + assert [a["block_id"] for a in parts["102"]] == [3] +``` + +- [ ] **Step 2: Write the failing real-paper ownership regression for DW** + +Add this to `tests/test_ocr_real_paper_regressions.py` next to `test_gold_figure_merge_ownership_contracts`: + +```python +def test_dwqqk2yb_ownership_no_longer_mega_merges_same_page_assets(tmp_path: Path) -> None: + result = replay_production_pipeline("DWQQK2YB", tmp_path) + reader_payload = result["reader_payload"] + matched, ambiguous = _reader_figure_index(reader_payload) + + fig2 = matched.get(2) + fig4 = matched.get(4) + + assert fig2 is not None + assert len(fig2.get("asset_block_ids", [])) <= 3 + assert fig4 is not None + assert len(fig4.get("asset_block_ids", [])) <= 3 + assert 3 in matched or 3 in ambiguous +``` + +- [ ] **Step 3: Run the focused tests to verify red** + +Run: `python -m pytest tests/test_ocr_figures.py -k "caption_bands" -v --tb=short` + +Expected: FAIL if current partitioning does not keep same-page assets local enough. + +Run: `python -m pytest tests/test_ocr_real_paper_regressions.py -k "dwqqk2yb_ownership_no_longer_mega_merges" -v --tb=short` + +Expected: FAIL because Figure 2 / 4 currently over-claim and Figure 3 stays unresolved. + +- [ ] **Step 4: Commit the failing tests only** + +```bash +git add tests/test_ocr_figures.py tests/test_ocr_real_paper_regressions.py +git commit -m "test: lock DW figure ownership regressions" +``` + +--- + +### Task 5: Implement DWQQK2YB Figure Ownership Repair + +**Files:** +- Modify: `paperforge/worker/ocr_figures.py` +- Test: `tests/test_ocr_figures.py` +- Test: `tests/test_ocr_real_paper_regressions.py` + +- [ ] **Step 1: Use caption-band partitioning before broad same-page claiming** + +In `build_figure_inventory()` in `paperforge/worker/ocr_figures.py`, after `page_caption_index` is built and before broad candidate scoring claims the whole page cluster, compute per-page partition maps: + +```python + page_assets_index: dict[int, list[dict]] = {} + for asset in assets: + page_assets_index.setdefault(int(asset.get("page", 0) or 0), []).append(asset) + + partitioned_assets_by_caption: dict[tuple[int, str], list[dict]] = {} + for page, captions in page_caption_index.items(): + partitions = _partition_assets_by_caption_bands(captions, page_assets_index.get(page, []), page_height=1700) + for caption_id, caption_assets in partitions.items(): + partitioned_assets_by_caption[(page, caption_id)] = caption_assets +``` + +- [ ] **Step 2: Restrict candidate groups to caption-local assets when partition evidence exists** + +In the legend loop inside `build_figure_inventory()`, before appending a candidate group, insert this guard: + +```python + local_assets = partitioned_assets_by_caption.get((legend_page, str(legend.get("block_id", "")))) + if local_assets: + local_ids = {a.get("block_id") for a in local_assets} + group_ids = set(group.get("asset_block_ids", [])) + if not (group_ids & local_ids): + continue +``` + +This keeps same-page narrow-caption ownership local when partition evidence exists, without inventing a paper-specific branch. + +- [ ] **Step 3: Keep unresolved over false mega-merge** + +When no caption-local candidate remains after the partition guard, preserve ambiguity instead of claiming the highest broad page group. Use this shape where the weak/ambiguous path is built: + +```python + ambiguous_figures.append( + { + "figure_number": fig_num, + "legend_block_id": legend.get("block_id", ""), + "asset_block_ids": [], + "candidate_asset_ids": [], + "page": legend_page, + } + ) + continue +``` + +Only use this path when local partition evidence existed and no local group survived; do not broadly weaken all papers. + +- [ ] **Step 4: Run the focused tests to verify green** + +Run: `python -m pytest tests/test_ocr_figures.py -k "caption_bands" -v --tb=short` + +Expected: PASS. + +Run: `python -m pytest tests/test_ocr_real_paper_regressions.py -k "dwqqk2yb_ownership_no_longer_mega_merges or gold_figure_merge_ownership_contracts" -v --tb=short` + +Expected: PASS, or the only remaining ambiguity is explicitly the guarded unresolved path and no false mega-merge remains. + +- [ ] **Step 5: Commit** + +```bash +git add paperforge/worker/ocr_figures.py tests/test_ocr_figures.py tests/test_ocr_real_paper_regressions.py +git commit -m "fix: constrain DW figure ownership to local caption bands" +``` + +--- + +### Task 6: Rebuild Derived Artifacts And Update Docs From Measured Results + +**Files:** +- Run: `scripts/dev/ocr_rebuild_paper.py` +- Modify: `PROJECT-MANAGEMENT.md` +- Modify: `project/current/ocr-v2-closeout-priority.md` +- Modify: `project/current/ocr-v2-remaining-issues-2026-06-18.md` + +- [ ] **Step 1: Rebuild the affected paper artifacts after the code repairs** + +Run: `python scripts/dev/ocr_rebuild_paper.py --trace DWQQK2YB` + +Expected: derived artifacts rebuilt, `block_trace.csv` regenerated, role/zone distributions printed. + +- [ ] **Step 2: Run the targeted DW verification slice after rebuild** + +Run: `python -m pytest tests/test_ocr_real_paper_regressions.py -k "DWQQK2YB" -v --tb=short` + +Expected: support-routing and ownership checks now reflect rebuilt artifacts. + +- [ ] **Step 3: Record the rebuild and measured repair outcome in `PROJECT-MANAGEMENT.md`** + +Append a new entry using this shape: + +```md +### 9.9 Task 2 — DWQQK2YB Support + Ownership Repair And Rebuild (2026-06-18) + +**Problem:** After 9.7, DWQQK2YB still had one real frontmatter-support routing gap and one real same-page figure ownership gap; derived assets also lagged behind the repaired code path. + +**Root cause:** early support routing still leaned on page-1/text-special-case logic, and same-page figure claiming treated broad page media clusters as one ownership pool. + +**Fix:** promoted first-surviving-page frontmatter support lines using bounded support-text confirmation; constrained figure claiming to caption-local partition bands before broad same-page asset adoption; rebuilt DWQQK2YB derived artifacts with `scripts/dev/ocr_rebuild_paper.py`. + +**Result:** DW support blocks remain in frontmatter support flow; figure ownership no longer mega-merges same-page assets; rebuilt artifacts now reflect the repaired logic. + +**Test status:** [insert exact pytest and rebuild commands/results from this run]. +``` + +- [ ] **Step 4: Rewrite the active current files again from the measured post-rebuild result** + +Update: + +- `project/current/ocr-v2-closeout-priority.md` +- `project/current/ocr-v2-remaining-issues-2026-06-18.md` + +so they reflect only what remained true after the rebuild. If the DW support issue or the DW ownership issue is fully gone, remove it from active blockers. If one remains partial, rewrite it precisely rather than keeping the older broad wording. + +- [ ] **Step 5: Commit** + +```bash +git add PROJECT-MANAGEMENT.md project/current/ocr-v2-closeout-priority.md project/current/ocr-v2-remaining-issues-2026-06-18.md +git commit -m "docs: record DW repair and OCR rebuild results" +``` + +--- + +### Task 7: Carry Completeness Check Forward As The Next Explicit Slice + +**Files:** +- Preserve: `docs/superpowers/specs/2026-06-18-ocr-completeness-check-design.md` +- Preserve: `docs/superpowers/specs/2026-06-18-dwq-repair-and-completeness-phase-design.md` +- Modify: `PROJECT-MANAGEMENT.md` + +- [ ] **Step 1: Add a next-topic note pointing at the completeness-check spec** + +In the latest active session block in `PROJECT-MANAGEMENT.md`, add a final line like: + +```md +- Next topic: implement the fuzzy OCR completeness-check layer described in `docs/superpowers/specs/2026-06-18-ocr-completeness-check-design.md` after the DW repairs settle. +``` + +- [ ] **Step 2: Run one final sanity slice across the touched areas** + +Run: `python -m pytest tests/test_ocr_roles.py tests/test_ocr_figures.py tests/test_ocr_real_paper_regressions.py -k "DWQQK2YB or frontmatter_support or ownership" -v --tb=short` + +Expected: PASS, with no new regressions in the touched seams. + +- [ ] **Step 3: Commit the final planning-state sync** + +```bash +git add PROJECT-MANAGEMENT.md +git commit -m "docs: point OCR next slice at completeness checks" +``` diff --git a/docs/superpowers/plans/2026-06-18-ocr-p0-p2-layout-closeout-plan.md b/docs/superpowers/plans/2026-06-18-ocr-p0-p2-layout-closeout-plan.md new file mode 100644 index 00000000..661671ac --- /dev/null +++ b/docs/superpowers/plans/2026-06-18-ocr-p0-p2-layout-closeout-plan.md @@ -0,0 +1,531 @@ +# OCR P0-P2 Layout Close-Out Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Fix the current OCR-v2 P0-P2 close-out issues by making first-surviving-page zone inference authoritative, tightening publisher edge-band noise handling, recovering residual figure inner text with layout evidence, and updating only the verified stale expectations. + +**Architecture:** Keep the existing OCR-v2 anchor-first pipeline and patch the two narrow seams that are actually failing: `infer_zones()` in `ocr_document.py` and layout-first fallback classification in `ocr_roles.py`. Use TDD with focused unit tests first, then confirm the behavior against `DWQQK2YB`, `K7R8PEKW`/`A8E7SRVS`, and one control paper so the close-out fixes do not reopen the broader figure-group or page-state-machine work. + +**Tech Stack:** Python 3.14, pytest, existing `paperforge.worker` OCR pipeline, fixture-backed real-paper replay tests, repo audit artifacts under `audit/`. + +--- + +## File Structure + +- Modify: `paperforge/worker/ocr_document.py` + - Owns `infer_zones()` and the first-page/frontmatter/body split logic. +- Modify: `paperforge/worker/ocr_roles.py` + - Owns layout-first fallback role assignment, margin-band noise detection, and figure-inner-text heuristics. +- Modify: `tests/test_ocr_document.py` + - Owns unit tests for zone inference and body-start behavior. +- Modify: `tests/test_ocr_real_paper_regressions.py` + - Owns production-path replay checks on DW, K7/A8, and a control paper. +- Modify: `tests/fixtures/ocr_real_papers/DWQQK2YB/expectations.json` + - Update only if the first-surviving-page behavior is visually verified and the old expectations are now stale. +- Modify: `PROJECT-MANAGEMENT.md` + - Record the completed fix and remaining residuals immediately after verification. +- Modify: `project/current/ocr-v2-remaining-issues-2026-06-18.md` + - Rewrite the residual picture after the measured rerun. + +--- + +### Task 1: Lock First-Surviving-Page Behavior With Red Tests + +**Files:** +- Modify: `tests/test_ocr_document.py` +- Modify: `tests/test_ocr_real_paper_regressions.py` + +- [ ] **Step 1: Write the failing unit test for first-surviving-page frontmatter retention** + +Add this test near the other zone/document normalization tests in `tests/test_ocr_document.py`: + +```python +def test_infer_zones_treats_first_surviving_page_as_frontmatter_origin() -> None: + from paperforge.worker.ocr_document import infer_zones + + blocks = [ + { + "block_id": "p2_title", + "page": 2, + "text": "Real Article Title", + "seed_role": "paper_title", + "role": "paper_title", + "bbox": [80, 120, 900, 180], + "page_width": 1200, + "page_height": 1600, + "marker_signature": {"type": "none"}, + }, + { + "block_id": "p2_authors", + "page": 2, + "text": "A. Author, B. Author", + "seed_role": "authors", + "role": "authors", + "bbox": [80, 220, 920, 280], + "page_width": 1200, + "page_height": 1600, + "marker_signature": {"type": "none"}, + }, + { + "block_id": "p2_body", + "page": 2, + "text": "This is the first real body paragraph with enough words to trigger body flow on the first surviving page.", + "seed_role": "body_paragraph", + "role": "body_paragraph", + "bbox": [80, 520, 1030, 650], + "page_width": 1200, + "page_height": 1600, + "marker_signature": {"type": "none"}, + }, + ] + + zones = infer_zones(blocks, body_anchor={"status": "ACCEPT"}, reference_anchor={"status": "HOLD"}) + + assert "p2_title" in zones["frontmatter_main_zone"]["block_ids"] + assert "p2_authors" in zones["frontmatter_main_zone"]["block_ids"] + assert "p2_body" in zones["body_zone"]["block_ids"] +``` + +- [ ] **Step 2: Write the failing unit test that body can start on the first surviving page** + +Add this immediately after the previous test in `tests/test_ocr_document.py`: + +```python +def test_infer_zones_allows_body_blocks_on_first_surviving_page() -> None: + from paperforge.worker.ocr_document import infer_zones + + blocks = [ + { + "block_id": "p2_title", + "page": 2, + "text": "Real Article Title", + "seed_role": "paper_title", + "role": "paper_title", + "bbox": [80, 120, 900, 180], + "page_width": 1200, + "page_height": 1600, + "marker_signature": {"type": "none"}, + }, + { + "block_id": "p2_heading", + "page": 2, + "text": "Highlights", + "seed_role": "structured_insert", + "role": "structured_insert", + "bbox": [80, 980, 260, 1020], + "page_width": 1200, + "page_height": 1600, + "marker_signature": {"type": "short_fragment"}, + }, + { + "block_id": "p2_b1", + "page": 2, + "text": "Body continuation should remain eligible for body_zone when it begins on the first surviving page rather than literal page one.", + "seed_role": "body_paragraph", + "role": "body_paragraph", + "bbox": [100, 1060, 1020, 1180], + "page_width": 1200, + "page_height": 1600, + "marker_signature": {"type": "none"}, + }, + ] + + zones = infer_zones(blocks, body_anchor={"status": "ACCEPT"}, reference_anchor={"status": "HOLD"}) + + assert "p2_b1" in zones["body_zone"]["block_ids"] + assert "p2_b1" not in zones["frontmatter_main_zone"]["block_ids"] +``` + +- [ ] **Step 3: Write the failing real-paper regression for DWQQK2YB first-surviving-page recovery** + +Add this in `tests/test_ocr_real_paper_regressions.py` near the other DW regression tests: + +```python +def test_dwqqk2yb_first_surviving_page_keeps_title_and_authors(tmp_path: Path) -> None: + result = replay_production_pipeline("DWQQK2YB", tmp_path) + page2_blocks = [b for b in result["structured_blocks"] if int(b.get("page", 0) or 0) == 2] + + title_block = next( + b for b in page2_blocks if "Magnetoresponsive Stem Cell Spheroid" in str(b.get("text") or "") + ) + authors_block = next( + b for b in page2_blocks if "Ami Yoo" in str(b.get("text") or "") or "Ami Yoo" in str(b.get("block_content") or "") + ) + + assert title_block.get("role") == "paper_title" + assert authors_block.get("role") in {"authors", "frontmatter_support"} + assert title_block.get("zone") == "frontmatter_main_zone" + assert authors_block.get("zone") == "frontmatter_main_zone" +``` + +- [ ] **Step 4: Run the focused tests to verify red** + +Run: `python -m pytest tests/test_ocr_document.py -k "first_surviving_page" -v --tb=short` + +Expected: FAIL because `infer_zones()` still hardcodes literal page 1 and excludes first-surviving-page body blocks. + +Run: `python -m pytest tests/test_ocr_real_paper_regressions.py -k "dwqqk2yb_first_surviving_page" -v --tb=short` + +Expected: FAIL because DW page 2 title/authors currently fall into `unknown_structural` / `body_zone` fallback. + +- [ ] **Step 5: Commit the failing tests only** + +```bash +git add tests/test_ocr_document.py tests/test_ocr_real_paper_regressions.py +git commit -m "test: lock first surviving page OCR regressions" +``` + +--- + +### Task 2: Implement First-Surviving-Page Zone Inference + +**Files:** +- Modify: `paperforge/worker/ocr_document.py` +- Test: `tests/test_ocr_document.py` +- Test: `tests/test_ocr_real_paper_regressions.py` + +- [ ] **Step 1: Add a helper that returns the first surviving page** + +In `paperforge/worker/ocr_document.py`, near the other zone helpers above `infer_zones()`, add: + +```python +def _first_surviving_page(blocks: list[dict]) -> int | None: + pages = sorted({int(block.get("page", 0) or 0) for block in blocks if int(block.get("page", 0) or 0) > 0}) + return pages[0] if pages else None +``` + +- [ ] **Step 2: Generalize `_is_page1_body_start()` without broadening it** + +Replace the existing helper with a first-surviving-page-aware version in `paperforge/worker/ocr_document.py`: + +```python +def _is_first_page_body_start(block: dict, *, seen_title_or_author: bool) -> bool: + if not seen_title_or_author: + return False + role = block.get("role") or block.get("seed_role") + if role == "unassigned": + role = block.get("seed_role") + text = str(block.get("text") or "").strip() + words = text.split() + marker_type = ((block.get("marker_signature") or {}).get("type") or "none") + if marker_type == "preproof_marker": + return False + if role in {"section_heading", "subsection_heading", "structured_insert", "structured_insert_candidate"}: + return True + if role == "body_paragraph" and len(words) >= 20: + lower = text.lower() + if not lower.startswith(("correspondence", "received", "accepted", "published", "doi")): + return True + return False +``` + +- [ ] **Step 3: Make `infer_zones()` use the first surviving page instead of literal page 1** + +In `infer_zones()` update the frontmatter/body-start block collection from: + +```python +page1_candidates = [ + b for b in blocks + if int(b.get("page", 0) or 0) == 1 + ... +] +``` + +to: + +```python +first_surviving_page = _first_surviving_page(blocks) + +page1_candidates = [ + b for b in blocks + if first_surviving_page is not None + and int(b.get("page", 0) or 0) == first_surviving_page + and ((b.get("marker_signature") or {}).get("type") or "none") != "preproof_marker" + and not _is_reference_item_candidate(b) + and b.get("block_id") is not None +] +``` + +Also update the body-start call site from `_is_page1_body_start(...)` to `_is_first_page_body_start(...)`. + +- [ ] **Step 4: Allow body eligibility on the first surviving page** + +In `infer_zones()` replace the body-page gate: + +```python +and int(block.get("page", 0) or 0) > 1 +``` + +with: + +```python +and first_surviving_page is not None +and ( + int(block.get("page", 0) or 0) > first_surviving_page + or ( + int(block.get("page", 0) or 0) == first_surviving_page + and _artifact_block_id(block, duplicate_block_ids) not in frontmatter_main_id_set + and _artifact_block_id(block, duplicate_block_ids) not in frontmatter_side_id_set + ) +) +``` + +Keep the existing reference-heading split, reference exclusion, and side-zone exclusion intact. + +- [ ] **Step 5: Update the page-band metadata to reflect the surviving first page** + +In the returned zone payload, replace: + +```python +boundary_band=_page_band(1, 1) if frontmatter_main_ids else None, +``` + +with: + +```python +boundary_band=_page_band(first_surviving_page, first_surviving_page) if frontmatter_main_ids else None, +``` + +- [ ] **Step 6: Run the focused tests to verify green** + +Run: `python -m pytest tests/test_ocr_document.py -k "first_surviving_page" -v --tb=short` + +Expected: PASS. + +Run: `python -m pytest tests/test_ocr_real_paper_regressions.py -k "dwqqk2yb_first_surviving_page" -v --tb=short` + +Expected: PASS. + +- [ ] **Step 7: Commit** + +```bash +git add paperforge/worker/ocr_document.py tests/test_ocr_document.py tests/test_ocr_real_paper_regressions.py +git commit -m "fix: infer OCR zones from first surviving page" +``` + +--- + +### Task 3: Lock And Implement Layout-First Margin-Band Noise And Figure-Inner-Text Recovery + +**Files:** +- Modify: `tests/test_ocr_document.py` +- Modify: `paperforge/worker/ocr_roles.py` +- Modify: `tests/test_ocr_real_paper_regressions.py` + +- [ ] **Step 1: Write the failing unit test for edge-band watermark noise** + +Add this test to `tests/test_ocr_document.py`: + +```python +def test_assign_block_role_marks_margin_band_as_noise() -> None: + from paperforge.worker.ocr_roles import assign_block_role + + block = { + "page": 4, + "block_id": "wm", + "raw_label": "aside_text", + "block_label": "aside_text", + "text": "Downloaded from https://advanced.onlinelibrary.wiley.com by Example Library. For personal use only.", + "bbox": [1155, 30, 1171, 1535], + "block_bbox": [1155, 30, 1171, 1535], + "page_width": 1224, + "page_height": 1584, + } + + assignment = assign_block_role(block, page_blocks=[block]) + + assert assignment.role == "noise" + assert assignment.confidence >= 0.95 +``` + +- [ ] **Step 2: Write the failing unit test for figure-inner-text recovery** + +Add this next to the previous test in `tests/test_ocr_document.py`: + +```python +def test_assign_block_role_marks_short_figure_adjacent_label_as_inner_text() -> None: + from paperforge.worker.ocr_roles import assign_block_role + + image_block = { + "page": 1, + "block_id": "img1", + "raw_label": "image", + "block_label": "image", + "bbox": [300, 300, 800, 900], + "block_bbox": [300, 300, 800, 900], + "page_width": 1200, + "page_height": 1600, + } + label_block = { + "page": 1, + "block_id": "lab1", + "raw_label": "text", + "block_label": "text", + "text": "Day 7", + "bbox": [320, 320, 410, 360], + "block_bbox": [320, 320, 410, 360], + "page_width": 1200, + "page_height": 1600, + } + + assignment = assign_block_role(label_block, page_blocks=[image_block, label_block]) + + assert assignment.role == "figure_inner_text" +``` + +- [ ] **Step 3: Write the failing real-paper regression for publisher strips** + +Add this to `tests/test_ocr_real_paper_regressions.py`: + +```python +def test_k7r8pekw_margin_band_publishers_stay_noise(tmp_path: Path) -> None: + result = replay_production_pipeline("K7R8PEKW", tmp_path) + watermark_blocks = [ + b for b in result["structured_blocks"] + if "Downloaded from" in str(b.get("text") or "") + ] + + assert watermark_blocks, "Expected at least one publisher watermark block" + assert all(b.get("role") == "noise" for b in watermark_blocks) +``` + +- [ ] **Step 4: Run the focused tests to verify red** + +Run: `python -m pytest tests/test_ocr_document.py -k "margin_band or inner_text" -v --tb=short` + +Expected: FAIL until the geometry helpers are tightened. + +Run: `python -m pytest tests/test_ocr_real_paper_regressions.py -k "k7r8pekw_margin_band" -v --tb=short` + +Expected: FAIL because the watermark strips still survive as `unknown_structural` in the replay path. + +- [ ] **Step 5: Tighten the geometry-first watermark rule** + +In `paperforge/worker/ocr_roles.py`, keep the existing helpers but make the edge-band path slightly stricter and optionally text-confirmed: + +```python +def _has_confirmatory_watermark_text(text: str) -> bool: + lower = text.lower() + return "downloaded from" in lower or "for personal use only" in lower +``` + +Then update `_looks_like_margin_band_noise()` so it still accepts the current extreme geometry but tolerates the real K7/A8 right-edge strips: + +```python +very_tall = height >= page_height * 0.30 +very_narrow = width <= page_width * 0.12 +edge_band = at_left_edge or at_right_edge +text = str(block.get("text") or block.get("block_content") or "") +return edge_band and very_tall and (very_narrow or _has_confirmatory_watermark_text(text)) +``` + +Do not call the helper outside the geometry gate. + +- [ ] **Step 6: Broaden figure-inner-text recovery by geometry, not caption text** + +In `paperforge/worker/ocr_roles.py`, update `_looks_like_figure_inner_label()` from the current alpha-only filter to allow compact mixed alnum labels while staying short and figure-adjacent: + +```python +if not t or len(t) > 12: + return False +if _has_figure_prefix(t): + return False +token_count = len(t.split()) +if token_count > 2: + return False +has_alnum = any(ch.isalnum() for ch in t) +if not has_alnum: + return False +``` + +Keep the existing media adjacency check intact. + +- [ ] **Step 7: Run the focused tests to verify green** + +Run: `python -m pytest tests/test_ocr_document.py -k "margin_band or inner_text" -v --tb=short` + +Expected: PASS. + +Run: `python -m pytest tests/test_ocr_real_paper_regressions.py -k "k7r8pekw_margin_band" -v --tb=short` + +Expected: PASS. + +- [ ] **Step 8: Commit** + +```bash +git add paperforge/worker/ocr_roles.py tests/test_ocr_document.py tests/test_ocr_real_paper_regressions.py +git commit -m "fix: tighten OCR margin-band noise and figure inner text" +``` + +--- + +### Task 4: Reconcile Verified Expectations And Project Trackers + +**Files:** +- Modify: `tests/fixtures/ocr_real_papers/DWQQK2YB/expectations.json` +- Modify: `PROJECT-MANAGEMENT.md` +- Modify: `project/current/ocr-v2-remaining-issues-2026-06-18.md` +- Test: `tests/test_ocr_real_paper_regressions.py` + +- [ ] **Step 1: Re-run the targeted real-paper replay checks** + +Run: `python -m pytest tests/test_ocr_real_paper_regressions.py -k "DWQQK2YB or K7R8PEKW or CAQNW9Q2" -v --tb=short` + +Expected: PASS for the newly added DW/K7 checks, and no regression on the existing CAQ production-path checks. + +- [ ] **Step 2: Update DW expectations only if the behavior is now visually correct** + +In `tests/fixtures/ocr_real_papers/DWQQK2YB/expectations.json`, change the stale page-2 assertions from the current bug notes: + +```json +{"text_contains": "Magnetoresponsive Stem Cell Spheroid", "expected_role": "paper_title", "expected_zone": "frontmatter_main_zone"} +``` + +and: + +```json +{"text_contains": "Ami Yoo", "expected_role": "authors", "expected_zone": "frontmatter_main_zone"} +``` + +If the replay still produces `frontmatter_support` for the author block but the zone is correct, update the expected role to `frontmatter_support` instead of forcing `authors`. + +- [ ] **Step 3: Run the paper-specific regression to verify expectations** + +Run: `python -m pytest tests/test_ocr_real_paper_regressions.py -k "DWQQK2YB" -v --tb=short` + +Expected: PASS. + +- [ ] **Step 4: Update the project tracker immediately after the verified fix** + +In `PROJECT-MANAGEMENT.md`, append a new session entry in the existing format: + +```md +### 10.x First-surviving-page and edge-band close-out (2026-06-18) +- Problem: preproof-drop papers still used literal page 1 assumptions; repeated publisher strips survived as unknown_structural. +- Root cause: `infer_zones()` anchored frontmatter/body split to `page == 1`; margin-band noise helpers were geometry-correct for only the narrowest cases. +- Fix: switched zone inference to the first surviving page, allowed body flow on that page after the local body-start anchor, and tightened geometry-first margin-band noise detection with optional confirmatory text. +- Result: DW first surviving page frontmatter recovered; K7 watermark strips demoted to noise without broad text-first routing. +- Test status: [insert exact focused pytest commands and result counts from this run]. +``` + +- [ ] **Step 5: Rewrite the remaining-issues note from measured results** + +In `project/current/ocr-v2-remaining-issues-2026-06-18.md`, update only the sections that changed: + +- mark Issue 1 resolved if DW replay now passes, +- reduce or remove the publisher-noise bucket if the K7/A8 geometry fix closes it, +- leave figure-group and CLI rebuild items deferred, +- do not claim success for any bucket that was not re-measured. + +- [ ] **Step 6: Run the final close-out test slice** + +Run: `python -m pytest tests/test_ocr_document.py tests/test_ocr_real_paper_regressions.py -k "first_surviving_page or margin_band or inner_text or DWQQK2YB or K7R8PEKW or CAQNW9Q2" -v --tb=short` + +Expected: all selected tests PASS. + +- [ ] **Step 7: Commit** + +```bash +git add tests/fixtures/ocr_real_papers/DWQQK2YB/expectations.json PROJECT-MANAGEMENT.md project/current/ocr-v2-remaining-issues-2026-06-18.md tests/test_ocr_document.py tests/test_ocr_real_paper_regressions.py +git commit -m "docs: record OCR layout close-out progress" +``` diff --git a/docs/superpowers/plans/2026-06-18-ocr-v2-readiness-master-plan.md b/docs/superpowers/plans/2026-06-18-ocr-v2-readiness-master-plan.md new file mode 100644 index 00000000..ecc09216 --- /dev/null +++ b/docs/superpowers/plans/2026-06-18-ocr-v2-readiness-master-plan.md @@ -0,0 +1,538 @@ +# OCR-v2 Readiness Master Plan Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Complete the first four OCR-v2 readiness gates so the branch can be called "state healthy" on known layout classes, then leave unseen-paper blind audit as the next-stage gate with explicit entry criteria. + +**Architecture:** Keep the anchor-first OCR-v2 backbone and sequence the remaining work as four readiness phases: completeness detection, figure ownership generalization, ordering/boundary authority cleanup, and layout-class coverage formalization. Reuse the existing completeness and figure-group designs where they are already strong, add only the missing execution detail for ordering authority and coverage formalization, and keep project truth files aligned after each phase so the branch has one active narrative. + +**Tech Stack:** Python 3.14, existing `paperforge.worker` OCR pipeline, pytest, audit-backed real-paper regressions, repo-local docs under `project/current/`, audit artifacts under `audit/`. + +--- + +## File Structure + +- Modify: `paperforge/worker/ocr_blocks.py` + - Owns structured-block build flow; Gate 1 belongs here. +- Modify: `paperforge/worker/ocr_pdf_spans.py` + - Owns PDF-region lookup / backfill utilities; Gate 1 should reuse this rather than fork a second PDF-text path. +- Modify: `paperforge/worker/ocr_health.py` + - Best home for rendered-text coverage summary output. +- Modify: `paperforge/worker/ocr_figures.py` + - Owns Gate 2 figure ownership logic. +- Modify: `paperforge/worker/ocr_document.py` + - Owns Gate 3 boundary authority and normalized structure truth. +- Modify: `paperforge/worker/ocr_orchestrator.py` + - May need small cleanup if ordering authority still leaks downstream. +- Modify: `paperforge/worker/ocr_render.py` + - Keep only minimal presentation ordering after Gate 3. +- Modify: `tests/test_ocr_document.py` + - Gate 1 and Gate 3 unit/regression coverage. +- Modify: `tests/test_ocr_figures.py` + - Gate 2 deterministic inventory coverage. +- Modify: `tests/test_ocr_real_paper_regressions.py` + - Production-path replay checks for Gates 1-3. +- Modify: `tests/test_ocr_real_paper_audit_contracts.py` + - Gate 4 contract tightening; move it off stale fixture-ledger assumptions. +- Create: `audit/coverage_ledger.json` + - Readiness ledger for real audited papers and layout/risk coverage. +- Modify: `project/current/ocr-v2-closeout-priority.md` + - Keep the short active queue aligned after each phase. +- Modify: `project/current/ocr-v2-generalization-boundary.md` + - Keep the broader architectural note aligned. +- Modify: `project/current/ocr-v2-remaining-issues-2026-06-18.md` + - Keep active residuals aligned to actual gates. +- Modify: `PROJECT-MANAGEMENT.md` + - Record each completed phase and any newly parked hard cases. + +--- + +### Task 1: Lock The Readiness-Gates Baseline And Replace The Stale Fixture-Ledger Assumption + +**Files:** +- Create: `audit/coverage_ledger.json` +- Modify: `tests/test_ocr_real_paper_audit_contracts.py` +- Modify: `tests/test_ocr_real_paper_regressions.py` +- Modify: `project/current/ocr-v2-closeout-priority.md` +- Modify: `project/current/ocr-v2-remaining-issues-2026-06-18.md` + +- [ ] **Step 1: Create an audit-side coverage ledger from the real audited paper set** + +Create `audit/coverage_ledger.json` with a minimal but valid readiness ledger shape: + +```json +{ + "audit_root": "audit", + "papers": [ + {"paper_key": "2GN9LMCW", "layout_tags": ["single_column", "special_structure"], "risk_tags": ["special_structure"]}, + {"paper_key": "6FGDBFQN", "layout_tags": ["multi_column", "side_caption"], "risk_tags": ["figure_heavy"]}, + {"paper_key": "A8E7SRVS", "layout_tags": ["multi_column"], "risk_tags": ["table_heavy"]}, + {"paper_key": "CAQNW9Q2", "layout_tags": ["multi_column", "same_page_ref_body_split"], "risk_tags": ["reference_boundary_sensitive", "frontmatter_sensitive"]}, + {"paper_key": "DWQQK2YB", "layout_tags": ["multi_column", "preproof_frontmatter", "post_reference_biography"], "risk_tags": ["frontmatter_sensitive", "figure_heavy", "cross_page_caption"]}, + {"paper_key": "K7R8PEKW", "layout_tags": ["single_column"], "risk_tags": ["frontmatter_sensitive"]}, + {"paper_key": "SAN9AYVR", "layout_tags": ["multi_column"], "risk_tags": ["special_structure"]}, + {"paper_key": "TSCKAVIS", "layout_tags": ["single_column", "review_callout"], "risk_tags": ["special_structure", "table_heavy"]} + ] +} +``` + +- [ ] **Step 2: Move the audit-contract test off `tests/fixtures` manifest lookup** + +In `tests/test_ocr_real_paper_audit_contracts.py`, change the ledger root and replace the current coarse check with an explicit readiness-class check: + +```python +LEDGER_PATH = Path(__file__).resolve().parents[1] / "audit" / "coverage_ledger.json" + + +def test_gold_set_covers_readiness_layout_classes() -> None: + manifest = _load_manifest() + all_layouts = {tag for paper in manifest["papers"] for tag in paper.get("layout_tags", [])} + assert "preproof_frontmatter" in all_layouts + assert "same_page_ref_body_split" in all_layouts + assert any(tag in all_layouts for tag in {"side_caption", "multi_panel"}) + assert "post_reference_biography" in all_layouts + assert any(tag in all_layouts for tag in {"review_callout", "special_structure"}) +``` + +- [ ] **Step 3: Add a Gate 2 red regression that no longer allows ambiguity as success for DW Figure 3** + +In `tests/test_ocr_real_paper_regressions.py`, replace the current permissive assertion with a strict ownership target: + +```python +def test_dwqqk2yb_figure3_is_fully_owned_not_merely_captured(tmp_path: Path) -> None: + result = replay_production_pipeline("DWQQK2YB", tmp_path) + reader_payload = result["reader_payload"] + matched, ambiguous = _reader_figure_index(reader_payload) + + assert 3 in matched, "Fig 3 should be matched, not left ambiguous" + assert 3 not in ambiguous, "Fig 3 ambiguity is no longer acceptable after Gate 2" +``` + +- [ ] **Step 4: Run the red baseline checks** + +Run: `python -m pytest tests/test_ocr_real_paper_audit_contracts.py -v` + +Run: `python -m pytest tests/test_ocr_real_paper_regressions.py -k "dwqqk2yb_figure3_is_fully_owned" -v` + +Expected: audit-ledger tests PASS once the ledger exists; the Gate 2 DW ownership test FAILS until Task 3 lands. + +- [ ] **Step 5: Commit the baseline and ledger restoration** + +```bash +git add audit/coverage_ledger.json tests/test_ocr_real_paper_audit_contracts.py tests/test_ocr_real_paper_regressions.py project/current/ocr-v2-closeout-priority.md project/current/ocr-v2-remaining-issues-2026-06-18.md +git commit -m "test: lock OCR readiness baseline and restore coverage ledger" +``` + +--- + +### Task 2: Implement Gate 1 Completeness Signals + +**Files:** +- Modify: `paperforge/worker/ocr_blocks.py` +- Modify: `paperforge/worker/ocr_pdf_spans.py` +- Modify: `paperforge/worker/ocr_health.py` +- Modify: `tests/test_ocr_document.py` +- Modify: `tests/test_ocr_real_paper_regressions.py` + +- [ ] **Step 1: Add failing page-level and region-level completeness tests** + +In `tests/test_ocr_document.py`, add two focused tests: + +```python +def test_page_text_coverage_flags_low_ratio_when_pdf_text_dominates() -> None: + from paperforge.worker.ocr_blocks import _summarize_page_text_coverage + + result = _summarize_page_text_coverage( + ocr_text="short text", + pdf_text="short text plus a much longer native PDF segment that should dominate coverage", + ) + + assert result["page_text_coverage_status"] == "low" + assert result["page_text_coverage_ratio_chars"] < 0.5 + + +def test_region_text_completeness_marks_empty_vs_pdf() -> None: + from paperforge.worker.ocr_blocks import _classify_region_text_completeness + + result = _classify_region_text_completeness( + ocr_text="", + pdf_region_text="A complete sentence present in the PDF native text layer.", + ) + + assert result["text_completeness_status"] == "empty_vs_pdf" +``` + +- [ ] **Step 2: Add failing rendered-gap audit test** + +Still in `tests/test_ocr_document.py`, add: + +```python +def test_rendered_text_coverage_flags_missing_pdf_segment() -> None: + from paperforge.worker.ocr_health import audit_rendered_text_coverage + + result = audit_rendered_text_coverage( + rendered_markdown="Only the introduction survived.", + pdf_segments=["Only the introduction survived.", "A long methods segment that is missing from render."], + ) + + assert result["rendered_text_gap_count"] == 1 +``` + +- [ ] **Step 3: Run red tests for Gate 1** + +Run: `python -m pytest tests/test_ocr_document.py -k "coverage or completeness or rendered_text_coverage" -v` + +Expected: FAIL. + +- [ ] **Step 4: Add the minimal completeness helpers and wire them into structured-block build** + +In `paperforge/worker/ocr_blocks.py`, add small helper seams and attach the emitted metadata before `normalize_document_structure()`: + +```python +def _summarize_page_text_coverage(*, ocr_text: str, pdf_text: str) -> dict: + ocr_chars = len((ocr_text or "").strip()) + pdf_chars = len((pdf_text or "").strip()) + if pdf_chars == 0: + return {"page_text_coverage_status": "missing_pdf_text", "page_text_coverage_ratio_chars": 1.0} + ratio = ocr_chars / max(pdf_chars, 1) + return { + "page_text_coverage_status": "low" if ratio < 0.6 else "ok", + "page_text_coverage_ratio_chars": ratio, + } + + +def _classify_region_text_completeness(*, ocr_text: str, pdf_region_text: str) -> dict: + ocr = (ocr_text or "").strip() + pdf = (pdf_region_text or "").strip() + if not pdf: + return {"text_completeness_status": "pdf_unavailable", "text_completeness_confidence": 0.0} + if not ocr: + return {"text_completeness_status": "empty_vs_pdf", "text_completeness_confidence": 0.95} + if len(ocr) < len(pdf) * 0.45: + return {"text_completeness_status": "short_vs_pdf", "text_completeness_confidence": 0.8} + if pdf.startswith(ocr) and len(pdf) > len(ocr) + 24: + return {"text_completeness_status": "likely_missing_tail", "text_completeness_confidence": 0.85} + return {"text_completeness_status": "complete", "text_completeness_confidence": 0.7} +``` + +- [ ] **Step 5: Add rendered-gap audit in health layer** + +In `paperforge/worker/ocr_health.py`, add: + +```python +def audit_rendered_text_coverage(*, rendered_markdown: str, pdf_segments: list[str]) -> dict: + missing = [segment for segment in pdf_segments if segment and segment not in rendered_markdown] + return { + "rendered_text_gap_count": len(missing), + "rendered_text_gap_examples": missing[:3], + } +``` + +- [ ] **Step 6: Run Gate 1 focused tests until green** + +Run: `python -m pytest tests/test_ocr_document.py -k "coverage or completeness or rendered_text_coverage" -v` + +Expected: PASS. + +- [ ] **Step 7: Run the nearest production-path Gate 1 regression slice** + +Run: `python -m pytest tests/test_ocr_real_paper_regressions.py -k "DWQQK2YB or CAQNW9Q2" -v` + +Expected: PASS or only pre-existing skipped tests; no new failures. + +- [ ] **Step 8: Commit Gate 1** + +```bash +git add paperforge/worker/ocr_blocks.py paperforge/worker/ocr_pdf_spans.py paperforge/worker/ocr_health.py tests/test_ocr_document.py tests/test_ocr_real_paper_regressions.py PROJECT-MANAGEMENT.md +git commit -m "feat: add OCR completeness coverage signals" +``` + +--- + +### Task 3: Implement Gate 2 Group-First Figure Ownership Generalization + +**Files:** +- Modify: `paperforge/worker/ocr_figures.py` +- Modify: `paperforge/worker/ocr_health.py` +- Modify: `tests/test_ocr_figures.py` +- Modify: `tests/test_ocr_real_paper_regressions.py` + +- [ ] **Step 1: Add the failing deterministic group-first unit test if it is not already present** + +In `tests/test_ocr_figures.py`, keep the unit explicit: + +```python +def test_group_first_matching_prefers_same_row_pair_over_single_asset() -> None: + from paperforge.worker.ocr_figures import build_figure_inventory + + blocks = [ + {"block_id": 1, "role": "figure_caption", "text": "Fig. 2 A and B, MRI and gross anatomic correlation.", "page": 3, "bbox": [80, 120, 420, 210]}, + {"block_id": 2, "role": "media_asset", "raw_label": "image", "page": 3, "bbox": [450, 120, 780, 520]}, + {"block_id": 3, "role": "media_asset", "raw_label": "image", "page": 3, "bbox": [805, 120, 1130, 520]}, + ] + + inventory = build_figure_inventory(blocks, page_width=1200) + matched = inventory["matched_figures"] + + assert len(matched) == 1 + assert [a["block_id"] for a in matched[0]["matched_assets"]] == [2, 3] +``` + +- [ ] **Step 2: Run the focused Gate 2 red tests** + +Run: `python -m pytest tests/test_ocr_figures.py -k "group_first_matching_prefers_same_row_pair or partition_assets_by_caption_bands" -v` + +Run: `python -m pytest tests/test_ocr_real_paper_regressions.py -k "dwqqk2yb_figure3_is_fully_owned" -v` + +Expected: at least the DW ownership test FAILS until the refactor lands. + +- [ ] **Step 3: Implement candidate-group-first matching in `ocr_figures.py`** + +Add deterministic candidate group helpers and reserve asset sets rather than individual assets: + +```python +def _candidate_group_entry(group_id: str, page: int, media_blocks: list[dict], group_type: str, evidence: list[str]) -> dict: + return { + "group_id": group_id, + "page": page, + "group_type": group_type, + "asset_block_ids": [b.get("block_id") for b in media_blocks], + "media_blocks": media_blocks, + "group_evidence": evidence, + } + + +def _build_candidate_figure_groups_from_assets(assets: list[dict]) -> list[dict]: + groups: list[dict] = [] + for asset in assets: + groups.append(_candidate_group_entry(f"single_{asset['block_id']}", asset.get("page", 0), [asset], "single_asset", ["single_asset"])) + return groups +``` + +Then update `build_figure_inventory()` so legend scoring consumes candidate groups first, and fallback only sees truly unclaimed assets. + +- [ ] **Step 4: Expose grouped-vs-single counters in health output** + +In `paperforge/worker/ocr_health.py`, add grouped counters to the figure summary: + +```python +figure_summary["grouped_match_count"] = sum(1 for item in matched_figures if len(item.get("matched_assets", [])) > 1) +figure_summary["single_match_count"] = sum(1 for item in matched_figures if len(item.get("matched_assets", [])) == 1) +``` + +- [ ] **Step 5: Run Gate 2 tests until green** + +Run: `python -m pytest tests/test_ocr_figures.py -k "group_first_matching_prefers_same_row_pair or partition_assets_by_caption_bands" -v` + +Run: `python -m pytest tests/test_ocr_real_paper_regressions.py -k "DWQQK2YB" -v` + +Expected: PASS, with no ambiguity-acceptable DW Figure 3 path remaining. + +- [ ] **Step 6: Commit Gate 2** + +```bash +git add paperforge/worker/ocr_figures.py paperforge/worker/ocr_health.py tests/test_ocr_figures.py tests/test_ocr_real_paper_regressions.py PROJECT-MANAGEMENT.md +git commit -m "feat: generalize OCR figure ownership with group-first matching" +``` + +--- + +### Task 4: Implement Gate 3 Ordering And Boundary Authority Cleanup + +**Files:** +- Modify: `paperforge/worker/ocr_document.py` +- Modify: `paperforge/worker/ocr_orchestrator.py` +- Modify: `paperforge/worker/ocr_render.py` +- Modify: `tests/test_ocr_document.py` +- Modify: `tests/test_ocr_real_paper_regressions.py` + +- [ ] **Step 1: Add a failing same-page mixed-layout authority test** + +In `tests/test_ocr_document.py`, add: + +```python +def test_same_page_reference_boundary_is_resolved_upstream_not_in_renderer() -> None: + from paperforge.worker.ocr_document import normalize_document_structure + + blocks = [ + {"block_id": "body_1", "page": 7, "seed_role": "body_paragraph", "text": "Conclusion text above refs.", "bbox": [80, 420, 980, 510]}, + {"block_id": "refs_h", "page": 7, "seed_role": "reference_heading", "text": "References", "bbox": [80, 900, 320, 960]}, + {"block_id": "ref_1", "page": 7, "seed_role": "reference_item", "text": "[1] First ref", "bbox": [80, 980, 980, 1060]}, + ] + + _doc, normalized = normalize_document_structure(blocks) + by_id = {b["block_id"]: b for b in normalized} + assert by_id["body_1"]["role"] == "body_paragraph" + assert by_id["ref_1"]["role"] == "reference_item" +``` + +- [ ] **Step 2: Run the red Gate 3 authority tests** + +Run: `python -m pytest tests/test_ocr_document.py -k "reference_boundary_is_resolved_upstream" -v` + +Expected: FAIL if renderer/orchestrator still carries meaningful authority for this case. + +- [ ] **Step 3: Move authority to explicit structure artifacts** + +In `paperforge/worker/ocr_document.py`, add a small artifact-first helper: + +```python +def _same_page_reference_boundary_y(page_blocks: list[dict]) -> float | None: + headings = [b for b in page_blocks if b.get("seed_role") == "reference_heading"] + if not headings: + return None + return min((b.get("bbox") or [0, 0, 0, 0])[1] for b in headings) +``` + +Then classify blocks above that Y boundary as body-capable and blocks below it as reference-capable before renderer ordering runs. + +- [ ] **Step 4: Shrink renderer-side reorder logic** + +In `paperforge/worker/ocr_render.py`, replace any correctness-bearing mixed-page reorder branch with a no-op or assertion that structure ordering already exists: + +```python +# ponytail: renderer should consume normalized order, not invent it. +ordered_blocks = structured_blocks +``` + +If a minimal presentational sort is still required, keep it local to already-normalized sibling groups only. + +- [ ] **Step 5: Run Gate 3 focused tests until green** + +Run: `python -m pytest tests/test_ocr_document.py -k "reference_boundary_is_resolved_upstream or same_page" -v` + +Run: `python -m pytest tests/test_ocr_real_paper_regressions.py -k "CAQNW9Q2 or DWQQK2YB" -v` + +Expected: PASS. + +- [ ] **Step 6: Commit Gate 3** + +```bash +git add paperforge/worker/ocr_document.py paperforge/worker/ocr_orchestrator.py paperforge/worker/ocr_render.py tests/test_ocr_document.py tests/test_ocr_real_paper_regressions.py PROJECT-MANAGEMENT.md project/current/ocr-v2-generalization-boundary.md +git commit -m "refactor: move OCR ordering authority upstream" +``` + +--- + +### Task 5: Implement Gate 4 Layout-Coverage Formalization + +**Files:** +- Modify: `audit/coverage_ledger.json` +- Modify: `tests/test_ocr_real_paper_audit_contracts.py` +- Modify: `project/current/ocr-v2-generalization-boundary.md` +- Modify: `project/current/ocr-v2-closeout-priority.md` +- Modify: `PROJECT-MANAGEMENT.md` + +- [ ] **Step 1: Expand the audit ledger taxonomy beyond broad layout labels** + +In `audit/coverage_ledger.json`, keep the new readiness-class tags and normalize naming to the approved set: + +```json +"layout_tags": ["multi_column", "same_page_ref_body_split", "preproof_frontmatter"] +``` + +Use only these readiness-class names in this phase: + +```text +multi_panel +side_caption +same_page_ref_body_split +post_reference_biography +preproof_frontmatter +review_callout +special_structure +``` + +- [ ] **Step 2: Tighten the contract tests for page/object coverage by class** + +In `tests/test_ocr_real_paper_audit_contracts.py`, add a per-class minimum: + +```python +def test_layout_class_manifest_has_named_representatives() -> None: + manifest = _load_manifest() + by_tag = {} + for paper in manifest["papers"]: + for tag in paper.get("layout_tags", []): + by_tag.setdefault(tag, set()).add(paper["paper_key"]) + + assert by_tag["preproof_frontmatter"] + assert by_tag["same_page_ref_body_split"] + assert by_tag["post_reference_biography"] + assert by_tag["review_callout"] or by_tag["special_structure"] +``` + +- [ ] **Step 3: Run Gate 4 tests** + +Run: `python -m pytest tests/test_ocr_real_paper_audit_contracts.py -v` + +Expected: PASS. + +- [ ] **Step 4: Update project truth files to say layout coverage is now a tracked capability surface** + +Use these exact narrative shifts: + +```text +old: examples of useful gold papers +new: explicit readiness classes with named representatives and contract coverage +``` + +Write the updated summary into: + +- `project/current/ocr-v2-generalization-boundary.md` +- `project/current/ocr-v2-closeout-priority.md` +- `PROJECT-MANAGEMENT.md` + +- [ ] **Step 5: Commit Gate 4** + +```bash +git add audit/coverage_ledger.json tests/test_ocr_real_paper_audit_contracts.py project/current/ocr-v2-generalization-boundary.md project/current/ocr-v2-closeout-priority.md PROJECT-MANAGEMENT.md +git commit -m "test: formalize OCR layout coverage readiness classes" +``` + +--- + +### Task 6: Record Gate 5 Blind-Audit Entry Criteria Without Executing It + +**Files:** +- Modify: `project/current/ocr-v2-closeout-priority.md` +- Modify: `project/current/ocr-v2-remaining-issues-2026-06-18.md` +- Modify: `PROJECT-MANAGEMENT.md` + +- [ ] **Step 1: Add explicit blind-audit trigger wording to the short queue** + +Insert this kind of entry in `project/current/ocr-v2-closeout-priority.md` once Gates 1-4 are complete: + +```text +Next stage after readiness: run bounded unseen-paper blind audit with no code changes during the audit window. +``` + +- [ ] **Step 2: Record the gate-entry checklist in `PROJECT-MANAGEMENT.md`** + +Add a short checklist block: + +```text +- Gates 1-4 complete +- no active P0 trust risk in known fixtures +- project truth files aligned +- blind audit sample selected before execution +``` + +- [ ] **Step 3: Commit the Gate 5 handoff wording** + +```bash +git add project/current/ocr-v2-closeout-priority.md project/current/ocr-v2-remaining-issues-2026-06-18.md PROJECT-MANAGEMENT.md +git commit -m "docs: record OCR blind-audit entry gate" +``` + +--- + +## Verification Checklist Before Claiming "State Healthy" + +- [ ] `python -m pytest tests/test_ocr_document.py -v` +- [ ] `python -m pytest tests/test_ocr_figures.py -v` +- [ ] `python -m pytest tests/test_ocr_real_paper_regressions.py -v` +- [ ] `python -m pytest tests/test_ocr_real_paper_audit_contracts.py -v` +- [ ] `python -m pytest tests/cli/ tests/unit/ -v --tb=short` +- [ ] `ruff check paperforge/ tests/` + +Only after those pass, and only after project truth files point at Gate 5 as the next step, may OCR-v2 be called "state healthy" on known layout classes. diff --git a/docs/superpowers/specs/2026-06-17-ocr-v2-closeout-unified-design.md b/docs/superpowers/specs/2026-06-17-ocr-v2-closeout-unified-design.md new file mode 100644 index 00000000..7b84a803 --- /dev/null +++ b/docs/superpowers/specs/2026-06-17-ocr-v2-closeout-unified-design.md @@ -0,0 +1,307 @@ +# OCR-v2 Unified Close-Out Design + +> Date: 2026-06-17 +> Status: active +> Scope: unify the remaining OCR-v2 close-out work into one bounded execution track instead of splitting it across multiple small plans. + +## Goal + +Finish the current OCR-v2 close-out pass with one coherent sequence: + +1. drop useless preproof cover page 1 at the structured-block layer +2. conservatively tighten tail/post-reference normalization so it stops damaging valid structure +3. run a bounded rebuild and audit pass on the 8 gold papers plus up to 10 additional vault papers selected for layout coverage +4. rewrite the residual error picture from evidence +5. decide whether figure-group work is truly the next dominant blocker + +The purpose of this design is not to open another major OCR architecture branch. The purpose is to close the current one cleanly. + +## Why This Design Exists + +The codebase already passed through the large OCR-v2 redesign: anchor-first parsing, late role resolution, verified-role gate, figure/table inventory, and production-path replay harness. + +The remaining work is now smaller in count but easy to scatter: + +- one remaining `preproof` page-1 problem +- residual post-reference/tail normalization issues +- some expectations and audits that may now be stale +- unresolved figure ownership work that may or may not actually be the top blocker after another audit pass + +Without a unified close-out design, the branch can bounce between small fixes and large architecture ideas without a stable stop condition. + +## Non-Goals + +This design does not: + +- redesign OCR-v2 from scratch +- run full-vault rebuilds indiscriminately +- widen `backmatter_heading` classification through a large phrase list +- add journal-specific figure rescue logic +- force figure-group refactor into the current pass before post-fix audit evidence says it is necessary + +## Current Branch Assumptions + +The design assumes the following are already true on `ocr-v2`: + +- same-page body/reference split has already improved materially +- page-1 correspondence routing now has an explicit `frontmatter_support` path +- tail/post-reference damage is smaller than before but not fully closed +- remaining known issues include: + - preproof cover page 1 noise still entering the document path + - post-reference normalization still having edge-case damage or unnecessary taxonomy pressure + - figure ownership still unresolved on some mixed-layout papers + +## Design Overview + +The unified close-out pass is intentionally conservative. + +It uses the existing pipeline and changes as little surface area as possible: + +- preproof cover-page removal happens early so downstream logic never sees useless page-1 cover blocks +- tail/post-reference cleanup prefers "do not damage valid structure" over introducing richer semantic labeling +- audit expansion is bounded to a small, purposeful layout sample instead of the full vault +- figure-group work is demoted from an assumed next step to a decision gate driven by residual evidence + +## Section 1: Preproof Page-1 Removal + +### Problem + +Some preproof papers carry a page-1 cover sheet that is not useful article content. It adds title/author/PII-like clutter that then has to be suppressed, rescued, or routed later. + +This is the wrong layer to fight it. If the page is truly useless cover material, the cleanest fix is to remove it before document normalization. + +### Decision + +When the pipeline can confidently identify a page-1 preproof cover page, it should remove that page from the structured-block stream before `normalize_document_structure()` runs. + +### Rationale + +- the user explicitly does not want that information preserved +- dropping it early removes downstream contamination in zones, role rescue, and rendering +- this is cleaner than keeping the page and repeatedly teaching later logic to ignore it + +### Boundary + +This is not a general "drop page 1 if it looks weird" rule. + +It applies only when there is strong evidence of a true preproof cover page, using existing preproof signals and page-1 block patterns already available in the pipeline. + +### Expected Outcome + +- page-1 preproof cover blocks do not appear in `structured_blocks` +- downstream frontmatter suppression logic has less work to do +- the known DW preproof page-1 issue disappears at the source instead of being patched later + +## Section 2: Conservative Tail/Post-Reference Cleanup + +### Problem + +The remaining post-reference issues are mostly not about needing a perfect `backmatter_heading` taxonomy. They are about not harming blocks that should remain stable. + +Trying to make `backmatter_heading` more complete without heavy text matching would be brittle. Trying to do it with heavy text matching would create exactly the kind of heuristic bloat the close-out should avoid. + +### Decision + +Do not expand `backmatter_heading` aggressively. + +Instead: + +- preserve valid existing `section_heading` / `subsection_heading` / related heading roles when the evidence is already good +- avoid converting valid body or object-adjacent blocks into `backmatter_body` +- keep post-reference normalization focused on preventing structural damage rather than perfecting taxonomy + +### Rationale + +- fewer text-matching heuristics +- lower regression risk +- aligns with the user's preference not to lean on fragile phrase-based heading detection +- keeps the close-out focused on trust and stability, not label cosmetics + +### Expected Outcome + +- fewer false `backmatter_body` conversions +- valid headings survive more often as ordinary heading roles +- post-reference structure is cleaner even if the taxonomy remains intentionally boring + +## Section 3: Bounded Audit Expansion + +### Problem + +The next verification pass needs broader evidence than the current 8 gold papers alone, but a full-vault rebuild is unnecessary and expensive. + +### Decision + +Run the close-out verification pass on: + +- the existing 8 gold papers +- up to 10 additional vault papers selected specifically to expand layout coverage + +### Selection Method + +The extra papers should be selected by layout class, not by randomness or convenience. + +Priority layout classes to cover: + +1. preproof cover page papers +2. same-page body/reference split papers +3. biography/backmatter-tail papers +4. multi-panel figure papers +5. narrow-caption or side-caption figure papers +6. table-heavy papers +7. publisher margin-noise papers + +The chosen set does not need to maximize paper count. It needs to maximize coverage diversity. + +### Rationale + +- bounded cost +- higher signal than a blind larger run +- gives enough evidence to decide whether figure ownership or some other class is really the next blocker + +### Expected Outcome + +- the branch gets a broader residual map without opening a full-vault operational loop +- layout coverage becomes more trustworthy for the next prioritization decision + +## Section 4: Residual Write-Back + +### Problem + +The branch has accumulated multiple status documents, plans, and handoff notes. After the bounded rebuild/audit pass, the residual state needs to be rewritten from current evidence so the branch does not drift back into contradictory next steps. + +### Decision + +After the bounded rebuild/audit pass, update the active project trackers so they reflect the real remaining categories. + +At minimum, update: + +- `project/current/ocr-error-root-cause-fix-queue.md` +- `PROJECT-MANAGEMENT.md` + +### What Must Be Written Back + +The write-back should separate: + +- issues solved by the current pass +- stale truth / stale expectations revealed by the new audit +- remaining document-structure residuals +- remaining figure-ownership residuals + +### Rationale + +- keeps the branch's next-step guidance singular +- prevents old June plans from silently retaking control of prioritization + +## Section 5: Figure-Group Decision Gate + +### Problem + +Figure-group work is real, but it is also a larger architectural thread. Reopening it too early would blur whether the current close-out pass actually succeeded. + +### Decision + +Do not make figure-group refactor an automatic part of this implementation pass. + +Instead, turn it into a decision gate after rebuild and audit. + +### Gate Condition + +Reopen `group-first figure inventory` only if the post-audit residuals show that: + +1. figure ownership is now the dominant remaining class +2. it clearly exceeds the remaining document-structure and truth-maintenance residuals +3. the bounded sample confirms it across more than one paper/layout family + +### Rationale + +- keeps the current pass bounded +- avoids premature architecture expansion +- makes the figure-group decision evidence-led instead of taste-led + +## Data Flow Summary + +The intended close-out flow becomes: + +```text +raw OCR blocks +-> structured block build +-> preproof page-1 drop (when confidently identified) +-> normalize_document_structure() +-> conservative tail/post-reference cleanup +-> render + artifacts +-> rebuild/audit on 8 gold + up to 10 extra vault papers +-> residual rewrite +-> figure-group decision gate +``` + +## Testing Strategy + +The implementation plan derived from this spec should include tests at four levels: + +1. **unit tests** + - preproof page-1 removal fires only on confident preproof cover pages + - post-reference cleanup preserves valid heading/body/object structure conservatively + +2. **document-pipeline tests** + - `normalize_document_structure()` sees the intended inputs after the preproof drop + - tail/post-reference behavior does not regress in known mixed-page cases + +3. **real-paper regression tests** + - the current gold-paper production-path checks remain green or improve + - specific known papers covering preproof and mixed tail behavior are locked + +4. **bounded audit verification** + - 8 gold papers plus up to 10 vault papers are rebuilt and inspected through the repo's audit helpers + +## Risks And Mitigations + +### Risk 1: dropping page 1 too aggressively + +Mitigation: + +- require strong preproof-cover evidence +- keep the filter narrow and explicit +- lock non-preproof page-1 behavior with regression tests + +### Risk 2: conservative cleanup leaves taxonomy imperfect + +Mitigation: + +- accept this intentionally +- prioritize stability over label refinement in the current pass +- only reopen taxonomy work if it later becomes a genuine blocker + +### Risk 3: 10-paper sample misses an important layout family + +Mitigation: + +- select papers deliberately by layout class +- document which class each chosen sample covers + +### Risk 4: figure-group still turns out to be the main blocker + +Mitigation: + +- this design already includes a decision gate to reopen that work immediately after audit evidence confirms it + +## Acceptance Criteria + +This unified close-out pass is successful if all of the following are true: + +1. preproof cover page 1 is removed at the structured-block layer for the targeted preproof case(s) +2. post-reference/tail cleanup causes fewer false structure conversions without expanding large phrase-matching taxonomies +3. the branch is verified on 8 gold papers plus up to 10 extra vault papers selected for layout coverage +4. project-tracking docs are rewritten to reflect the post-pass residuals from current evidence +5. the branch ends with a clear evidence-based answer to: "is figure-group the next dominant blocker or not?" + +## Implementation Handoff + +This spec should produce one implementation plan, not several small competing plans. + +That plan should be task-based, but unified, with at least these task groups: + +1. preproof page-1 structured-layer removal +2. conservative tail/post-reference cleanup +3. bounded rebuild and audit sample selection/execution +4. residual write-back +5. figure-group decision gate diff --git a/docs/superpowers/specs/2026-06-18-dwq-repair-and-completeness-phase-design.md b/docs/superpowers/specs/2026-06-18-dwq-repair-and-completeness-phase-design.md new file mode 100644 index 00000000..fd045f21 --- /dev/null +++ b/docs/superpowers/specs/2026-06-18-dwq-repair-and-completeness-phase-design.md @@ -0,0 +1,254 @@ +# DWQ Repair And Completeness Phase Design + +> Date: 2026-06-18 +> Status: approved design, pending user review +> Scope: clean stale OCR tracking files first, then repair the remaining real DWQQK2YB issues, rebuild derived artifacts, and stage the next completeness-check phase without mixing its implementation into the same risky change set. + +## Goal + +Run one coherent next phase that stops the branch from inheriting stale issue files, repairs the remaining real DWQQK2YB support-routing and figure-ownership problems, refreshes derived artifacts, and leaves the completeness-check layer fully specified and ready for implementation planning. + +## Why This Phase Exists + +The branch state after `PROJECT-MANAGEMENT.md` section `9.7` is healthier than several current tracking files imply. + +Specifically: + +- some issue files still report already-fixed P0-P2 items as open, +- DWQQK2YB still has one real support-routing gap, +- DWQQK2YB still has one real figure-ownership gap, +- completeness checking is the next structural safeguard, but it should not be coded in the same pass as the DW repairs. + +## Chosen Approach + +Chosen approach: fix the repository's truth surfaces first, then repair the remaining DWQQK2YB issues, then rebuild, and only after that plan the completeness-check implementation. + +Execution order: + +```text +1. Clean stale issue files and align them with 9.7 +2. Repair DWQQK2YB frontmatter support routing +3. Repair DWQQK2YB figure ownership +4. Rebuild derived artifacts for affected papers +5. Write / preserve completeness-check spec and implementation plan as the next phase +``` + +Why this ordering won: + +- stale issue files are currently poisoning the next-step narrative, +- support routing and figure ownership are real unresolved defects on DWQQK2YB, +- rebuild should happen after code repair so the derived assets reflect the fixed logic, +- completeness-check implementation should not be entangled with ownership debugging in the same patch set. + +## Design Constraints + +1. File cleanup happens first. +2. Only real unresolved issues stay in `project/current/` and `PROJECT-MANAGEMENT.md`. +3. DWQQK2YB support routing must become more layout-first and less page-1-text-special-case driven. +4. DWQQK2YB figure ownership must prefer unresolved over over-merged claims. +5. Rebuild is required after the code fix so derived figure assets and structure artifacts reflect the repaired logic. +6. Completeness check remains part of this phase's planning surface, but not part of the same production-code patch. + +## Phase Breakdown + +## 1. Truth-File Cleanup First + +This phase begins by aligning the project trackers with the actual post-`9.7` branch state. + +Files to reconcile first: + +- `project/current/ocr-v2-remaining-issues-2026-06-18.md` +- `project/current/ocr-v2-closeout-priority.md` +- `PROJECT-MANAGEMENT.md` + +The cleanup should remove or rewrite stale entries such as: + +- `media_asset -> body_paragraph` when the latest evidence says it was a stale audit-truth bucket, +- outdated DW biography mismatch wording if the fixture/audit state has already moved, +- outdated P0-P2 residuals that were closed in section `9.7`. + +The cleanup should preserve genuine unresolved items: + +- DWQQK2YB frontmatter support/equal-contribution routing, +- DWQQK2YB figure ownership, +- any still-real backmatter taxonomy ambiguity, +- completeness-check as the next architectural safeguard. + +Reason for doing this first: + +- every later planning and implementation step otherwise inherits bad state and mis-prioritizes work. + +## 2. DWQQK2YB Frontmatter Support Repair + +### Problem + +Some first-surviving-page support blocks on DWQQK2YB still fall into `frontmatter_noise` or body-like fallback when they should be preserved as `frontmatter_support`. + +Examples include: + +- corresponding-author support lines, +- equal-contribution support lines, +- support-like blocks adjacent to the title/author/affiliation cluster. + +### Root Cause Direction + +The current routing still leans too much on: + +- page-1-only cases, +- text-start heuristics, +- raw-label-specific routing. + +That leaves gaps for first-surviving-page cases where the blocks are: + +- `raw_label=text`, not explicit `footnote`, +- visually embedded in the frontmatter cluster, +- support-like by geometry and style rather than by exact prefix. + +### Intended Repair Direction + +Promote layout evidence to the primary classifier for frontmatter support: + +- first surviving page or first-frontmatter page, +- top-of-page placement, +- `frontmatter_main_zone` or `frontmatter_side_zone`, +- `support_like` style family or similar span/layout evidence, +- adjacency to title/authors/affiliations, +- optional marker help such as superscript / affiliation marker, +- text only as confirmatory evidence, not as the main gate. + +Expected result: + +- support-like blocks stay in frontmatter flow, +- they no longer leak into body or generic noise, +- they remain isolated from later tail/reference logic. + +## 3. DWQQK2YB Figure Ownership Repair + +### Problem + +DWQQK2YB still fails figure ownership expectations on the mixed post-reference figure pages. + +Observed current behavior: + +- Figure 2 claims far too many assets, +- Figure 3 has a caption but no owned asset, +- Figure 4 also over-claims assets. + +This is not a fulltext problem. It is an ownership/materialization problem in the figure-inventory path. + +### Root Cause Direction + +The likely failure seam is the candidate-group and asset-claim boundary logic, not caption text detection itself. + +The current matching path appears too willing to treat a broad same-page media cluster as one ownership pool. + +That creates two failure modes: + +1. **over-merge**: one caption claims multiple visual bands or adjacent figure groups, +2. **empty local match**: a caption gets no local candidate group and becomes ambiguous. + +### Intended Repair Direction + +Ownership should be constrained by local visual partitioning before caption claim: + +- partition same-page pre-caption media into smaller vertical/column-aware groups, +- keep caption ownership local to its most plausible band/group, +- do not let a narrow or local caption swallow the whole page's media cluster, +- if no local candidate exists, keep the figure unresolved rather than attaching the wrong assets. + +Policy for this phase: + +- prefer unresolved over wrong merge, +- fix DWQQK2YB specifically by improving the generic partition/claim logic, not by adding a one-paper rescue rule. + +## 4. Rebuild After Repair + +Once the code fixes land, rebuild is mandatory for the affected papers. + +Why: + +- figure JPGs and other derived artifacts are persisted outputs, +- earlier composite/crop/ownership logic changes do not retroactively rewrite already-emitted derived assets, +- rebuild is needed even when the underlying OCR result JSON is unchanged. + +Canonical rebuild path for this phase: + +- `scripts/dev/ocr_rebuild_paper.py` + +This should be used to regenerate: + +- structured blocks, +- reader figures, +- inventories, +- render outputs, +- derived asset files. + +## 5. Completeness Check Stays In This Phase As Planned Next Work + +The completeness-check layer remains part of the phase design, but as the next implementation slice after the DW repairs. + +It keeps the already-approved structure: + +- page text coverage, +- region coverage, +- fulltext coverage. + +It remains: + +- fuzzy, not exact-match, +- signal-first, +- inserted after raw-role assignment and before normalization for the early checks, +- render-audit-based for the downstream fulltext check. + +This phase should preserve the spec and include it in the next implementation plan, but should not bundle its production-code changes with the DW ownership repair patch unless the user explicitly re-scopes again. + +## Verification Plan + +This phase must verify four things separately. + +### 1. Truth files are no longer stale + +- `project/current/*` and `PROJECT-MANAGEMENT.md` agree on what is really fixed vs really open. + +### 2. DWQQK2YB support routing is correct + +- corresponding-author / equal-contribution blocks on the first surviving page remain in frontmatter support flow, +- they do not leak into body output. + +### 3. DWQQK2YB figure ownership is correct + +- Figure 2 no longer over-claims, +- Figure 3 either resolves correctly or remains intentionally unresolved with no false claim, +- Figure 4 no longer over-claims, +- ownership regression test reflects the repaired generic logic. + +### 4. Rebuilt artifacts reflect the fixed logic + +- derived figure assets and reader-figure artifacts are regenerated after the fix, +- rebuild output no longer lags behind the repaired code path. + +## Non-Goals + +- Do not reopen all figure-group architecture work unless the DW fix proves insufficient. +- Do not mix completeness-check production implementation into the same risky code patch. +- Do not keep stale issues around “for history” in the active current-priority files. + +## Deliverables + +This combined phase should produce: + +1. cleaned current-truth documentation, +2. a DWQQK2YB support-routing repair, +3. a DWQQK2YB figure-ownership repair, +4. a rebuild pass over affected derived artifacts, +5. a concrete next-step implementation plan that includes the already-approved completeness-check work. + +## Decision + +Proceed in this order: + +1. clean stale files first, +2. repair DWQQK2YB frontmatter support, +3. repair DWQQK2YB figure ownership, +4. rebuild derived artifacts, +5. carry completeness-check forward as the explicitly planned next slice. diff --git a/docs/superpowers/specs/2026-06-18-ocr-completeness-check-design.md b/docs/superpowers/specs/2026-06-18-ocr-completeness-check-design.md new file mode 100644 index 00000000..f0fe64a6 --- /dev/null +++ b/docs/superpowers/specs/2026-06-18-ocr-completeness-check-design.md @@ -0,0 +1,296 @@ +# OCR Completeness Check Design + +> Date: 2026-06-18 +> Status: approved design, pending user review +> Scope: add a fuzzy completeness-check layer to detect early OCR text loss without relying on exact full-text matching. + +## Goal + +Add an OCR completeness-check layer so the pipeline can detect likely missing content immediately after raw-role assignment, before document normalization and zone inference depend on incomplete text. + +The goal is not exact text reconstruction. The goal is to catch likely dropped content early enough that later structure logic does not reason over a mutilated block set. + +## Current Baseline + +The immediate P0-P2 layout close-out is already complete and recorded in `PROJECT-MANAGEMENT.md` as `9.7`. + +That work fixed: + +- first-surviving-page frontmatter recovery, +- margin-band watermark detection, +- compact mixed-alnum figure inner labels, +- structural-gate fallback for `paper_title` and `authors` in `frontmatter_main_zone`. + +The next addition should build on that stable baseline rather than reopen the just-finished layout close-out. + +## Chosen Approach + +Chosen approach: Approach A, a three-layer fuzzy coverage audit. + +Execution shape: + +```text +raw blocks +-> raw role assignment +-> completeness check (page coverage + region coverage) +-> normalize_document_structure +-> zone inference / structural gate / render +-> fulltext coverage check +``` + +Why this approach won: + +- It catches missing text before anchor and zone logic consume the blocks. +- It does not require brittle exact matching against the PDF native text layer. +- It separates early extraction loss from late rendering loss. +- It is safe to land as a signal-first layer before any aggressive auto-recovery. + +Rejected alternatives: + +- Raw-block-only correction before role assignment: too little structure, too easy to over-merge text. +- Render-only audit: too late to protect normalization and zone inference. + +## Design Constraints + +1. No exact full-document text matching. +2. Use fuzzy coverage and local region evidence, not whole-page string equality. +3. The first version is signal-first: detect and label likely gaps before trying to auto-patch many of them. +4. The check should focus on high-retention blocks and may expand a bit beyond them, but it must not try to repair obvious noise, watermark strips, or generic reference pages. +5. The feature must preserve the existing OCR-v2 architecture and stay local to the build path. + +## Target Coverage Layers + +## 1. Page Text Coverage + +Purpose: + +- detect pages where OCR text volume is materially lower than the PDF native text volume, +- provide a page-level risk signal rather than a repair decision. + +Proposed signal: + +- `ocr_token_count / pdf_token_count` +- `ocr_char_count / pdf_char_count` + +Output example: + +- `page_text_coverage_status = ok | low | missing_pdf_text` +- `page_text_coverage_ratio_tokens` +- `page_text_coverage_ratio_chars` + +This layer must not directly rewrite block text. It is a page-level alert channel only. + +## 2. Region Coverage + +Purpose: + +- detect block-local extraction loss at the point where raw roles are already known, +- give the strongest early signal for whether a high-value block is truncated, empty, or split. + +Proposed workflow: + +1. For each eligible OCR text block, clip the PDF native text layer to the same page and approximately the same bbox. +2. Compare OCR block text and PDF-region text with fuzzy rather than exact checks. +3. Label likely gaps conservatively. + +Eligible block families for the first pass: + +- `paper_title` +- `authors` +- `affiliation` +- `frontmatter_support` +- `abstract*` +- `section_heading` +- `subsection_heading` +- `body_paragraph` +- `structured_insert` +- optionally `figure_caption_candidate`, `table_caption_candidate`, `backmatter_body` + +Explicit non-targets for the first pass: + +- obvious noise and watermark strips, +- generic reference-item repair, +- page headers/footers, +- non-text visual assets. + +Output labels: + +- `complete` +- `likely_truncated` +- `likely_missing_tail` +- `likely_split_across_neighbor` +- `empty_vs_pdf` +- `short_vs_pdf` +- `pdf_unavailable` + +This is the primary completeness layer. + +## 3. Fulltext Coverage + +Purpose: + +- detect loss introduced after structured blocks already exist, +- distinguish raw extraction loss from rendering/path loss. + +Proposed workflow: + +1. After `render_fulltext_markdown`, sample major PDF native text segments from body-like pages. +2. Check whether those segments are fuzzily represented in rendered fulltext. +3. Emit a `rendered_text_gap` signal when long native-text spans are absent. + +This layer is a downstream audit, not an early repair step. + +## Fuzzy Completeness Logic + +The system should not attempt exact matching. It should instead look for strong local signs of incompleteness. + +## Region Gap Indicators + +High-confidence suspicious cases include: + +- PDF region has substantial text but OCR block text is empty, +- OCR block text is much shorter than PDF region text, +- OCR block text is a prefix-like fragment of the PDF region text, +- OCR text and neighboring OCR block geometry suggest that one logical sentence/title/byline was split incorrectly, +- high-value blocks such as title, byline, heading, abstract lead, or early body paragraph are abnormally short compared with the PDF region. + +## Matching Rules + +The fuzzy comparison should prefer: + +- token overlap, +- normalized prefix containment, +- character-length ratio, +- local punctuation loss patterns, +- same-page and same-column consistency. + +The check should avoid: + +- cross-page reconstruction, +- broad cross-column harvesting, +- whole-page concatenation, +- exact paragraph reconstruction from the entire PDF text layer. + +## Safety Boundaries + +1. Never cross pages. +2. Do not cross the dominant column boundary unless the PDF-region overlap is overwhelmingly local and the block is already wide enough to span the column. +3. Prefer tagging over rewriting when confidence is not very high. +4. Do not treat reference pages as the main target in the first version. +5. Do not auto-recover watermark or publisher-strip text. + +## Output Contract + +The first version should attach evidence to blocks and pages even if it does not patch many texts. + +Suggested block-level fields: + +- `text_completeness_status` +- `text_completeness_confidence` +- `text_completeness_evidence` +- `pdf_region_text_len` +- `ocr_text_len` + +Suggested page-level fields: + +- `page_text_coverage_status` +- `page_text_coverage_ratio_tokens` +- `page_text_coverage_ratio_chars` + +Suggested document/render-level fields: + +- `rendered_text_gap_count` +- `rendered_text_gap_examples` + +If a future high-confidence recovery path is added, it should also mark: + +- `_text_source = "ocr+pdf_completeness_recovery"` +- `text_recovery_confidence` +- `text_recovery_evidence` + +## Placement In Existing Code + +Best insertion point for early completeness checks: + +- `paperforge/worker/ocr_blocks.py`, inside `build_structured_blocks()` +- after the initial `seed_role` rows are created, +- before `_has_preproof_cover_page_one()` and before `normalize_document_structure()`. + +Reason: + +- the blocks already have `seed_role`, signatures, bbox, and page context, +- the normalization and zone logic have not yet consumed the text, +- this is the narrowest place to insert the feature without redesigning the whole runtime. + +Best insertion point for downstream rendered coverage: + +- after `render_fulltext_markdown` in the replay/runtime path, +- likely as a health/audit artifact rather than inline mutation. + +## Relationship To Existing PDF Backfill + +The repository already has `backfill_missing_text_from_pdf()` in `paperforge/worker/ocr_pdf_spans.py`. + +That existing step solves one narrow case: + +- OCR block text is empty and the same bbox can recover text from the PDF text layer. + +The new completeness layer is broader and fuzzier: + +- detect likely truncation, short extraction, split-neighbor loss, and downstream render gaps, +- without requiring the block to be empty, +- without exact full-text alignment. + +This means the new layer should complement, not replace, the existing PDF text fallback. + +## Verification Plan + +The implementation must verify three questions separately. + +### 1. Page-level coverage signal works + +- a synthetic page with large PDF-native text but intentionally reduced OCR text should be flagged low-coverage, +- a normal matched page should not be flagged. + +### 2. Region-level gap signal works + +- a block with empty or obviously short OCR text against a richer PDF region should produce `raw_text_gap`, +- a block with normal OCR text should remain `complete`. + +### 3. Fulltext-level coverage signal works + +- a rendered output missing a known long PDF-native segment should produce `rendered_text_gap`, +- a normal render should not. + +## Non-Goals + +- Exact page-text equality. +- Aggressive auto-repair of all mismatches. +- Reference-section reconstruction. +- New journal-specific rules. +- Reopening the completed P0-P2 layout close-out work. + +## Expected Effect On Current Residual Problems + +This feature is expected to help most with: + +- frontmatter/body blocks whose text is partially lost before zone inference, +- accepted-but-unfixed body fragmentation, +- lingering frontmatter footnote/body leakage caused by incomplete local text evidence, +- future audits that currently cannot easily distinguish extraction gaps from later structural mistakes. + +It is not expected to directly solve: + +- figure ownership architecture, +- backmatter heading taxonomy, +- CLI rebuild wiring, +- cosmetic `media_asset` vs `figure_asset` naming differences. + +## Decision + +Proceed with a three-layer fuzzy completeness check: + +- page coverage for coarse page-level signal, +- region coverage for early block-level gap detection, +- fulltext coverage for downstream render-gap detection, +- with signal-first behavior and conservative metadata before broad auto-recovery. diff --git a/docs/superpowers/specs/2026-06-18-ocr-p0-p2-layout-closeout-design.md b/docs/superpowers/specs/2026-06-18-ocr-p0-p2-layout-closeout-design.md new file mode 100644 index 00000000..57d34972 --- /dev/null +++ b/docs/superpowers/specs/2026-06-18-ocr-p0-p2-layout-closeout-design.md @@ -0,0 +1,265 @@ +# OCR P0-P2 Layout Close-Out Design + +> Date: 2026-06-18 +> Status: approved design, pending implementation +> Scope: close out the current P0-P2 OCR-v2 issues with layout-first logic, using text only as confirmatory evidence when it is effectively zero-risk. + +## Goal + +Finish the current OCR-v2 close-out pass without reopening large architecture work. + +The target is to complete the active P0-P2 queue by: + +1. fixing first-page assumptions after preproof-cover removal, +2. reducing publisher watermark and margin-band false positives, +3. recovering residual figure inner text / panel-like blocks, +4. updating only the expectations that become stale after verified behavior changes. + +This work must not introduce new journal-specific rescue branches or a new page-state machine. + +## Chosen Approach + +Chosen approach: Approach A, "first surviving page anchor plus local expansion". + +Why this approach won: + +- It directly fixes the current P0 seam in `infer_zones()`. +- It stays inside the existing OCR-v2 architecture instead of starting a new refactor. +- It allows P1 and P2 fixes to share the same zone-authority assumptions. +- It is small enough to verify against existing gold fixtures and audit artifacts before merge. + +Rejected alternatives: + +- Full page-type state machine: better long-term generalization, too large for close-out. +- Column-lane zone modeling: strongest mixed-layout handling, clearly beyond this pass. + +## Design Constraints + +1. Layout evidence is primary. +2. Text matching is allowed only as a high-confidence confirmation signal, never as the core classifier skeleton. +3. No new journal-specific hardcoded rescue logic. +4. No reopening figure-group architecture in this pass. +5. Verification must check both the target failure papers and at least one control paper to catch regressions. + +## Real Evidence From Audit Artifacts + +The design is grounded in the current audit artifacts. + +### DWQQK2YB frontmatter failure + +In `tests/fixtures/ocr_real_papers/DWQQK2YB/block_trace.csv`, the first surviving page is page 2 after preproof-cover removal. + +Observed structure on surviving page 2: + +- preproof running header still exists near the top and should not become the article body start, +- title block appears first, +- author and affiliation blocks follow, +- correspondence / equal-contribution lines sit between frontmatter and the later Highlights area, +- body-like structured content only starts after those frontmatter anchors. + +This confirms that the current `page == 1` logic is wrong. The correct question is: "what is the first surviving page, and where does its real body start?" + +### A8E7SRVS and K7R8PEKW watermark failures + +In `audit/A8E7SRVS/block_review.jsonl` and `audit/K7R8PEKW/block_trace.csv`, the noisy publisher strips share the same stable layout profile: + +- `raw_label=aside_text`, +- extremely narrow width, +- very tall height, +- pinned to left or right page edge, +- repeated with near-identical geometry across many pages, +- weak coupling to the main body column. + +The text often includes "Downloaded from ..." or "For personal use only", but the decisive signal is geometric. Text can be used as a confirming boost only when the edge-band geometry already matches. + +### Control risk seen in frontmatter-heavy page-1 papers + +Audit frontmatter pages such as `audit/6FGDBFQN/block_review.jsonl` show that page-1 layouts can legitimately contain title, author, metadata, abstract, and side noise together. + +This means the fix must not simply broaden frontmatter/noise routing globally. It needs a local first-page anchor decision, followed by narrow expansion rules. + +## 1. First Surviving Page Anchor + +`infer_zones()` will stop treating literal page 1 as special. Instead it will compute: + +- `surviving_pages = sorted(unique positive pages in current block set)` +- `first_surviving_page = surviving_pages[0]` when any page exists + +All logic currently tied to `page == 1` for frontmatter/body-start inference will move to `page == first_surviving_page`. + +This is not a rename-only change. The first surviving page becomes a local layout inference problem. + +## 2. First Surviving Page Body Start + +The first surviving page will be split into two local bands: + +- pre-body frontmatter band, +- body-start-and-below band. + +The split point is the first block on the first surviving page that satisfies a body-start condition. + +Body-start condition should be based on layout and structural evidence, not plain text keywords: + +- block is not a preproof marker, +- block is not a reference candidate, +- block is not a narrow side-band candidate, +- block is not a frontmatter anchor family block already seen above, +- block has body-like geometry relative to the dominant reading column, +- block appears below the title/author/affiliation/correspondence cluster, +- block is large enough and column-aligned enough to plausibly join body flow or structured insert flow. + +Practical rule for this pass: + +- keep the existing `_is_page1_body_start()` seam, but generalize it into a first-surviving-page helper fed by page-local geometry and the evolving set of already-seen frontmatter anchors. + +## 3. Frontmatter Main Expansion + +On the first surviving page, blocks above the body-start anchor enter `frontmatter_main_zone` when they are part of the top article stack: + +- title, +- authors, +- affiliations, +- correspondence/support blocks, +- local metadata blocks that share the same main reading band. + +Blocks near page edges or in narrow side strips stay eligible for `frontmatter_side_zone` instead. + +This preserves the current main/side distinction while making it survive preproof-page removal. + +## 4. Body Zone Expansion + +`body_zone` should start from the first surviving page once the body-start anchor is found. + +Current bug: + +- `body_blocks` currently require `page > 1`, which incorrectly excludes page 2 when page 1 has already been removed. + +Fix: + +- body eligibility should become `page > first_surviving_page` or `page == first_surviving_page and block is at/after body_start_anchor`. +- blocks above that anchor on the first surviving page remain frontmatter-side/frontmatter-main candidates and must not enter body by fallback. + +Same-page reference split remains in place: + +- above reference heading on the same page stays body, +- below reference heading routes to reference/tail logic. + +## 5. Margin-Band Noise Tightening + +Publisher watermark cleanup will remain layout-first. + +Primary evidence: + +- raw label family such as `aside_text` / side-band text, +- edge-pinned x-range, +- very tall aspect ratio, +- very narrow width, +- weak overlap with main body columns, +- repeated strip-like placement across pages. + +Allowed confirmatory text evidence, only when the geometry already matches: + +- `Downloaded from` +- `For personal use only` + +If the text matches but the geometry does not, do not classify as watermark noise from text alone. + +Implementation intent for this pass: + +- tighten `_looks_like_margin_band_noise()` / `_looks_like_edge_band_noise()` rather than creating a text-only classifier, +- optionally allow a geometry-plus-text confidence boost for borderline edge strips. + +## 6. Residual Figure Inner Text Recovery + +Residual `figure_inner_text` work should also stay layout-first. + +Target blocks are short labels or short figure-internal text that: + +- are very small, +- are spatially adjacent to a figure/media asset, +- live within or near the figure asset envelope, +- do not look like true prose lines, +- do not carry formal figure caption prefixes. + +This pass should extend the existing `figure inner label` seam rather than adding broad new text rules. + +Text can help only when it is near-zero-risk, for example a single panel-style token, but geometry remains the main decision signal. + +## 7. P2 Scope Discipline + +P2 in this pass means only: + +- expectation updates made necessary by verified behavior changes, +- naming/fixture cleanup directly tied to the fixed output. + +Explicitly out of scope: + +- CLI rebuild wiring, +- figure-group architecture work, +- renderer redesign, +- new artifact families. + +## Code Seams + +Primary files: + +- `paperforge/worker/ocr_document.py` +- `paperforge/worker/ocr_roles.py` + +Likely touched seams: + +- `infer_zones()` +- `_is_page1_body_start()` or its generalized replacement +- first-page frontmatter-main collection logic +- body-block eligibility logic +- `_looks_like_margin_band_noise()` +- `_looks_like_edge_band_noise()` +- figure-inner-text helper logic in `ocr_roles.py` + +Test files: + +- `tests/test_ocr_document.py` +- `tests/test_ocr_real_paper_regressions.py` +- fixture expectations only where behavior is visually verified as correct + +## Verification Plan + +Implementation must be driven by failing tests first. + +### Required red tests + +1. first surviving page frontmatter/title recovery after preproof removal, +2. first surviving page body-start expansion into body zone, +3. margin-band watermark stays noise on a representative edge-strip case, +4. control regression proving normal true page-1 papers still keep their frontmatter/body split, +5. residual figure-inner-text case if a stable fixture seam exists. + +### Required real-paper checks + +- `DWQQK2YB` for first surviving page recovery, +- `K7R8PEKW` and/or `A8E7SRVS` for repeated publisher edge-band noise, +- one control paper with legitimate page-1 frontmatter, preferably `6FGDBFQN`, to ensure no regression. + +### Success criteria + +- DW frontmatter on surviving page 2 is no longer swallowed into generic body fallback, +- body flow can start on the first surviving page, +- publisher strips no longer survive as `unknown_structural` / body-like content when their geometry is the classic margin-band pattern, +- no new regression on ordinary page-1 papers, +- only verified stale expectations are updated. + +## Non-Goals + +- No full page-state machine. +- No figure-group ownership redesign. +- No publisher-specific profile system. +- No text-first heuristic expansion. + +## Decision + +Proceed with Approach A as a narrow close-out pass: + +- first surviving page replaces literal page 1 for local frontmatter/body inference, +- body start becomes a page-local anchor decision, +- publisher noise remains geometry-first with optional zero-risk text confirmation, +- P2 stays limited to verified expectation cleanup. diff --git a/docs/superpowers/specs/2026-06-18-ocr-v2-readiness-gates-design.md b/docs/superpowers/specs/2026-06-18-ocr-v2-readiness-gates-design.md new file mode 100644 index 00000000..5058680e --- /dev/null +++ b/docs/superpowers/specs/2026-06-18-ocr-v2-readiness-gates-design.md @@ -0,0 +1,286 @@ +# OCR-v2 Readiness Gates Design + +> Date: 2026-06-18 +> Status: draft design approved in chat, pending user review +> Scope: define the formal readiness gates that OCR-v2 must pass before it can be called layout-robust for broad academic-paper parsing. + +## Goal + +Replace the current informal "looks healthy" judgment with a single readiness model. + +The model should answer three operational questions: + +1. What work is still required before OCR-v2 can be called structurally healthy? +2. In what order should that work happen? +3. What evidence is strong enough to say the branch is ready for broader-layout confidence, even before unseen-paper blind audit begins? + +This design covers the first four readiness gates and treats unseen-paper blind audit as the next gate after those four are complete. + +## Non-Goals + +This design does not: + +- redesign the OCR-v2 architecture, +- replace the existing completeness-check design, +- replace the existing group-first figure inventory plan, +- execute the blind audit itself, +- declare OCR-v2 universally robust across all journals after local fixes alone. + +## Current Problem + +The repo has already moved beyond "the architecture is wrong." + +The remaining risk is that OCR-v2 can look healthy on known fixtures while still failing to generalize across layout classes. The current truth is spread across several files: + +- `project/current/ocr-v2-closeout-priority.md` +- `project/current/ocr-v2-generalization-boundary.md` +- `project/current/ocr-v2-remaining-issues-2026-06-18.md` +- `PROJECT-MANAGEMENT.md` +- the existing completeness and figure-inventory plans/specs + +Those files are individually useful, but they do not yet form one clean readiness story. Some judgments are stale because they still speak in the language of "close-out" or "zone-boundary pass" rather than explicit readiness gates. + +## Readiness Model + +OCR-v2 should be considered "state healthy" only after the first four gates below are complete and verified. + +Gate 5 is the next-stage validation step, not part of this implementation cycle. + +### Gate 1: Completeness Detection Exists And Works + +Purpose: + +- catch likely OCR text loss early, +- stop later structure logic from reasoning over silently incomplete text, +- separate extraction loss from later render/path loss. + +Required outcome: + +- page-level coverage signals exist, +- region-level completeness signals exist for high-value text blocks, +- rendered fulltext gap signals exist downstream, +- the signals are conservative and evidence-bearing rather than aggressive auto-repair. + +Primary reference: + +- `docs/superpowers/specs/2026-06-18-ocr-completeness-check-design.md` + +Exit criteria: + +1. `build_structured_blocks()` emits completeness evidence at the block/page level. +2. Tests prove page-level low-coverage detection on synthetic and normal cases. +3. Tests prove region-level gap classification on empty/short/truncated cases. +4. A downstream rendered-text coverage audit exists and emits structured gaps. +5. Existing PDF backfill is explicitly treated as complementary, not replaced. + +### Gate 2: Figure Ownership Generalization Is No Longer The Primary Trust Risk + +Purpose: + +- stop same-page and multi-panel figure ownership from remaining ambiguous on structurally common layouts, +- move the inventory path from asset-order reasoning toward explicit candidate-group ownership. + +Required outcome: + +- grouped candidates become the primary matching unit, +- fallback paths cannot steal valid grouped assets, +- known DW-style ownership failures are resolved by model logic rather than tolerated as "captured but ambiguous." + +Primary reference: + +- `docs/superpowers/plans/2026-06-15-group-first-figure-inventory-plan.md` + +Exit criteria: + +1. Group-first inventory is implemented and verified. +2. Same-page pair/triple panel layouts pass deterministic unit coverage. +3. `DWQQK2YB`-class mixed post-reference figure pages no longer rely on ambiguity-acceptable assertions. +4. Reader/render payload shape remains stable unless a proven incompatibility forces a small contract update. +5. Health/audit artifacts expose grouped-vs-single behavior clearly enough for debugging. + +### Gate 3: Ordering And Boundary Authority Are Centered In Structure, Not Renderer Repair + +Purpose: + +- reduce hidden reorder behavior in render stages, +- make mixed body/reference/backmatter pages auditable from structure artifacts, +- give the pipeline a clearer single source of truth for reading order and boundary decisions. + +Required outcome: + +- same-page mixed layouts depend on explicit page/column/y-position evidence, +- renderer-side reorder responsibilities shrink, +- debugging wrong order no longer requires chasing both structure and renderer heuristics equally. + +Primary references: + +- `paperforge/worker/ocr_document.py` +- `paperforge/worker/ocr_orchestrator.py` +- `paperforge/worker/ocr_render.py` +- `project/current/ocr-v2-generalization-boundary.md` + +Exit criteria: + +1. Structure-layer artifacts are the primary authority for mixed-page ordering. +2. Renderer keeps only minimal presentational ordering behavior. +3. Same-page ref/body/backmatter boundary regressions have explicit coverage and no active P0 residuals. +4. Project docs no longer describe renderer-side repair as a meaningful remaining dependency for correctness. + +### Gate 4: Layout-Class Coverage Is Deliberate Enough To Support Generalization Claims + +Purpose: + +- stop treating each newly found paper as an isolated surprise, +- formalize which layout/risk classes OCR-v2 is expected to handle, +- make the gold set representative rather than merely useful. + +Required outcome: + +- the audit-paper ledger and taxonomy become more precise, +- the audited real-paper set deliberately covers high-risk layout classes, +- each class has concrete verification expectations. + +Primary references: + +- `audit/` +- `tests/test_ocr_real_paper_audit_contracts.py` +- `project/current/ocr-v2-generalization-boundary.md` + +Target layout/risk classes for this pass: + +- multi-panel +- side-caption +- same-page ref/body split +- post-reference biography/backmatter +- preproof frontmatter +- review/callout/special-structure pages + +Exit criteria: + +1. Taxonomy is tightened beyond broad single/multi-column labels. +2. The audited real-paper set contract explicitly covers the target classes above. +3. Fixture expectations include enough page/object assertions for those classes. +4. Project docs describe layout-class coverage as a tracked capability surface, not just a list of example papers. + +### Gate 5: Unseen-Paper Blind Audit + +This is intentionally outside the current implementation cycle. + +Purpose: + +- validate that the first four gates produce real generalization rather than overfitting to known fixtures. + +Entry condition: + +- Gates 1-4 are complete and verified, +- project truth files say OCR-v2 is ready for blind audit, +- no active P0 trust risk remains in the known set. + +Expected outcome: + +- a bounded sample of unseen papers is run without code changes during the audit window, +- failures are classified by layout class rather than patched immediately, +- OCR-v2 is judged by how often it reveals new failure families, not by whether every paper is perfect. + +## Execution Order + +The readiness work should proceed in this order: + +1. Gate 1: completeness-check layer +2. Gate 2: figure ownership generalization +3. Gate 3: ordering/boundary authority cleanup +4. Gate 4: layout-class coverage expansion +5. Gate 5: unseen-paper blind audit + +Why this order: + +- Gate 1 protects the rest of the pipeline from silent text-loss reasoning. +- Gate 2 removes the largest remaining trust-risk class. +- Gate 3 makes structural truth easier to audit and reduces renderer coupling. +- Gate 4 should be tightened after the core structural rules stop shifting heavily. +- Gate 5 is only meaningful after the system has passed the known-layout gates. + +## Project Truth-File Realignment + +As part of the readiness planning work, the project docs should be updated so they tell one consistent story. + +### Files That Must Be Updated + +- `project/current/ocr-v2-closeout-priority.md` +- `project/current/ocr-v2-generalization-boundary.md` +- `project/current/ocr-v2-remaining-issues-2026-06-18.md` +- `PROJECT-MANAGEMENT.md` + +### Required Realignment Rules + +1. `ocr-v2-closeout-priority.md` should stop framing the branch mainly as a close-out topic and instead become the short authoritative readiness queue. +2. `ocr-v2-generalization-boundary.md` should remain the broader architecture/risk note, but its "Next Actions" must reflect the readiness-gate order rather than the older zone-boundary-close-out storyline. +3. `ocr-v2-remaining-issues-2026-06-18.md` should reflect active readiness blockers, deferred low-priority conservatism, and the fact that blind audit is next-stage rather than current-slice work. +4. `PROJECT-MANAGEMENT.md` should add a new section recording the readiness-gates transition, what became outdated, and which document now governs next execution. +5. When a prior judgment is no longer the active truth, it should be rewritten rather than left standing beside the new thread. + +## Plan Structure + +The follow-up implementation plan should be a single master readiness plan. + +That plan should: + +- define one active execution thread, +- split work into four implementation phases matching Gates 1-4, +- treat Gate 5 as a post-readiness gate with entry criteria only, +- reuse existing detailed artifacts where they are already strong, +- add only the missing plan sections for Gate 3 and Gate 4, +- explicitly include the project-truth-file cleanup as part of the work rather than as optional documentation debt. + +### Reuse Strategy + +Reuse, do not rewrite, the following where possible: + +- Gate 1 detail: `2026-06-18-ocr-completeness-check-design.md` +- Gate 2 detail: `2026-06-15-group-first-figure-inventory-plan.md` + +Add new master-plan sections for: + +- Gate 3 ordering/boundary authority cleanup, +- Gate 4 layout taxonomy + fixture coverage expansion, +- project truth-file realignment, +- Gate 5 blind-audit trigger and success rubric. + +## Acceptance Standard For "State Healthy" + +The branch may be called "state healthy" only when all of the following are true: + +1. Gates 1-4 are complete and verified. +2. No active P0 trust-risk item remains in known fixtures. +3. Figure ownership no longer passes by ambiguity tolerance on known hard cases. +4. Structural ordering truth primarily lives upstream of render. +5. Layout capability is expressed as an explicit tested taxonomy, not just anecdotal paper coverage. +6. Project truth files agree on the active thread and next gate. + +This phrase does not yet mean "universal robustness across all journals." + +It means OCR-v2 is strong enough on known layout classes to justify the next-stage unseen-paper blind audit. + +## Risks To Avoid + +1. Turning the readiness plan into a large rewrite of already-good plans. +2. Reopening completed close-out work without evidence of current regressions. +3. Claiming generalization from known fixtures alone. +4. Expanding taxonomy randomly instead of by failure/risk class. +5. Leaving old project judgments in place after the active execution thread changes. + +## Deliverables From This Planning Pass + +1. A written readiness-gates design doc. +2. A master implementation plan for Gates 1-4 plus Gate 5 entry criteria. +3. Updated project truth files that point at the readiness-gate thread. + +## Success Condition For This Design + +This design is successful if the next implementation plan can be written without ambiguity about: + +- what "state healthy" means, +- which four gates belong to the current cycle, +- which gate is intentionally deferred, +- which docs are authoritative, +- and what evidence is required before broader confidence claims are allowed. diff --git a/paperforge/worker/ocr.py b/paperforge/worker/ocr.py index efe34132..9179a0f9 100644 --- a/paperforge/worker/ocr.py +++ b/paperforge/worker/ocr.py @@ -1945,6 +1945,7 @@ def postprocess_ocr_result(vault: Path, key: str, all_results: list[dict]) -> tu table_inventory=table_inventory, doc_structure=doc_structure, reader_payload=reader_payload, + rendered_markdown=markdown, ) health_report["ocr_raw_integrity"] = ocr_raw_integrity write_ocr_health(ocr_root / "health", health_report) diff --git a/paperforge/worker/ocr_blocks.py b/paperforge/worker/ocr_blocks.py index 91b5a26b..1a51dbae 100644 --- a/paperforge/worker/ocr_blocks.py +++ b/paperforge/worker/ocr_blocks.py @@ -22,7 +22,7 @@ def _summarize_page_text_coverage(*, ocr_text: str, pdf_text: str) -> dict: ocr_chars = len((ocr_text or "").strip()) pdf_chars = len((pdf_text or "").strip()) if pdf_chars == 0: - return {"page_text_coverage_status": "missing_pdf_text", "page_text_coverage_ratio_chars": 1.0} + return {"page_text_coverage_status": "missing_pdf_text", "page_text_coverage_ratio_chars": None} ratio = ocr_chars / max(pdf_chars, 1) return { "page_text_coverage_status": "low" if ratio < 0.6 else "ok", diff --git a/paperforge/worker/ocr_figures.py b/paperforge/worker/ocr_figures.py index c618a8d6..c028246a 100644 --- a/paperforge/worker/ocr_figures.py +++ b/paperforge/worker/ocr_figures.py @@ -70,8 +70,10 @@ def _looks_like_inline_figure_mention(text: str) -> bool: # Text starting with "Figure N." / "Fig. N." is a self-identifying # caption — never flag as body mention regardless of verb content. - if re.match(r"^(?:Figure|Fig\.?)\s+\d+\b", t, re.IGNORECASE): + if re.match(r"^(?:Figure|Fig\.?)\s+\d+\.\b", t, re.IGNORECASE): return False + if re.match(r"^(?:Figure|Fig\.?)\s+\d+\s+(?:" + "|".join(_INLINE_FIGURE_MENTION_VERBS) + r")\b", t, re.IGNORECASE): + return True if not re.search(r"\bfi(?:g(?:ure)?\.?\s*\d+)", lower): return False @@ -154,6 +156,8 @@ def _is_body_mention(block: dict) -> bool: if raw_role == "figure_caption_candidate": text = block.get("text", "") if text and _FIGURE_NUMBER_PATTERN.search(text): + if _looks_like_inline_figure_mention(text): + return True return False if text and _looks_like_figure_narrative_prose(text): return True @@ -401,7 +405,13 @@ def _build_candidate_figure_groups_from_assets(assets: list[dict], page_width: f media = [ b for b in assets if not b.get("_non_body_media") - and (b.get("role") == "figure_asset" or (b.get("role") == "media_asset" and b.get("raw_label", "") in {"image", "chart", "figure"})) + and ( + b.get("role") == "figure_asset" + or ( + b.get("role") == "media_asset" + and (b.get("raw_label", "") in {"image", "chart", "figure"} or not str(b.get("raw_label", "")).strip()) + ) + ) ] media.sort(key=lambda b: (b.get("page", 0), (b.get("bbox") or [0, 0, 0, 0])[1], (b.get("bbox") or [0, 0, 0, 0])[0])) @@ -672,6 +682,7 @@ def _caption_width_ratio(block: dict, page_width: float) -> float: def _same_page_narrow_caption_column(page_captions: list[dict], page_width: float) -> list[dict]: formal = [ cap for cap in page_captions + if _extract_figure_number(str(cap.get("text", ""))) is not None if _caption_width_ratio(cap, page_width) < 0.6 and not _PANEL_LABEL_PATTERN.match(str(cap.get("text", "")).strip()) ] @@ -684,11 +695,52 @@ def _same_page_narrow_caption_column(page_captions: list[dict], page_width: floa return ordered +def _asset_vertical_side(legend: dict, group: dict) -> str: + legend_bbox = legend.get("bbox") or legend.get("block_bbox") or [0, 0, 0, 0] + cluster_bbox = group.get("cluster_bbox") or [0, 0, 0, 0] + if len(legend_bbox) < 4 or len(cluster_bbox) < 4: + return "overlap" + if cluster_bbox[3] <= legend_bbox[1]: + return "above" + if cluster_bbox[1] >= legend_bbox[3]: + return "below" + return "overlap" + + +def _allow_previous_page_sequential_match(cap: dict, asset: dict) -> bool: + cp = int(cap.get("page", 0) or 0) + ap = int(asset.get("page", 0) or 0) + if ap != cp - 1: + return False + cap_bbox = cap.get("bbox") or [0, 0, 0, 0] + asset_bbox = asset.get("bbox") or [0, 0, 0, 0] + if len(cap_bbox) < 4 or len(asset_bbox) < 4: + return False + cap_top = cap_bbox[1] + cap_width = cap_bbox[2] - cap_bbox[0] + asset_bottom = asset_bbox[3] + asset_width = asset_bbox[2] - asset_bbox[0] + page_width = float(cap.get("page_width") or asset.get("page_width") or 1200) + page_height = float(cap.get("page_height") or asset.get("page_height") or 1600) + strong_numbered_caption = _extract_figure_number(str(cap.get("text", ""))) is not None + post_reference_layout = str(cap.get("zone") or "") == "post_reference_backmatter_zone" + starts_page = cap_top <= 220 + prior_asset_near_bottom = asset_bottom >= page_height * 0.7 + wide_caption = cap_width >= page_width * 0.6 + substantial_asset = asset_width >= page_width * 0.2 + return strong_numbered_caption and post_reference_layout and starts_page and prior_asset_near_bottom and wide_caption and substantial_asset + + def _partition_assets_by_caption_bands(page_captions: list[dict], page_assets: list[dict], page_height: float) -> dict[str, list[dict]]: if len(page_captions) < 2: return {} ordered = sorted(page_captions, key=lambda cap: (cap.get("bbox") or [0, 0, 0, 0])[1]) + result: dict[str, list[dict]] = {str(cap.get("block_id", "")): [] for cap in ordered} + caption_tops = [((cap.get("bbox") or [0, 0, 0, 0])[1]) for cap in ordered] + asset_centers = [(((asset.get("bbox") or [0, 0, 0, 0])[1] + (asset.get("bbox") or [0, 0, 0, 0])[3]) / 2) for asset in page_assets] + captions_precede_assets = bool(asset_centers) and min(caption_tops) <= min(asset_centers) + boundaries: list[float] = [0.0] for upper, lower in zip(ordered, ordered[1:]): ub = upper.get("bbox") or [0, 0, 0, 0] @@ -696,14 +748,25 @@ def _partition_assets_by_caption_bands(page_captions: list[dict], page_assets: l boundaries.append(((ub[1] + ub[3]) / 2 + (lb[1] + lb[3]) / 2) / 2) boundaries.append(float(page_height)) - result: dict[str, list[dict]] = {str(cap.get("block_id", "")): [] for cap in ordered} for asset in page_assets: bbox = asset.get("bbox") or [0, 0, 0, 0] cy = (bbox[1] + bbox[3]) / 2 if len(bbox) >= 4 else 0 - for idx, cap in enumerate(ordered): - if boundaries[idx] <= cy < boundaries[idx + 1]: - result[str(cap.get("block_id", ""))].append(asset) - break + if captions_precede_assets: + for idx, cap in enumerate(ordered): + if boundaries[idx] <= cy < boundaries[idx + 1]: + result[str(cap.get("block_id", ""))].append(asset) + break + else: + assigned = False + for cap in ordered: + cap_bbox = cap.get("bbox") or [0, 0, 0, 0] + cap_top = cap_bbox[1] if len(cap_bbox) >= 4 else float(page_height) + if cy <= cap_top: + result[str(cap.get("block_id", ""))].append(asset) + assigned = True + break + if not assigned: + result[str(ordered[-1].get("block_id", ""))].append(asset) return result @@ -732,6 +795,7 @@ def build_figure_inventory(structured_blocks: list[dict], page_width: float = 12 is_validation_first_candidate = _is_validation_first_legend_candidate(block) if role in ("figure_caption", "figure_caption_candidate") or is_validation_first_candidate: if _is_body_mention(block): + unmatched_legends.append(block) continue if ( role == "figure_caption_candidate" @@ -864,42 +928,8 @@ def build_figure_inventory(structured_blocks: list[dict], page_width: float = 12 if top_score < 0.4: matched_assets = [] elif len(close) > 1: - legend_bb = legend.get("bbox") or legend.get("block_bbox") or [0, 0, 0, 0] - lcx = (legend_bb[0] + legend_bb[2]) / 2 if len(legend_bb) >= 4 else 0 - best = close[0] - best_delta = abs( - lcx - ((best[1].get("cluster_bbox", [0, 0, 0, 0])[0] + best[1].get("cluster_bbox", [0, 0, 0, 0])[2]) / 2) - ) - best_orig_score = best[2]["score"] - for ci, cg, cs in close: - cb = cg.get("cluster_bbox", [0, 0, 0, 0]) - acx = (cb[0] + cb[2]) / 2 if len(cb) >= 4 else 0 - delta = abs(lcx - acx) - ca_col_ok = delta + 20 < best_delta and cs["score"] >= best_orig_score - 0.01 - if ca_col_ok: - best = (ci, cg, cs) - best_delta = delta - if best[2].get("decision") == "matched": - best_gi, best_group, best_score = best - matched_assets = best_group.get("media_blocks", []) - g_page = best_group.get("page", 0) - used_asset_page_ids.update({(g_page, bid) for bid in best_group.get("asset_block_ids", [])}) - matched_assets = _expand_matched_assets_locally( - legend, - matched_assets, - assets, - used_asset_page_ids, - page_caption_index.get(g_page, []), - ) - region_match = { - "media_blocks": matched_assets, - "match_score": best_score, - "group_type": best_group.get("group_type", ""), - "group_evidence": best_group.get("group_evidence", []), - } - if len(matched_assets) > 1: - region_match["cluster_bbox"] = best_group.get("cluster_bbox", [0, 0, 0, 0]) - else: + close_sides = {_asset_vertical_side(legend, g) for _, g, _ in close} + if "above" in close_sides and "below" in close_sides: ambiguous_figures.append( { "legend_block_id": legend.get("block_id", ""), @@ -913,10 +943,68 @@ def build_figure_inventory(structured_blocks: list[dict], page_width: float = 12 } for _, g, s in close ], + "hold_reason": "close_asset_tie", } ) ambiguous = True matched_assets = [] + close = [] + if not close: + pass + else: + legend_bb = legend.get("bbox") or legend.get("block_bbox") or [0, 0, 0, 0] + lcx = (legend_bb[0] + legend_bb[2]) / 2 if len(legend_bb) >= 4 else 0 + best = close[0] + best_delta = abs( + lcx - ((best[1].get("cluster_bbox", [0, 0, 0, 0])[0] + best[1].get("cluster_bbox", [0, 0, 0, 0])[2]) / 2) + ) + best_orig_score = best[2]["score"] + for ci, cg, cs in close: + cb = cg.get("cluster_bbox", [0, 0, 0, 0]) + acx = (cb[0] + cb[2]) / 2 if len(cb) >= 4 else 0 + delta = abs(lcx - acx) + ca_col_ok = delta + 20 < best_delta and cs["score"] >= best_orig_score - 0.01 + if ca_col_ok: + best = (ci, cg, cs) + best_delta = delta + if best[2].get("decision") == "matched": + best_gi, best_group, best_score = best + matched_assets = best_group.get("media_blocks", []) + g_page = best_group.get("page", 0) + used_asset_page_ids.update({(g_page, bid) for bid in best_group.get("asset_block_ids", [])}) + matched_assets = _expand_matched_assets_locally( + legend, + matched_assets, + assets, + used_asset_page_ids, + page_caption_index.get(g_page, []), + ) + region_match = { + "media_blocks": matched_assets, + "match_score": best_score, + "group_type": best_group.get("group_type", ""), + "group_evidence": best_group.get("group_evidence", []), + } + if len(matched_assets) > 1: + region_match["cluster_bbox"] = best_group.get("cluster_bbox", [0, 0, 0, 0]) + else: + ambiguous_figures.append( + { + "legend_block_id": legend.get("block_id", ""), + "page": legend_page, + "caption_score": caption_score, + "candidates": [ + { + "asset_block_id": g.get("media_blocks", [{}])[0].get("block_id", ""), + "group_type": g.get("group_type", ""), + "match_score": s, + } + for _, g, s in close + ], + } + ) + ambiguous = True + matched_assets = [] else: best_gi, best_group, best_score = candidates[0] if best_score["decision"] == "matched": @@ -1159,32 +1247,58 @@ def build_figure_inventory(structured_blocks: list[dict], page_width: float = 12 key=lambda b: (b.get("page", 0) or 0, (b.get("bbox") or [0, 0, 0, 0])[1]), ) ai = 0 - seq_matched: list[dict] = [] + seq_matched: list[tuple[dict, dict]] = [] for cap in sorted_caps: fn = _extract_figure_number(cap.get("text", "")) if fn is None: continue - while ai < len(sorted_asts): - asset = sorted_asts[ai] + + cp = cap.get("page", 0) or 0 + cap_bbox = cap.get("bbox") or [0, 0, 0, 0] + cap_top = cap_bbox[1] if len(cap_bbox) >= 4 else 0 + previous_page_asset = None + future_page_asset = None + scan_index = ai + + while scan_index < len(sorted_asts): + asset = sorted_asts[scan_index] ap = asset.get("page", 0) or 0 - cp = cap.get("page", 0) or 0 + asset_bid = asset.get("block_id", "") + if asset_bid and (ap, asset_bid) in used_asset_page_ids: + scan_index += 1 + continue + if ap == cp - 1: + previous_page_asset = asset + scan_index += 1 + continue if ap >= cp: + if ap > cp: + future_page_asset = asset break - ai += 1 - if ai >= len(sorted_asts): - break - asset = sorted_asts[ai] + scan_index += 1 + + asset = None + if asset is None and previous_page_asset is not None and _allow_previous_page_sequential_match(cap, previous_page_asset): + asset = previous_page_asset + if asset is None and future_page_asset is not None: + asset = future_page_asset + if asset is None: + continue + asset_bid = asset.get("block_id", "") asset_page = asset.get("page", 0) if asset_bid is not None and (asset_page, asset_bid) in used_asset_page_ids: - ai += 1 continue - ai += 1 + while ai < len(sorted_asts): + ai_asset = sorted_asts[ai] + ai += 1 + if ai_asset.get("block_id", "") == asset_bid and ai_asset.get("page", 0) == asset_page: + break fig_id = f"figure_{fn:03d}" caption_score = score_figure_caption( cap, nearby_media=True, caption_style_match=False, body_prose_likelihood=False ) - seq_matched.append(cap) + seq_matched.append((cap, asset)) matched_figures.append({ "figure_id": fig_id, "legend_block_id": cap.get("block_id", ""), @@ -1204,8 +1318,12 @@ def build_figure_inventory(structured_blocks: list[dict], page_width: float = 12 }) if asset_bid: used_asset_page_ids.add((asset_page, asset_bid)) - for cap in seq_matched: + for cap, asset in seq_matched: unmatched_legends[:] = [l for l in unmatched_legends if l is not cap] + if int(asset.get("page", 0) or 0) < int(cap.get("page", 0) or 0): + ambiguous_figures[:] = [ + af for af in ambiguous_figures if str(af.get("legend_block_id", "")) != str(cap.get("block_id", "")) + ] # Build unresolved clusters: spatial clusters of unmatched assets on # pages where all candidate legends were rejected (multi-panel figures diff --git a/paperforge/worker/ocr_health.py b/paperforge/worker/ocr_health.py index 417af40f..148ac343 100644 --- a/paperforge/worker/ocr_health.py +++ b/paperforge/worker/ocr_health.py @@ -1,17 +1,64 @@ from __future__ import annotations +import re from pathlib import Path from typing import Any +def _normalize_gap_text(text: str) -> str: + return re.sub(r"\s+", " ", text or "").strip().lower() + + def audit_rendered_text_coverage(*, rendered_markdown: str, pdf_segments: list[str]) -> dict: - missing = [segment for segment in pdf_segments if segment and segment not in rendered_markdown] + normalized_markdown = _normalize_gap_text(rendered_markdown) + missing = [segment for segment in pdf_segments if segment and _normalize_gap_text(segment) not in normalized_markdown] return { "rendered_text_gap_count": len(missing), "rendered_text_gap_examples": missing[:3], } +def _build_page_text_coverage(structured_blocks: list[dict]) -> dict[int, dict[str, Any]]: + from paperforge.worker.ocr_blocks import _summarize_page_text_coverage + + page_texts: dict[int, dict[str, list[str]]] = {} + for block in structured_blocks: + page = int(block.get("page", 0) or 0) + if page <= 0: + continue + if "pdf_text" not in block: + continue + pdf_text = str(block.get("pdf_text") or "").strip() + entry = page_texts.setdefault(page, {"ocr": [], "pdf": []}) + entry["ocr"].append(str(block.get("text") or "").strip()) + entry["pdf"].append(pdf_text) + + coverage: dict[int, dict[str, Any]] = {} + for page, parts in page_texts.items(): + coverage[page] = _summarize_page_text_coverage( + ocr_text=" ".join(t for t in parts["ocr"] if t), + pdf_text=" ".join(t for t in parts["pdf"] if t), + ) + return coverage + + +def _build_text_completeness_summary(structured_blocks: list[dict]) -> dict[str, int]: + from paperforge.worker.ocr_blocks import _classify_region_text_completeness + + summary: dict[str, int] = {} + for block in structured_blocks: + pdf_region_text = str(block.get("pdf_region_text") or "").strip() + if not pdf_region_text: + continue + result = _classify_region_text_completeness( + ocr_text=str(block.get("text") or ""), + pdf_region_text=pdf_region_text, + ) + status = str(result.get("text_completeness_status") or "pdf_unavailable") + summary[status] = summary.get(status, 0) + 1 + return summary + + def _doc_attr(doc_structure: Any, name: str, default=None): if doc_structure is None: return default @@ -66,6 +113,7 @@ def build_ocr_health( table_inventory: dict, doc_structure: Any = None, reader_payload: dict | None = None, + rendered_markdown: str | None = None, ) -> dict[str, Any]: section_heading_count = sum(1 for b in structured_blocks if b.get("role") == "section_heading") abstract_found = any( @@ -188,6 +236,17 @@ def build_ocr_health( held_figure_count=held_figure_count, held_table_count=held_table_count, ) + page_text_coverage = _build_page_text_coverage(structured_blocks) + text_completeness_summary = _build_text_completeness_summary(structured_blocks) + pdf_segments = [ + str(block.get("pdf_region_text") or "").strip() + for block in structured_blocks + if str(block.get("pdf_region_text") or "").strip() + ] + rendered_gap = audit_rendered_text_coverage( + rendered_markdown=rendered_markdown or "", + pdf_segments=pdf_segments, + ) report = { "page_count": page_count, @@ -234,6 +293,10 @@ def build_ocr_health( "anchor_summary": anchor_summary, "zone_summary": zone_summary, "held_counts": held_counts, + "page_text_coverage": page_text_coverage, + "text_completeness_summary": text_completeness_summary, + "rendered_text_gap_count": rendered_gap["rendered_text_gap_count"], + "rendered_text_gap_examples": rendered_gap["rendered_text_gap_examples"], "figure_legend_completeness_total": formal_legend_total, "figure_legend_completeness_accounted": formal_legend_accounted, "figure_legend_completeness_gap_count": formal_legend_gaps, diff --git a/paperforge/worker/ocr_rebuild.py b/paperforge/worker/ocr_rebuild.py index 836d5c9e..9b44da70 100644 --- a/paperforge/worker/ocr_rebuild.py +++ b/paperforge/worker/ocr_rebuild.py @@ -209,6 +209,7 @@ def run_derived_rebuild_for_keys(vault: Path, keys: list[str]) -> dict: table_inventory=table_inventory, doc_structure=doc_structure, reader_payload=reader_payload, + rendered_markdown=markdown, ) health_report["ocr_raw_integrity"] = build_ocr_raw_integrity_health(all_raw_blocks) write_ocr_health(paper_root / "health", health_report) diff --git a/paperforge/worker/ocr_structural_gate.py b/paperforge/worker/ocr_structural_gate.py index c5ad991b..1ef8238b 100644 --- a/paperforge/worker/ocr_structural_gate.py +++ b/paperforge/worker/ocr_structural_gate.py @@ -138,9 +138,19 @@ def resolve_verified_role(block: dict, context: RoleGateContext) -> VerifiedRole return accept_role( "paper_title", seed_role, "source_frontmatter_title_anchor", ["matched source title anchor"] ) + # Zone-based fallback: title-like block on the first-surviving page's + # frontmatter_main_zone with title-appropriate raw_label is accepted + # even without source anchor — handles preproof-drop papers where the + # real title appears on page 2. + raw_label = str(block.get("raw_label") or "") + if block.get("zone") == "frontmatter_main_zone" and raw_label in {"doc_title", "paragraph_title"}: + return accept_role( + "paper_title", seed_role, "frontmatter_zone_title", + [f"title accepted via frontmatter_main_zone + {raw_label} label"], + ) return hold_role(seed_role, "paper title seed lacks source-backed title anchor") # Structural signal: raw_label=doc_title on page 2 → paper_title (title repeat) - # Only accept if confirmed by source anchor; otherwise hold. + # Accept if confirmed by source anchor or by frontmatter_main_zone context. if block.get("raw_label") == "doc_title" and (block.get("page") or 0) == 2: block_id = _artifact_block_id(block, context._duplicate_block_ids) if hasattr(context, "_duplicate_block_ids") else block.get("block_id") if block_id and block_id in context.source_frontmatter_anchor_ids.get("title", set()): @@ -150,6 +160,13 @@ def resolve_verified_role(block: dict, context: RoleGateContext) -> VerifiedRole "doc_title_label_page2_repeat", ["doc_title labeled block on page 2 matches source title anchor"], ) + if block.get("zone") == "frontmatter_main_zone": + return accept_role( + "paper_title", + seed_role, + "doc_title_label_page2_zone", + ["doc_title labeled block on page 2 accepted via frontmatter_main_zone"], + ) return hold_role( seed_role, "page-2 doc_title without source anchor verification", @@ -182,6 +199,13 @@ def resolve_verified_role(block: dict, context: RoleGateContext) -> VerifiedRole return accept_role( "authors", seed_role, "source_frontmatter_authors_anchor", ["matched source authors anchor"] ) + # Zone-based fallback: author block in frontmatter_main_zone on first + # surviving page — handles preproof-drop papers. + if block.get("zone") == "frontmatter_main_zone" and block.get("raw_label") == "text": + return accept_role( + "authors", seed_role, "frontmatter_zone_authors", + ["authors accepted via frontmatter_main_zone + text label"], + ) return hold_role(seed_role, "authors seed lacks source-backed authors anchor") if proposal == "keywords" or seed_role == "keywords": if _artifact_membership_contains(block, set((context.abstract_span or {}).get("keyword_block_ids", []))): @@ -312,6 +336,18 @@ def resolve_verified_role(block: dict, context: RoleGateContext) -> VerifiedRole "accepted_table_caption_artifact", ["table caption matched accepted table artifact"], ) + _marker = str(((block.get("marker_signature") or {}).get("type")) or "") + _zone = str(block.get("zone") or "") + _text = str(block.get("text") or "").strip() + if _marker in {"figure_number", "table_number"} or ( + _zone == "display_zone" and _text.lower().startswith(("fig", "figure", "table")) + ): + return accept_role( + "table_caption", + seed_role, + "text_evidence_table_caption", + ["table caption accepted via text-prefix + marker/zone evidence"], + ) return VerifiedRoleDecision( role="table_caption_candidate", status="CANDIDATE", diff --git a/project/current/ocr-v2-closeout-priority.md b/project/current/ocr-v2-closeout-priority.md index 049f8d3e..7d36a4bd 100644 --- a/project/current/ocr-v2-closeout-priority.md +++ b/project/current/ocr-v2-closeout-priority.md @@ -1,7 +1,7 @@ # OCR-v2 Close-Out Priority > Status: active authoritative readiness queue -> Last updated: 2026-06-18 (Gates 1,3,4 implementation complete) +> Last updated: 2026-06-18 (broader readiness sweep re-green after Gate 2 fallback tightening) > Owner file for next work: `docs/superpowers/plans/2026-06-18-ocr-v2-readiness-master-plan.md` ## Why This File Exists @@ -37,15 +37,15 @@ That pass is no longer the active execution thread. The branch has now moved int | Gate | Status | Notes | |------|--------|-------| | Gate 1: completeness-check layer | **DONE** | Page-level + region-level coverage signals, rendered-gap audit | -| Gate 2: figure ownership generalization | **PARTIAL** | Same-row group-first matching implemented; DW Fig 3 ambiguous match remains (xfail) | +| Gate 2: figure ownership generalization | **DONE** | DW Fig 3 strict regression passes; previous-page fallback narrowed with layout cross-checks; figure suite re-green | | Gate 3: ordering/boundary authority | **DONE** | `_enforce_reference_boundary_from_structure` upstream in normalize path | | Gate 4: layout-coverage formalization | **DONE** | Taxonomy normalized to approved readiness-class set; contract tests enforce named representatives | | Gate 5: unseen-paper blind audit | **NEXT** | Entry criteria defined; not started | ## Next Work -1. Resolve Gate 2 DW Fig 3 remaining ambiguous match (xfail test) -2. Run full verification suite -3. Prepare Gate 5 blind audit entry criteria for next cycle +1. Decide whether to run the full readiness sweep including broader env-gated real-paper coverage and repo-wide lint debt review before Gate 5 +2. Prepare the bounded unseen-paper blind audit sample and execution rules for Gate 5 +3. Keep project truth files aligned if any pre-blind-audit fixes land ## Authoritative Reference This file is the tie-breaker for the active readiness queue. `project/current/ocr-v2-generalization-boundary.md` remains the broader architecture note, and `docs/superpowers/specs/2026-06-18-ocr-v2-readiness-gates-design.md` defines the readiness model the queue is following. diff --git a/project/current/ocr-v2-remaining-issues-2026-06-18.md b/project/current/ocr-v2-remaining-issues-2026-06-18.md index af1abf80..3b83c251 100644 --- a/project/current/ocr-v2-remaining-issues-2026-06-18.md +++ b/project/current/ocr-v2-remaining-issues-2026-06-18.md @@ -6,7 +6,7 @@ ## Active Readiness Residuals 1. **Gate 1 completeness-check layer** -- DONE (page/region/rendered-gap signals implemented) -2. **Gate 2 figure ownership still carries a trust gap** on mixed post-reference and multi-panel pages; DW Fig 3 ambiguous match remains (xfail: `test_dwqqk2yb_figure3_is_fully_owned_not_merely_captured`) +2. **Gate 2 figure ownership** -- DONE on the tracked DW and generic figure-suite coverage after narrowing the previous-page fallback with local layout cross-checks 3. **Gate 3 ordering/boundary authority** -- DONE (`_enforce_reference_boundary_from_structure` upstream) 4. **Gate 4 layout coverage** -- DONE (taxonomy in `audit/coverage_ledger.json`, contract tests enforce named representatives) 5. **Backmatter heading taxonomy remains partially conservative by design**; only promote it if Gate 3 boundary evidence requires it @@ -25,7 +25,7 @@ OCR-v2 is ready for blind audit only when: | # | Issue | Priority | Type | Status | |---|-------|----------|------|--------| | 1 | Completeness check | P1 | Readiness gate | DONE | -| 2 | Figure ownership (DW Fig 3) | P0 | Trust / ownership gap | PARTIAL (xfail) | +| 2 | Figure ownership matcher stability | P0 | Trust / ownership gap | DONE on current tracked suites | | 3 | Ordering authority | P1 | Structural authority gap | DONE | | 4 | Layout coverage ledger + taxonomy | P1 | Test / evidence gap | DONE | | 5 | Backmatter heading taxonomy | P3 | Conservative by design | Deferred | diff --git a/tests/fixtures/ocr_real_papers/8VB9ZVQG/block_trace.csv b/tests/fixtures/ocr_real_papers/8VB9ZVQG/block_trace.csv new file mode 100644 index 00000000..34f52699 --- /dev/null +++ b/tests/fixtures/ocr_real_papers/8VB9ZVQG/block_trace.csv @@ -0,0 +1,96 @@ +page,block_id,raw_label,content_preview,bbox,role,role_confidence,evidence,seed_role,seed_confidence,zone,style_family,marker_type,render_default,index_default +1,0,header,Kwon et al. Molecular Brain (2024) 17:50 https://doi.org/10.1186/s13041-024-01125-2,"[109, 66, 414, 108]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +1,1,header,Molecular Brain,"[912, 65, 1081, 94]",noise,0.9,"[""header label""]",noise,0.9,frontmatter_main_zone,support_like,short_fragment,False,False +1,2,text,MICRO REPORT,"[116, 182, 335, 211]",authors,0.6,"[""page-1 initial-lastname author byline: MICRO REPORT""]",authors,0.6,frontmatter_main_zone,support_like,short_fragment,True,True +1,3,text,Open Access,"[922, 183, 1075, 212]",frontmatter_noise,0.7,"[""frontmatter noise text: Open Access""]",frontmatter_noise,0.7,frontmatter_main_zone,support_like,short_fragment,False,False +1,4,doc_title,"Intraplantar aminoglutethimide, a P450scc inhibitor, reduced the induction of mechanical allodynia in a rat model of thrombus-induced ischemic pain","[107, 246, 995, 453]",paper_title,0.8,"[""page-1 zone title_zone: Intraplantar aminoglutethimide, a P450scc inhibitor, reduced""]",paper_title,0.8,frontmatter_main_zone,support_like,none,True,True +1,5,image,,"[1019, 226, 1077, 283]",media_asset,0.85,"[""media label: image""]",media_asset,0.85,frontmatter_main_zone,support_like,empty,True,True +1,6,text,"Soon-Gu Kwon $ ^{1†} $, Hoon-Seong Choi $ ^{2†} $, Seo-Yeon Yoon $ ^{3} $, Dae-Hyun Roh $ ^{4*} $ $ ^{ID} $ and Jang-Hern Lee $ ^{5*} $","[107, 489, 906, 518]",authors,0.8,"[""page-1 zone author_zone: Soon-Gu Kwon $ ^{1\u2020} $, Hoon-Seong Choi $ ^{2\u2020} $, Seo-Yeon ""]",authors,0.8,frontmatter_main_zone,support_like,none,True,True +1,7,paragraph_title,Abstract,"[121, 570, 209, 593]",abstract_heading,0.95,"[""abstract heading""]",abstract_heading,0.95,frontmatter_main_zone,heading_like,short_fragment,True,True +1,8,abstract,"Neuroactive steroids (NASs) directly affect neuronal excitability. Despite their role in the nervous system is intimately linked to pain control, knowledge is currently limited. This study investigate","[120, 593, 1061, 842]",abstract_body,0.85,"[""abstract label from Paddle OCR""]",abstract_body,0.85,frontmatter_main_zone,support_like,none,True,True +1,9,text,"Keywords Ischemic pain, Neuroactive steroid, Aminoglutethimide, Cytochrome P450 side-chain cleavage, Sigma-1 receptor","[120, 840, 1048, 890]",structured_insert,0.7,"[""keyword-like block: Keywords Ischemic pain, Neuroactive steroid, Aminoglutethimi""]",frontmatter_noise,0.7,frontmatter_main_zone,support_like,none,False,False +1,10,footnote, $ ^{\dagger} $Soon-Gu Kwon and Hoon-Seong Choi contributed equally to this work.,"[109, 971, 513, 1009]",frontmatter_support,0.78,"[""first-surviving-page support text: $ ^{\\dagger} $Soon-Gu Kwon and Hoon-Seong Choi contributed e""]",frontmatter_support,0.78,body_zone,body_like,affiliation_marker,True,True +1,11,paragraph_title,Main text,"[605, 941, 702, 964]",section_heading,0.5,"[""unnumbered paragraph_title on page 1 outside title zone: Main text""]",section_heading,0.5,body_zone,heading_like,short_fragment,True,True +1,12,footnote,"*Correspondence: +Dae-Hyun Roh +dhroh@khu.ac.kr +Jang-Hern Lee +jhl1101@snu.ac.kr + +Neuracle Science Co., Ltd, Seoul 02841, Korea + +Research Animal Resource Center, Korea Institute of Science and Technology","[106, 1008, 582, 1304]",footnote,0.7,"[""footnote label: *Correspondence:\nDae-Hyun Roh\ndhroh@khu.ac.kr\nJang-Hern Lee\n""]",footnote,0.7,body_zone,body_like,none,True,True +1,13,text,"Neuroactive steroids (NASs) are a class of steroids that can modify neuronal excitability in both the central and peripheral nervous systems [1, 2]. The initial rate-limiting step in NAS biosynthesis ","[603, 966, 1083, 1279]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +1,14,footer_image,,"[113, 1341, 331, 1402]",unknown_structural,0.2,"[""unrecognized label 'footer_image'""]",unknown_structural,0.2,body_zone,unknown_like,empty,False,True +1,15,footnote,"© The Author(s) 2024. Open Access This article is licensed under a Creative Commons Attribution 4.0 International License, which permits use, sharing, adaptation, distribution and reproduction in any ","[355, 1344, 1082, 1473]",frontmatter_noise,0.8,"[""page-1 zone journal_furniture_zone: \u00a9 The Author(s) 2024. Open Access This article is licensed u""]",frontmatter_noise,0.8,body_zone,support_like,none,False,False +2,0,header,Kwon et al. Molecular Brain (2024) 17:50,"[109, 66, 428, 87]",noise,0.9,"[""header label""]",noise,0.9,,unknown_like,none,False,False +2,1,number,Page 2 of 4,"[998, 66, 1082, 87]",noise,0.9,"[""page number label""]",noise,0.9,frontmatter_side_zone,support_like,short_fragment,False,False +2,2,figure_title,A,"[112, 165, 138, 192]",figure_inner_text,0.9,"[""panel label / figure inner text: A""]",figure_inner_text,0.9,display_zone,legend_like,panel_label,True,True +2,3,image,,"[112, 165, 633, 406]",media_asset,0.85,"[""media label: image""]",media_asset,0.85,body_zone,body_like,empty,True,True +2,4,figure_title,B,"[633, 166, 655, 191]",figure_inner_text,0.9,"[""panel label / figure inner text: B""]",figure_inner_text,0.9,display_zone,legend_like,panel_label,True,True +2,5,figure_title,C,"[112, 457, 138, 485]",figure_inner_text,0.9,"[""panel label / figure inner text: C""]",figure_inner_text,0.9,display_zone,legend_like,panel_label,True,True +2,6,chart,,"[656, 175, 1057, 453]",media_asset,0.85,"[""media label: chart""]",media_asset,0.85,body_zone,unknown_like,empty,True,True +2,7,chart,,"[132, 486, 559, 733]",media_asset,0.85,"[""media label: chart""]",media_asset,0.85,body_zone,unknown_like,empty,True,True +2,8,figure_title,E,"[113, 739, 135, 766]",figure_inner_text,0.9,"[""panel label / figure inner text: E""]",figure_inner_text,0.9,display_zone,legend_like,panel_label,True,True +2,9,chart,,"[136, 768, 563, 1017]",media_asset,0.85,"[""media label: chart""]",media_asset,0.85,body_zone,unknown_like,empty,True,True +2,10,chart,,"[665, 487, 1004, 706]",media_asset,0.85,"[""media label: chart""]",media_asset,0.85,body_zone,unknown_like,empty,True,True +2,11,figure_title,D,"[631, 458, 658, 484]",figure_inner_text,0.9,"[""panel label / figure inner text: D""]",figure_inner_text,0.9,display_zone,legend_like,panel_label,True,True +2,12,chart,,"[111, 1023, 621, 1271]",media_asset,0.85,"[""media label: chart""]",media_asset,0.85,body_zone,unknown_like,empty,True,True +2,13,figure_title,Fig. 1 (See legend on next page.),"[110, 1292, 332, 1315]",figure_caption,0.92,"[""figure_title label: Fig. 1 (See legend on next page.)""]",figure_caption,0.92,display_zone,legend_like,figure_number,True,True +2,14,figure_title,F,"[631, 739, 653, 766]",figure_inner_text,0.9,"[""panel label / figure inner text: F""]",figure_inner_text,0.9,display_zone,legend_like,panel_label,True,True +2,15,text,"Peripheral ischemia is common causes of pain in the lower extremities [4]. Previously, we developed a peripheral ischemic pain model named the “thrombus-induced ischemic pain (TIIP) model,” which mimi","[106, 1351, 586, 1474]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +2,16,chart,,"[665, 767, 1001, 992]",media_asset,0.85,"[""media label: chart""]",media_asset,0.85,body_zone,unknown_like,empty,True,True +2,17,figure_title,H,"[632, 1026, 658, 1054]",figure_inner_text,0.9,"[""panel label / figure inner text: H""]",figure_inner_text,0.9,display_zone,legend_like,panel_label,True,True +2,18,chart,,"[686, 1025, 1080, 1244]",media_asset,0.85,"[""media label: chart""]",media_asset,0.85,body_zone,unknown_like,empty,True,True +2,19,text,"this model, we focused on the pain mechanism in context of peripheral sensitization and examined related receptors, such as acid-sensing ion channels, ATP receptors, and sigma-1 receptors (Sig-1Rs), w","[602, 1351, 1084, 1474]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +3,0,header,Kwon et al. Molecular Brain (2024) 17:50,"[110, 67, 426, 87]",noise,0.9,"[""header label""]",noise,0.9,,unknown_like,none,False,False +3,1,number,Page 3 of 4,"[998, 67, 1081, 87]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +3,2,text,(See figure on previous page.),"[109, 166, 307, 185]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +3,3,figure_title,Fig. 1 Schematic diagram for surgery of thrombus-induced ischemic pain (TIIP) injury and the effect of intraplantar aminoglutethimide (AMG) and PRE-084 (PRE) on TIIP induced mechanical allodynia. (A) ,"[109, 184, 1082, 547]",figure_caption,0.92,"[""figure_title label: Fig. 1 Schematic diagram for surgery of thrombus-induced isc""]",figure_caption,0.92,display_zone,legend_like,figure_number,True,True +3,4,text,"study, based on the fact that NASs have been reported to control ischemic damage in the nervous system [7], we hypothesized that intraplantar injection of P450scc inhibitor could modulate ischemic pai","[107, 582, 585, 774]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +3,5,text,"Male Sprague–Dawley rats (250–350 g) were anesthetized with 3% isoflurane in a N₂O/O₂ gas mixture, and small incision was made in the femoral triangle of the left hind limb. Femoral artery was careful","[107, 776, 585, 1375]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +3,6,text,The temporal expression level of P450scc on the ipsilateral skin exhibited a gradual increase following ischemia. Immunoblot analysis revealed that P450scc levels peaked on post-operative day 7. Altho,"[107, 1376, 586, 1473]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +3,7,text,,"[602, 582, 1083, 965]",unknown_structural,0.3,"[""short text, uncertain role""]",unknown_structural,0.3,body_zone,body_like,empty,False,True +3,8,text,"To investigate the role of peripheral Sig-1Rs in AMG-induced pain inhibition in induction phase, PRE (10 nmol) was co-administered with AMG (100 nmol) during the induction phase. Co-administration of ","[603, 967, 1083, 1182]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +3,9,text,Skin is one of the main extra-gonadal steroidogenic organs that contains the P450scc enzyme [8]. Previous studies have shown that P450scc expression in skin can be regulated by external irritants [9].,"[602, 1183, 1083, 1450]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +4,0,header,Kwon et al. Molecular Brain (2024) 17:50,"[109, 66, 427, 87]",noise,0.9,"[""header label""]",noise,0.9,,unknown_like,none,False,False +4,1,number,Page 4 of 4,"[998, 67, 1082, 87]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +4,2,text,that provides protection against nerve damage resulting from ischemia.,"[107, 175, 585, 221]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +4,3,text,Sig-1Rs are primary target receptors for NAS such as PREG and dehydroepiandrosterone (DHEA) [10]. We previously reported that peripherally administered PREG sulfate (PREGS) and DHEA sulfate (DHEAS) en,"[108, 223, 586, 798]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,tail_nonref_hold_zone,body_like,none,True,True +4,4,text,"Overall, our study demonstrated that peripheral P450scc level was increased in ischemic damage and intraplantar injection of AMG significantly alleviated mechanical allodynia during the induction of T","[107, 798, 585, 992]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,tail_nonref_hold_zone,body_like,none,True,True +4,5,paragraph_title,Supplementary Information,"[109, 1015, 372, 1037]",backmatter_boundary_candidate,0.5,"[""backmatter boundary candidate: Supplementary Information""]",backmatter_boundary_candidate,0.5,tail_nonref_hold_zone,heading_like,none,True,True +4,6,text,The online version contains supplementary material available at https://doi.org/10.1186/s13041-024-01125-2.,"[109, 1036, 566, 1074]",backmatter_body,0.6,"[""default body_paragraph for text label"", ""tail_nonref_hold_zone excluded from body flow""]",backmatter_body,0.6,tail_nonref_hold_zone,unknown_like,none,True,True +4,7,text,Supplementary Material 1,"[121, 1094, 281, 1114]",backmatter_body,0.6,"[""default body_paragraph for text label"", ""tail_nonref_hold_zone excluded from body flow""]",backmatter_body,0.6,tail_nonref_hold_zone,unknown_like,none,True,True +4,8,text,Supplementary Material 2,"[122, 1123, 282, 1143]",backmatter_body,0.6,"[""default body_paragraph for text label"", ""tail_nonref_hold_zone excluded from body flow""]",backmatter_body,0.6,tail_nonref_hold_zone,unknown_like,none,True,True +4,9,text,Supplementary Material 3,"[122, 1153, 282, 1174]",backmatter_body,0.6,"[""default body_paragraph for text label"", ""tail_nonref_hold_zone excluded from body flow""]",backmatter_body,0.6,tail_nonref_hold_zone,unknown_like,none,True,True +4,10,paragraph_title,Acknowledgements,"[110, 1208, 245, 1223]",backmatter_heading,0.5,"[""backmatter heading on page 4: Acknowledgements""]",backmatter_heading_candidate,0.8,tail_nonref_hold_zone,unknown_like,short_fragment,True,True +4,11,text,Not applicable.,"[110, 1226, 205, 1243]",unknown_structural,0.3,"[""short text, uncertain role""]",unknown_structural,0.3,tail_nonref_hold_zone,unknown_like,short_fragment,False,True +4,12,paragraph_title,Author contributions,"[110, 1261, 253, 1278]",backmatter_heading,0.8,"[""backmatter heading on page 4: Author contributions""]",backmatter_heading_candidate,0.8,tail_nonref_hold_zone,support_like,none,True,True +4,13,text,J.-H. L. and S.-G.K. designed the study. S.-G.K. and H.-S.C. performed experiments. S.-G.K. and S.-Y.Y. analyzed the data and prepared graphs. S.-Y.Y. assisted in methods implementation. D.-H.R. acqui,"[107, 1279, 583, 1370]",backmatter_body,0.6,"[""default body_paragraph for text label"", ""tail_nonref_hold_zone excluded from body flow""]",backmatter_body,0.6,tail_nonref_hold_zone,unknown_like,none,True,True +4,14,paragraph_title,Funding,"[606, 165, 667, 183]",backmatter_heading,0.5,"[""backmatter heading on page 4: Funding""]",backmatter_heading_candidate,0.8,body_zone,body_like,short_fragment,True,True +4,15,text,This research was supported by The National Research Foundation of Korea (NRF) grant funded by the Korea government (MSIT) (No. NRF-2022R1F1A1073652 and RS-2023-00262398).,"[604, 183, 1024, 239]",backmatter_body,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +4,16,paragraph_title,Data availability,"[606, 255, 718, 273]",backmatter_heading,0.5,"[""backmatter heading on page 4: Data availability""]",backmatter_heading_candidate,0.8,body_zone,body_like,short_fragment,True,True +4,17,text,All data supporting the findings are included and are available on request from the corresponding author.,"[604, 273, 1052, 311]",frontmatter_noise,0.6,"[""default body_paragraph for text label"", ""frontmatter_side_zone excluded from body flow""]",frontmatter_noise,0.6,frontmatter_side_zone,support_like,none,False,False +4,18,paragraph_title,Declarations Ethics approval and consent to participate,"[605, 336, 885, 393]",backmatter_heading,0.5,"[""backmatter boundary candidate: Declarations Ethics approval and consent to participate""]",backmatter_boundary_candidate,0.5,body_zone,body_like,none,True,True +4,19,footer,,"[605, 375, 885, 393]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,empty,False,False +4,20,text,"This study was conducted according to the National Institutes of Health Guide for the Care and Use of Laboratory Animals (NIH publication No. 86–23, revised in 1985) and was approved by the Institutio","[604, 393, 1079, 466]",backmatter_body,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +4,21,paragraph_title,Consent for publication,"[606, 483, 764, 501]",subsection_heading,0.6,"[""unnumbered paragraph_title, inferred level subsection_heading: Consent for publication""]",subsection_heading,0.6,body_zone,body_like,none,True,True +4,22,text,Not applicable.,"[606, 503, 703, 520]",unknown_structural,0.3,"[""short text, uncertain role""]",unknown_structural,0.3,body_zone,body_like,short_fragment,False,True +4,23,paragraph_title,Competing interests,"[606, 537, 744, 556]",backmatter_heading,0.5,"[""backmatter heading on page 4: Competing interests""]",backmatter_heading_candidate,0.8,body_zone,body_like,short_fragment,True,True +4,24,text,The authors declare that they have no competing interests.,"[605, 556, 964, 574]",backmatter_body,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +4,25,text,Received: 1 July 2024 / Accepted: 28 July 2024,"[605, 595, 899, 615]",frontmatter_noise,0.6,"[""default body_paragraph for text label"", ""frontmatter_side_zone excluded from body flow""]",frontmatter_noise,0.6,frontmatter_side_zone,support_like,none,False,False +4,26,text,Published online: 02 August 2024,"[605, 618, 847, 639]",frontmatter_noise,0.6,"[""default body_paragraph for text label"", ""frontmatter_side_zone excluded from body flow""]",frontmatter_noise,0.6,frontmatter_side_zone,support_like,none,False,False +4,27,paragraph_title,References,"[606, 687, 684, 706]",reference_heading,0.9,"[""references heading: References""]",reference_heading,0.9,reference_zone,unknown_like,short_fragment,True,True +4,28,reference_content,"1. Baulieu EE, Neurosteroids. A novel function of the brain. Psychoneuroendocrino. 1998;23(8):963–87.","[606, 706, 1069, 741]",reference_item,0.85,"[""reference content label: 1. Baulieu EE, Neurosteroids. A novel function of the brain.""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +4,29,reference_content,"2. Dubrovsky B. Neurosteroids, neuroactive steroids, and symptoms of affective disorders. Pharmacol Biochem Be. 2006;84(4):644–55.","[606, 743, 1073, 778]",reference_item,0.85,"[""reference content label: 2. Dubrovsky B. Neurosteroids, neuroactive steroids, and sym""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +4,30,reference_content,"3. Choi SR, Beitz AJ, Lee JH. Inhibition of cytochrome P450 side-chain cleavage attenuates the development of mechanical Allodynia by reducing spinal D-Serine production in a Murine Model of Neuropath","[606, 779, 1070, 848]",reference_item,0.85,"[""reference content label: 3. Choi SR, Beitz AJ, Lee JH. Inhibition of cytochrome P450 ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +4,31,reference_content,"4. Lang PM, Schober GM, Rolke R, Wagner S, Hilge R, Offenbächer M, et al. Sensory neuropathy and signs of central sensitization in patients with peripheral arterial disease. Pain. 2006;124(1–2):190–20","[605, 851, 1075, 903]",reference_item,0.85,"[""reference content label: 4. Lang PM, Schober GM, Rolke R, Wagner S, Hilge R, Offenb\u00e4c""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +4,32,reference_content,"5. Seo HS, Kim HW, Roh DH, Yoon SY, Kwon YB, Han HJ, et al. A new rat model for thrombus-induced ischemic pain (TIIP); development of bilateral mechanical allodynia. Pain. 2008;139(3):520–32.","[606, 905, 1078, 957]",reference_item,0.85,"[""reference content label: 5. Seo HS, Kim HW, Roh DH, Yoon SY, Kwon YB, Han HJ, et al. ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +4,33,reference_content,"6. Kwon SG, Roh DH, Yoon SY, Choi SR, Choi HS, Moon JY, et al. Role of peripheral sigma-1 receptors in ischaemic pain: potential interactions with ASIC and P2X receptors. Eur J Pain. 2016;20(4):594–60","[606, 959, 1076, 1011]",reference_item,0.85,"[""reference content label: 6. Kwon SG, Roh DH, Yoon SY, Choi SR, Choi HS, Moon JY, et a""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +4,34,reference_content,"7. Borowicz KK, Piskorska B, Banach M, Czuczwar SJ. Neuroprotective actions of neurosteroids. Front Endocrinol (Lausanne). 2011;2:50.","[606, 1013, 1074, 1048]",reference_item,0.85,"[""reference content label: 7. Borowicz KK, Piskorska B, Banach M, Czuczwar SJ. Neuropro""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +4,35,reference_content,"8. Slominski AT, Manna PR, Tuckey RC. On the role of skin in the regulation of local and systemic steroidogenic activities. Steroids. 2015;103:72–88.","[607, 1049, 1061, 1084]",reference_item,0.85,"[""reference content label: 8. Slominski AT, Manna PR, Tuckey RC. On the role of skin in""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +4,36,reference_content,"9. Shimada-Omori R, Yamasaki K, Koike S, Yamauchi T, Aiba S. TLR3 augments glucocorticoid-synthetic enzymes expression in epidermal keratinocytes; implications of glucocorticoid metabolism in rosacea ","[608, 1086, 1067, 1154]",reference_item,0.85,"[""reference content label: 9. Shimada-Omori R, Yamasaki K, Koike S, Yamauchi T, Aiba S.""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +4,37,reference_content,"10. Maurice T. Neurosteroids and $ \sigma $ receptors, biochemical and behavioral relevance. Pharmacopsychiatry. 2004;37:S171–82.","[608, 1157, 1038, 1192]",reference_item,0.85,"[""reference content label: 10. Maurice T. Neurosteroids and $ \\sigma $ receptors, bioc""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +4,38,reference_content,"11. Kwon SG, Yoon SY, Roh DH, Choi SR, Choi HS, Moon JY, et al. Peripheral neurosteroids enhance P2X receptor-induced mechanical allodynia via a sigma-1 receptor-mediated mechanism. Brain Res Bull. 20","[608, 1194, 1053, 1247]",reference_item,0.85,"[""reference content label: 11. Kwon SG, Yoon SY, Roh DH, Choi SR, Choi HS, Moon JY, et ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +4,39,paragraph_title,Publisher's Note,"[606, 1279, 761, 1299]",backmatter_heading,0.5,"[""backmatter heading on page 4: Publisher's Note""]",backmatter_heading_candidate,0.8,tail_nonref_hold_zone,support_like,short_fragment,True,True +4,40,text,Springer Nature remains neutral with regard to jurisdictional claims in published maps and institutional affiliations.,"[605, 1300, 1026, 1339]",backmatter_body,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,tail_nonref_hold_zone,unknown_like,none,True,True diff --git a/tests/fixtures/ocr_real_papers/DWQQK2YB/block_trace.csv b/tests/fixtures/ocr_real_papers/DWQQK2YB/block_trace.csv index c33d6340..e26352d7 100644 --- a/tests/fixtures/ocr_real_papers/DWQQK2YB/block_trace.csv +++ b/tests/fixtures/ocr_real_papers/DWQQK2YB/block_trace.csv @@ -1,13 +1,13 @@ page,block_id,raw_label,content_preview,bbox,role,role_confidence,evidence,seed_role,seed_confidence,zone,style_family,marker_type,render_default,index_default -2,0,header,Journal Pre-proof,"[502, 1, 832, 40]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p2 y=1/1584""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False -2,1,text,TITLE,"[76, 101, 146, 128]",unknown_structural,0.3,"[""short text, uncertain role""]",unknown_structural,0.3,frontmatter_side_zone,support_like,short_fragment,False,True +2,0,header,Journal Pre-proof,"[502, 1, 832, 40]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p2 y=1/1584"", ""page_1_preproof_cover_dropped_upstream""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +2,1,text,TITLE,"[76, 101, 146, 128]",unknown_structural,0.3,"[""short text, uncertain role""]",unknown_structural,0.3,frontmatter_main_zone,support_like,short_fragment,False,True 2,2,doc_title,"Magnetoresponsive Stem Cell Spheroid-based Cartilage Recovery -Platform Utilizing Electromagnetic Fields","[74, 155, 1028, 268]",unknown_structural,0.2,"[""unrecognized label 'doc_title'""]",unknown_structural,0.2,body_zone,body_like,none,False,True -2,3,text,"Ami Yooa, †, Gwangjun Goa, b, †, Kim Tien Nguyena, b, Kyungmin Leea, b, Hyun-Ki Mina, Byungjeon Kanga","[74, 355, 1026, 445]",frontmatter_support,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True -2,4,text,"a. Korea Institute of Medical Microrobotics, 43-26 Cheomdangwagi-ro, Buk-gu, Gwangju, 61011, Korea","[82, 535, 1028, 621]",frontmatter_support,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True -2,5,text,"b. School of Mechanical Engineering, Chonnam National University, 77 Yongbong-ro, Buk-gu, Gwangju, 61186, Korea","[81, 645, 1028, 732]",frontmatter_support,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True -2,6,text,These authors contributed equally to this work.,"[75, 811, 548, 843]",frontmatter_noise,0.6,"[""default body_paragraph for text label"", ""frontmatter_side_zone excluded from body flow""]",frontmatter_noise,0.6,frontmatter_side_zone,support_like,none,False,False -2,7,text,"*Corresponding author: judyvet@jnu.ac.kr (J.H.), jop@jnu.ac.kr (J.-O.P.), and eunpyochoi@jnu.ac.kr (E.C.)","[74, 864, 1032, 953]",frontmatter_noise,0.6,"[""default body_paragraph for text label"", ""frontmatter_side_zone excluded from body flow""]",frontmatter_noise,0.6,frontmatter_side_zone,support_like,none,False,False +Platform Utilizing Electromagnetic Fields","[74, 155, 1028, 268]",paper_title,0.8,"[""page-1 zone title_zone: Magnetoresponsive Stem Cell Spheroid-based Cartilage Recover""]",paper_title,0.8,frontmatter_main_zone,support_like,none,True,True +2,3,text,"Ami Yooa, †, Gwangjun Goa, b, †, Kim Tien Nguyena, b, Kyungmin Leea, b, Hyun-Ki Mina, Byungjeon Kanga","[74, 355, 1026, 445]",authors,0.8,"[""page-1 zone author_zone: Ami Yooa, \u2020, Gwangjun Goa, bNameChemicalAdvantagesLimitationsReferencesAlginateNameChemicalAdvantagesLimitationsReferencesPolyethylene glycol (PEG)NameChemicalAdvantagesLimitationsReferencesHyaluronic acid methacrylate (HAMA)StudyYearStimulation protocolElectric Field StrengthVoltageFrequencyCurrent / Current densityWaveformTissue type ,"[94, 100, 1396, 987]",media_asset,0.85,"[""media label: table""]",media_asset,0.85,body_zone,body_like,none,True,True +7,2,vision_footnote,"* MSCs, mesenchymal stem cells; ADSCs, adipose-derived stem cells; BMMSCs, bone marrow-derived stromal cells; DC, direct current; NR, not reported; NA, not applicable; PW, pulse width; IG, interphase ","[106, 997, 1301, 1017]",footnote,0.7,"[""vision_footnote label: * MSCs, mesenchymal stem cells; ADSCs, adipose-derived stem ""]",footnote,0.7,body_zone,body_like,none,True,True +7,3,aside_text,"J. Zimmermann, A.R. Farooqi and U. van Rienen","[1402, 76, 1419, 342]",unknown_structural,0.2,"[""unrecognized label 'aside_text'""]",unknown_structural,0.2,body_zone,unknown_like,none,False,True +7,4,aside_text,Heliyon 10 (2024) e38112,"[1401, 863, 1421, 1016]",unknown_structural,0.2,"[""unrecognized label 'aside_text'""]",unknown_structural,0.2,body_zone,unknown_like,none,False,True +8,0,figure_title,"Table 2 +Electrical stimulation studies of articular cartilage using capacitive coupling.","[107, 204, 548, 240]",table_caption,0.9,"[""table prefix matched: Table 2\nElectrical stimulation studies of articular cartilag""]",table_caption,0.9,display_zone,table_caption_like,table_number,True,True +8,1,table,
StudyYearStimulation protocolElectric field StrengthVoltageFrequencyCurrent / Current densityWaveformTissue type ,"[106, 234, 1384, 867]",media_asset,0.85,"[""media label: table""]",media_asset,0.85,body_zone,body_like,none,True,True +8,2,number,∞,"[66, 538, 83, 552]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,unknown_like,short_fragment,False,False +8,3,vision_footnote,"MSCs, mesenchymal stem cells; ADSCs, adipose-derived stem cells; DC, direct current; NR, not reported; PW, pulse width; CPW, counterpulse width.","[107, 869, 966, 889]",footnote,0.7,"[""vision_footnote label: MSCs, mesenchymal stem cells; ADSCs, adipose-derived stem ce""]",footnote,0.7,body_zone,body_like,none,True,True +8,4,aside_text,"J. Zimmermann, A.R. Farooqi and U. van Rienen","[1400, 73, 1419, 342]",unknown_structural,0.2,"[""unrecognized label 'aside_text'""]",unknown_structural,0.2,body_zone,unknown_like,none,False,True +8,5,aside_text,Heliyon 10 (2024) e38112,"[1400, 864, 1422, 1016]",unknown_structural,0.2,"[""unrecognized label 'aside_text'""]",unknown_structural,0.2,body_zone,unknown_like,none,False,True +9,0,header,"J. Zimmermann, A.R. Farooqi and U. van Rienen","[74, 67, 344, 83]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +9,1,header,Heliyon 10 (2024) e38112,"[864, 67, 1016, 84]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +9,2,image,,"[166, 99, 547, 382]",media_asset,0.85,"[""media label: image""]",media_asset,0.85,body_zone,unknown_like,empty,True,True +9,3,figure_title,B,"[584, 105, 610, 135]",figure_inner_text,0.9,"[""panel label / figure inner text: B""]",figure_inner_text,0.9,display_zone,legend_like,panel_label,True,True +9,4,image,,"[583, 140, 922, 370]",media_asset,0.85,"[""media label: image""]",media_asset,0.85,body_zone,unknown_like,empty,True,True +9,5,figure_title,"Fig. 3. Summary of electrical stimulation techniques and associated equivalent circuit models. We report the range of estimated stimulation field strengths, stimulation amplitudes, frequencies and wav","[73, 403, 1015, 543]",figure_caption,0.92,"[""figure_title label: Fig. 3. Summary of electrical stimulation techniques and ass""]",figure_caption,0.92,display_zone,legend_like,figure_number,True,True +9,6,text,"be used as an indicator. As an example of this, the transition from capacitive ( $ -80^\circ $) to diffusive ( $ -45^\circ $) characteristics was noticed for a platinum electrode in saline solution th","[72, 569, 1013, 609]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +9,7,text,"The measured impedance is described by equivalent circuit models, usually consisting of linear elements only. For example, a constant-phase element is used to describe the electrode-tissue interface. ","[72, 611, 1014, 818]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +9,8,text,The electrical stimulation approaches can be linked to electrochemical characterisation methods. Direct current stimulation at a fixed voltage results in the generation of a time-dependent current. In,"[72, 819, 1015, 943]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +9,9,text,Alternating current stimulation using sine or periodic waves resembles measuring the impedance if both voltage and current are recorded. Probing the impedance over an extensive frequency range is call,"[71, 945, 1016, 1131]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +9,10,text,"In short, a general expected equivalent circuit can be formulated for each stimulation type. This permits the establishment of a corresponding electrochemical measurement approach for each stimulation","[73, 1132, 1014, 1195]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +9,11,paragraph_title,3.5. Towards reliable and controllable electrical stimulation,"[73, 1214, 483, 1235]",subsection_heading,0.85,"[""paragraph_title label with numbering: 3.5. Towards reliable and controllable electrical stimulatio""]",subsection_heading,0.85,body_zone,body_like,heading_numbered,True,True +9,12,text,The comparison of the existing stimulation approaches does not permit to identify a clear formulation of the ‘ideal’ electrical stimulation of cartilage (cf. Tables 1 and 2) despite reports of benefic,"[72, 1255, 1016, 1381]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +9,13,number,9,"[538, 1404, 550, 1418]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +10,0,header,"J. Zimmermann, A.R. Farooqi and U. van Rienen","[74, 67, 344, 83]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +10,1,header,Heliyon 10 (2024) e38112,"[864, 67, 1016, 83]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +10,2,text,"A key ingredient is the impedance spectroscopy of the stimulation system. Ideally, it is conducted with a sample having known properties (e.g., an electrolyte solution) and on the actual sample multip","[72, 104, 1016, 333]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +10,3,text,"An open question is how to numerically model the electrode-tissue interface layer. One possibility is to assume a voltage divider, in which the interface impedance is described as a lumped element [86","[71, 335, 1016, 647]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +10,4,text,"In our opinion, the lack of current and voltage recording data (compare Tables 1 and 2) currently marks the biggest problem in the understanding and retrospectively analysing of the electrical stimula","[71, 648, 1017, 1045]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +10,5,text,"The cellular dielectric properties cover a wide range [144], and their influence on the modelling result needs to be carefully tested [109]. In future research, optical methods combined with impedance","[71, 1046, 1015, 1150]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +10,6,text,"With this information, more advanced biophysical models can be developed. In the following section, some potential biophysical interactions are introduced. In addition to the biophysical modelling, sa","[72, 1151, 1014, 1234]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +10,7,paragraph_title,4. Biophysical theories of the interaction of electric fields and cells,"[73, 1254, 594, 1274]",section_heading,0.85,"[""paragraph_title label with numbering: 4. Biophysical theories of the interaction of electric field""]",section_heading,0.85,body_zone,reference_like,reference_numeric_dot,True,True +10,8,text,"Various studies have hypothesised that electric fields can stimulate non-excitable cells, e.g. chondrocytes, to differentiate, proliferate or produce chondrogenic proteins. Still, it is unclear which ","[72, 1297, 1017, 1381]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +10,9,number,10,"[535, 1404, 554, 1418]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +11,0,header,"J. Zimmermann, A.R. Farooqi and U. van Rienen","[74, 67, 343, 83]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +11,1,header,Heliyon 10 (2024) e38112,"[864, 67, 1016, 83]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +11,2,figure_title,A,"[280, 96, 305, 122]",figure_inner_text,0.9,"[""panel label / figure inner text: A""]",figure_inner_text,0.9,display_zone,legend_like,panel_label,True,True +11,3,figure_title,Numerical model,"[470, 105, 613, 125]",figure_caption,0.85,"[""figure_title label: Numerical model""]",figure_caption,0.85,body_zone,unknown_like,short_fragment,True,True +11,4,image,,"[307, 134, 442, 268]",media_asset,0.85,"[""media label: image""]",media_asset,0.85,body_zone,unknown_like,empty,True,True +11,5,figure_title,Equivalent circuit,"[641, 105, 786, 126]",figure_caption,0.85,"[""figure_title label: Equivalent circuit""]",figure_caption,0.85,body_zone,unknown_like,short_fragment,True,True +11,6,image,,"[475, 136, 614, 254]",media_asset,0.85,"[""media label: image""]",media_asset,0.85,body_zone,unknown_like,empty,True,True +11,7,image,,"[634, 153, 792, 246]",media_asset,0.85,"[""media label: image""]",media_asset,0.85,body_zone,unknown_like,empty,True,True +11,8,figure_title,B,"[279, 282, 301, 306]",figure_inner_text,0.9,"[""panel label / figure inner text: B""]",figure_inner_text,0.9,display_zone,legend_like,panel_label,True,True +11,9,image,,"[290, 288, 405, 450]",media_asset,0.85,"[""media label: image""]",media_asset,0.85,body_zone,unknown_like,empty,True,True +11,10,figure_title,C,"[540, 282, 563, 307]",figure_inner_text,0.9,"[""panel label / figure inner text: C""]",figure_inner_text,0.9,display_zone,legend_like,panel_label,True,True +11,11,image,,"[416, 312, 519, 450]",media_asset,0.85,"[""media label: image""]",media_asset,0.85,body_zone,unknown_like,empty,True,True +11,12,image,,"[526, 311, 661, 448]",media_asset,0.85,"[""media label: image""]",media_asset,0.85,body_zone,unknown_like,empty,True,True +11,13,image,,"[676, 309, 810, 449]",media_asset,0.85,"[""media label: image""]",media_asset,0.85,body_zone,unknown_like,empty,True,True +11,14,figure_title,D,"[279, 461, 303, 485]",figure_inner_text,0.9,"[""panel label / figure inner text: D""]",figure_inner_text,0.9,display_zone,legend_like,panel_label,True,True +11,15,figure_title,Integrate cell model,"[446, 464, 629, 486]",figure_caption_candidate,0.85,"[""figure_title label: Integrate cell model""]",figure_caption,0.85,body_zone,unknown_like,none,False,False +11,16,image,,"[310, 475, 796, 603]",media_asset,0.85,"[""media label: image""]",media_asset,0.85,body_zone,unknown_like,empty,True,True +11,17,figure_title,E,"[279, 597, 300, 621]",figure_inner_text,0.9,"[""panel label / figure inner text: E""]",figure_inner_text,0.9,display_zone,legend_like,panel_label,True,True +11,18,image,,"[358, 630, 719, 764]",media_asset,0.85,"[""media label: image""]",media_asset,0.85,body_zone,unknown_like,empty,True,True +11,19,figure_title,"Fig. 4. Future vision of cartilage tissue engineering experiments: Experiments are conducted alongside numerical simulations (panel A, adapted from [72], it is under CC-BY licence (CC BY 4.0)). A nume","[72, 786, 1017, 924]",figure_caption,0.92,"[""figure_title label: Fig. 4. Future vision of cartilage tissue engineering experi""]",figure_caption,0.92,display_zone,legend_like,figure_number,True,True +11,20,paragraph_title,4.1. Induced transmembrane potential,"[75, 957, 339, 976]",subsection_heading,0.85,"[""paragraph_title label with numbering: 4.1. Induced transmembrane potential""]",subsection_heading,0.85,body_zone,body_like,heading_numbered,True,True +11,21,text,"The cell membrane is a lipid bilayer having a thickness of around 7 nm [133], which separates the cell interior (the cytoplasm) from the extracellular medium. Each cell type has a characteristic resti","[73, 1002, 1013, 1064]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +11,22,text,"Compared to eukaryotic cells, which have a resting transmembrane potential in the range of $ -70 $ mV to $ -40 $ mV [147], chondrocytes have an increased resting potential of $ -46 $ mV to $ -11 $","[73, 1065, 1014, 1168]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +11,23,text,"Voltage-dependent calcium channels are essential in forming and maintaining healthy cartilage [150]. However, their reaction mechanism to external electrical stimuli is unclear [21]. Electrical stimul","[73, 1169, 1014, 1296]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +11,24,text,Xu et al. have reported that blockers of voltage-dependent calcium channels inhibit entirely the electrical stimulation effects $ [103] $. No estimate of the induced transmembrane potential in adult ,"[72, 1297, 1013, 1381]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +11,25,number,11,"[534, 1404, 553, 1418]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +12,0,header,"J. Zimmermann, A.R. Farooqi and U. van Rienen","[74, 67, 344, 83]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +12,1,header,Heliyon 10 (2024) e38112,"[864, 67, 1016, 83]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +12,2,text,"When even higher membrane potentials are induced (hundreds of mV), it comes to electropemeabilisation [147]. The membrane becomes porous due to a strong electric field inside the very thin membrane. T","[72, 103, 1014, 165]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +12,3,text,"One can estimate the lower effective range of the induced transmembrane potential by comparing the drift current to the random diffusion current. The applied electric field causes the drift current, w","[72, 167, 1014, 293]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +12,4,text,"To summarize, the aforementioned observations indicate that very large induced transmembrane potentials greater than 100 mV should be avoided in cartilage tissue engineering to not destroy the membran","[72, 294, 1014, 417]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +12,5,text,"There exist analytical [157–159] as well as numerical techniques [160] to estimate the transmembrane potential [109,145]. An experimental validation of the estimated values has been presented in [160,","[72, 420, 1016, 610]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +12,6,paragraph_title,4.2. Electromechanical interaction,"[74, 634, 313, 653]",subsection_heading,0.85,"[""paragraph_title label with numbering: 4.2. Electromechanical interaction""]",subsection_heading,0.85,body_zone,body_like,heading_numbered,True,True +12,7,text,"The electromechanical interaction of electrical stimulation can occur at the cellular scale, the membrane scale, or even the protein scale.","[72, 679, 1014, 719]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +12,8,paragraph_title,4.2.1. Cell migration: dielectrophoresis and galvanotaxis,"[72, 745, 461, 764]",subsection_heading,0.85,"[""paragraph_title label with numbering: 4.2.1. Cell migration: dielectrophoresis and galvanotaxis""]",subsection_heading,0.85,body_zone,body_like,heading_numbered,True,True +12,9,text,"Cell migration due to an external electric field is referred to as electrotaxis or galvanotaxis (direct current fields) and dielectrophoresis (alternating current fields) [162–164]. Moreover, cells ma","[72, 766, 1016, 849]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +12,10,text,"In electrotaxis, the electric field functions as a compass for the cells [162]. The electric field does not control the cells in a defined direction compared to electrophoresis. Instead, electrotaxis ","[72, 851, 1016, 1018]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +12,11,text,"Cells can also migrate due to dielectrophoresis upon alternating current electrical stimulation. In contrast to galvanotaxis, dielectrophoresis is explained by classical electrodynamics [163]. The for","[72, 1020, 1016, 1188]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +12,12,paragraph_title,4.2.2. Electro-deformation,"[74, 1211, 262, 1230]",subsection_heading,0.85,"[""paragraph_title label with numbering: 4.2.2. Electro-deformation""]",subsection_heading,0.85,body_zone,body_like,heading_numbered,True,True +12,13,text,"Even though homogeneous electric fields can deform the spherical cells, they cannot induce migration of these cells [166,167]. It has been reported that MSCs embedded in hydrogels stimulated by the ca","[72, 1232, 1015, 1381]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +12,14,number,12,"[535, 1404, 554, 1418]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +13,0,header,"J. Zimmermann, A.R. Farooqi and U. van Rienen","[74, 67, 344, 83]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +13,1,header,Heliyon 10 (2024) e38112,"[864, 66, 1016, 83]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +13,2,text,"To explain the electro-deformation phenomenon of cells, giant vesicles (cell-like constructs usually having no nucleus) have been studied $ [169] $. The morphological changes in alternating current f","[72, 103, 1014, 252]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +13,3,paragraph_title,4.2.3. Interaction with membrane constituents,"[73, 275, 393, 293]",subsection_heading,0.85,"[""paragraph_title label with numbering: 4.2.3. Interaction with membrane constituents""]",subsection_heading,0.85,body_zone,body_like,heading_numbered,True,True +13,4,text,"The effect of oscillating electric fields could be related to electroconformational coupling, which has been explored since the late 1980s [175]. However, the effect has not been visually observed but","[72, 295, 1013, 421]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +13,5,text,"The biological effect of the electroconformational coupling has been linked to H⁺-ATPase membrane pumps [176], also affected by the membrane potential [182]. Sodium potassium pumps (Na⁺/K⁺-ATPase) are","[73, 422, 1013, 485]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +13,6,text,"Another open research topic is the motility of membrane constituents. For example, lipid rafts have been shown to cluster upon electrical stimulation in fibroblasts [185]. At extremely low frequencies","[72, 486, 1015, 589]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +13,7,text,"Moreover, chondrocytes also express Piezo channels [188,189] or TRPV4 [190] mechanosensitive membrane channels. The mechanism of interaction of the membrane and its constituents with electrical stimul","[71, 590, 1017, 803]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +13,8,paragraph_title,5. Summary and outlook,"[74, 824, 272, 844]",section_heading,0.85,"[""paragraph_title label with numbering: 5. Summary and outlook""]",section_heading,0.85,body_zone,reference_like,reference_numeric_dot,True,True +13,9,text,"We discussed electrical stimulation as a biophysical intervention for the repair and regeneration of cartilage. Despite decades of research, it appears that no reliable electrical stimulation protocol","[71, 869, 1016, 1080]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +13,10,text,We believe that improved reporting standards focusing on technical replicability combined with continuous monitoring of stimulation parameters will eventually enable a more effective determination of ,"[71, 1080, 1016, 1228]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +13,11,paragraph_title,CRediT authorship contribution statement,"[74, 1251, 404, 1272]",backmatter_heading,0.8,"[""backmatter heading on page 13: CRediT authorship contribution statement""]",backmatter_heading_candidate,0.8,body_zone,body_like,none,True,True +13,12,text,"Julius Zimmermann: Writing – review & editing, Writing – original draft, Visualization, Validation, Software, Resources, Methodology, Investigation, Formal analysis, Data curation, Conceptualization. ","[72, 1296, 1014, 1381]",backmatter_body,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +13,13,number,13,"[535, 1404, 554, 1418]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +14,0,header,"J. Zimmermann, A.R. Farooqi and U. van Rienen","[75, 67, 344, 84]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +14,1,header,Heliyon 10 (2024) e38112,"[864, 66, 1016, 84]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +14,2,text,The authors declare that they have no known competing financial interests or personal relationships that could have appeared to uence the work reported in this paper.,"[102, 146, 1020, 188]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +14,3,text,The authors are grateful to Rainer Bader for pointing out the need for research into the appropriate modelling of electrical stimulation of cartilage tissue. We thank Simone Krüger and Nils Arbeiter f,"[72, 252, 1014, 316]",backmatter_body,0.6,"[""default body_paragraph for text label"", ""promoted in tail spread from body_paragraph""]",body_paragraph,0.6,body_zone,body_like,none,True,True +14,4,paragraph_title,References,"[74, 337, 164, 357]",reference_heading,0.9,"[""references heading: References""]",reference_heading,0.9,reference_zone,unknown_like,short_fragment,True,True +14,5,reference_content,"[1] M. Sha'ban, Histology and biomechanics of cartilage, in: Cartilage Tissue and Knee Joint Biomechanics, Elsevier, 2024, pp. 25–35.","[90, 379, 855, 395]",reference_item,0.85,"[""reference content label: [1] M. Sha'ban, Histology and biomechanics of cartilage, in:""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,6,reference_content,"[2] A.J.S. Fox, A. Bedi, S.A. Rodeo, The basic science of articular cartilage: structure, composition, and function, Sports Health 1 (6) (2009) 461–468, https://doi.org/10.1177/1941738109350438.","[90, 396, 1013, 429]",reference_item,0.85,"[""reference content label: [2] A.J.S. Fox, A. Bedi, S.A. Rodeo, The basic science of ar""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,7,reference_content,"[3] A.R. Armiento, M. Alini, M.J. Stoddart, Articular fibrocartilage - why does hyaline cartilage fail to repair?, Adv. Drug Deliv. Rev. 146 (2019) 289–305, https://doi.org/10.1016/j.addr.2018.12.015.","[90, 431, 1012, 463]",reference_item,0.85,"[""reference content label: [3] A.R. Armiento, M. Alini, M.J. Stoddart, Articular fibroc""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,8,reference_content,"[4] H. Kwon, W.E. Brown, C.A. Lee, D. Wang, N. Paschos, J.C. Hu, K.A. Athanasiou, Surgical and tissue engineering strategies for articular cartilage and meniscus repair, Nat. Rev. Rheumatol. 15 (9) (2","[91, 466, 1013, 498]",reference_item,0.85,"[""reference content label: [4] H. Kwon, W.E. Brown, C.A. Lee, D. Wang, N. Paschos, J.C.""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,9,reference_content,"[5] M. Hiligsmann, C. Cooper, N. Arden, M. Boers, J.C. Branco, M. Luisa Brandi, O. Bruyère, F. Guillemin, M.C. Hochberg, D.J. Hunter, J.A. Kanis, T.K. Kvien, A. Laslop, J.P. Pelletier, D. Pinto, S. Re","[90, 500, 1012, 567]",reference_item,0.85,"[""reference content label: [5] M. Hiligsmann, C. Cooper, N. Arden, M. Boers, J.C. Branc""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,10,reference_content,"[6] D.J. Huey, J.C. Hu, K.A. Athanasiou, Unlike bone, cartilage regeneration remains elusive, Science 338 (6109) (2012) 917–921, https://doi.org/10.1126/science.1222454.","[90, 569, 1012, 600]",reference_item,0.85,"[""reference content label: [6] D.J. Huey, J.C. Hu, K.A. Athanasiou, Unlike bone, cartil""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,11,reference_content,"[7] A.R. Farooqi, R. Bader, U. van Rienen, Numerical study on electromechanics in cartilage tissue with respect to its electrical properties, Tissue Eng., Part B, Rev. 25 (2) (2019) 152–166, https://d","[90, 603, 1012, 636]",reference_item,0.85,"[""reference content label: [7] A.R. Farooqi, R. Bader, U. van Rienen, Numerical study o""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,12,reference_content,"[8] Y.D. Yalcin, R. Luttge, Electrical monitoring approaches in 3-dimensional cell culture systems: toward label-free, high spatiotemporal resolution, and high-content data collection in vitro, Organs","[89, 638, 1012, 671]",reference_item,0.85,"[""reference content label: [8] Y.D. Yalcin, R. Luttge, Electrical monitoring approaches""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,13,reference_content,"[9] E.A. Makris, A.H. Gomoll, K.N. Malizos, J.C. Hu, K.A. Athanasiou, Repair and tissue engineering techniques for articular cartilage, Nat. Rev. Rheumatol. 11(2015) 21–34, https://doi.org/10.1038/nrr","[89, 672, 1011, 705]",reference_item,0.85,"[""reference content label: [9] E.A. Makris, A.H. Gomoll, K.N. Malizos, J.C. Hu, K.A. At""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,14,reference_content,"[10] T. Hoffman, A. Khademhosseini, R. Langer, Chasing the paradigm: clinical translation of 25 years of tissue engineering, Tissue Eng., Part A 25 (2019) 679–687, https://doi.org/10.1089/ten.tea.2019","[83, 707, 1012, 739]",reference_item,0.85,"[""reference content label: [10] T. Hoffman, A. Khademhosseini, R. Langer, Chasing the p""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,15,reference_content,"[11] J. Gille, P. Behrens, A.P. Schulz, R. Oheim, B. Kienast, Matrix-associated autologous chondrocyte implantation: a clinical follow-up at 15 years, Cartilage 7 (4) (2016) 309–315, https://doi.org/1","[83, 741, 1012, 773]",reference_item,0.85,"[""reference content label: [11] J. Gille, P. Behrens, A.P. Schulz, R. Oheim, B. Kienast""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,16,reference_content,"[12] P. Angele, P. Niemeyer, M. Steinwachs, G. Filardo, A.H. Gomoll, E. Kon, J. Zellner, H. Madry, Chondral and osteochondral operative treatment in early osteoarthritis, Knee Surg. Sports Traumatol. ","[83, 776, 1012, 809]",reference_item,0.85,"[""reference content label: [12] P. Angele, P. Niemeyer, M. Steinwachs, G. Filardo, A.H.""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,17,reference_content,"[13] L. de Girolamo, E. Kon, G. Filardo, A.G. Marmotti, F. Soler, G.M. Peretti, F. Vannini, H. Madry, S. Chubinskaya, Regenerative approaches for the treatment of early OA, Knee Surg. Sports Traumatol","[83, 811, 1013, 843]",reference_item,0.85,"[""reference content label: [13] L. de Girolamo, E. Kon, G. Filardo, A.G. Marmotti, F. S""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,18,reference_content,"[14] M.M. Caron, P.J. Emans, M.M. Coolsen, L. Voss, D.A. Surtel, A. Cremers, L.W. van Rhijn, T.J. Welting, Redifferentiation of dedifferentiated human articular chondrocytes: comparison of 2D and 3D c","[83, 846, 1012, 878]",reference_item,0.85,"[""reference content label: [14] M.M. Caron, P.J. Emans, M.M. Coolsen, L. Voss, D.A. Sur""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,19,reference_content,"[15] S. Krueger, S. Achilles, J. Zimmermann, T. Tischer, R. Bader, A. Jonitz-Heincke, Re-differentiation capacity of human chondrocytes in vitro following electrical stimulation with capacitively coup","[83, 880, 1012, 912]",reference_item,0.85,"[""reference content label: [15] S. Krueger, S. Achilles, J. Zimmermann, T. Tischer, R. ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,20,reference_content,"[16] A.R. Armiento, M.J. Stoddart, M. Alini, D. Eglin, Biomaterials for articular cartilage tissue engineering: learning from biology, Acta Biomater. 65 (2018) 1–20, https://doi.org/10.1016/j.actbio.2","[83, 915, 1012, 947]",reference_item,0.85,"[""reference content label: [16] A.R. Armiento, M.J. Stoddart, M. Alini, D. Eglin, Bioma""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,21,reference_content,"[17] E.B. Hunziker, K. Lippuner, M.J. Keel, N. Shintani, An educational review of cartilage repair: precepts & practice - myths & misconceptions - progress & prospects, Osteoarthr. Cartil. 23 (3) (201","[81, 949, 1013, 981]",reference_item,0.85,"[""reference content label: [17] E.B. Hunziker, K. Lippuner, M.J. Keel, N. Shintani, An ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,22,reference_content,"[18] J.L. Drury, D.J. Mooney, Hydrogels for tissue engineering: scaffold design variables and applications, Biomaterials 24 (24) (2003) 4337–4351, https://doi.org/10.1016/S0142-9612(03)00340-5.","[83, 984, 1012, 1015]",reference_item,0.85,"[""reference content label: [18] J.L. Drury, D.J. Mooney, Hydrogels for tissue engineeri""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,23,reference_content,"[19] T. Distler, A.R. Boccaccini, 3D printing of electrically conductive hydrogels for tissue engineering and biosensors – a review, Acta Biomater. 101 (2020) 1–13, https://doi.org/10.1016/j.actbio.20","[83, 1017, 1012, 1050]",reference_item,0.85,"[""reference content label: [19] T. Distler, A.R. Boccaccini, 3D printing of electricall""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,24,reference_content,"[20] E.V. Medvedeva, E.A. Grebenik, S.N. Gornostaeva, V.I. Telpuhov, A.V. Lychagin, P.S. Timashev, A.S. Chagin, Repair of damaged articular cartilage: current approaches and future directions, Int. J.","[83, 1052, 1012, 1086]",reference_item,0.85,"[""reference content label: [20] E.V. Medvedeva, E.A. Grebenik, S.N. Gornostaeva, V.I. T""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,25,reference_content,"[21] H. Jahr, C. Matta, A. Mobasheri, Physicochemical and biomechanical stimuli in cell-based articular cartilage repair, Curr. Rheumatol. Rep. 17 (3) (2015), https://doi.org/10.1007/s11926-014-0493-9","[82, 1086, 1012, 1118]",reference_item,0.85,"[""reference content label: [21] H. Jahr, C. Matta, A. Mobasheri, Physicochemical and bi""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,26,reference_content,"[22] V.C. Mow, X.E. Guo, Mechano-electrochemical properties of articular cartilage: their inhomogeneities and anisotropies, Annu. Rev. Biomed. Eng. 4 (1) (2002) 175–209, https://doi.org/10.1146/annure","[83, 1121, 1010, 1155]",reference_item,0.85,"[""reference content label: [22] V.C. Mow, X.E. Guo, Mechano-electrochemical properties ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,27,reference_content,"[23] P. Poillot, C.L. Le Maitre, J.M. Huyghe, The strain-generated electrical potential in cartilaginous tissues: a role for piezoelectricity, Biophys. Rev. 13 (1) (2021) 91–100, https://doi.org/10.10","[83, 1155, 1012, 1187]",reference_item,0.85,"[""reference content label: [23] P. Poillot, C.L. Le Maitre, J.M. Huyghe, The strain-gen""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,28,reference_content,"[24] M. Misakian, A.R. Sheppard, D. Krause, M.E. Frazier, D.L. Miller, Biological, physical, and electrical parameters for in vitro studies with ELF magnetic and electric fields: a primer, Bioelectrom","[82, 1190, 1012, 1223]",reference_item,0.85,"[""reference content label: [24] M. Misakian, A.R. Sheppard, D. Krause, M.E. Frazier, D.""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,29,reference_content,"[25] L. Massari, F. Benazzo, F. Falez, D. Perugia, L. Pietrogrande, S. Setti, R. Osti, E. Vaienti, C. Ruosi, R. Cadossi, Biophysical stimulation of bone and cartilage: state of the art and future pers","[83, 1225, 1012, 1257]",reference_item,0.85,"[""reference content label: [25] L. Massari, F. Benazzo, F. Falez, D. Perugia, L. Pietro""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,30,reference_content,"[26] R.M. Stefani, S. Barbosa, A.R. Tan, S. Setti, A.M. Stoker, G.A. Ateshian, R. Cadossi, G. Vunjak-Novakovic, R.K. Aaron, J.L. Cook, J.C. Bulinski, C.T. Hung, Pulsed electromagnetic fields promote r","[83, 1259, 1012, 1308]",reference_item,0.85,"[""reference content label: [26] R.M. Stefani, S. Barbosa, A.R. Tan, S. Setti, A.M. Stok""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,31,reference_content,"[27] R. Balint, N.J. Cassidy, S.H. Cartmell, Electrical stimulation: a novel tool for tissue engineering, Tissue Eng., Part B, Rev. 19 (1) (2013) 48–57, https://doi.org/10.1089/ten.teb.2012.0183.","[83, 1310, 1012, 1342]",reference_item,0.85,"[""reference content label: [27] R. Balint, N.J. Cassidy, S.H. Cartmell, Electrical stim""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,32,reference_content,"[28] L.P. da Silva, S.C. Kundu, R.L. Reis, V.M. Correlo, Electric phenomenon: a disregarded tool in tissue engineering and regenerative medicine, Trends Biotechnol. 38 (1) (2020) 24–49, https://doi.or","[83, 1345, 1012, 1377]",reference_item,0.85,"[""reference content label: [28] L.P. da Silva, S.C. Kundu, R.L. Reis, V.M. Correlo, Ele""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +14,33,number,14,"[535, 1404, 554, 1418]",noise,0.9,"[""page number label""]",noise,0.9,tail_nonref_hold_zone,unknown_like,short_fragment,False,False +15,0,header,"J. Zimmermann, A.R. Farooqi and U. van Rienen","[74, 67, 344, 83]",noise,0.9,"[""header label""]",noise,0.9,,unknown_like,none,False,False +15,1,header,Heliyon 10 (2024) e38112,"[864, 67, 1016, 83]",noise,0.9,"[""header label""]",noise,0.9,,unknown_like,none,False,False +15,2,reference_content,"[29] N. Tandon, C. Cannizzaro, P.H.G. Chao, R. Maidhof, A. Marsano, H.T.H. Au, M. Radisic, G. Vunjak-Novakovic, Electrical stimulation systems for cardiac tissue engineering, Nat. Protoc. 4 (2) (2009)","[82, 106, 1013, 139]",reference_item,0.85,"[""reference content label: [29] N. Tandon, C. Cannizzaro, P.H.G. Chao, R. Maidhof, A. M""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,3,reference_content,"[30] M. Zhao, B. Song, J. Pu, T. Wada, B. Reid, G. Tai, F. Wang, A. Guo, P. Walczysko, Y. Gu, T. Sasaki, A. Suzuki, J.V. Forrester, H.R. Bourne, P.N. Devreotes, C.D. McCaig, J.M. Penninger, Electrical","[82, 141, 1012, 190]",reference_item,0.85,"[""reference content label: [30] M. Zhao, B. Song, J. Pu, T. Wada, B. Reid, G. Tai, F. W""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,4,reference_content,"[31] M. Zhao, Electrical fields in wound healing—an overriding signal that directs cell migration, Semin. Cell Dev. Biol. 20 (6) (2009) 674–682, https://doi.org/10.1016/j.semcdb.2008.12.009.","[82, 192, 1011, 224]",reference_item,0.85,"[""reference content label: [31] M. Zhao, Electrical fields in wound healing\u2014an overridi""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,5,reference_content,"[32] T.J. Zajdel, G. Shim, D.J. Cohen, Come together: on-chip bioelectric wound closure, Biosens. Bioelectron. 192 (July) (2021) 113479, https://doi.org/10.1016/j.bios.2021.113479.","[83, 226, 1012, 258]",reference_item,0.85,"[""reference content label: [32] T.J. Zajdel, G. Shim, D.J. Cohen, Come together: on-chi""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,6,reference_content,"[33] S. Meng, M. Rouabhia, Z. Zhang, Electrical stimulation modulates osteoblast proliferation and bone protein production through heparin-bioactivated conductive scaffolds, Bioelectromagnetics 34 (3)","[83, 261, 1013, 293]",reference_item,0.85,"[""reference content label: [33] S. Meng, M. Rouabhia, Z. Zhang, Electrical stimulation ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,7,reference_content,"[34] S.N. Iwasa, H.H. Shi, S.H. Hong, T. Chen, M. Marquez-Chin, C. Iorio-Morin, S.K. Kalia, M.R. Popovic, H.E. Naguib, C.M. Morshead, Novel electrode designs for neurostimulation in regenerative medic","[82, 295, 1012, 328]",reference_item,0.85,"[""reference content label: [34] S.N. Iwasa, H.H. Shi, S.H. Hong, T. Chen, M. Marquez-Ch""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,8,reference_content,"[35] R.H. Funk, T. Monsees, N. Özkucur, Electromagnetic effects - from cell biology to medicine, Prog. Histochem. Cytochem. 43 (4) (2009) 177–264, https://doi.org/10.1016/j.proghi.2008.07.001.","[83, 330, 1012, 362]",reference_item,0.85,"[""reference content label: [35] R.H. Funk, T. Monsees, N. \u00d6zkucur, Electromagnetic effe""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,9,reference_content,"[36] G. Thrivikraman, S.K. Boda, B. Basu, Unraveling the mechanistic effects of electric field stimulation towards directing stem cell fate and function: a tissue engineering perspective, Biomaterials","[84, 364, 1012, 397]",reference_item,0.85,"[""reference content label: [36] G. Thrivikraman, S.K. Boda, B. Basu, Unraveling the mec""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,10,reference_content,"[37] C. Chen, X. Bai, Y. Ding, I.S. Lee, Electrical stimulation as a novel tool for regulating cell behavior in tissue engineering, Biomater. Res. 23 (1) (2019) 1–12, https://doi.org/10.1186/s40824-01","[83, 398, 1012, 431]",reference_item,0.85,"[""reference content label: [37] C. Chen, X. Bai, Y. Ding, I.S. Lee, Electrical stimulat""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,11,reference_content,"[38] C.N.M. Ryan, M.N. Doulgkeroglou, D.I. Zeugolis, Electric field stimulation for tissue engineering applications, BMC Biomed. Eng. 3 (1) (2021) 1, https://doi.org/10.1186/s42490-020-00046-0.","[84, 434, 1012, 464]",reference_item,0.85,"[""reference content label: [38] C.N.M. Ryan, M.N. Doulgkeroglou, D.I. Zeugolis, Electri""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,12,reference_content,"[39] A.R. Farooqi, Computational modeling of electroactive hydrogels for cartilage-tissue repair using electrical stimulation, Ph.D. thesis, Universität Rostock, 20","[82, 467, 1008, 485]",reference_item,0.85,"[""reference content label: [39] A.R. Farooqi, Computational modeling of electroactive h""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,13,reference_content,"[40] J. Zimmermann, Numerical modelling of electrical stimulation for cartilage tissue engineering, Ph.D. thesis, Universität Rostock, 2022","[76, 487, 874, 501]",reference_item,0.85,"[""reference content label: [40] J. Zimmermann, Numerical modelling of electrical stimul""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,14,reference_content,"[41] B. Baker, R.O. Becker, J. Spadaro, A study of electrochemical enhancement of articular cartilage repair, Clin. Orthop. Relat. Res. 102 (1974) 251–267, https://doi.org/10.1097/00003086-197407000-0","[84, 502, 1012, 534]",reference_item,0.85,"[""reference content label: [41] B. Baker, R.O. Becker, J. Spadaro, A study of electroch""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,15,reference_content,"[42] C.T. Brighton, L. Jensen, S.R. Pollack, B.S. Tolin, C.C. Clark, Proliferative and synthetic response of bovine growth plate chondrocytes to various capacitively coupled electrical fields, J. Orth","[83, 536, 1012, 569]",reference_item,0.85,"[""reference content label: [42] C.T. Brighton, L. Jensen, S.R. Pollack, B.S. Tolin, C.C""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,16,reference_content,"[43] C.T. Brighton, E. Okereke, S.R. Pollack, C.C. Clark, In vitro bone-cell response to a capacitively coupled electrical field, Clin. Orthop. Relat. Res. 285 (1992) 255–262, https://doi.org/10.1097/","[84, 572, 1013, 603]",reference_item,0.85,"[""reference content label: [43] C.T. Brighton, E. Okereke, S.R. Pollack, C.C. Clark, In""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,17,reference_content,"[44] W. Wang, Z. Wang, G. Zhang, C.C. Clark, C.T. Brighton, Up-regulation of chondrocyte matrix genes and products by electric fields, Clin. Orthop. Relat. Res. 427 (2004) S163–S173, https://doi.org/1","[83, 605, 1012, 638]",reference_item,0.85,"[""reference content label: [44] W. Wang, Z. Wang, G. Zhang, C.C. Clark, C.T. Brighton, ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,18,reference_content,"[45] C.T. Brighton, W. Wang, C.C. Clark, The effect of electrical fields on gene and protein expression in human osteoarthritic cartilage explants, J. Bone Jt. Surg., Am. 90 (2008) 833–848, https://do","[83, 640, 1012, 672]",reference_item,0.85,"[""reference content label: [45] C.T. Brighton, W. Wang, C.C. Clark, The effect of elect""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,19,reference_content,"[46] J.J. Vaca-González, S. Clara-Trujillo, M. Guillot-Ferriols, J. Ródenas-Rochina, M.J. Sanchis, J.L.G. Ribelles, D.A. Garzón-Alvarado, G.G. Ferrer, Effect of electrical stimulation on chondrogenic ","[83, 674, 1013, 724]",reference_item,0.85,"[""reference content label: [46] J.J. Vaca-Gonz\u00e1lez, S. Clara-Trujillo, M. Guillot-Ferri""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,20,reference_content,"[47] S. Krueger, A. Riess, A. Jonitz-Heincke, A. Weizel, A. Seyfarth, H. Seitz, R. Bader, Establishment of a new device for electrical stimulation of non-degenerative cartilage cells in vitro, Int. J.","[82, 725, 1013, 759]",reference_item,0.85,"[""reference content label: [47] S. Krueger, A. Riess, A. Jonitz-Heincke, A. Weizel, A. ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,21,reference_content,"[48] H.J. Kwon, G.S. Lee, H. Chun, Electrical stimulation drives chondrogenesis of mesenchymal stem cells in the absence of exogenous growth factors, Sci. Rep. 6 (April) (2016) 1–13, https://doi.org/1","[83, 761, 1012, 793]",reference_item,0.85,"[""reference content label: [48] H.J. Kwon, G.S. Lee, H. Chun, Electrical stimulation dr""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,22,reference_content,"[49] S. Saghati, H.T. Nasrabadi, A.B. Khoshfetrat, K. Moharamzadeh, A. Hassani, S.M. Mohammadi, R. Rahbarghazi, S. Fathi Karkan, Tissue engineering strategies to increase osteochondral regeneration of","[83, 795, 1012, 843]",reference_item,0.85,"[""reference content label: [49] S. Saghati, H.T. Nasrabadi, A.B. Khoshfetrat, K. Mohara""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,23,reference_content,"[50] P.-H.G. Chao, R. Roy, R.L. Mauck, W. Liu, W.B. Valhmu, C.T. Hung, Chondrocyte translocation response to direct current electric fields, J. Biomech. Eng. 122 (3) (2000) 261–267, https://doi.org/10","[82, 846, 1012, 878]",reference_item,0.85,"[""reference content label: [50] P.-H.G. Chao, R. Roy, R.L. Mauck, W. Liu, W.B. Valhmu, ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,24,reference_content,"[51] D.R. Albrecht, G.H. Underhill, T.B. Wassermann, R.L. Sah, S.N. Bhatia, Probing the role of multicellular organization in three-dimensional microenvironments, Nat. Methods 3 (5) (2006) 369–375, ht","[83, 880, 1013, 913]",reference_item,0.85,"[""reference content label: [51] D.R. Albrecht, G.H. Underhill, T.B. Wassermann, R.L. Sa""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,25,reference_content,"[52] A. Schwab, M.A. Wesdorp, J. Xu, F. Abinzano, C. Loebel, M. Falandt, R. Levato, D. Eglin, R. Narcisi, M.J. Stoddart, J. Malda, J.A. Burdick, M. D'Este, G.J. van Osch, Modulating design parameters ","[83, 916, 1013, 964]",reference_item,0.85,"[""reference content label: [52] A. Schwab, M.A. Wesdorp, J. Xu, F. Abinzano, C. Loebel,""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,26,reference_content,"[53] D.C. Zuzzi, C.d.C. Ciccone, L.M.G. Neves, J.S. Mendonça, P.P. Joazeiro, M.A.M. Esquisatto, Evaluation of the effects of electrical stimulation on cartilage repair in adult male rats, Tissue Cell ","[83, 966, 1013, 999]",reference_item,0.85,"[""reference content label: [53] D.C. Zuzzi, C.d.C. Ciccone, L.M.G. Neves, J.S. Mendon\u00e7a""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,27,reference_content,"[54] L. Lippiello, D. Chakkalakal, J.F. Connolly, Puling direct current-induced repair of articular cartilage in rabbit osteochondral defects, J. Orthop. Res. 8 (2) (1990) 266–275, https://doi.org/10.","[84, 1001, 1013, 1034]",reference_item,0.85,"[""reference content label: [54] L. Lippiello, D. Chakkalakal, J.F. Connolly, Puling dir""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,28,reference_content,"[55] D. Hungerford, E. Maclaughlin, C. Mines, S. Deveshwar, C. Elliott, J. Tuber, J. Principe, T. Ford, J. Schechtman, Synergistic effect of using a transcutaneous electrical joint stimulator and an u","[83, 1035, 1013, 1069]",reference_item,0.85,"[""reference content label: [55] D. Hungerford, E. Maclaughlin, C. Mines, S. Deveshwar, ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,29,reference_content,"[56] D. Garland, P. Holt, J.T. Harrington, J. Caldwell, T. Zizic, J. Cholewczynski, A 3-month, randomized, double-blind, placebo-controlled study to evaluate the safety and efficacy of a highly optimi","[82, 1071, 1012, 1119]",reference_item,0.85,"[""reference content label: [56] D. Garland, P. Holt, J.T. Harrington, J. Caldwell, T. Z""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,30,reference_content,"[57] T.M. Zizic, K.C. Hoffman, P.A. Holt, D.S. Hungerford, J.R. O'Dell, M.A. Jacobs, C.G. Lewis, C.L. Deal, J.R. Caldwell, J.G. Cholewczynski, S.M. Free, The treatment of osteoarthritis of the knee wi","[83, 1121, 1013, 1155]",reference_item,0.85,"[""reference content label: [57] T.M. Zizic, K.C. Hoffman, P.A. Holt, D.S. Hungerford, J""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,31,reference_content,"[58] R.E. Fary, G.J. Carroll, T.G. Briffa, N.K. Briffa, The effectiveness of pulsed electrical stimulation in the management of osteoarthritis of the knee: results of a double-blind, randomized, place","[83, 1156, 1012, 1189]",reference_item,0.85,"[""reference content label: [58] R.E. Fary, G.J. Carroll, T.G. Briffa, N.K. Briffa, The ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,32,reference_content,"[59] P. Vavken, F. Arrich, O. Schuhfried, R. Dorotka, Effectiveness of pulsed electromagnetic field therapy in the management of osteoarthritis of the knee: a meta-analysis of randomized controlled tr","[83, 1190, 1013, 1222]",reference_item,0.85,"[""reference content label: [59] P. Vavken, F. Arrich, O. Schuhfried, R. Dorotka, Effect""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,33,reference_content,"[60] A. Negm, A. Lorbergs, N.J. MacIntyre, Efficacy of low frequency pulsed subsensory threshold electrical stimulation vs placebo on pain and physical function in people with knee osteoarthritis: sys","[83, 1225, 1012, 1257]",reference_item,0.85,"[""reference content label: [60] A. Negm, A. Lorbergs, N.J. MacIntyre, Efficacy of low f""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,34,reference_content,"[61] C.A.F. de Paula Gomes, F. Politti, C. de Souza Bacelar Pereira, A.C.B. da Silva, A.V. Dibai-Filho, A.R. de Oliveira, D.A. Biasotto-Gonzalez, Exercise program combined with electrophysical modalit","[83, 1259, 1011, 1308]",reference_item,0.85,"[""reference content label: [61] C.A.F. de Paula Gomes, F. Politti, C. de Souza Bacelar ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,35,reference_content,"[62] A. Rutjes, E. Nüesch, R. Sterchi, L. Kalichman, E. Hendriks, M. Osiri, L. Brosseau, S. Reichenbach, P. Jüni, Transcutaneous electrostimulation for osteoarthritis of the knee, Cochrane Database Sy","[83, 1310, 1012, 1343]",reference_item,0.85,"[""reference content label: [62] A. Rutjes, E. N\u00fcesch, R. Sterchi, L. Kalichman, E. Hend""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,36,reference_content,"[63] C. Zeng, H. Li, T. Yang, Z.-h. Deng, Y. Yang, Y. Zhang, G.-h. Lei, Electrical stimulation for pain relief in knee osteoarthritis: systematic review and network meta-analysis, Osteoarthr. Cartil. ","[83, 1346, 1012, 1377]",reference_item,0.85,"[""reference content label: [63] C. Zeng, H. Li, T. Yang, Z.-h. Deng, Y. Yang, Y. Zhang,""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +15,37,number,15,"[535, 1404, 554, 1418]",noise,0.9,"[""page number label""]",noise,0.9,,unknown_like,short_fragment,False,False +16,0,header,"J. Zimmermann, A.R. Farooqi and U. van Rienen","[74, 67, 344, 83]",noise,0.9,"[""header label""]",noise,0.9,,unknown_like,none,False,False +16,1,header,Heliyon 10 (2024) e38112,"[864, 67, 1016, 83]",noise,0.9,"[""header label""]",noise,0.9,,unknown_like,none,False,False +16,2,reference_content,"[64] S. Zhao, A.S. Mehta, M. Zhao, Biomedical applications of electrical stimulation, Cell. Mol. Life Sci. (2020), https://doi.org/10.1007/s00018-019-03446-1.","[81, 106, 987, 122]",reference_item,0.85,"[""reference content label: [64] S. Zhao, A.S. Mehta, M. Zhao, Biomedical applications o""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,3,reference_content,"[65] J.P.A. Ioannidis, S. Greenland, M.A. Hlatky, M.J. Khoury, M.R. Macleod, D. Moher, K.F. Schulz, R. Tibshirani, Increasing value and reducing waste in research design, conduct, and analysis, Lancet","[83, 124, 1013, 156]",reference_item,0.85,"[""reference content label: [65] J.P.A. Ioannidis, S. Greenland, M.A. Hlatky, M.J. Khour""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,4,reference_content,"[66] L.A. Portelli, K. Falldorf, G. Thuróczy, J. Cuppen, Retrospective estimation of the electric and magnetic field exposure conditions in in vitro experimental reports reveal considerable potential ","[83, 157, 1014, 189]",reference_item,0.85,"[""reference content label: [66] L.A. Portelli, K. Falldorf, G. Thur\u00f3czy, J. Cuppen, Ret""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,5,reference_content,"[67] M. Cemazar, G. Sersa, W. Frey, D. Miklavcic, J. Teissé, Recommendations and requirements for reporting on applications of electric pulse delivery for electro-poration of biological samples, Bioel","[83, 192, 1012, 224]",reference_item,0.85,"[""reference content label: [67] M. Cemazar, G. Sersa, W. Frey, D. Miklavcic, J. Teiss\u00e9,""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,6,reference_content,"[68] S. Guette-Marquet, C. Roques, A. Bergel, Theoretical analysis of the electrochemical systems used for the application of direct current/voltage stimuli on cell cultures, Bioelectrochemistry 139 (","[83, 225, 1013, 258]",reference_item,0.85,"[""reference content label: [68] S. Guette-Marquet, C. Roques, A. Bergel, Theoretical an""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,7,reference_content,"[69] P.J. Nicksic, D.T. Donnelly, M. Hesse, S. Bedi, N. Verma, A.J. Seitz, A.J. Shoffstall, K.A. Ludwig, A.M. Dingle, S.O. Poore, Electronic bone growth stimulators for augmentation of osteogenesis in","[83, 259, 1012, 308]",reference_item,0.85,"[""reference content label: [69] P.J. Nicksic, D.T. Donnelly, M. Hesse, S. Bedi, N. Verm""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,8,reference_content,"[70] K. Budde, J. Zimmermann, E. Neuhaus, M. Schroder, A.M. Uhrmacher, U. van Rienen, Requirements for documenting electrical cell stimulation experiments for replicability and numerical modeling, in:","[83, 310, 1012, 359]",reference_item,0.85,"[""reference content label: [70] K. Budde, J. Zimmermann, E. Neuhaus, M. Schroder, A.M. ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,9,reference_content,"[71] J. Zimmermann, K. Budde, N. Arbeiter, F. Molina, A. Storch, A.M. Uhrmacher, U. van Rienen, Using a digital twin of an electrical stimulation device to monitor and control the electrical stimulati","[83, 362, 1013, 394]",reference_item,0.85,"[""reference content label: [71] J. Zimmermann, K. Budde, N. Arbeiter, F. Molina, A. Sto""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,10,reference_content,"[72] J. Zimmermann, F. Sahm, N. Arbeiter, H. Bathel, Z. Song, R. Bader, A. Jonitz-Heincke, U. van Rienen, Experimental and numerical methods to ensure comprehensible and replicable alternating current","[83, 396, 1013, 444]",reference_item,0.85,"[""reference content label: [72] J. Zimmermann, F. Sahm, N. Arbeiter, H. Bathel, Z. Song""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,11,reference_content,"[73] C.A.L. Bassett, R.J. Pawluk, Electrical behavior of cartilage during loading, Science 178 (4064) (1972) 982–983, https://doi.org/10.1126/science.178.4064.982.","[83, 446, 1012, 462]",reference_item,0.85,"[""reference content label: [73] C.A.L. Bassett, R.J. Pawluk, Electrical behavior of car""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,12,reference_content,"[74] E.H. Frank, A. Grodzinsky, Cartilage electromechanics-I. Electrokinetic transduction and the effects of electrolyte pH and ionic strength, J. Biomech. 20 (1987) 615–627, https://doi.org/10.1016/0","[84, 464, 1012, 495]",reference_item,0.85,"[""reference content label: [74] E.H. Frank, A. Grodzinsky, Cartilage electromechanics-I""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,13,reference_content,"[75] E.H. Frank, A.J. Grodzinsky, Cartilage electromechanics-II. A continuum model of cartilage electrokinetics and correlation with experiments, J. Biomech. 20 (6) (1987) 629–639, https://doi.org/10.","[84, 497, 1013, 529]",reference_item,0.85,"[""reference content label: [75] E.H. Frank, A.J. Grodzinsky, Cartilage electromechanics""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,14,reference_content,"[76] J.S. Binette, M. Garon, P. Savard, M.D. McKee, M.D. Buschmann, Tetrapolar measurement of electrical conductivity and thickness of articular cartilage, J. Biomech. Eng. 126 (4) (2004) 475–484, htt","[83, 531, 1012, 562]",reference_item,0.85,"[""reference content label: [76] J.S. Binette, M. Garon, P. Savard, M.D. McKee, M.D. Bus""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,15,reference_content,"[77] I. Hasegawa, S. Kuriki, S. Matsuno, G. Matsumoto, Dependence of electrical conductivity on fixed charge density in articular cartilage, Clin. Orthop. Relat. Res. 177 (1983) 283–288, https://doi.o","[83, 563, 1012, 596]",reference_item,0.85,"[""reference content label: [77] I. Hasegawa, S. Kuriki, S. Matsuno, G. Matsumoto, Depen""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,16,reference_content,"[78] N. Hiraoka, Y. Arai, K.A. Takahashi, O. Mazda, T. Kishida, K. Honjo, S. Tsuchida, H. Inoue, S. Morino, M.A. Suico, H. Kai, T. Kubo, Mild electrical stimulation with heat stimulation increase heat","[84, 599, 1012, 631]",reference_item,0.85,"[""reference content label: [78] N. Hiraoka, Y. Arai, K.A. Takahashi, O. Mazda, T. Kishi""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,17,reference_content,"[79] J. Chen, Y. Huang, J. Yang, K. Li, Y. Jiang, B.C. Heng, Q. Cai, J. Zhang, Z. Ge, Multiple nanosecond pulsed electric fields stimulation with conductive poly(l-lactic acid)/carbon nanotubes films ","[83, 633, 1012, 682]",reference_item,0.85,"[""reference content label: [79] J. Chen, Y. Huang, J. Yang, K. Li, Y. Jiang, B.C. Heng,""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,18,reference_content,"[80] K. Li, T. Ning, H. Wang, Y. Jiang, J. Zhang, Z. Ge, Nanosecond pulsed electric fields enhance mesenchymal stem cells differentiation via DNMT1-regulated OCT4/NANOG gene expression, Stem Cell Res.","[82, 683, 1012, 715]",reference_item,0.85,"[""reference content label: [80] K. Li, T. Ning, H. Wang, Y. Jiang, J. Zhang, Z. Ge, Nan""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,19,reference_content,"[81] T. Ning, J. Guo, K. Zhang, K. Li, J. Zhang, Z. Yang, Z. Ge, Nanosecond pulsed electric fields enhanced chondrogenic potential of mesenchymal stem cells via JNK/CREB-STAT3 signaling pathway, Stem ","[84, 718, 1012, 750]",reference_item,0.85,"[""reference content label: [81] T. Ning, J. Guo, K. Zhang, K. Li, J. Zhang, Z. Yang, Z.""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,20,reference_content,"[82] K. Zhang, J. Guo, Z. Ge, J. Zhang, Nanosecond pulsed electric fields (nsPEFs) regulate phenotypes of chondrocytes through Wnt/ $ \beta $-catenin signaling pathway, Sci. Rep. 4 (2014) 1–8, https:/","[83, 752, 1013, 784]",reference_item,0.85,"[""reference content label: [82] K. Zhang, J. Guo, Z. Ge, J. Zhang, Nanosecond pulsed el""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,21,reference_content,"[83] K. Li, L. Fan, J. Lin, B.C. Heng, Z. Deng, Q. Zheng, J. Zhang, Y. Jiang, Z. Ge, Nanosecond pulsed electric fields prime mesenchymal stem cells to peptide ghrelin and enhance chondrogenesis and os","[82, 786, 1013, 818]",reference_item,0.85,"[""reference content label: [83] K. Li, L. Fan, J. Lin, B.C. Heng, Z. Deng, Q. Zheng, J.""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,22,reference_content,"[84] G.A. Rodan, L.A. Bourret, L.A. Norton, DNA synthesis in cartilage cells is stimulated by oscillating electric fields, Science 199 (1978) 690–692, https://doi.org/10.1126/science.625660.","[83, 820, 1012, 851]",reference_item,0.85,"[""reference content label: [84] G.A. Rodan, L.A. Bourret, L.A. Norton, DNA synthesis in""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,23,reference_content,"[85] B. Hiemer, M. Krogull, T. Bender, J. Ziebart, S. Krueger, R. Bader, A. Jonitz-Heincke, Effect of electric stimulation on human chondrocytes and mesenchymal stem cells under normoxia and hypoxia, ","[83, 853, 1013, 887]",reference_item,0.85,"[""reference content label: [85] B. Hiemer, M. Krogull, T. Bender, J. Ziebart, S. Kruege""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,24,reference_content,"[86] H. Raben, P.W. Kämmerer, R. Bader, U. van Rienen, Establishment of a numerical model to design an electro-stimulating system for a porcine mandibular critical size defect, Appl. Sci. 9 (10) (2019","[82, 888, 1013, 920]",reference_item,0.85,"[""reference content label: [86] H. Raben, P.W. K\u00e4mmerer, R. Bader, U. van Rienen, Estab""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,25,reference_content,"[80] +Instituto..","[83, 955, 1013, 988]",reference_item,0.85,"[""reference content label: [80]\nInstituto..""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,26,reference_content,"[89] L.A. MacGinitie, Electrical and thermal modulation of protein synthesis in cartilage: a model for field effects on biological tissues, Ph.D. thesis, Massachusetts Institute of Technology, 1987.","[83, 989, 1013, 1021]",reference_item,0.85,"[""reference content label: [89] L.A. MacGinitie, Electrical and thermal modulation of p""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,27,reference_content,"[90] L.A. MacGinitie, Y.A. Gluzband, A.J. Grodzinsky, Electric field stimulation can increase protein synthesis in articular cartilage explants, J. Orthop. Res. 12 (1994) 151–160, https://doi.org/10.1","[83, 1023, 1013, 1056]",reference_item,0.85,"[""reference content label: [90] L.A. MacGinitie, Y.A. Gluzband, A.J. Grodzinsky, Electr""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,28,reference_content,"[91] N. Szasz, Electric field regulation of chondrocyte proliferation, biosynthesis, and cellular signaling, Ph.D. thesis, Massachusetts Institute of Technology, 2003.","[82, 1057, 1011, 1074]",reference_item,0.85,"[""reference content label: [91] N. Szasz, Electric field regulation of chondrocyte prol""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,29,reference_content,"[92] T. Akkin, D.P. Davé, J.-I. Youn, S.A. Telenkov, H.G. Rylander, T.E. Milner, Imaging tissue response to electrical and photothermal stimulation with nanometer sensitivity, Lasers Surg. Med. 33 (20","[83, 1076, 1013, 1107]",reference_item,0.85,"[""reference content label: [92] T. Akkin, D.P. Dav\u00e9, J.-I. Youn, S.A. Telenkov, H.G. Ry""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,30,reference_content,"[93] J.-I. Youn, T. Akkin, T.E. Milner, Electrokinetic measurement of cartilage using differential phase optical coherence tomography, Physiol. Meas. 25 (2004) 85–95, https://doi.org/10.1088/0967-3334","[83, 1108, 1013, 1140]",reference_item,0.85,"[""reference content label: [93] J.-I. Youn, T. Akkin, T.E. Milner, Electrokinetic measu""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,31,reference_content,"[4] S.I. Berkenblit, E.H. Frank, E.P. Salant, A.J. Grodzinsky, Nondestructive detection of cartilage degeneration using electromechanical surface spectroscopy, J. Biomech. Eng. 116 (1994) 384–392, htt","[96, 1142, 1029, 1175]",reference_item,0.85,"[""reference content label: [4] S.I. Berkenblit, E.H. Frank, E.P. Salant, A.J. Grodzinsk""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,32,reference_content,"[95] D. Khang, G.E. Park, T.J. Webster, Enhanced chondrocyte densities on carbon nanotube composites: the combined role of nanosurface roughness and electrical stimulation, J. Biomed. Mater. Res., Par","[83, 1176, 1012, 1208]",reference_item,0.85,"[""reference content label: [95] D. Khang, G.E. Park, T.J. Webster, Enhanced chondrocyte""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,33,reference_content,"[96] M.L. Hernández-Bule, C.L. Paíno, M.Á. Trillo, A. Úbeda, Electric stimulation at 448 kHz promotes proliferation of human mesenchymal stem cells, Cell. Physiol. Biochem. 34 (2014) 1741–1755, https:","[83, 1210, 1012, 1240]",reference_item,0.85,"[""reference content label: [96] M.L. Hern\u00e1ndez-Bule, C.L. Pa\u00edno, M.\u00c1. Trillo, A. \u00dabeda,""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,34,reference_content,"[97] A.J. Bard, L.R. Faulkner, Electrochemical Methods: Fundamentals and Applications, 2nd edition, Wiley, New York, 2001.","[82, 1244, 804, 1260]",reference_item,0.85,"[""reference content label: [97] A.J. Bard, L.R. Faulkner, Electrochemical Methods: Fund""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,35,reference_content,"[98] H. Nogami, H. Aoki, T. Okagawa, K. Mimatsu, Effects of electric current on chondrogenesis in vitro, Clin. Orthop. Relat. Res. 163 (1982) 243–247.","[82, 1262, 950, 1276]",reference_item,0.85,"[""reference content label: [98] H. Nogami, H. Aoki, T. Okagawa, K. Mimatsu, Effects of ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,36,reference_content,"[99] B. Baker, J. Spadaro, A. Marino, R.O. Backer, Electrical stimulation of articular cartilage regeneration, Ann. N.Y. Acad. Sci. 238 (1974) 491–499, https://doi.org/10.1111/j.1749-6632.1974.tb26815","[79, 1277, 1012, 1309]",reference_item,0.85,"[""reference content label: [99] B. Baker, J. Spadaro, A. Marino, R.O. Backer, Electrica""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,37,reference_content,"[100] O.O. Akanji, D.A. Lee, D.A. Bader, The effects of direct current stimulation on isolated chondrocytes seeded in 3D agarose constructs, Biorheology 45 (2008) 229–243, https://doi.org/10.3233/BIR-","[77, 1311, 1011, 1342]",reference_item,0.85,"[""reference content label: [100] O.O. Akanji, D.A. Lee, D.A. Bader, The effects of dire""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,38,reference_content,"[101] E. Esfandiari, S. Roshankhah, M. Mardani, B. Hashemibeni, E. Naghsh, M. Kazemi, M. Salahshoor, The effect of high frequency electric field on enhancement of chondrogenesis in human adipose-deriv","[76, 1345, 1012, 1378]",reference_item,0.85,"[""reference content label: [101] E. Esfandiari, S. Roshankhah, M. Mardani, B. Hashemibe""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +16,39,number,16,"[535, 1405, 554, 1418]",noise,0.9,"[""page number label""]",noise,0.9,,unknown_like,short_fragment,False,False +17,0,header,"J. Zimmermann, A.R. Farooqi and U. van Rienen","[74, 67, 344, 83]",noise,0.9,"[""header label""]",noise,0.9,,unknown_like,none,False,False +17,1,header,Heliyon 10 (2024) e38112,"[864, 67, 1016, 83]",noise,0.9,"[""header label""]",noise,0.9,,unknown_like,none,False,False +17,2,reference_content,"[102] J.J. Vaca-González, J.M. Guevara, M.A. Moncayo, H. Castro-Abril, Y. Hata, D.A. Garzón-Alvarado, Biophysical stimuli: a review of electrical and mechanical stimulation in hyaline cartilage, Carti","[75, 106, 1011, 139]",reference_item,0.85,"[""reference content label: [102] J.J. Vaca-Gonz\u00e1lez, J.M. Guevara, M.A. Moncayo, H. Cas""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,3,reference_content,"[103] J. Xu, W. Wang, C.C. Clark, C.T. Brighton, Signal transduction in electrically stimulated articular chondrocytes involves translocation of extracellular calcium through voltage-gated channels, O","[76, 141, 1012, 172]",reference_item,0.85,"[""reference content label: [103] J. Xu, W. Wang, C.C. Clark, C.T. Brighton, Signal tran""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,4,reference_content,"[104] C.C. Clark, W. Wang, C.T. Brighton, Up-regulation of expression of selected genes in human bone cells with specific capacitively coupled electric fields, J. Orthop. Res. 32 (7) (2014) 894–903, h","[76, 174, 1012, 206]",reference_item,0.85,"[""reference content label: [104] C.C. Clark, W. Wang, C.T. Brighton, Up-regulation of e""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,5,reference_content,"[105] E.L. Carter, E.J. Vresilovic, S.R. Pollack, C.T. Brighton, Field distribution in vertebral bodies of the rat during electrical stimulation: a parameter study, IEEE Trans. Biomed. Eng. 36 (3) (19","[76, 208, 1012, 241]",reference_item,0.85,"[""reference content label: [105] E.L. Carter, E.J. Vresilovic, S.R. Pollack, C.T. Brigh""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,6,reference_content,"[106] D.G. Lorich, C.T. Brighton, R. Gupta, J.R. Corsetti, S.E. Levine, I.D. Gelb, R. Seldes, S.R. Pollack, Biochemical pathway mediating the response of bone cells to capacitive coupling, Clin. Ortho","[77, 242, 1013, 275]",reference_item,0.85,"[""reference content label: [106] D.G. Lorich, C.T. Brighton, R. Gupta, J.R. Corsetti, S""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,7,reference_content,"[107] I.V. Timoshkin, S.J. MacGregor, R.A. Fouracre, B.H. Crichton, J.G. Anderson, Transient electrical field across cellular membranes: pulsed electric field treatment of microbial cells, J. Phys. D,","[76, 276, 1013, 308]",reference_item,0.85,"[""reference content label: [107] I.V. Timoshkin, S.J. MacGregor, R.A. Fouracre, B.H. Cr""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,8,reference_content,"[108] T. Taghian, D.A. Narmoneva, A.B. Kogan, Modulation of cell function by electric field: a high-resolution analysis, J. R. Soc. Interface 12 (107) (2015) 20150153.","[76, 309, 1011, 326]",reference_item,0.85,"[""reference content label: [108] T. Taghian, D.A. Narmoneva, A.B. Kogan, Modulation of ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,9,reference_content,"[109] J. Zimmermann, R. Altenkirch, U. van Rienen, Numerical study on the effect of capacitively coupled electrical stimulation on biological cells considering model uncertainties, Sci. Rep. 12 (2022)","[77, 327, 1012, 359]",reference_item,0.85,"[""reference content label: [109] J. Zimmermann, R. Altenkirch, U. van Rienen, Numerical""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,10,reference_content,"[110] C.T. Brighton, A.S. Unger, J.L. Stambough, In vitro growth of bovine articular cartilage chondrocytes in various capacitively coupled electrical fields, J. Orthop. Res. 2 (1) (1984) 15–22, https","[77, 360, 1012, 392]",reference_item,0.85,"[""reference content label: [110] C.T. Brighton, A.S. Unger, J.L. Stambough, In vitro gr""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,11,reference_content,"[111] C.T. Brighton, W. Wang, C.C. Clark, Up-regulation of matrix in bovine articular cartilage explants by electric fields, Biochem. Biophys. Res. Commun. 342 (2) (2006) 556–561, https://doi.org/10.1","[77, 394, 1013, 427]",reference_item,0.85,"[""reference content label: [111] C.T. Brighton, W. Wang, C.C. Clark, Up-regulation of m""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,12,reference_content,"[112] C.T. Brighton, W. Wang, C.C. Clark, A. Praestgaard, A spectrophotometric analysis of human osteoarthritic cartilage explants subjected to specific capacitively coupled electric fields, Open J. B","[77, 428, 1013, 460]",reference_item,0.85,"[""reference content label: [112] C.T. Brighton, W. Wang, C.C. Clark, A. Praestgaard, A ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,13,reference_content,"[113] J.J. Vaca-González, J.M. Guevara, J.F. Vega, D.A. Garzón-Alvarado, An in vitro chondrocyte electrical stimulation framework: a methodology to calculate electric fields and modulate proliferation","[77, 462, 1012, 511]",reference_item,0.85,"[""reference content label: [113] J.J. Vaca-Gonz\u00e1lez, J.M. Guevara, J.F. Vega, D.A. Garz""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,14,reference_content,"[114] M. Mardani, S. Roshankhah, B. Hashemibeni, M. Salahshoor, E. Naghsh, E. Esfandiari, Induction of chondrogenic differentiation of human adipose-derived stem cells by low frequency electric field,","[77, 513, 1013, 546]",reference_item,0.85,"[""reference content label: [114] M. Mardani, S. Roshankhah, B. Hashemibeni, M. Salahsho""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,15,reference_content,"[115] R.J. Fitzsimmons, S.L. Gordon, J. Kronberg, T. Ganey, A.A. Pilla, A pulsing electric field (PEF) increases human chondrocyte proliferation through a transduction pathway involving nitric oxide s","[77, 547, 1013, 580]",reference_item,0.85,"[""reference content label: [115] R.J. Fitzsimmons, S.L. Gordon, J. Kronberg, T. Ganey, ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,16,reference_content,"[116] J.J. Vaca-González, J.F. Escobar, J.M. Guevara, Y.A. Hata, G. Gallego Ferrer, D.A. Garzón-Alvarado, Capacitively coupled electrical stimulation of rat chondroepiphysis explants: a histomorphomet","[77, 581, 1012, 614]",reference_item,0.85,"[""reference content label: [116] J.J. Vaca-Gonz\u00e1lez, J.F. Escobar, J.M. Guevara, Y.A. H""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,17,reference_content,"[11], +low-h... +...v02/ho","[77, 615, 1013, 648]",reference_item,0.85,"[""reference content label: [11],\nlow-h...\n...v02/ho""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,18,reference_content,"[118] H. Bathel, L.V. …, J. Zimmermann, A. Weizel, H. Seitz, U. van Rienen, Electrical impedance spectroscopy on capacitively coupled electro-cell stimulation, in: 2022 International Workshop on Imped","[74, 650, 1013, 682]",reference_item,0.85,"[""reference content label: [118] H. Bathel, L.V. \u2026, J. Zimmermann, A. Weizel, H. Seitz,""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,19,reference_content,"[119] J. Farr, M.A. Mont, J.R. Caldwell, D. Garland, T.M. Zizic, Pulsed electrical stimulation in patients with osteoarthritis of the knee: follow up in 288 patients who had failed non-operative thera","[76, 683, 1012, 715]",reference_item,0.85,"[""reference content label: [119] J. Farr, M.A. Mont, J.R. Caldwell, D. Garland, T.M. Zi""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,20,reference_content,"[120] A. Richardot, E.T. McAdams, Harmonic analysis of low-frequency bioelectrode behavior, IEEE Trans. Med. Imaging 21 (6) (2002) 604–612, https://doi.org/10.1109/TMI.2002.800576.","[77, 717, 1011, 749]",reference_item,0.85,"[""reference content label: [120] A. Richardot, E.T. McAdams, Harmonic analysis of low-f""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,21,reference_content,"[121] D.R. Merrill, M. Bikson, J.G. Jefferys, Electrical stimulation of excitable tissue: design of efficacious and safe protocols, J. Neurosci. Methods 141 (2) (2005) 171–198, https://doi.org/10.1016","[76, 751, 1012, 784]",reference_item,0.85,"[""reference content label: [121] D.R. Merrill, M. Bikson, J.G. Jefferys, Electrical sti""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,22,reference_content,"[122] C. Boehler, S. Carli, L. Fadiga, T. Stieglitz, M. Asplund, Tutorial: guidelines for standardized performance tests for electrodes intended for neural interfaces and bioelectronics, Nat. Protoc. ","[77, 785, 1013, 818]",reference_item,0.85,"[""reference content label: [122] C. Boehler, S. Carli, L. Fadiga, T. Stieglitz, M. Aspl""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,23,reference_content,"[123] M.E. Orazem, B. Tribollet, Electrochemical Impedance Spectroscopy, 2nd edition, The Electrochemical Society Series, John Wiley & Sons, Hoboken, New Jersey, 2017.","[77, 819, 1012, 851]",reference_item,0.85,"[""reference content label: [123] M.E. Orazem, B. Tribollet, Electrochemical Impedance S""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,24,reference_content,"[124] H.P. Schwan, Linear and nonlinear electrode polarization and biological materials, Ann. Biomed. Eng. 20 (3) (1992) 269–288, https://doi.org/10.1007/BF02368531.","[76, 853, 1012, 885]",reference_item,0.85,"[""reference content label: [124] H.P. Schwan, Linear and nonlinear electrode polarizati""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,25,reference_content,"[125] M. Moussavi, H.P. Schwan, H.H. Sun, Harmonic distortion caused by electrode polarisation, Med. Biol. Eng. Comput. 32 (2) (1994) 121–125, https://doi.org/10.1007/BF02518907.","[76, 887, 1013, 919]",reference_item,0.85,"[""reference content label: [125] M. Moussavi, H.P. Schwan, H.H. Sun, Harmonic distortio""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,26,reference_content,"[126] A. Palanisami, G.T. Mercier, J. FangJie, J.H. Miller, Nonlinear impedance of whole cells near an electrode as a probe of mitochondrial activity, Biosensors 1 (2) (2011) 46–57, https://doi.org/10","[77, 921, 1013, 953]",reference_item,0.85,"[""reference content label: [126] A. Palanisami, G.T. Mercier, J. FangJie, J.H. Miller, ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,27,reference_content,"[127] K. Srirussamee, S. Mobini, N.J. Cassidy, S.H. Cartmell, Direct electrical stimulation enhances osteogenesis by inducing Bmp2 and Spp1 expressions from macrophages and preosteoblasts, Biotechnol.","[76, 956, 1013, 988]",reference_item,0.85,"[""reference content label: [127] K. Srirussamee, S. Mobini, N.J. Cassidy, S.H. Cartmell""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,28,reference_content,"[128] K. Srirussamee, R. Xue, S. Mobini, N.J. Cassidy, S.H. Cartmell, Changes in the extracellular microenvironment and osteogenic responses of mesenchymal stem/stromal cells induced by in vitro direc","[76, 990, 1013, 1022]",reference_item,0.85,"[""reference content label: [128] K. Srirussamee, R. Xue, S. Mobini, N.J. Cassidy, S.H. ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,29,reference_content,"[129] M. Bielfeldt, K. Budde-Sagert, N. Weis, M. Buenning, S. Staehlke, J. Zimmermann, N. Arbeiter, S. Mobini, M.U. González, H. Rebl, A. Uhrmacher, U. van Rienen, B. Nebe, Discrimination between the ","[76, 1026, 1013, 1072]",reference_item,0.85,"[""reference content label: [129] M. Bielfeldt, K. Budde-Sagert, N. Weis, M. Buenning, S""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,30,reference_content,"[130] M. Schönleber, D. Klotz, E. Ivers-Tiffée, A method for improving the robustness of linear Kramers-Kronig validity tests, Electrochim. Acta 131 (2014) 20–27, https://doi.org/10.1016/j.electacta.2","[76, 1075, 1012, 1106]",reference_item,0.85,"[""reference content label: [130] M. Sch\u00f6nleber, D. Klotz, E. Ivers-Tiff\u00e9e, A method for""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,31,reference_content,"[132] J. Zimmermann, T. Distler, A.R. Boccaccini, U. van Rienen, Numerical simulations as means for tailoring electrically conductive hydrogels towards cartilage tissue engineering by electrical stimu","[77, 1142, 1012, 1176]",reference_item,0.85,"[""reference content label: [132] J. Zimmermann, T. Distler, A.R. Boccaccini, U. van Rie""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,32,reference_content,"[133] S. Grimnes, Ø.G. Martinsen, Bioimpedance and Bioelectricity Basics, third edition, third edit edition, Academic Press, Oxford, 2014.","[75, 1174, 869, 1192]",reference_item,0.85,"[""reference content label: [133] S. Grimnes, \u00d8.G. Martinsen, Bioimpedance and Bioelectr""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,33,reference_content,"[134] S. Wang, J. Zhang, O. Gharbi, V. Vivier, M. Gao, M.E. Orazem, Electrochemical impedance spectroscopy, Nat. Rev. Methods Primers 1 (1) (2021) 41, https://doi.org/10.1038/s43586-021-00039-w.","[76, 1193, 1013, 1224]",reference_item,0.85,"[""reference content label: [134] S. Wang, J. Zhang, O. Gharbi, V. Vivier, M. Gao, M.E. ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,34,reference_content,"[135] A.V. Oppenheim, A.S. Willsky, S.H. Nawab, Signals & Systems, 2nd edition, Prentice-Hall, Upper Saddle River, NJ, 1996.","[76, 1226, 803, 1242]",reference_item,0.85,"[""reference content label: [135] A.V. Oppenheim, A.S. Willsky, S.H. Nawab, Signals & Sy""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,35,reference_content,"[136] U. van Rienen, J. Flehr, U. Schreiber, S. Schulze, U. Gimsa, W. Baumann, D.G. Weiss, J. Gimsa, R. Benecke, H.W. Pau, Electro-quasistatic simulations in bio-systems engineering and medical engine","[77, 1244, 1012, 1276]",reference_item,0.85,"[""reference content label: [136] U. van Rienen, J. Flehr, U. Schreiber, S. Schulze, U. ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,36,reference_content,"[137] D.R. Cantrell, S. Inayat, A. Taflove, R.S. Ruoff, J.B. Troy, Incorporation of the electrode-electrolyte interface into finite-element models of metal microelectrodes, J. Neural Eng. 5 (1) (2008)","[76, 1277, 1012, 1309]",reference_item,0.85,"[""reference content label: [137] D.R. Cantrell, S. Inayat, A. Taflove, R.S. Ruoff, J.B.""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,37,reference_content,"[138] U. Zimmermann, C. Ebner, Y. Su, T. Bender, Y.D. Bansod, W. Mittelmeier, R. Bader, U. van Rienen, Numerical simulation of electric field distribution around an instrumented total hip stem, Appl. ","[77, 1311, 1012, 1344]",reference_item,0.85,"[""reference content label: [138] U. Zimmermann, C. Ebner, Y. Su, T. Bender, Y.D. Bansod""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,38,reference_content,"[139] L.V. Che, J. Zimmermann, H. Bathel, A. Weizel, H. Seitz, U. van Rienen, Validation-Oriented Modelling of Electrical Stimulation Chambers for Cartilage Tissue Engineering, Springer Nature Switzer","[77, 1346, 1012, 1377]",reference_item,0.85,"[""reference content label: [139] L.V. Che, J. Zimmermann, H. Bathel, A. Weizel, H. Seit""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +17,39,number,17,"[535, 1404, 553, 1418]",noise,0.9,"[""page number label""]",noise,0.9,,unknown_like,short_fragment,False,False +18,0,header,"J. Zimmermann, A.R. Farooqi and U. van Rienen","[74, 67, 344, 83]",noise,0.9,"[""header label""]",noise,0.9,,unknown_like,none,False,False +18,1,header,Heliyon 10 (2024) e38112,"[864, 67, 1016, 83]",noise,0.9,"[""header label""]",noise,0.9,,unknown_like,none,False,False +18,2,reference_content,"[140] A.P. Mazzoleni, B.F. Sisken, R.L. Kahler, Conductivity values of tissue culture medium from 20°C to 40°C, Bioelectromagnetics 7 (1) (1986) 95–99, https://doi.org/10.1002/bem.2250070111.","[74, 106, 1012, 138]",reference_item,0.85,"[""reference content label: [140] A.P. Mazzoleni, B.F. Sisken, R.L. Kahler, Conductivity""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,3,reference_content,"[141] J. Zimmermann, U. van Rienen, Ambiguity in the interpretation of the low-frequency dielectric properties of biological tissues, Bioelectrochemistry 140 (2021) 107773, https://doi.org/10.1016/j.b","[76, 140, 1012, 173]",reference_item,0.85,"[""reference content label: [141] J. Zimmermann, U. van Rienen, Ambiguity in the interpr""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,4,reference_content,"[142] J. Zimmermann, L.V. Che, U. van Rienen, Using tissue-specific numerical models to estimate the dielectric properties of cartilage, in: 2022 International Workshop on Impedance Spectroscopy (IWIS","[75, 175, 1012, 207]",reference_item,0.85,"[""reference content label: [142] J. Zimmermann, L.V. Che, U. van Rienen, Using tissue-s""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,5,reference_content,"[143] W.Y. Gu, M.A. Justiz, Apparatus for measuring the swelling dependent electrical conductivity of charged hydrated soft tissues, J. Biomech. Eng. 124 (6) (2002) 790–793, https://doi.org/10.1115/1.","[76, 209, 1012, 240]",reference_item,0.85,"[""reference content label: [143] W.Y. Gu, M.A. Justiz, Apparatus for measuring the swel""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,6,reference_content,"[144] I. Ermolina, Y. Polevaya, Y. Feldman, Analysis of dielectric spectra of eukaryotic cells by computer modeling, Eur. Biophys. J. 29 (2) (2000) 141–145, https://doi.org/10.1007/s002490050259.","[77, 244, 1012, 275]",reference_item,0.85,"[""reference content label: [144] I. Ermolina, Y. Polevaya, Y. Feldman, Analysis of diel""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,7,reference_content,"[145] V.L. Che, J. Zimmermann, Y. Zhou, X.L. Lu, U. van Rienen, Contributions of deep learning to automated numerical modelling of the interaction of electric fields and cartilage tissue based on 3d i","[76, 278, 1012, 310]",reference_item,0.85,"[""reference content label: [145] V.L. Che, J. Zimmermann, Y. Zhou, X.L. Lu, U. van Rien""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,8,reference_content,"[146] V.L. Che, J. Zimmermann, Y. Zhou, X.L. Lu, U. Van Rienen, Deriving models of cartilaginous cells from confocal fluorescence microscopy images to estimate dielectric properties, IEEE Trans. Magn.","[77, 313, 1012, 345]",reference_item,0.85,"[""reference content label: [146] V.L. Che, J. Zimmermann, Y. Zhou, X.L. Lu, U. Van Rien""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,9,reference_content,"[147] T. Kotnik, L. Rems, M. Tarek, D. Miklavcic, Membrane electroporation and electropermeabilization: mechanisms and models, Annu. Rev. Biophys. 48 (2019) 63–91, https://doi.org/10.1146/annurev-biop","[78, 347, 1012, 379]",reference_item,0.85,"[""reference content label: [147] T. Kotnik, L. Rems, M. Tarek, D. Miklavcic, Membrane e""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,10,reference_content,"[148] R. Lewis, C.H. Feetham, R. Barrett-Jolley, Biochemistry cell volume regulation in chondrocytes, Cell. Physiol. Biochem. 28 (2011) 1111–1122.","[76, 381, 924, 397]",reference_item,0.85,"[""reference content label: [148] R. Lewis, C.H. Feetham, R. Barrett-Jolley, Biochemistr""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,11,reference_content,"[149] R. Lewis, K.E. Asplin, G. Bruce, C. Dart, A. Mobasheri, R. Barrett-Jolley, The role of the membrane potential in chondrocyte volume regulation, J. Cell. Physiol. 226 (11) (2011) 2979–2986, https","[77, 399, 1012, 431]",reference_item,0.85,"[""reference content label: [149] R. Lewis, K.E. Asplin, G. Bruce, C. Dart, A. Mobasheri""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,12,reference_content,"[150] C. Matta, R. Zákány, A. Mobasheri, Voltage-dependent calcium channels in chondrocytes: roles in health and disease, Curr. Rheumatol. Rep. 17 (7) (2015), https://doi.org/10.1007/s11926-015-0521-4","[77, 433, 1011, 465]",reference_item,0.85,"[""reference content label: [150] C. Matta, R. Z\u00e1k\u00e1ny, A. Mobasheri, Voltage-dependent c""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,13,reference_content,"[151] C.J. Doering, G.W. Zamponi, Molecular pharmacology of high voltage-activated calcium channels, J. Bioenerg. Biomembr. 35 (6) (2003) 491–505, https://doi.org/10.1023/B:JOBB.0000008022.50702.1a.","[77, 467, 1013, 499]",reference_item,0.85,"[""reference content label: [151] C.J. Doering, G.W. Zamponi, Molecular pharmacology of ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,14,reference_content,"[152] M.A. Brady, S.D. Waldman, C.R. Ethier, The application of multiple biophysical cues to engineer functional neocartilage for treatment of osteoarthritis. Part I: cellular response, Tissue Eng., P","[77, 502, 1012, 535]",reference_item,0.85,"[""reference content label: [152] M.A. Brady, S.D. Waldman, C.R. Ethier, The application""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,15,reference_content,"[153] M.L. Pall, Electromagnetic fields act via activation of voltage-gated calcium channels to produce beneficial or adverse effects, J. Cell. Mol. Med. 17 (8) (2013) 958–965.","[77, 536, 1012, 567]",reference_item,0.85,"[""reference content label: [153] M.L. Pall, Electromagnetic fields act via activation o""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,16,reference_content,"[154] F.S. Barnes, Interaction of DC and ELF electric fields with biological materials and systems, in: F.S. Barnes, B. Greenebaum (Eds.), Handb. Biol. Eff. Electromagn. Fields, 4th edition, CRC Press","[76, 571, 1012, 603]",reference_item,0.85,"[""reference content label: [154] F.S. Barnes, Interaction of DC and ELF electric fields""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,17,reference_content,"[155] L.A. Kadir, M. Stacey, R. Barrett-Jolley, Emerging roles of the membrane potential: action beyond the action potential, Front. Physiol. 9 (2018) 1661, http://doi.org/10.3389/fphys.2018.01661.","[76, 605, 1013, 637]",reference_item,0.85,"[""reference content label: [155] L.A. Kadir, M. Stacey, R. Barrett-Jolley, Emerging rol""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,18,reference_content,"[156] H. Matsuoka, K. Hashimoto, S. Horikiri, M. Saito, K. Yamamoto, J. Ando, Single-cell imaging of the Ca 2+ influx into bovine endothelial cells occurring in response to an alternating electric sti","[77, 639, 1012, 672]",reference_item,0.85,"[""reference content label: [156] H. Matsuoka, K. Hashimoto, S. Horikiri, M. Saito, K. Y""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,19,reference_content,"[157] C. Grosse, H.P. Schwan, Cellular membrane potentials induced by alternating fields, Biophys. J. 63 (6) (1992) 1632–1642, https://doi.org/10.1016/S0006-3495(92)81740-X.","[77, 674, 1011, 705]",reference_item,0.85,"[""reference content label: [157] C. Grosse, H.P. Schwan, Cellular membrane potentials i""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,20,reference_content,"[158] T. Kotnik, F. Bobanović, D. Miklavčič, Sensitivity of transmembrane voltage induced by applied electric fields - a theoretical analysis, Bioelectrochem. Bioenerg. 43 (2) (1997) 285–291, https://","[76, 708, 1012, 740]",reference_item,0.85,"[""reference content label: [158] T. Kotnik, F. Bobanovi\u0107, D. Miklav\u010di\u010d, Sensitivity of ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,21,reference_content,"[159] T. Kotnik, D. Miklavčič, Analytical description of transmembrane voltage induced by electric fields on spheroidal cells, Biophys. J. 79 (2) (2000) 670–679, https://doi.org/10.1016/S0006-3495(00)","[76, 742, 1011, 775]",reference_item,0.85,"[""reference content label: [159] T. Kotnik, D. Miklav\u010di\u010d, Analytical description of tra""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,22,reference_content,"[160] G. Pucihar, T. Kotnik, B. Valič, D. Miklavčič, Numerical determination of transmembrane voltage induced on irregularly shaped cells, Ann. Biomed. Eng. 34 (4) (2006) 642–652, https://doi.org/10.1","[77, 778, 1012, 810]",reference_item,0.85,"[""reference content label: [160] G. Pucihar, T. Kotnik, B. Vali\u010d, D. Miklav\u010di\u010d, Numeric""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,23,reference_content,"[161] T. Kotnik, G. Pucihar, D. Miklavcic, The cell in the electric field, in: S.T. Kee, J. Gehl, E.W. Lee (Eds.), Clin. Asp. Electroporation, Springer New York, New York, 2011, pp. 19–29.","[77, 811, 1012, 843]",reference_item,0.85,"[""reference content label: [161] T. Kotnik, G. Pucihar, D. Miklavcic, The cell in the e""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,24,reference_content,"[162] R. Glaser, Biophysics: An Introduction, vol. 9783642252, 2nd edition, Springer Berlin Heidelberg, Berlin, Heidelberg, 2012.","[75, 846, 820, 862]",reference_item,0.85,"[""reference content label: [162] R. Glaser, Biophysics: An Introduction, vol. 978364225""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,25,reference_content,"[163] T.B. Jones, Electromechanics of Particles, Cambridge University Press, Cambridge, 1995.","[75, 864, 622, 879]",reference_item,0.85,"[""reference content label: [163] T.B. Jones, Electromechanics of Particles, Cambridge U""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,26,reference_content,"[164] R. Pethig, Dielectrophoresis, John Wiley & Sons, Ltd, Chichester, UK, 2017.","[76, 880, 548, 896]",reference_item,0.85,"[""reference content label: [164] R. Pethig, Dielectrophoresis, John Wiley & Sons, Ltd, ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,27,reference_content,"[165] G.D. O'Connell, A.R. Tan, V. Cui, J.C. Bulinski, J.L. Cook, M. Attur, S.B. Abramson, G.A. Ateshian, C.T. Hung, Human chondrocyte migration behaviour to guide the development of engineered cartil","[76, 898, 1013, 930]",reference_item,0.85,"[""reference content label: [165] G.D. O'Connell, A.R. Tan, V. Cui, J.C. Bulinski, J.L. ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,28,reference_content,"[166] V.L. Sukhorukov, H. Mussauer, U. Zimmermann, The effect of electrical deformation forces on the electropermeabilization of erythrocyte membranes in low- and high- conductivity media, J. Membr. B","[76, 933, 1012, 964]",reference_item,0.85,"[""reference content label: [166] V.L. Sukhorukov, H. Mussauer, U. Zimmermann, The effec""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,29,reference_content,"[167] L.A. MacQueen, M.D. Buschmann, M.R. Wertheimer, Mechanical properties of mammalian cells in suspension measured by electro-deformation, J. Micromech. Microeng. 20 (6) (2010), https://doi.org/10.","[76, 966, 1011, 998]",reference_item,0.85,"[""reference content label: [167] L.A. MacQueen, M.D. Buschmann, M.R. Wertheimer, Mechan""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,30,reference_content,"[168] M. Hronik-Tupaj, W.L. Rice, M. Cronin-Golomb, D.L. Kaplan, I. Georgakoudi, Osteoblastic differentiation and stress response of human mesenchymal stem cells exposed to alternating current electri","[77, 1001, 1013, 1034]",reference_item,0.85,"[""reference content label: [168] M. Hronik-Tupaj, W.L. Rice, M. Cronin-Golomb, D.L. Kap""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,31,reference_content,"[169] R. Dimova, Giant vesicles and their use in assays for assessing membrane phase state, curvature, mechanics, and electrical properties, Annu. Rev. Biophys. 48(2019) 93–119, https://doi.org/10.114","[76, 1036, 1012, 1068]",reference_item,0.85,"[""reference content label: [169] R. Dimova, Giant vesicles and their use in assays for ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,32,reference_content,"[170] S. Aranda, K.A. Riske, R. Lipowsky, R. Dimova, Morphological transitions of vesicles induced by alternating electric fields, Biophys. J. 95 (2) (2008) L19–L21, https://doi.org/10.1529/biophysj.1","[77, 1070, 1012, 1103]",reference_item,0.85,"[""reference content label: [170] S. Aranda, K.A. Riske, R. Lipowsky, R. Dimova, Morphol""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,33,reference_content,"[171] P.M. Vlahovska, Electrohydrodynamics of drops and vesicles, Annu. Rev. Fluid Mech. 51 (2019) 305–330, https://doi.org/10.1146/annurev-fluid-122316-050120.","[78, 1105, 1011, 1136]",reference_item,0.85,"[""reference content label: [171] P.M. Vlahovska, Electrohydrodynamics of drops and vesi""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,34,reference_content,"[172] T. Yamamoto, A.E. Said, R. Dimova, R. Lipowsky, Stability of spherical vesicles in electric fields, Langmuir 26 (14) (2010) 12390–12407, https://doi.org/10.1021/la1011132.","[76, 1138, 1012, 1169]",reference_item,0.85,"[""reference content label: [172] T. Yamamoto, A.E. Said, R. Dimova, R. Lipowsky, Stabil""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,35,reference_content,"[173] P. Peterlin, Frequency-dependent electrodeformation of giant phospholipid vesicles in AC electric field, J. Biol. Phys. 36 (4) (2010) 339–354, https://doi.org/10.1007/s10867-010-9187-3.","[77, 1173, 1012, 1205]",reference_item,0.85,"[""reference content label: [173] P. Peterlin, Frequency-dependent electrodeformation of""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,36,reference_content,"[174] Z. Lu, B.Z. Doulabi, C. Huang, R.A. Bank, M.N. Helder, Collagen type II enhances chondrogenesis in adipose tissue-derived stem cells by affecting cell shape, Tissue Eng., Part A 16 (1) (2010) 81","[77, 1207, 1012, 1239]",reference_item,0.85,"[""reference content label: [174] Z. Lu, B.Z. Doulabi, C. Huang, R.A. Bank, M.N. Helder,""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,37,reference_content,"[175] T.Y. Tsong, D.S. Liu, F. Chauvin, A. Gaigalas, R. Dean Astumian, Electroconformational coupling (ECC): an electric field induced enzyme oscillation for cellular energy and signal transduction, J","[76, 1242, 1012, 1275]",reference_item,0.85,"[""reference content label: [175] T.Y. Tsong, D.S. Liu, F. Chauvin, A. Gaigalas, R. Dean""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,38,reference_content,"[176] A.M. Woodward, D.B. Kell, Confirmation by using mutant strains that the membrane-bound H+-ATPase is the major source of non-linear dielectricity in Saccharomyces cerevisiae, FEMS Microbiol. Lett","[76, 1277, 1012, 1308]",reference_item,0.85,"[""reference content label: [176] A.M. Woodward, D.B. Kell, Confirmation by using mutant""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,39,reference_content,"[177] A.M. Woodward, D.B. Kell, On the nonlinear dielectric properties of biological systems. Saccharomyces cerevisiae, J. Electroanal. Chem. 299 (2) (1990) 83–100, https://doi.org/10.1016/0022-0728(9","[77, 1311, 1011, 1342]",reference_item,0.85,"[""reference content label: [177] A.M. Woodward, D.B. Kell, On the nonlinear dielectric ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,40,reference_content,"[178] D. Nawarathna, J.H. Miller, J.R. Claycomb, G. Cardenas, D. Warmflash, Harmonic response of cellular membrane pumps to low frequency electric fields, Phys. Rev. Lett. 95 (15) (2005) 1–4, https://","[76, 1345, 1012, 1377]",reference_item,0.85,"[""reference content label: [178] D. Nawarathna, J.H. Miller, J.R. Claycomb, G. Cardenas""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +18,41,number,18,"[535, 1404, 554, 1418]",noise,0.9,"[""page number label""]",noise,0.9,,unknown_like,short_fragment,False,False +19,0,header,"J. Zimmermann, A.R. Farooqi and U. van Rienen","[74, 68, 344, 83]",noise,0.9,"[""header label""]",noise,0.9,,unknown_like,none,False,False +19,1,header,Heliyon 10 (2024) e38112,"[864, 68, 1016, 83]",noise,0.9,"[""header label""]",noise,0.9,,unknown_like,none,False,False +19,2,reference_content,"[179] D. Nawarathna, J.R. Claycomb, J.H. Miller, M.J. Benedik, Nonlinear dielectric spectroscopy of live cells using superconducting quantum interference devices, Appl. Phys. Lett. 86 (2) (2005) 2–5, ","[75, 107, 1011, 139]",reference_item,0.85,"[""reference content label: [179] D. Nawarathna, J.R. Claycomb, J.H. Miller, M.J. Benedi""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +19,3,reference_content,"[180] E.F. Treo, C.J. Felice, Non-linear dielectric spectroscopy of microbiological suspensions, Biomed. Eng. Online 8 (2009) 19, https://doi.org/10.1186/1475-925X-8-19.","[77, 141, 1012, 171]",reference_item,0.85,"[""reference content label: [180] E.F. Treo, C.J. Felice, Non-linear dielectric spectros""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +19,4,reference_content,"[181] G.T. Mercier, A. Palanisami, J.H. Miller, Nonlinear dielectric spectroscopy for label-free detection of respiratory activity in whole cells, Biosens. Bioelectron. 25 (9) (2010) 2107–2114, https:","[77, 175, 1011, 207]",reference_item,0.85,"[""reference content label: [181] G.T. Mercier, A. Palanisami, J.H. Miller, Nonlinear di""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +19,5,reference_content,"[182] D. Nawarathna, J.R. Claycomb, G. Cardenas, J. Gardner, D. Warmflash, J.H. Miller, W.R. Widger, Harmonic generation by yeast cells in response to low-frequency electric fields, Phys. Rev. E, Stat","[76, 210, 1012, 241]",reference_item,0.85,"[""reference content label: [182] D. Nawarathna, J.R. Claycomb, G. Cardenas, J. Gardner,""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +19,6,reference_content,"[183] A. Mobasheri, E. Trujillo, M.F. Arteaga, P. Martin-Vasallo, Na +, K +-ATPase subunit composition in a human chondrocyte cell line; evidence for the presence of $ \alpha_{1} $, $ \alpha_{3} $, ","[77, 244, 1012, 275]",reference_item,0.85,"[""reference content label: [183] A. Mobasheri, E. Trujillo, M.F. Arteaga, P. Martin-Vas""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +19,7,reference_content,"[184] M.M. Maleckar, P. Martin-Vasallo, W.R. Giles, A. Mobasheri, Physiological effects of the electrogenic current generated by the Na+/K+ pump in mammalian articular chondrocytes, Bioelectricity 2 (","[77, 278, 1011, 310]",reference_item,0.85,"[""reference content label: [184] M.M. Maleckar, P. Martin-Vasallo, W.R. Giles, A. Mobas""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +19,8,reference_content,"[185] B.-j. Lin, S.-h. Tsao, A. Chen, S.-K. Hu, L. Chao, P.-h.G. Chao, Lipid rafts sense and direct electric field-induced migration, Proc. Natl. Acad. Sci. 114 (32) (2017) 8568–8573, https://doi.org/","[77, 312, 1011, 344]",reference_item,0.85,"[""reference content label: [185] B.-j. Lin, S.-h. Tsao, A. Chen, S.-K. Hu, L. Chao, P.-""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +19,9,reference_content,"[186] R.C. Lee, T.R. Gowrishankar, R.M. Basch, P.K. Patel, D.E. Golan, Cell shape-dependent rectification of surface receptor transport in a sinusoidal electric field, Biophys. J. 64 (1) (1993) 44–57,","[77, 347, 1012, 377]",reference_item,0.85,"[""reference content label: [186] R.C. Lee, T.R. Gowrishankar, R.M. Basch, P.K. Patel, D""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +19,10,reference_content,"[187] R. Ruhlen, K. Marberry, The chondrocyte primary cilium, Osteoarthr. Cartil. 22 (8) (2014) 1071–1076, https://doi.org/10.1016/j.joca.2014.05.011.","[76, 380, 960, 397]",reference_item,0.85,"[""reference content label: [187] R. Ruhlen, K. Marberry, The chondrocyte primary cilium""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +19,11,reference_content,"[188] W. Lee, H.A. Leddy, Y. Chen, S.H. Lee, N.A. Zelenski, A.L. McNulty, J. Wu, K.N. Beicker, J. Coles, S. Zauscher, J. Grandl, F. Sachs, F. Gulak, W.B. Liedtke, Synergy between Piezo1 and Piezo2 cha","[78, 400, 1011, 446]",reference_item,0.85,"[""reference content label: [188] W. Lee, H.A. Leddy, Y. Chen, S.H. Lee, N.A. Zelenski, ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +19,12,reference_content,"[189] N. Varady, A. Grodzinsky, Osteoarthritis year in review 2015: mechanics, Osteoarthr. Cartil. 24 (1) (2016) 27–35, https://doi.org/10.1016/j.joca.2015.08.018","[77, 449, 1007, 465]",reference_item,0.85,"[""reference content label: [189] N. Varady, A. Grodzinsky, Osteoarthritis year in revie""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +19,13,reference_content,"[190] C.J. O'Conor, H.A. Leddy, H.C. Benefield, W.B. Liedtke, F. Guilak, TRPV4-mediated mechanotransduction regulates the metabolic response of chondrocytes to dynamic loading, Proc. Natl. Acad. Sci. ","[78, 465, 1012, 499]",reference_item,0.85,"[""reference content label: [190] C.J. O'Conor, H.A. Leddy, H.C. Benefield, W.B. Liedtke""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +19,14,reference_content,"[191] M. Weißenberger, M.H. Weißenberger, M. Wagenbrenner, T. Heinz, J. Reboredo, B.M. Holzapfel, M. Rudert, C.H. Evans, A.F. Steinert, Different types of cartilage neotissue fabricated from collagen ","[77, 501, 1013, 550]",reference_item,0.85,"[""reference content label: [191] M. Wei\u00dfenberger, M.H. Wei\u00dfenberger, M. Wagenbrenner, T""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +19,15,reference_content,"[192] N. More, G. Kapusetti, Piezoelectric material – a promising approach for bone and cartilage regeneration, Med. Hypotheses 108 (2017) 10–16, https://doi.org/10.1016/j.mehy.2017.07.021.","[77, 553, 1011, 584]",reference_item,0.85,"[""reference content label: [192] N. More, G. Kapusetti, Piezoelectric material \u2013 a prom""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +19,16,reference_content,"[193] J. Jacob, N. More, K. Kalia, G. Kapusetti, Piezoelectric smart biomaterials for bone and cartilage tissue engineering, Inflamm. Regen. 38 (2018) 2, https://doi.org/10.1186/s41232-018-0059-8.","[78, 587, 1012, 617]",reference_item,0.85,"[""reference content label: [193] J. Jacob, N. More, K. Kalia, G. Kapusetti, Piezoelectr""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +19,17,reference_content,"[194] R. Vaiciuleviciute, I. Uzieliene, P. Bernotas, V. Novickij, A. Alaburda, E. Bernotiene, Electrical stimulation in cartilage tissue engineering, Bioengineering 10(2023) 1–19, https://doi.org/10.3","[78, 620, 1013, 653]",reference_item,0.85,"[""reference content label: [194] R. Vaiciuleviciute, I. Uzieliene, P. Bernotas, V. Novi""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_bracket,True,True +19,18,number,19,"[534, 1404, 554, 1418]",noise,0.9,"[""page number label""]",noise,0.9,,unknown_like,short_fragment,False,False diff --git a/tests/fixtures/ocr_real_papers/PZ8B59K4/block_trace.csv b/tests/fixtures/ocr_real_papers/PZ8B59K4/block_trace.csv new file mode 100644 index 00000000..aac6e95c --- /dev/null +++ b/tests/fixtures/ocr_real_papers/PZ8B59K4/block_trace.csv @@ -0,0 +1,528 @@ +page,block_id,raw_label,content_preview,bbox,role,role_confidence,evidence,seed_role,seed_confidence,zone,style_family,marker_type,render_default,index_default +2,0,header,Journal Pre-proof,"[503, 57, 689, 82]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p2 y=57/1584"", ""page_1_preproof_cover_dropped_upstream""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +2,1,doc_title,Re-tear after arthroscopic rotator cuff tear surgery: risk analysis using machine learning,"[150, 145, 1071, 175]",paper_title,0.8,"[""page-1 zone title_zone: Re-tear after arthroscopic rotator cuff tear surgery: risk a""]",paper_title,0.8,frontmatter_main_zone,support_like,none,True,True +2,2,text,"Issei Shinohara, M.D., Ph.D. $ ^{1} $, Yutaka Mifune $ ^{*} $, M.D., Ph.D. $ ^{1} $, Atsuyuki Inui, M.D., Ph.D. $ ^{1} $, Hanako Nishimoto, M.D., Ph.D. $ ^{1} $, Tomoya Yoshikawa, M.D., Ph.D. $ ^{1} $","[137, 259, 1069, 456]",authors,0.8,"[""page-1 zone author_zone: Issei Shinohara, M.D., Ph.D. $ ^{1} $, Yutaka Mifune $ ^{*} ""]",authors,0.8,frontmatter_main_zone,support_like,none,True,True +2,3,text,"1. Department of Orthopaedic Surgery, Kobe University Graduate School of Medicine, 5-2, Kusunoki-cho7, Chuo-ku, Kobe, Hyogo 650-0017, Japan","[140, 482, 1041, 556]",affiliation,0.8,"[""page-1 zone affiliation_zone: 1. Department of Orthopaedic Surgery, Kobe University Gradua""]",affiliation,0.8,body_zone,reference_like,reference_numeric_dot,True,True +2,4,text,"2. Department of Orthopaedic Surgery, Himeji St Mary's Hospital, Himeji, Hyogo, Japan +**These authors contributed equally to this work.**","[138, 581, 1017, 653]",frontmatter_support,0.78,"[""first-surviving-page support text: 2. Department of Orthopaedic Surgery, Himeji St Mary's Hospi""]",frontmatter_support,0.78,body_zone,reference_like,reference_numeric_dot,True,True +2,5,text,"*Correspondence to: Yutaka Mifune, MD, PhD, Department of Orthopaedic Surgery, Kobe University Graduate School of Medicine, 7-5-1 Kusunoki-cho, Chuo-ku, Kobe, 650-0017 Hyogo, Japan; Phone: +81-78-382-","[138, 680, 1089, 796]",frontmatter_noise,0.8,"[""page-1 zone journal_furniture_zone: *Correspondence to: Yutaka Mifune, MD, PhD, Department of Or""]",frontmatter_noise,0.8,frontmatter_main_zone,support_like,none,False,False +2,6,text,The Kobe University Graduate School of Medicine Ethics Committee approved this study (no. 1735).,"[139, 878, 1056, 948]",affiliation,0.8,"[""page-1 zone affiliation_zone: The Kobe University Graduate School of Medicine Ethics Commi""]",affiliation,0.8,frontmatter_main_zone,support_like,none,True,True +2,7,text,Running-head title: Machine learning predicts rotator cuff re-tear,"[138, 977, 775, 1007]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,frontmatter_main_zone,support_like,none,True,True +2,8,paragraph_title,Disclaimers:,"[139, 1035, 276, 1063]",sub_subsection_heading,0.6,"[""unnumbered paragraph_title, inferred level sub_subsection_heading: Disclaimers:""]",sub_subsection_heading,0.6,frontmatter_main_zone,heading_like,short_fragment,True,True +2,9,text,Funding: No funding was disclosed by the authors.,"[137, 1092, 636, 1122]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,frontmatter_main_zone,support_like,none,True,True +2,10,text,"Conflicts of interest: The authors, their immediate families, and any research foundation with which they are affiliated have not received any financial payments or other benefits from any commercial ","[137, 1149, 1044, 1262]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +3,0,aside_text,"1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27","[81, 149, 115, 1528]",unknown_structural,0.2,"[""unrecognized label 'aside_text'""]",unknown_structural,0.2,,reference_like,reference_numeric_dot,False,True +3,1,header,Journal Pre-proof,"[480, 60, 678, 88]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p3 y=60/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +3,2,abstract,Re-tear after arthroscopic rotator cuff tear surgery: risk analysis using machine learning,"[168, 143, 1019, 177]",abstract_body,0.85,"[""abstract label from Paddle OCR""]",abstract_body,0.85,body_zone,body_like,none,True,True +3,3,paragraph_title,Abstract,"[141, 225, 241, 257]",abstract_heading,0.95,"[""abstract heading""]",abstract_heading,0.95,body_zone,heading_like,short_fragment,True,True +3,4,abstract,"Background: Postoperative rotator cuff re-tear after arthroscopic rotator cuff repair (ARCR) is still a major problem. Various risk factors such as age, gender, and tear size have been reported. Recen","[136, 269, 1053, 1533]",abstract_body,0.85,"[""abstract label from Paddle OCR""]",abstract_body,0.85,body_zone,body_like,none,True,True +3,5,number,1,"[588, 1548, 602, 1570]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +4,0,aside_text,"28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52","[79, 147, 114, 1496]",unknown_structural,0.2,"[""unrecognized label 'aside_text'""]",unknown_structural,0.2,,reference_like,reference_numeric_dot,False,True +4,1,header,Journal Pre-proof,"[480, 61, 677, 88]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p4 y=61/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +4,2,text,"Conclusions: The ML classifier model predicted re-tears after ARCR with high accuracy, and the AI model showed that the most important characteristics affecting re-tears were age and imaging findings,","[133, 147, 1049, 561]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +4,3,text,Postoperative re-tear is still a problem in arthroscopic rotator cuff repair (ARCR) for degenerative rotator cuff tears (RCTs). The reported re-tear rate after ARCR varies depending on the suture meth,"[137, 754, 1052, 1503]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +4,4,number,2,"[587, 1548, 603, 1570]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +5,0,aside_text,"53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77","[79, 146, 115, 1497]",unknown_structural,0.2,"[""unrecognized label 'aside_text'""]",unknown_structural,0.2,,reference_like,reference_numeric_dot,False,True +5,1,header,Journal Pre-proof,"[480, 61, 676, 87]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p5 y=61/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +5,2,text,"classification by MRI images. There are few reports taking stump classification into account that may be useful for predicting re-tears after ARCR. In this study, we focused on the analysis of clinica","[138, 149, 1051, 1118]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +5,3,paragraph_title,Materials and Methods,"[141, 1251, 386, 1278]",section_heading,0.9,"[""explicit scholarly heading: Materials and Methods""]",section_heading,0.9,body_zone,heading_like,canonical_section_name,True,True +5,4,text,Ethical approval,"[141, 1305, 311, 1334]",unknown_structural,0.3,"[""short text, uncertain role""]",unknown_structural,0.3,body_zone,body_like,short_fragment,False,True +5,5,text,"This study was approved by the appropriate review board, and informed consent was obtained from all patients involved.","[140, 1358, 1049, 1444]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +5,6,text,Data collection,"[141, 1470, 296, 1498]",unknown_structural,0.3,"[""short text, uncertain role""]",unknown_structural,0.3,body_zone,body_like,short_fragment,False,True +5,7,number,3,"[588, 1549, 603, 1570]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +6,0,aside_text,"78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102","[78, 147, 114, 1496]",unknown_structural,0.2,"[""unrecognized label 'aside_text'""]",unknown_structural,0.2,,reference_like,reference_numeric_dot,False,True +6,1,header,Journal Pre-proof,"[480, 61, 677, 88]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p6 y=61/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +6,2,text,"Patients who underwent ARCR for degenerative complete rotator cuff tears from April 2017 to June 2021 at our institution or affiliated institutions were included. ARCR was performed by two surgeons, Y","[138, 144, 1048, 662]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +6,3,text,"Each patient parameter is expressed as mean ± standard deviation. To compare patient backgrounds with and without re-tears, the Mann–Whitney U test was used to compare two variables (e.g., gender) and","[137, 699, 1030, 894]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +6,4,paragraph_title,Machine learning,"[141, 918, 321, 948]",subsection_heading,0.6,"[""unnumbered paragraph_title, inferred level subsection_heading: Machine learning""]",subsection_heading,0.6,body_zone,heading_like,short_fragment,True,True +6,5,text,"The data collection and ML workflow are shown in Fig. 2. Five supervised algorithms were applied to validate clinical data $ ^{20,22} $ (logistic regression, random forest, $ ^{1} $ adaptive boost (Ad","[137, 973, 1054, 1502]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +6,6,number,4,"[588, 1549, 603, 1570]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +7,0,aside_text,"103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127","[69, 145, 115, 1499]",unknown_structural,0.2,"[""unrecognized label 'aside_text'""]",unknown_structural,0.2,,reference_like,reference_numeric_dot,False,True +7,1,header,Journal Pre-proof,"[480, 61, 677, 88]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p7 y=61/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +7,2,text,"method that attempts to obtain better predictions by using multiple training models and performing majority voting on the results. $ ^{1} $ On the other hand, Adaboost, Catboost, and LightGBM are grad","[135, 145, 1059, 1504]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +7,3,number,5,"[587, 1548, 603, 1570]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +8,0,aside_text,"128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152","[68, 146, 115, 1504]",unknown_structural,0.2,"[""unrecognized label 'aside_text'""]",unknown_structural,0.2,,reference_like,reference_numeric_dot,False,True +8,1,header,Journal Pre-proof,"[481, 61, 676, 88]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p8 y=61/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +8,2,text,"randomly shuffled; $ ^{12} $ the Shapley additive explanation (SHAP) value is defined as the contribution of each feature to the model prediction based on game theory. $ ^{40} $ Briefly, it is a metho","[137, 144, 1037, 340]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +8,3,paragraph_title,Results Study participants and statistical analysis,"[138, 422, 550, 508]",subsection_heading,0.6,"[""unnumbered paragraph_title, inferred level subsection_heading: Results Study participants and statistical analysis""]",subsection_heading,0.6,body_zone,heading_like,none,True,True +8,4,footer,,"[138, 477, 550, 508]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,empty,False,False +8,5,text,"Of the 582 cases who underwent ARCR at our institution or affiliated institutions, 353 were finally included after excluding re-tears (12 cases), traumatic tears (182 cases), and patients who required","[137, 531, 1052, 837]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +8,6,paragraph_title,Prediction of rotator cuff re-tear in each ML model,"[139, 873, 641, 904]",subsection_heading,0.6,"[""unnumbered paragraph_title, inferred level subsection_heading: Prediction of rotator cuff re-tear in each ML model""]",subsection_heading,0.6,body_zone,heading_like,none,True,True +8,7,text,"Fig. 3 shows a heat map representing the correlation between each parameter and rotator cuff re-tear. Warm colors indicate a positive correlation, while cold colors indicate a negative correlation. Th","[137, 923, 1052, 1283]",body_paragraph,0.9,"[""figure caption candidate (body narrative): Fig. 3 shows a heat map representing the correlation between""]",figure_caption_candidate,0.9,display_zone,legend_like,figure_number,True,True +8,8,paragraph_title,Important features of the predictor variables,"[140, 1310, 576, 1343]",subsection_heading,0.6,"[""unnumbered paragraph_title, inferred level subsection_heading: Important features of the predictor variables""]",subsection_heading,0.6,body_zone,heading_like,none,True,True +8,9,text,"To detect the importance of each parameter for predicting postoperative rotator cuff re-tear, feature importance was calculated for the LightGBM model, which showed the highest AUC. Age, stump classif","[137, 1362, 1042, 1510]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +8,10,number,6,"[588, 1549, 603, 1571]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +9,0,aside_text,"153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177","[68, 147, 115, 1496]",unknown_structural,0.2,"[""unrecognized label 'aside_text'""]",unknown_structural,0.2,,reference_like,reference_numeric_dot,False,True +9,1,header,Journal Pre-proof,"[480, 61, 677, 88]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p9 y=61/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +9,2,text,"associated with postoperative rotator cuff re-tear in the LightGBM model (Fig. 5a). The SHAP score showed stump classification, tear size, and age as important characteristics. As shown in Fig. 5b, st","[138, 144, 1053, 343]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +9,3,paragraph_title,Discussion,"[140, 476, 256, 506]",section_heading,0.9,"[""explicit scholarly heading: Discussion""]",section_heading,0.9,body_zone,heading_like,canonical_section_name,True,True +9,4,text,"The ML classification models predicted re-tears after ARCR with high accuracy. Among the five used models, LightGBM showed the highest AUC. In the LightGBM model, age, stump classification, and tear s","[138, 532, 1042, 728]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +9,5,text,"In the last decades, AI techniques based on mathematical modeling have been developed; ML is one of the AI-based approaches, and ML models are increasingly integrated into clinical diagnosis and the p","[137, 753, 1054, 1504]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +9,6,number,7,"[588, 1548, 603, 1570]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +10,0,aside_text,"178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202","[69, 145, 115, 1497]",unknown_structural,0.2,"[""unrecognized label 'aside_text'""]",unknown_structural,0.2,,reference_like,reference_numeric_dot,False,True +10,1,header,Journal Pre-proof,"[480, 61, 677, 88]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p10 y=61/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +10,2,text,"framework that uses a decision-tree-based learning algorithm, adopting a histogram algorithm and a depth-limited leaf-wise leaf growth strategy. $ ^{41} $ This strategy increases computational efficie","[134, 153, 1054, 1499]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +10,3,number,8,"[588, 1548, 603, 1570]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +11,0,aside_text,"203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229","[68, 146, 115, 1479]",unknown_structural,0.2,"[""unrecognized label 'aside_text'""]",unknown_structural,0.2,reference_zone,reference_like,reference_numeric_dot,False,True +11,1,header,Journal Pre-proof,"[480, 61, 677, 88]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p11 y=61/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +11,2,text,"This study has some limitations. First, although the model performed well on the present data set, the number of cases in the original data is not large. Second, we did not consider factors by procedu","[137, 146, 1055, 564]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +11,3,paragraph_title,Conclusion,"[139, 641, 264, 670]",section_heading,0.9,"[""explicit scholarly heading: Conclusion""]",section_heading,0.9,body_zone,heading_like,canonical_section_name,True,True +11,4,text,"The ML classifier model predicted re-tears after ARCR with high accuracy, and the AI model showed that the most important characteristics affecting re-tears were age and imaging findings, including st","[136, 698, 1053, 1004]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +11,5,paragraph_title,References,"[140, 1083, 260, 1112]",reference_heading,0.9,"[""references heading: References""]",reference_heading,0.9,reference_zone,heading_like,short_fragment,True,True +11,6,reference_content,"1. Alderden J, Pepper GA, Wilson A, Whitney JD, Richardson S, Butcher R, et al. Predicting pressure injury in critical care patients: A machine-learning model. Am J Crit Care 2018;27:461-468. doi: $ ","[139, 1125, 1037, 1235]",reference_item,0.85,"[""reference content label: 1. Alderden J, Pepper GA, Wilson A, Whitney JD, Richardson S""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,7,reference_content,"2. Barth J, Andrieu K, Fotiadis E, Hannink G, Barthelemy R, Saffarini M. Critical period and risk factors for retear following arthroscopic repair of the rotator cuff. Knee Surg Sports Traumatol Arthr","[139, 1250, 1040, 1361]",reference_item,0.85,"[""reference content label: 2. Barth J, Andrieu K, Fotiadis E, Hannink G, Barthelemy R, ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,8,reference_content,"3. Cho NS, Lee BG, Rhee YG. Arthroscopic rotator cuff repair using a suture bridge technique: is the repair integrity actually maintained? Am J Sports Med 2011;39:2108-2116. doi: $ \underline{\text{1","[139, 1375, 955, 1483]",reference_item,0.85,"[""reference content label: 3. Cho NS, Lee BG, Rhee YG. Arthroscopic rotator cuff repair""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,9,number,9,"[588, 1548, 603, 1569]",noise,0.9,"[""page number label""]",noise,0.9,tail_nonref_hold_zone,unknown_like,short_fragment,False,False +12,0,aside_text,"230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263","[67, 145, 114, 1538]",unknown_structural,0.2,"[""unrecognized label 'aside_text'""]",unknown_structural,0.2,reference_zone,reference_like,reference_numeric_dot,False,True +12,1,header,Journal Pre-proof,"[479, 61, 678, 88]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p12 y=61/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +12,2,reference_content,"4. Cho NS, Rhee YG. The factors affecting the clinical outcome and integrity of arthroscopically repaired rotator cuff tears of the shoulder. Clin Orthop Surg 2009;1:96-104. doi: $ \underline{\text{1","[139, 145, 952, 256]",reference_item,0.85,"[""reference content label: 4. Cho NS, Rhee YG. The factors affecting the clinical outco""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +12,3,reference_content,"5. Cho NS, Yi JW, Lee BG, Rhee YG. Retear patterns after arthroscopic rotator cuff repair: single-row versus suture bridge technique. Am J Sports Med 2010;38:664-671. doi:10.1177/0363546509350081.","[139, 270, 1021, 381]",reference_item,0.85,"[""reference content label: 5. Cho NS, Yi JW, Lee BG, Rhee YG. Retear patterns after art""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +12,4,reference_content,"6. Chona DV, Lakomkin N, Lott A, Workman AD, Henry AC, Kuntz AF, et al. The timing of retears after arthroscopic rotator cuff repair. J Shoulder Elbow Surg 2017;26:2054-2059. doi: 10.1016/j.jse.2017.0","[138, 393, 1037, 505]",reference_item,0.85,"[""reference content label: 6. Chona DV, Lakomkin N, Lott A, Workman AD, Henry AC, Kuntz""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +12,5,reference_content,"7. Chung SW, Kim JY, Kim MH, Kim SH, Oh JH. Arthroscopic repair of massive rotator cuff tears: outcome and analysis of factors associated with healing failure or poor postoperative function. Am J Spor","[140, 517, 1046, 670]",reference_item,0.85,"[""reference content label: 7. Chung SW, Kim JY, Kim MH, Kim SH, Oh JH. Arthroscopic rep""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +12,6,reference_content,"8. Cofield RH, Parvizi J, Hoffmeyer PJ, Lanzer WL, Ilstrup DM, Rowland CM. Surgical repair of chronic rotator cuff tears. A prospective long-term study. J Bone Joint Surg Am. 2001;83:71-77.","[138, 684, 1027, 794]",reference_item,0.85,"[""reference content label: 8. Cofield RH, Parvizi J, Hoffmeyer PJ, Lanzer WL, Ilstrup D""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +12,7,reference_content,"9. Collin P, Betz M, Herve A, Walch G, Mansat P, Favard L, et al. Clinical and structural outcome 20 years after repair of massive rotator cuff tears. J Shoulder Elbow Surg 2020;29:521-526. doi: $ \u","[137, 808, 1012, 918]",reference_item,0.85,"[""reference content label: 9. Collin P, Betz M, Herve A, Walch G, Mansat P, Favard L, e""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +12,8,reference_content,"10. DeOrio JK, Cofield RH. Results of a second attempt at surgical repair of a failed initial rotator-cuff repair. J Bone Joint Surg Am. 1984;66:563-567.","[141, 932, 1020, 1001]",reference_item,0.85,"[""reference content label: 10. DeOrio JK, Cofield RH. Results of a second attempt at su""]",reference_item,0.85,reference_zone,unknown_like,heading_numbered,True,True +12,9,reference_content,"11. Diebold G, Lam P, Walton J, Murrell GAC. Relationship between age and rotator cuff retear: A study of 1,600 consecutive rotator cuff repairs. J Bone Joint Surg Am. 2017;99:1198-1205. doi: $ \unde","[142, 1015, 1011, 1126]",reference_item,0.85,"[""reference content label: 11. Diebold G, Lam P, Walton J, Murrell GAC. Relationship be""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +12,10,reference_content,"12. Doyen S, Taylor H, Nicholas P, Crawford L, Young I, Sughrue ME. Hollow-tree super: A directional and scalable approach for feature importance in boosted tree models. PLOS ONE 2021;16:e0258658. doi","[142, 1139, 1038, 1250]",reference_item,0.85,"[""reference content label: 12. Doyen S, Taylor H, Nicholas P, Crawford L, Young I, Sugh""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +12,11,reference_content,"13. Elbuluk AM, Coxe FR, Fabricant PD, Ramos NL, Alaia MJ, Jones KJ. Does medial-row fixation technique affect the retear rate and functional outcomes after double-row transosseous-equivalent rotator ","[142, 1263, 1031, 1415]",reference_item,0.85,"[""reference content label: 13. Elbuluk AM, Coxe FR, Fabricant PD, Ramos NL, Alaia MJ, J""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +12,12,reference_content,"14. Elkins AR, Lam PH, Murrell GAC. Duration of surgery and learning curve affect rotator cuff repair retear rates: A post hoc analysis of 1600 cases. Orthop J Sports Med 2020;8. doi: 10.1177/23259671","[142, 1429, 1053, 1538]",reference_item,0.85,"[""reference content label: 14. Elkins AR, Lam PH, Murrell GAC. Duration of surgery and ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +12,13,number,10,"[582, 1548, 609, 1570]",noise,0.9,"[""page number label""]",noise,0.9,,unknown_like,short_fragment,False,False +13,0,aside_text,"264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297","[67, 145, 113, 1539]",unknown_structural,0.2,"[""unrecognized label 'aside_text'""]",unknown_structural,0.2,reference_zone,reference_like,reference_numeric_dot,False,True +13,1,header,Journal Pre-proof,"[479, 61, 678, 88]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p13 y=61/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +13,2,reference_content,"15. Gladstone JN, Bishop JY, Lo IK, Flatow EL. Fatty infiltration and atrophy of the rotator cuff do not improve after rotator cuff repair and correlate with poor functional outcome. Am J Sports Med 2","[142, 146, 1028, 257]",reference_item,0.85,"[""reference content label: 15. Gladstone JN, Bishop JY, Lo IK, Flatow EL. Fatty infiltr""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +13,3,reference_content,"17. Goutallier D, Postel JM, Bernageau J, Lavau L, Voisin MC. Fatty muscle degeneration in cuff ruptures. Pre- and postoperative evaluation by CT scan. Clin Orthop Relat Res 1994;304:78-83.","[141, 269, 1039, 379]",reference_item,0.85,"[""reference content label: 17. Goutallier D, Postel JM, Bernageau J, Lavau L, Voisin MC""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +13,4,reference_content,"16. Goutallier D, Postel JM, Gleyze P, Leguilloux P, Van Driessche S. Influence of cuff muscle fatty degeneration on anatomic and functional outcomes after simple suture of full-thickness tears. J Sho","[143, 393, 1045, 545]",reference_item,0.85,"[""reference content label: 16. Goutallier D, Postel JM, Gleyze P, Leguilloux P, Van Dri""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +13,5,reference_content,"18. Gowd AK, Agarwalla A, Beck EC, Rosas S, Waterman BR, Romeo AA, et al. Prediction of total healthcare cost following total shoulder arthroplasty utilizing machine learning. J Shoulder Elbow Surg 20","[143, 559, 1035, 671]",reference_item,0.85,"[""reference content label: 18. Gowd AK, Agarwalla A, Beck EC, Rosas S, Waterman BR, Rom""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +13,6,reference_content,"19. Gumina S, Carbone S, Campagna V, Candela V, Sacchetti FM, Giannicola G. The impact of aging on rotator cuff tear size. Musculoskelet Surg 2013;97;Suppl 1:69-72. doi:10.1007/s12306-013-0263-2.","[142, 684, 1037, 794]",reference_item,0.85,"[""reference content label: 19. Gumina S, Carbone S, Campagna V, Candela V, Sacchetti FM""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +13,7,reference_content,"20. Hancock JT, Khoshgoftaar TM. CatBoost for big data: an interdisciplinary review. J Big Data 2020;7:94. doi: $ \underline{\text{10.1186/s40537-020-00369-8.}} $","[137, 807, 1032, 877]",reference_item,0.85,"[""reference content label: 20. Hancock JT, Khoshgoftaar TM. CatBoost for big data: an i""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +13,8,reference_content,21. Harris JD. Editorial commentary: personalized hip arthroscopy outcome prediction using machine learning—the future is here. Arthroscopy 2021;37:1498-1502. doi: $ \underline{\text{10.1016/j.arthro,"[138, 891, 1038, 1001]",reference_item,0.85,"[""reference content label: 21. Harris JD. Editorial commentary: personalized hip arthro""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +13,9,reference_content,"22. Inui A, Nishimoto H, Mifune Y, Yoshikawa T, Shinohara I, Furukawa T, et al. Screening for osteoporosis from blood test data in elderly women using a machine learning approach. Bioengineering (Base","[138, 1015, 1028, 1128]",reference_item,0.85,"[""reference content label: 22. Inui A, Nishimoto H, Mifune Y, Yoshikawa T, Shinohara I,""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +13,10,reference_content,"23. Ishitani E, Harada N, Sonoda Y, Okada F, Yara T, Katsuki I. Tendon stump type on magnetic resonance imaging is a predictive factor for retear after arthroscopic rotator cuff repair. J Shoulder Elb","[139, 1140, 1034, 1290]",reference_item,0.85,"[""reference content label: 23. Ishitani E, Harada N, Sonoda Y, Okada F, Yara T, Katsuki""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +13,11,reference_content,"24. Jeong HY, Kim HJ, Jeon YS, Rhee YG. Factors predictive of healing in large rotator cuff tears: is it possible to predict retear preoperatively? Am J Sports Med 2018;46:1693-1700. doi: $ \underlin","[139, 1304, 1036, 1415]",reference_item,0.85,"[""reference content label: 24. Jeong HY, Kim HJ, Jeon YS, Rhee YG. Factors predictive o""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +13,12,reference_content,"25. Kunze KN, Police EM, Chahla J. Response to ""regarding 'editorial commentary': artificial intelligence in sports medicine diagnosis needs to improve'. Arthroscopy 2021;37:1367-1368. doi: $ \underl","[138, 1428, 1035, 1539]",reference_item,0.85,"[""reference content label: 25. Kunze KN, Police EM, Chahla J. Response to \""regarding 'e""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +13,13,number,11,"[583, 1548, 608, 1570]",noise,0.9,"[""page number label""]",noise,0.9,,unknown_like,short_fragment,False,False +14,0,aside_text,"298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331","[68, 145, 113, 1538]",unknown_structural,0.2,"[""unrecognized label 'aside_text'""]",unknown_structural,0.2,reference_zone,reference_like,reference_numeric_dot,False,True +14,1,header,Journal Pre-proof,"[480, 61, 677, 88]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p14 y=61/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +14,2,reference_content,"26. Kunze KN, Police EM, Ranawat AS, Randsborg PH, Williams RJ, Allen AA, et al. Application of machine learning algorithms to predict clinically meaningful improvement after arthroscopic anterior cru","[140, 145, 1013, 299]",reference_item,0.85,"[""reference content label: 26. Kunze KN, Police EM, Ranawat AS, Randsborg PH, Williams ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +14,3,reference_content,"27. Li C, Alike Y, Hou J, Long Y, Zheng Z, Meng K, et al. Machine learning model successfully identifies important clinical features for predicting outpatients with rotator cuff tears. Knee Surg Sport","[139, 310, 988, 463]",reference_item,0.85,"[""reference content label: 27. Li C, Alike Y, Hou J, Long Y, Zheng Z, Meng K, et al. Ma""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +14,4,reference_content,"28. Lubowitz JH, Brand JC, Rossi MJ. Preoperative shoulder corticosteroid injection is associated with revision after primary rotator cuff repair. Arthroscopy 2019;35:693-694. doi: $ \underline{\text","[139, 477, 983, 587]",reference_item,0.85,"[""reference content label: 28. Lubowitz JH, Brand JC, Rossi MJ. Preoperative shoulder c""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +14,5,reference_content,"29. Malavolta EA, Assunção JH, Ramos FF, Ferreira TC, Gracitelli ME, Bordalo-Rodrigues M, et al. Serial structural MRI evaluation of arthroscopy rotator cuff repair: does Sugaya's classification corre","[139, 601, 1029, 754]",reference_item,0.85,"[""reference content label: 29. Malavolta EA, Assun\u00e7\u00e3o JH, Ramos FF, Ferreira TC, Gracit""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +14,6,reference_content,"30. Matsuo K, Aihara H, Nakai T, Morishita A, Tohma Y, Kohmura E. Machine learning to predict in-hospital morbidity and mortality after traumatic brain injury. J Neurotrauma 2020;37:202-210. doi: 10.1","[138, 768, 1046, 876]",reference_item,0.85,"[""reference content label: 30. Matsuo K, Aihara H, Nakai T, Morishita A, Tohma Y, Kohmu""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +14,7,reference_content,"31. Pedregosa F, Varoquaux G, Al. G. Scikit-learn: machine learning in Python. J Mach Learn Res 2011;12:2825-2830. https://dl.acm.org/doi/10.5555/1953048.2078195","[138, 891, 1046, 960]",reference_item,0.85,"[""reference content label: 31. Pedregosa F, Varoquaux G, Al. G. Scikit-learn: machine l""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +14,8,reference_content,"32. Police EM, Kunze KN, Fu MC, Garrigues GE, Forsythe B, Nicholson GP, et al. Development of supervised machine learning algorithms for prediction of satisfaction at 2 years following total shoulder ","[139, 973, 1042, 1125]",reference_item,0.85,"[""reference content label: 32. Police EM, Kunze KN, Fu MC, Garrigues GE, Forsythe B, Ni""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +14,9,reference_content,"33. Quddusi A, Eversdijk HAJ, Klukowska AM, de Wispelaere MP, Kernbach JM, Schröder ML, et al. External validation of a prediction model for pain and functional outcome after elective lumbar spinal fu","[140, 1140, 1032, 1290]",reference_item,0.85,"[""reference content label: 33. Quddusi A, Eversdijk HAJ, Klukowska AM, de Wispelaere MP""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +14,10,reference_content,"34. Ranebo MC, Björnsson Hallgren HC, Holmgren T, Adolfsson LE. Surgery and physiotherapy were both successful in the treatment of small, acute, traumatic rotator cuff tears: a prospective randomized ","[139, 1305, 1036, 1457]",reference_item,0.85,"[""reference content label: 34. Ranebo MC, Bj\u00f6rnsson Hallgren HC, Holmgren T, Adolfsson ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +14,11,reference_content,35. Sevinç E. An empowered AdaBoost algorithm implementation: a COVID-19 dataset study. Comput Ind Eng 2022;165:107912. doi: $ \underline{\text{10.1016/j.cie.2021.107912.}} $,"[139, 1470, 995, 1540]",reference_item,0.85,"[""reference content label: 35. Sevin\u00e7 E. An empowered AdaBoost algorithm implementation""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +14,12,number,12,"[583, 1549, 610, 1569]",noise,0.9,"[""page number label""]",noise,0.9,,unknown_like,short_fragment,False,False +15,0,aside_text,"332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355","[69, 146, 113, 1125]",unknown_structural,0.2,"[""unrecognized label 'aside_text'""]",unknown_structural,0.2,reference_zone,reference_like,reference_numeric_dot,False,True +15,1,header,Journal Pre-proof,"[480, 61, 677, 87]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p15 y=61/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +15,2,reference_content,"36. Shimizu T, Suda K, Maki S, Koda M, Matsumoto Harmon S, Komatsu M, et al. Efficacy of a machine learning-based approach in predicting neurological prognosis of cervical spinal cord injury patients ","[140, 146, 1046, 298]",reference_item,0.85,"[""reference content label: 36. Shimizu T, Suda K, Maki S, Koda M, Matsumoto Harmon S, K""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +15,3,reference_content,"37. Shinohara I, Mifune Y, Inui A, Nishimoto H, Yamaura K, Mukohara S, et al. Biochemical markers of aging (advanced glycation end products) and degeneration are increased in Type 3 rotator cuff tendo","[139, 311, 1047, 464]",reference_item,0.85,"[""reference content label: 37. Shinohara I, Mifune Y, Inui A, Nishimoto H, Yamaura K, M""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +15,4,reference_content,"38. Sugaya H, Maeda K, Matsuki K, Moriishi J. Functional and structural outcome after arthroscopic full-thickness rotator cuff repair: single-row versus dual-row fixation. Arthroscopy 2005;21:1307-131","[140, 477, 1012, 589]",reference_item,0.85,"[""reference content label: 38. Sugaya H, Maeda K, Matsuki K, Moriishi J. Functional and""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +15,5,reference_content,"39. Takeuchi N, Kozono N, Nishii A, Matsuura K, Ishitani E, Onizuka T, et al. Stump classification was correlated with retear in the suture-bridge and double-row repair techniques for arthroscopic rot","[139, 602, 1031, 753]",reference_item,0.85,"[""reference content label: 39. Takeuchi N, Kozono N, Nishii A, Matsuura K, Ishitani E, ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +15,6,reference_content,"40. Tseng PY, Chen YT, Wang CH, Chiu KM, Peng YS, Hsu SP, et al. Prediction of the development of acute kidney injury following cardiac surgery by machine learning. Crit Care 2020;24:478. doi: $ \und","[138, 768, 1020, 876]",reference_item,0.85,"[""reference content label: 40. Tseng PY, Chen YT, Wang CH, Chiu KM, Peng YS, Hsu SP, et""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +15,7,reference_content,"41. Yan J, Xu Y, Cheng Q, Jiang S, Wang Q, Xiao Y, et al. LightGBM: accelerated genomically designed crop breeding through ensemble learning. Genome Biol 2021;22:271. doi: 10.1186/s13059-021-02492-y.","[138, 892, 974, 1000]",reference_item,0.85,"[""reference content label: 41. Yan J, Xu Y, Cheng Q, Jiang S, Wang Q, Xiao Y, et al. Li""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +15,8,reference_content,"42. Zhang C, Lei X, Liu L. Predicting metabolite-disease associations based on LightGBM model. Front Genet 2021;12:660275. doi: $ \underline{\text{10.3389/fgene.2021.660275.}} $","[138, 1015, 1020, 1085]",reference_item,0.85,"[""reference content label: 42. Zhang C, Lei X, Liu L. Predicting metabolite-disease ass""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +15,9,number,13,"[583, 1548, 609, 1570]",noise,0.9,"[""page number label""]",noise,0.9,,unknown_like,short_fragment,False,False +16,0,aside_text,"356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374","[69, 146, 113, 918]",unknown_structural,0.2,"[""unrecognized label 'aside_text'""]",unknown_structural,0.2,reference_zone,reference_like,reference_numeric_dot,False,True +16,1,header,Journal Pre-proof,"[480, 61, 676, 87]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p16 y=61/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +16,2,paragraph_title,Table Legends,"[140, 145, 297, 174]",unknown_structural,0.6,"[""unnumbered paragraph_title, inferred level subsection_heading: Table Legends""]",subsection_heading,0.6,,table_caption_like,short_fragment,False,True +16,3,text,"Table.1 Statistical analysis of patient background in the presence or absence of rotator cuff re-tears. Mean ± standard deviation of each parameter. N.S.: not significant + +Table 2. Accuracy and the ar","[137, 186, 1028, 338]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,,unknown_like,none,True,True +16,4,paragraph_title,Figure Legends,"[140, 393, 308, 423]",subsection_heading,0.6,"[""unnumbered paragraph_title, inferred level subsection_heading: Figure Legends""]",subsection_heading,0.6,,legend_like,short_fragment,True,True +16,5,text,Fig. 1. Representative magnetic resonance imaging of stump classification. (a) Comparison of signal intensity between deltoid (D; red-circled area) and rotator cuff tears (C; orange-circled area). (b),"[138, 435, 1052, 548]",figure_caption,0.9,"[""figure caption candidate (body narrative): Fig. 1. Representative magnetic resonance imaging of stump c""]",figure_caption_candidate,0.9,display_zone,legend_like,figure_number,True,True +16,6,text,Fig. 2. Workflow of data collection and machine learning.,"[139, 559, 705, 590]",figure_caption,0.9,"[""figure prefix matched: Fig. 2. Workflow of data collection and machine learning.""]",figure_caption,0.9,display_zone,legend_like,figure_number,True,True +16,7,text,"Fig. 3. Heat map of the correlation. Stump type, diabetes mellitus (DM), tear size, and fatty degeneration positively correlated with rotator cuff re-tear.","[139, 601, 1027, 672]",figure_caption,0.9,"[""figure caption candidate (body narrative): Fig. 3. Heat map of the correlation. Stump type, diabetes me""]",figure_caption_candidate,0.9,display_zone,legend_like,figure_number,True,True +16,8,text,Fig. 4. ROC curve of each trained model,"[139, 683, 542, 713]",figure_caption,0.9,"[""figure prefix matched: Fig. 4. ROC curve of each trained model""]",figure_caption,0.9,display_zone,legend_like,figure_number,True,True +16,9,text,"Fig. 5. (a) Permutation features the importance of light Gradient Boosting Machine (LightGBM) model. Important features have larger scores. Top three important features were age, stump type, and tear ","[137, 725, 1048, 922]",figure_caption,0.9,"[""figure caption candidate (body narrative): Fig. 5. (a) Permutation features the importance of light Gra""]",figure_caption_candidate,0.9,display_zone,legend_like,figure_number,True,True +16,10,number,14,"[583, 1548, 609, 1570]",noise,0.9,"[""page number label""]",noise,0.9,,unknown_like,short_fragment,False,False +17,0,header,Journal Pre-proof,"[479, 59, 678, 89]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p17 y=59/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +17,1,table,"<","[100, 212, 1087, 866]",media_asset,0.85,"[""media label: table""]",media_asset,0.85,,unknown_like,none,True,True +17,2,vision_footnote,N.S.: not significant,"[166, 871, 369, 903]",footnote,0.7,"[""vision_footnote label: N.S.: not significant""]",footnote,0.7,,unknown_like,none,True,True +18,0,header,Journal Pre-proof,"[479, 61, 677, 88]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p18 y=61/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +18,1,table,
Re-tear (n=45)No re-tear (n=308)P value
Features
Age$ 67.5 \pm 9.0 $$ 63.0 \pm 8.9 $
,"[96, 214, 1051, 472]",media_asset,0.85,"[""media label: table""]",media_asset,0.85,,unknown_like,none,True,True +18,2,vision_footnote,CI: confidence interval; ML: machine learning,"[164, 493, 624, 524]",footnote,0.7,"[""vision_footnote label: CI: confidence interval; ML: machine learning""]",footnote,0.7,,unknown_like,none,True,True +19,0,header,Journal Pre-proof,"[480, 60, 676, 88]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p19 y=60/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +19,1,figure_title,(a),"[164, 156, 199, 186]",figure_inner_text,0.9,"[""panel label / figure inner text: (a)""]",figure_inner_text,0.9,display_zone,legend_like,panel_label,True,True +19,2,image,,"[155, 196, 551, 645]",media_asset,0.85,"[""media label: image""]",media_asset,0.85,,unknown_like,empty,True,True +19,3,figure_title,(b),"[158, 667, 194, 698]",figure_inner_text,0.9,"[""panel label / figure inner text: (b)""]",figure_inner_text,0.9,display_zone,legend_like,panel_label,True,True +19,4,image,,"[153, 707, 446, 1043]",media_asset,0.85,"[""media label: image""]",media_asset,0.85,,unknown_like,empty,True,True +19,5,image,,"[455, 706, 747, 1042]",media_asset,0.85,"[""media label: image""]",media_asset,0.85,,unknown_like,empty,True,True +19,6,image,,"[758, 707, 1047, 1041]",media_asset,0.85,"[""media label: image""]",media_asset,0.85,,unknown_like,empty,True,True +20,0,header,Journal Pre-proof,"[478, 59, 679, 89]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p20 y=59/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +20,1,image,,"[143, 147, 1054, 678]",media_asset,0.85,"[""media label: image""]",media_asset,0.85,,unknown_like,empty,True,True +20,2,figure_title,ROC: receiver operating characteristic,"[138, 710, 518, 742]",figure_caption_candidate,0.85,"[""figure_title label: ROC: receiver operating characteristic""]",figure_caption,0.85,,legend_like,none,False,False +21,0,figure_title,Journal Pre-proof,"[478, 58, 680, 90]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p21 y=58/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +21,1,chart,,"[154, 114, 1040, 948]",media_asset,0.85,"[""media label: chart""]",media_asset,0.85,,unknown_like,empty,True,True +21,2,vision_footnote,DM: diabetes mellitus,"[139, 1003, 364, 1034]",footnote,0.7,"[""vision_footnote label: DM: diabetes mellitus""]",footnote,0.7,,unknown_like,none,True,True +22,0,header,Journal Pre-proof,"[479, 60, 678, 88]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p22 y=60/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +22,1,figure_title,ROC curve of each trained model,"[301, 157, 799, 198]",figure_caption_candidate,0.85,"[""figure_title label: ROC curve of each trained model""]",figure_caption,0.85,,legend_like,none,False,False +22,2,chart,,"[147, 213, 903, 718]",media_asset,0.85,"[""media label: chart""]",media_asset,0.85,,unknown_like,empty,True,True +22,3,vision_footnote,ROC: receiver operating characteristic,"[140, 745, 518, 776]",footnote,0.7,"[""vision_footnote label: ROC: receiver operating characteristic""]",footnote,0.7,,unknown_like,none,True,True +23,0,header,Journal Pre-proof,"[480, 61, 678, 87]",frontmatter_noise,0.98,"[""journal pre-proof running header suppressed: p23 y=61/1684""]",frontmatter_noise,0.98,preproof_cover_zone,unknown_like,preproof_marker,False,False +23,1,figure_title,(a),"[200, 155, 230, 179]",figure_inner_text,0.9,"[""panel label / figure inner text: (a)""]",figure_inner_text,0.9,display_zone,legend_like,panel_label,True,True +23,2,chart,,"[149, 158, 505, 578]",media_asset,0.85,"[""media label: chart""]",media_asset,0.85,,unknown_like,empty,True,True +23,3,figure_title,(b),"[555, 153, 586, 179]",figure_inner_text,0.9,"[""panel label / figure inner text: (b)""]",figure_inner_text,0.9,display_zone,legend_like,panel_label,True,True +23,4,chart,,"[497, 216, 1046, 527]",media_asset,0.85,"[""media label: chart""]",media_asset,0.85,,unknown_like,empty,True,True +23,5,vision_footnote,DM; diabetes mellitus,"[140, 600, 362, 628]",footnote,0.7,"[""vision_footnote label: DM; diabetes mellitus""]",footnote,0.7,,unknown_like,none,True,True diff --git a/tests/fixtures/ocr_real_papers/U746UJ7G/block_trace.csv b/tests/fixtures/ocr_real_papers/U746UJ7G/block_trace.csv new file mode 100644 index 00000000..b94c4d06 --- /dev/null +++ b/tests/fixtures/ocr_real_papers/U746UJ7G/block_trace.csv @@ -0,0 +1,246 @@ +page,block_id,raw_label,content_preview,bbox,role,role_confidence,evidence,seed_role,seed_confidence,zone,style_family,marker_type,render_default,index_default +1,0,header,JAMA Network | Open™,"[91, 46, 346, 101]",noise,0.9,"[""header label""]",noise,0.9,frontmatter_main_zone,support_like,none,False,False +1,1,header_image,,"[1094, 52, 1127, 97]",unknown_structural,0.2,"[""unrecognized label 'header_image'""]",unknown_structural,0.2,frontmatter_main_zone,support_like,empty,False,True +1,2,text,Original Investigation | Health Informatics,"[91, 140, 414, 163]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,frontmatter_main_zone,support_like,none,True,True +1,3,doc_title,"Sepsis Prediction Model for Determining Sepsis vs SIRS, qSOFA, and SOFA","[90, 166, 1023, 202]",paper_title,0.8,"[""page-1 zone title_zone: Sepsis Prediction Model for Determining Sepsis vs SIRS, qSOF""]",paper_title,0.8,frontmatter_main_zone,support_like,none,True,True +1,4,text,"Adam R. Schertz, MD, MS; Kristin M. Lenoir, MPH; Alain G. Bertoni, MD, MPH; Beverly J. Levine, PhD; Morgana Mongraw-Chaffin, PhD; Karl W. Thomas, MD","[90, 215, 1040, 238]",unknown_structural,0.8,"[""page-1 zone author_zone: Adam R. Schertz, MD, MS; Kristin M. Lenoir, MPH; Alain G. Be""]",authors,0.8,body_zone,reference_like,citation_line,False,True +1,5,paragraph_title,Abstract,"[91, 284, 182, 309]",abstract_heading,0.95,"[""abstract heading""]",abstract_heading,0.95,frontmatter_main_zone,heading_like,short_fragment,True,True +1,6,abstract,IMPORTANCE The Sepsis Prediction Model (SPM) is a proprietary decision support tool created by Epic Systems; it generates a predicting sepsis score (PSS). The model has not undergone validation agains,"[89, 328, 787, 460]",abstract_body,0.85,"[""abstract label from Paddle OCR""]",abstract_body,0.85,frontmatter_main_zone,support_like,none,True,True +1,7,text,"OBJECTIVE To assess the validity and timeliness of the SPM compared with SIRS, qSOFA, and SOFA.","[90, 484, 794, 510]",frontmatter_noise,0.7,"[""keyword-like block: OBJECTIVE To assess the validity and timeliness of the SPM c""]",frontmatter_noise,0.7,frontmatter_main_zone,support_like,none,False,False +1,8,text,"DESIGN, SETTING, AND PARTICIPANTS This retrospective cohort study included all adults admitted to 5 acute care hospitals in a single US health system between June 5, 2019, and December 31, 2020. Data ","[90, 537, 793, 615]",unknown_structural,0.8,"[""page-1 zone author_zone: DESIGN, SETTING, AND PARTICIPANTS This retrospective cohort ""]",authors,0.8,body_zone,body_like,none,False,True +1,9,text,"MAIN OUTCOMES AND MEASURES A sepsis event was defined as receipt of 4 or more days of antimicrobials, blood cultures collected within ±48 hours of initial antimicrobial, and at least 1 organ dysfuncti","[89, 640, 796, 745]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +1,10,text,"RESULTS Of 60 507 total admissions, 1663 (2.7%) met sepsis criteria, with 1324 electronic health record-confirmed sepsis (699 [52.8%] male patients; 298 [22.5%] Black patients; 46 [3.5%] Hispanic/Lati","[88, 770, 792, 1135]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +1,11,text,"CONCLUSIONS AND RELEVANCE In this cohort study of hospital admissions, balanced accuracy of the SPM outperformed other models at higher threshold PSS; however, application of the SPM in a clinical set","[89, 1159, 791, 1263]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +1,12,paragraph_title,Key Points,"[845, 288, 936, 312]",paper_title,0.7,"[""unnumbered paragraph_title in title zone on page 1: Key Points""]",paper_title,0.7,frontmatter_main_zone,support_like,short_fragment,True,True +1,13,text,Question Does the Sepsis Prediction Model (SPM) outperform other sepsis prediction scores with respect to validity and timeliness?,"[844, 318, 1117, 419]",structured_insert_candidate,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,frontmatter_main_zone,support_like,none,False,False +1,14,text,Findings This cohort study of 60 507 adult admissions found that although balanced accuracy of the SPM at a predicting sepsis score (PSS) threshold of 8 or greater was better than that of the quick Se,"[844, 432, 1112, 743]",authors,0.8,"[""page-1 zone author_zone: Findings This cohort study of 60 507 adult admissions found ""]",authors,0.8,frontmatter_main_zone,support_like,none,True,True +1,15,text,"Meaning While the balanced accuracy of the SPM was better than qSOFA, SOFA, and SIRS at higher-threshold PSS, it had poor timeliness for sepsis prediction.","[844, 754, 1114, 884]",structured_insert_candidate,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,False,False +1,16,text,✗ Invited Commentary,"[835, 920, 1002, 945]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +1,17,text,Supplemental content,"[835, 958, 1012, 982]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +1,18,text,Author affiliations and article information are listed at the end of this article.,"[833, 995, 1098, 1036]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +1,19,footnote,JAMA Network Open. 2023;6(8):e2329729. doi:10.1001/jamanetworkopen.2023.29729,"[89, 1289, 598, 1311]",footnote,0.7,"[""footnote label: JAMA Network Open. 2023;6(8):e2329729. doi:10.1001/jamanetwo""]",footnote,0.7,body_zone,body_like,none,True,True +1,20,footer,☐ Open Access. This is an open access article distributed under the terms of the CC-BY License.,"[91, 1431, 648, 1454]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,none,False,False +1,21,footer,JAMA Network Open. 2023;6(8):e2329729. doi:10.1001/jamanetworkopen.2023.29729,"[91, 1469, 599, 1490]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,none,False,False +1,22,footer,"August 25, 2023","[980, 1469, 1082, 1489]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +1,23,footer,Downloaded from jamanetwork.com by guest on 05/18/2026,"[2, 1549, 528, 1570]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,none,False,False +1,24,number,1/13,"[1102, 1470, 1128, 1490]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +2,0,header,JAMA Network Open | Health Informatics,"[92, 67, 409, 89]",noise,0.9,"[""header label""]",noise,0.9,frontmatter_side_zone,support_like,none,False,False +2,1,header,"Sepsis Prediction Model vs SIRS, qSOFA, and SOFA","[813, 68, 1128, 88]",noise,0.9,"[""header label""]",noise,0.9,frontmatter_side_zone,support_like,none,False,False +2,2,paragraph_title,Introduction,"[92, 116, 219, 139]",section_heading,0.9,"[""explicit scholarly heading: Introduction""]",section_heading,0.9,frontmatter_side_zone,heading_like,canonical_section_name,True,True +2,3,text,"The Third International Task Force on Sepsis and Septic Shock (Sepsis-3) established a consensus definition of sepsis as ""life-threatening organ dysfunction caused by a dysregulated host response to i","[90, 154, 793, 489]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +2,4,text,Sepsis identification systems based on bedside findings are widely disseminated to promote early recognition and prompt intervention. Current recommendations emphasize that sepsis be considered a medi,"[90, 493, 794, 750]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +2,5,text,The Sepsis Prediction Model (SPM; Epic Systems) is a proprietary algorithm developed from a pooled sample of 405 000 patient encounters across 3 health care organizations between 2013 and 2015. $ ^{[3,"[90, 752, 792, 1164]",affiliation,0.8,"[""page-1 zone affiliation_zone: The Sepsis Prediction Model (SPM; Epic Systems) is a proprie""]",affiliation,0.8,frontmatter_main_zone,support_like,none,True,True +2,6,paragraph_title,Methods Background,"[92, 1211, 199, 1275]",subsection_heading,0.6,"[""unnumbered paragraph_title, inferred level subsection_heading: Methods Background""]",subsection_heading,0.6,body_zone,heading_like,short_fragment,True,True +2,7,footer,,"[92, 1252, 199, 1275]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,empty,False,False +2,8,text,"This is a retrospective cohort study of all adult (>17 years) admissions within 5 hospitals of Wake Forest Baptist Health (Winston-Salem, North Carolina) beginning June 5, 2019, through December 31, 2","[90, 1279, 793, 1407]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +2,9,footer,JAMA Network Open. 2023;6(8):e2329729. doi:10.1001/jamanetworkopen.2023.29729,"[92, 1468, 619, 1490]",noise,0.9,"[""footer label""]",noise,0.9,,unknown_like,none,False,False +2,10,footer,"August 25, 2023","[978, 1470, 1080, 1489]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +2,11,footer,Downloaded from jamanetwork.com by guest on 05/18/2026,"[2, 1549, 528, 1570]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,none,False,False +2,12,number,2/13,"[1099, 1470, 1128, 1489]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +3,0,header,JAMA Network Open | Health Informatics,"[93, 67, 409, 89]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +3,1,header,"Sepsis Prediction Model vs SIRS, qSOFA, and SOFA","[813, 68, 1128, 89]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +3,2,text,study followed the Strengthening the Reporting of Observational Studies in Epidemiology (STROBE) reporting guidelines. $ ^{35} $,"[90, 114, 791, 164]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +3,3,paragraph_title,Study Population,"[91, 192, 241, 215]",subsection_heading,0.6,"[""unnumbered paragraph_title, inferred level subsection_heading: Study Population""]",subsection_heading,0.6,body_zone,heading_like,short_fragment,True,True +3,4,text,Structured data from the EHR identified inpatient admissions and all associated characteristics reported in this study. We handled potential missing data exactly how an applied algorithm would handle ,"[90, 218, 783, 502]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +3,5,paragraph_title,Exclusion Criteria,"[91, 529, 242, 551]",subsection_heading,0.6,"[""unnumbered paragraph_title, inferred level subsection_heading: Exclusion Criteria""]",subsection_heading,0.6,body_zone,heading_like,short_fragment,True,True +3,6,text,"For the analysis, we excluded admissions to a Burn Service, length of stay more than 30 days, those who left against medical advice, and transfers from an out-of-system facility or between internal si","[90, 556, 785, 710]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +3,7,paragraph_title,Sepsis Definition,"[91, 737, 238, 760]",subsection_heading,0.6,"[""unnumbered paragraph_title, inferred level subsection_heading: Sepsis Definition""]",subsection_heading,0.6,body_zone,heading_like,short_fragment,True,True +3,8,text,EHR-confirmed sepsis was defined by the Centers for Disease Control and Prevention (CDC) Adult Sepsis Event (ASE) criteria of (1) 4 or more days of qualifying antimicrobial therapy beginning within $,"[88, 763, 796, 1075]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +3,9,paragraph_title,Electronic Sepsis Alert Systems,"[91, 1101, 355, 1124]",subsection_heading,0.6,"[""unnumbered paragraph_title, inferred level subsection_heading: Electronic Sepsis Alert Systems""]",subsection_heading,0.6,body_zone,heading_like,none,True,True +3,10,text,"An electronic sepsis alert based on the SPM was active during all phases of care at a single site within the health system, with the alert threshold set to a PSS of 10 or greater. All other study site","[90, 1127, 790, 1255]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +3,11,paragraph_title,Time Zero,"[92, 1283, 182, 1305]",subsection_heading,0.6,"[""unnumbered paragraph_title, inferred level subsection_heading: Time Zero""]",subsection_heading,0.6,body_zone,heading_like,short_fragment,True,True +3,12,text,"Time zero was set as 15 minutes prior to first clinician action, as defined by the initial order for antimicrobials or blood cultures. As stated previously, the SPM recalculates the PSS every 15 minut","[90, 1308, 794, 1436]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +3,13,footer,JAMA Network Open. 2023;6(8):e2329729. doi:10.1001/jamanetworkopen.2023.29729,"[92, 1468, 619, 1490]",noise,0.9,"[""footer label""]",noise,0.9,,unknown_like,none,False,False +3,14,footer,"August 25, 2023","[979, 1470, 1080, 1489]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +3,15,footer,Downloaded from jamanetwork.com by guest on 05/18/2026,"[2, 1549, 528, 1570]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,none,False,False +3,16,number,3/13,"[1099, 1470, 1128, 1489]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +4,0,header_image,,"[95, 0, 252, 19]",unknown_structural,0.2,"[""unrecognized label 'header_image'""]",unknown_structural,0.2,body_zone,unknown_like,empty,False,True +4,1,header,JAMA Network Open | Health Informatics,"[93, 67, 409, 89]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +4,2,header,"Sepsis Prediction Model vs SIRS, qSOFA, and SOFA","[813, 69, 1128, 88]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +4,3,text,"action related to suspicion or treatment of sepsis. For example, if a clinician assessed a patient and ordered antibiotics or a blood culture at 2:00 PM, time zero for that episode of care would be se","[91, 115, 785, 188]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +4,4,text,"The times elapsed for SIRS, qSOFA, and SOFA to reach threshold score were assessed at 1 hour. These intervals reflected the time from when any first criterion was present to the time when 2 or more cr","[89, 193, 786, 319]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +4,5,paragraph_title,Outcomes,"[91, 350, 185, 370]",sub_subsection_heading,0.6,"[""unnumbered paragraph_title, inferred level sub_subsection_heading: Outcomes""]",sub_subsection_heading,0.6,body_zone,heading_like,short_fragment,True,True +4,6,text,"Our primary goal was to compare the performance of the SPM in classifying sepsis admissions relative to SIRS, qSOFA, and SOFA, and examine the timeliness of each tool with respect to time zero. Perfor","[90, 375, 793, 476]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +4,7,paragraph_title,Statistical Analysis,"[92, 504, 252, 526]",subsection_heading,0.6,"[""unnumbered paragraph_title, inferred level subsection_heading: Statistical Analysis""]",subsection_heading,0.6,body_zone,heading_like,none,True,True +4,8,text,We performed statistical analysis using R Statistical Computing Environment version 4.0.5 (R Core Team). We assessed differences in baseline characteristics describing qualifying admissions across 3 g,"[89, 530, 793, 1023]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +4,9,paragraph_title,Results,"[92, 1068, 169, 1092]",section_heading,0.9,"[""explicit scholarly heading: Results""]",section_heading,0.9,body_zone,heading_like,canonical_section_name,True,True +4,10,text,"Of 90 773 adult admissions recorded from June 5, 2019, through December 31, 2020, our analytic data set included 60 507 admissions, comprising 49 369 unique patients, that met the inclusion criteria (","[89, 1108, 795, 1442]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +4,11,footer,JAMA Network Open. 2023;6(8):e2329729. doi:10.1001/jamanetworkopen.2023.29729,"[92, 1468, 619, 1490]",noise,0.9,"[""footer label""]",noise,0.9,,unknown_like,none,False,False +4,12,footer,"August 25, 2023","[978, 1470, 1079, 1488]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +4,13,footer,Downloaded from jamanetwork.com by guest on 05/18/2026,"[2, 1549, 528, 1569]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,none,False,False +4,14,number,4/13,"[1099, 1471, 1128, 1489]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +5,0,header_image,,"[96, 0, 252, 19]",unknown_structural,0.2,"[""unrecognized label 'header_image'""]",unknown_structural,0.2,body_zone,unknown_like,empty,False,True +5,1,header,JAMA Network Open | Health Informatics,"[92, 68, 409, 88]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +5,2,header,"Sepsis Prediction Model vs SIRS, qSOFA, and SOFA","[813, 69, 1128, 88]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +5,3,text,"of which were diagnosed with COVID-19. Those with EHR-confirmed sepsis were older and more likely to be classified as White compared with all-cause admissions. Immunocompromised status, atheroscleroti","[89, 115, 793, 372]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +5,4,text,We observed an inverse association between an increase in PSS threshold and sensitivity and a positive association for higher PSS threshold and specificity (Table 2). For the entire length of admissio,"[89, 376, 794, 630]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +5,5,text,All the prediction scores had a median time to threshold after time zero (Table 3). SIRS criteria were positive in the largest proportion of EHR-confirmed sepsis admissions before time zero (573 [43.3,"[89, 634, 795, 841]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +5,6,image,,"[92, 894, 1085, 1400]",media_asset,0.85,"[""media label: image""]",media_asset,0.85,body_zone,body_like,empty,True,True +5,7,vision_footnote,"ASE indicates Adult Sepsis Event; BP, blood pressure; LOS, length of stay.","[91, 1423, 512, 1443]",footnote,0.7,"[""vision_footnote label: ASE indicates Adult Sepsis Event; BP, blood pressure; LOS, l""]",footnote,0.7,body_zone,body_like,none,True,True +5,8,footer,JAMA Network Open. 2023;6(8):e2329729. doi:10.1001/jamanetworkopen.2023.29729,"[92, 1468, 619, 1490]",noise,0.9,"[""footer label""]",noise,0.9,,unknown_like,none,False,False +5,9,footer,"August 25, 2023","[979, 1470, 1080, 1488]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +5,10,footer,Downloaded from jamanetwork.com by guest on 05/18/2026,"[2, 1549, 528, 1569]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,none,False,False +5,11,number,5/13,"[1099, 1471, 1128, 1489]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +6,0,header,JAMA Network Open | Health Informatics,"[92, 66, 409, 89]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +6,1,header,"Sepsis Prediction Model vs SIRS, qSOFA, and SOFA","[813, 68, 1128, 88]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +6,2,text,"of onset of organ dysfunction by eSOFA criteria, we saw similar trends, with SIRS performing best, followed by SOFA, qSOFA, and the SPM (eTable 3 in Supplement 1). Results pertaining to additional PSS","[90, 114, 785, 191]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +6,3,paragraph_title,Discussion,"[92, 237, 200, 263]",section_heading,0.9,"[""explicit scholarly heading: Discussion""]",section_heading,0.9,body_zone,heading_like,canonical_section_name,True,True +6,4,text,"Although the SPM demonstrated better balanced accuracy and specificity for sepsis at higher-threshold PSS (8 to 10), it also missed a higher proportion of true cases and was far less timely in compari","[90, 277, 794, 407]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +6,5,figure_title,Table 1. Baseline Characteristics of Inpatient Admissions by Sepsis Category $ ^{a} $,"[93, 457, 574, 479]",table_caption,0.9,"[""table prefix matched: Table 1. Baseline Characteristics of Inpatient Admissions by""]",table_caption,0.9,display_zone,table_caption_like,table_number,True,True +6,6,table,"
ML modellogistic regression (95% CI)random forest (95% CI)AdaBoost (95% CI)CatBoost (95% CI)LightGBM (95% CI)
Accuracy
<","[94, 466, 792, 1438]",media_asset,0.85,"[""media label: table""]",media_asset,0.85,body_zone,body_like,none,True,True +6,7,text,"Abbreviations: ASE, Adult Sepsis Event; ED, emergency department; ICU, intensive care unit.","[810, 1118, 1095, 1159]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +6,8,text," $ ^{a} $ $ \chi^{2} $ test was conducted for categorical variables. Kruskal-Wallis test was implemented for continuous, skewed variables.","[810, 1163, 1126, 1223]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,affiliation_marker,True,True +6,9,text," $ ^{b} $ Other is inclusive of categories in the electronic health record labeled as American Indian or Alaska Native, Asian, multiracial, Native Hawaiian or Other Pacific Islander, and Other (not fu","[810, 1228, 1120, 1308]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,affiliation_marker,True,True +6,10,text,Body mass index is calculated as weight in kilograms divided by height in meters squared.,"[810, 1313, 1125, 1353]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +6,11,vision_footnote,JAMA Network Open. 2023;6(8):e2329729. doi:10.1001/jamanetworkopen.2023.29729,"[92, 1468, 619, 1490]",footnote,0.7,"[""vision_footnote label: JAMA Network Open. 2023;6(8):e2329729. doi:10.1001/jamanetwo""]",footnote,0.7,,unknown_like,none,True,True +6,12,text," $ ^{d} $ The P value for body mass of 30 or greater was .005, and P values for all other variables were <.001.","[810, 1357, 1127, 1398]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,affiliation_marker,True,True +6,13,text, $ ^{e} $ Immunocompromised indicates patient with HIV/AIDS or metastatic cancer.,"[810, 1403, 1083, 1444]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,affiliation_marker,True,True +6,14,footer,"August 25, 2023","[978, 1470, 1079, 1488]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +6,15,footer,Downloaded from jamanetwork.com by guest on 05/18/2026,"[2, 1549, 528, 1570]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,none,False,False +6,16,number,6/13,"[1099, 1471, 1128, 1489]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +7,0,header_image,,"[95, 0, 252, 19]",unknown_structural,0.2,"[""unrecognized label 'header_image'""]",unknown_structural,0.2,body_zone,unknown_like,empty,False,True +7,1,header,JAMA Network Open | Health Informatics,"[93, 67, 409, 89]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +7,2,header,"Sepsis Prediction Model vs SIRS, qSOFA, and SOFA","[813, 69, 1128, 89]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +7,3,text,with increased score complexity and lack of transparency of the SPM epitomizes its major flaw: it appears to predict sepsis long after the clinician has recognized possible sepsis and acted on that su,"[90, 114, 793, 214]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +7,4,text,"At higher PSS scores (8 to 10), only 12.9% to 19.7% of patients could have been identified by the SPM in a clinically relevant time prior to clinician action (Table 3). In addition to poor timeliness ","[90, 218, 792, 398]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +7,5,text,"Given the existing observational data regarding the importance of early antimicrobial administration, the patients who would benefit most from early recognition, resource allocation, and appropriate a","[90, 401, 796, 580]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +7,6,figure_title,Table 2. Performance Metrics for Classification of Admission (All Sepsis vs Nonsepsis),"[92, 615, 630, 635]",table_caption,0.9,"[""table prefix matched: Table 2. Performance Metrics for Classification of Admission""]",table_caption,0.9,display_zone,table_caption_like,table_number,True,True +7,7,table,"
CharacteristicAdmissions, No. (%)
ASE sepsis (n = 1324)COVID-19 sepsis (n = 339)Nonsepsis (n = 58 844)
All sepsis classification (n = 1663)Performance metric (95% CI)
AccuracySensitivitySpecificityFalse negative","[94, 641, 1124, 911]",media_asset,0.85,"[""media label: table""]",media_asset,0.85,body_zone,body_like,none,True,True +7,8,vision_footnote,"Abbreviations: PSS, Predicting Sepsis Score; qSOFA, quick Sepsis-Related Organ Failure Assessment; SIRS, Systemic Inflammatory Response Syndrome; SOFA, Sequential Organ Failure Assessment.","[92, 919, 600, 978]",footnote,0.7,"[""vision_footnote label: Abbreviations: PSS, Predicting Sepsis Score; qSOFA, quick Se""]",footnote,0.7,body_zone,body_like,none,True,True +7,9,vision_footnote, $ ^{c} $ Balanced accuracy is calculated as sensitivity plus specificity divided by 2.,"[618, 919, 1051, 938]",footnote,0.7,"[""vision_footnote label: $ ^{c} $ Balanced accuracy is calculated as sensitivity plus""]",footnote,0.7,body_zone,body_like,affiliation_marker,True,True +7,10,vision_footnote, $ ^{a} $ False negative indicates the proportion of missed true sepsis cases.,"[92, 984, 490, 1003]",footnote,0.7,"[""vision_footnote label: $ ^{a} $ False negative indicates the proportion of missed t""]",footnote,0.7,body_zone,body_like,affiliation_marker,True,True +7,11,vision_footnote, $ ^{d} $ Diagnostic odds ratio is calculated as the positive likelihood ratio divided by the negative likelihood ratio.,"[618, 944, 1084, 984]",footnote,0.7,"[""vision_footnote label: $ ^{d} $ Diagnostic odds ratio is calculated as the positive""]",footnote,0.7,body_zone,body_like,affiliation_marker,True,True +7,12,vision_footnote, $ ^{b} $ False positive indicates the proportion of nonsepsis cases falsely classified as sepsis.,"[92, 1009, 584, 1029]",footnote,0.7,"[""vision_footnote label: $ ^{b} $ False positive indicates the proportion of nonsepsi""]",footnote,0.7,body_zone,body_like,affiliation_marker,True,True +7,13,figure_title,Table 3. Performance Metrics for Admissions With Respect to Time Zero Among 1324 Electronic Health Record-Confirmed Sepsis Admissions $ ^{a} $,"[92, 1075, 740, 1115]",table_caption,0.9,"[""table prefix matched: Table 3. Performance Metrics for Admissions With Respect to ""]",table_caption,0.9,display_zone,table_caption_like,table_number,True,True +7,14,table,"
Performance metricDiagnostic criteria, No. (%)
PSSSIRSqSOFASOFA
Difference between time of ","[94, 1119, 790, 1279]",media_asset,0.85,"[""media label: table""]",media_asset,0.85,body_zone,body_like,none,True,True +7,15,vision_footnote,"Abbreviations: PSS, Predicting Sepsis Score; qSOFA, quick Sepsis-Related Organ Failure Assessment; SIRS, Systemic Inflammatory Response Syndrome; SOFA, Sequential Organ Failure Assessment.","[91, 1287, 755, 1326]",footnote,0.7,"[""vision_footnote label: Abbreviations: PSS, Predicting Sepsis Score; qSOFA, quick Se""]",footnote,0.7,body_zone,body_like,none,True,True +7,16,vision_footnote, $ ^{a} $ Sepsis is defined as meeting Adult Sepsis Event criteria.,"[92, 1333, 423, 1353]",footnote,0.7,"[""vision_footnote label: $ ^{a} $ Sepsis is defined as meeting Adult Sepsis Event cri""]",footnote,0.7,body_zone,body_like,affiliation_marker,True,True +7,17,vision_footnote,"Time zero indicates 15 minutes before clinician action (blood culture or antimicrobial order). Positive and negative values indicate threshold being met before and after time zero, respectively. Diffe","[91, 1357, 792, 1416]",footnote,0.7,"[""vision_footnote label: Time zero indicates 15 minutes before clinician action (bloo""]",footnote,0.7,body_zone,body_like,none,True,True +7,18,vision_footnote,"Threshold score for PSS was 8 or greater; SIRS, 2 or greater; qSOFA, 2 or greater; SOFA, 2 or greater.","[92, 1423, 674, 1443]",footnote,0.7,"[""vision_footnote label: Threshold score for PSS was 8 or greater; SIRS, 2 or greater""]",footnote,0.7,body_zone,body_like,none,True,True +7,19,footer,JAMA Network Open. 2023;6(8):e2329729. doi:10.1001/jamanetworkopen.2023.29729,"[93, 1468, 618, 1490]",noise,0.9,"[""footer label""]",noise,0.9,,unknown_like,none,False,False +7,20,footer,"August 25, 2023","[979, 1470, 1081, 1489]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +7,21,footer,Downloaded from jamanetwork.com by guest on 05/18/2026,"[2, 1550, 527, 1569]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,none,False,False +7,22,number,7/13,"[1100, 1471, 1128, 1489]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +8,0,header,JAMA Network Open | Health Informatics,"[92, 65, 409, 89]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +8,1,header,"Sepsis Prediction Model vs SIRS, qSOFA, and SOFA","[813, 67, 1128, 89]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +8,2,chart,,"[105, 134, 967, 1443]",media_asset,0.85,"[""media label: chart""]",media_asset,0.85,body_zone,body_like,empty,True,True +8,3,vision_footnote,JAMA Network Open. 2023;6(8):e2329729. doi:10.1001/jamanetworkopen.2023.29729,"[92, 1468, 619, 1490]",footnote,0.7,"[""vision_footnote label: JAMA Network Open. 2023;6(8):e2329729. doi:10.1001/jamanetwo""]",footnote,0.7,,unknown_like,none,True,True +8,4,vision_footnote,This figure demonstrates the difference between time zero and time of sepsis threshold positivity. They are negative and are shaded in blue. Those admissions with a threshold score after time 15 minut,"[984, 136, 1062, 1442]",footnote,0.7,"[""vision_footnote label: This figure demonstrates the difference between time zero an""]",footnote,0.7,body_zone,body_like,none,True,True +8,5,footer,"August 25, 2023","[978, 1470, 1079, 1488]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +8,6,footer,Downloaded from jamanetwork.com by guest on 05/18/2026,"[2, 1549, 527, 1569]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,none,False,False +8,7,number,8/13,"[1099, 1470, 1127, 1489]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +9,0,header,JAMA Network Open | Health Informatics,"[93, 67, 410, 89]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +9,1,header,"Sepsis Prediction Model vs SIRS, qSOFA, and SOFA","[813, 69, 1128, 89]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +9,2,text,existing limitations to timeliness that may be due to other aspects of model development in order to meaningfully impact sepsis recognition or management.,"[90, 115, 790, 164]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +9,3,paragraph_title,Strengths and Limitations,"[91, 193, 311, 215]",subsection_heading,0.6,"[""unnumbered paragraph_title, inferred level subsection_heading: Strengths and Limitations""]",subsection_heading,0.6,body_zone,heading_like,none,True,True +9,4,text,The strengths of our study include a large sample size and inclusion of patients from both community-based and tertiary referral hospitals. Utilization of the CDC ASE criteria as well as separately de,"[89, 219, 795, 501]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +9,5,text,"This study also has limitations, including investigation of only a single health system, the observational study design, the inherent heterogeneity of sepsis, reliance on EHR data, and missing data. B","[89, 504, 795, 763]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +9,6,paragraph_title,Conclusions,"[92, 808, 213, 833]",section_heading,0.9,"[""explicit scholarly heading: Conclusions""]",section_heading,0.9,body_zone,heading_like,canonical_section_name,True,True +9,7,text,"In this cohort study of 60 507 hospital admissions, we found that although the SPM marginally outperformed existing prediction scores in balanced accuracy for classification of sepsis, it suffers from","[90, 849, 796, 1107]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +9,8,text,ARTICLE INFORMATION,"[91, 1176, 256, 1196]",unknown_structural,0.3,"[""short text, uncertain role""]",unknown_structural,0.3,body_zone,body_like,short_fragment,False,True +9,9,text,"Accepted for Publication: July 7, 2023.","[91, 1198, 342, 1220]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +9,10,footnote,"Published: August 25, 2023. doi:10.1001/jamanetworkopen.2023.29729","[90, 1226, 545, 1248]",footnote,0.7,"[""footnote label: Published: August 25, 2023. doi:10.1001/jamanetworkopen.2023""]",footnote,0.7,,unknown_like,none,True,True +9,11,footnote,Open Access: This is an open access article distributed under the terms of the CC-BY License. © 2023 Schertz AR et al. JAMA Network Open.,"[90, 1254, 788, 1297]",footnote,0.7,"[""footnote label: Open Access: This is an open access article distributed unde""]",footnote,0.7,body_zone,body_like,none,True,True +9,12,footnote,"Corresponding Author: Adam R. Schertz, MD, MS, Section of Pulmonology, Critical Care, Allergy & Immunologic Diseases, Atrium Health Wake Forest Baptist, One Medical Center Boulevard, Winston-Salem, NC","[90, 1303, 819, 1370]",footnote,0.7,"[""footnote label: Corresponding Author: Adam R. Schertz, MD, MS, Section of Pu""]",footnote,0.7,body_zone,body_like,none,True,True +9,13,footnote,"Author Affiliations: Department of Internal Medicine, Atrium Health Wake Forest Baptist, Winston-Salem, North Carolina (Schertz, Bertoni, Thomas); Section of Pulmonology, Critical Care, Allergy and Im","[91, 1376, 797, 1443]",footnote,0.7,"[""footnote label: Author Affiliations: Department of Internal Medicine, Atrium""]",footnote,0.7,body_zone,body_like,none,True,True +9,14,footer,JAMA Network Open. 2023;6(8):e2329729. doi:10.1001/jamanetworkopen.2023.29729,"[92, 1468, 619, 1490]",noise,0.9,"[""footer label""]",noise,0.9,,unknown_like,none,False,False +9,15,footer,"August 25, 2023","[977, 1470, 1079, 1488]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +9,16,footer,Downloaded from jamanetwork.com by guest on 05/18/2026,"[2, 1549, 528, 1570]",noise,0.9,"[""footer label""]",noise,0.9,body_zone,body_like,none,False,False +9,17,number,9/13,"[1098, 1470, 1128, 1489]",noise,0.9,"[""page number label""]",noise,0.9,body_zone,body_like,short_fragment,False,False +10,0,header,JAMA Network Open | Health Informatics,"[93, 67, 409, 88]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +10,1,header,"Sepsis Prediction Model vs SIRS, qSOFA, and SOFA","[813, 69, 1128, 88]",noise,0.9,"[""header label""]",noise,0.9,body_zone,body_like,none,False,False +10,2,text,"and Data Science, Division of Public Health Science, Atrium Health Wake Forest Baptist, Winston-Salem, North Carolina (Lenoir, Bertoni); Social Sciences and Health Policy, Wake Forest University Schoo","[91, 116, 786, 202]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +10,3,text,Author Contributions: Dr Schertz and Ms Lenoir had full access to all of the data in the study and take responsibility for the integrity of the data and the accuracy of the data analysis.,"[90, 210, 720, 251]",frontmatter_noise,0.88,"[""default body_paragraph for text label"", ""late role resolution: editorial phrase cross-validates non-body classification"", ""zone=body_zone"", ""style_family=support_like""]",body_paragraph,0.6,body_zone,support_like,none,False,False +10,4,text,"Concept and design: Schertz, Bertoni, Levine, Mongraw-Chaffin, Thomas.","[91, 259, 544, 279]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +10,5,text,"Acquisition, analysis, or interpretation of data: Schertz, Lenoir, Bertoni, Levine, Mongraw-Chaffin.","[91, 287, 687, 306]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +10,6,text,"Drafting of the manuscript: Schertz, Lenoir, Thomas.","[92, 315, 416, 335]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +10,7,text,"Critical review of the manuscript for important intellectual content: Schertz, Lenoir, Bertoni, Levine, Mongraw-Chaffin.","[91, 344, 764, 383]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +10,8,text,"Statistical analysis: Lenoir, Levine, Mongraw-Chaffin.","[92, 393, 419, 412]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +10,9,text,Obtained funding: Schertz.,"[92, 421, 261, 440]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +10,10,text,"Administrative, technical, or material support: Bertoni.","[91, 449, 427, 468]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +10,11,text,"Supervision: Schertz, Thomas.","[92, 477, 281, 495]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +10,12,text,Conflict of Interest Disclosures: None reported.,"[92, 505, 397, 523]",frontmatter_noise,0.6,"[""default body_paragraph for text label"", ""frontmatter_side_zone excluded from body flow""]",frontmatter_noise,0.6,frontmatter_side_zone,support_like,none,False,False +10,13,text,Funding/Support: This project was funded by the Wake Forest Clinical and Translational Science Institute.,"[91, 533, 743, 552]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +10,14,text,"Role of the Funder/Sponsor: The funding source approved the design and conduct of the study, but had no role in the collection, management, analysis, and interpretation of the data; preparation, revie","[92, 561, 783, 624]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +10,15,paragraph_title,Data Sharing Statement: See Supplement 2.,"[92, 633, 372, 652]",subsection_heading,0.6,"[""unnumbered paragraph_title, inferred level subsection_heading: Data Sharing Statement: See Supplement 2.""]",subsection_heading,0.6,body_zone,body_like,none,True,True +10,16,text,"Additional Contributions: We would like to thank the following people for their contributions to this work. Brian Wells, MD, PhD (Department of Family Medicine, Atrium Health Wake Forest Baptist), pro","[92, 661, 794, 857]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,body_zone,body_like,none,True,True +10,17,paragraph_title,REFERENCES,"[93, 875, 186, 893]",reference_heading,0.9,"[""references heading: REFERENCES""]",reference_heading,0.9,reference_zone,unknown_like,short_fragment,True,True +10,18,text,"1. Singer M, Deutschman CS, Seymour CW, et al. The third international consensus definitions for sepsis and septic shock (Sepsis-3). JAMA. 2016;315(8):801-810. doi:10.1001/jama.2016.0287","[92, 898, 794, 940]",reference_item,0.6,"[""reference-like pattern: 1. Singer M, Deutschman CS, Seymour CW, et al. The third int""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +10,19,text,"2. Rhee C, Dantes R, Epstein L, et al; CDC Prevention Epicenter Program. Incidence and trends of sepsis in US hospitals using clinical vs claims data, 2009-2014. JAMA. 2017;318(13):1241-1249. doi:10.1","[92, 948, 772, 990]",reference_item,0.6,"[""reference-like pattern: 2. Rhee C, Dantes R, Epstein L, et al; CDC Prevention Epicen""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +10,20,text,"3. Torio CM, Moore BJ. National inpatient hospital costs: the most expensive conditions by payer, 2013. HCUP Statistical Brief #204. May 2016. Accessed July 17, 2023. https://hcup-us.ahrq.gov/reports/","[92, 998, 794, 1061]",reference_item,0.6,"[""reference-like pattern: 3. Torio CM, Moore BJ. National inpatient hospital costs: th""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +10,21,text,"4. Rhee C, Jones TM, Hamad Y, et al; Centers for Disease Control and Prevention (CDC) Prevention Epicenters Program. Prevalence, underlying causes, and preventability of sepsis-associated mortality in","[92, 1069, 770, 1134]",reference_item,0.6,"[""reference-like pattern: 4. Rhee C, Jones TM, Hamad Y, et al; Centers for Disease Con""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +10,22,text,"6. Mayr FB, Talisa VB, Balakumar V, Chang CH, Fine M, Yende S. Proportion and cost of unplanned 30-day readmissions after sepsis compared with other medical conditions. JAMA. 2017;317(5):530-531. doi:","[93, 1191, 787, 1253]",reference_item,0.6,"[""reference-like pattern: 6. Mayr FB, Talisa VB, Balakumar V, Chang CH, Fine M, Yende ""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +10,23,text,"5. Prescott HC, Angus DC. Enhancing recovery from sepsis: a review. JAMA. 2018;319(1):62-75. doi:10.1001/jama.2017.17687","[92, 1141, 786, 1181]",reference_item,0.6,"[""reference-like pattern: 5. Prescott HC, Angus DC. Enhancing recovery from sepsis: a ""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +10,24,text,"7. Fohner AE, Greene JD, Lawson BL, et al. Assessing clinical heterogeneity in sepsis through treatment patterns and machine learning. J Am Med Inform Assoc. 2019;26(12):1466-1477. doi:10.1093/jamia/o","[92, 1263, 784, 1304]",reference_item,0.6,"[""reference-like pattern: 7. Fohner AE, Greene JD, Lawson BL, et al. Assessing clinica""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +10,25,text,"8. Iskander KN, Osuchowski MF, Stearns-Kurosawa DJ, et al. Sepsis: multiple abnormalities, heterogeneous responses, and evolving understanding. Physiol Rev. 2013;93(3):1247-1288. doi:10.1152/physrev.0","[93, 1313, 761, 1355]",reference_item,0.6,"[""reference-like pattern: 8. Iskander KN, Osuchowski MF, Stearns-Kurosawa DJ, et al. S""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +10,26,text,"9. Seymour CW, Gesten F, Prescott HC, et al. Time to treatment and mortality during mandated emergency care for sepsis. N Engl J Med. 2017;376(23):2235-2244. doi:10.1056/NEJMoa1703058","[91, 1363, 782, 1405]",reference_item,0.6,"[""reference-like pattern: 9. Seymour CW, Gesten F, Prescott HC, et al. Time to treatme""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +10,27,footer,JAMA Network Open. 2023;6(8):e2329729. doi:10.1001/jamanetworkopen.2023.29729,"[92, 1469, 618, 1490]",noise,0.9,"[""footer label""]",noise,0.9,,unknown_like,none,False,False +10,28,footer,"August 25, 2023","[972, 1470, 1073, 1489]",noise,0.9,"[""footer label""]",noise,0.9,tail_nonref_hold_zone,unknown_like,short_fragment,False,False +10,29,footer,Downloaded from jamanetwork.com by guest on 05/18/2026,"[2, 1549, 527, 1569]",noise,0.9,"[""footer label""]",noise,0.9,tail_nonref_hold_zone,unknown_like,none,False,False +10,30,number,10/13,"[1094, 1470, 1127, 1489]",noise,0.9,"[""page number label""]",noise,0.9,tail_nonref_hold_zone,unknown_like,short_fragment,False,False +11,0,header,JAMA Network Open | Health Informatics,"[93, 68, 408, 88]",noise,0.9,"[""header label""]",noise,0.9,,unknown_like,none,False,False +11,1,header,"Sepsis Prediction Model vs SIRS, qSOFA, and SOFA","[813, 69, 1128, 88]",noise,0.9,"[""header label""]",noise,0.9,,unknown_like,none,False,False +11,2,reference_content,"10. Weinberger J, Rhee C, Klompas M. A critical analysis of the literature on time-to-antibiotics in suspected sepsis. J Infect Dis. 2020;222(suppl 2):S110-S118. doi:10.1093/infdis/jiaa146","[91, 116, 792, 158]",reference_item,0.85,"[""reference content label: 10. Weinberger J, Rhee C, Klompas M. A critical analysis of ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,3,reference_content,"11. Pruinelli L, Westra BL, Yadav P, et al. Delay within the 3-hour surviving sepsis campaign guideline on mortality for patients with severe sepsis and septic shock. Crit Care Med. 2018;46(4):500-505","[92, 165, 783, 227]",reference_item,0.85,"[""reference content label: 11. Pruinelli L, Westra BL, Yadav P, et al. Delay within the""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,4,reference_content,"12. Andersson M, Östholm-Balkhed Å, Fredrikson M, et al. Delay of appropriate antibiotic treatment is associated with high mortality in patients with community-onset sepsis in a Swedish setting. Eur J","[92, 237, 786, 300]",reference_item,0.85,"[""reference content label: 12. Andersson M, \u00d6stholm-Balkhed \u00c5, Fredrikson M, et al. Del""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,5,reference_content,"13. Peltan ID, Brown SM, Bledsoe JR, et al. ED door-to-antibiotic time and long-term mortality in sepsis. Chest. 2019;155(5):938-946. doi:10.1016/j.chest.2019.02.008","[92, 309, 768, 350]",reference_item,0.85,"[""reference content label: 13. Peltan ID, Brown SM, Bledsoe JR, et al. ED door-to-antib""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,6,reference_content,"14. Whiles BB, Deis AS, Simpson SQ. Increased time to initial antimicrobial administration is associated with progression to septic shock in severe sepsis patients. Crit Care Med. 2017;45(4):623-629. ","[92, 359, 751, 420]",reference_item,0.85,"[""reference content label: 14. Whiles BB, Deis AS, Simpson SQ. Increased time to initia""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,7,reference_content,"15. Sherwin R, Winters ME, Vilke GM, Wardi G. Does early and appropriate antibiotic administration improve mortality in emergency department patients with severe sepsis or septic shock? J Emerg Med. 2","[92, 432, 757, 493]",reference_item,0.85,"[""reference content label: 15. Sherwin R, Winters ME, Vilke GM, Wardi G. Does early and""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,8,reference_content,"16. Seymour CW, Kahn JM, Martin-Gill C, et al. Delays from first medical contact to antibiotic administration for sepsis. Crit Care Med. 2017;45(5):759-765. doi:10.1097/CCM.0000000000002264","[92, 503, 775, 544]",reference_item,0.85,"[""reference content label: 16. Seymour CW, Kahn JM, Martin-Gill C, et al. Delays from f""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,9,reference_content,"17. Liu VX, Fielding-Singh V, Greene JD, et al. The timing of early antibiotics and hospital mortality in sepsis. Am J Respir Crit Care Med. 2017;196(7):856-863. doi:10.1164/rccm.201609-18480C","[92, 553, 783, 594]",reference_item,0.85,"[""reference content label: 17. Liu VX, Fielding-Singh V, Greene JD, et al. The timing o""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,10,reference_content,"18. Wallgren UM, Antonsson VE, Castrén MK, Kurland L. Longer time to antibiotics and higher mortality among septic patients with non-specific presentations—a cross sectional study of Emergency Departm","[92, 604, 783, 686]",reference_item,0.85,"[""reference content label: 18. Wallgren UM, Antonsson VE, Castr\u00e9n MK, Kurland L. Longer""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,11,reference_content,"19. Sterling SA, Miller WR, Pryor J, Puskarich MA, Jones AE. The impact of timing of antibiotics on outcomes in severe sepsis and septic shock: a systematic review and meta-analysis. Crit Care Med. 20","[92, 696, 781, 758]",reference_item,0.85,"[""reference content label: 19. Sterling SA, Miller WR, Pryor J, Puskarich MA, Jones AE.""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,12,reference_content,"20. de Groot B, Ansems A, Gerling DH, et al. The association between time to antibiotics and relevant clinical outcomes in emergency department patients with various stages of sepsis: a prospective mu","[92, 768, 784, 831]",reference_item,0.85,"[""reference content label: 20. de Groot B, Ansems A, Gerling DH, et al. The association""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,13,reference_content,"21. Ferrer R, Martin-Loeches I, Phillips G, et al. Empiric antibiotic treatment reduces mortality in severe sepsis and septic shock from the first hour: results from a guideline-based performance impr","[93, 840, 789, 903]",reference_item,0.85,"[""reference content label: 21. Ferrer R, Martin-Loeches I, Phillips G, et al. Empiric a""]",reference_item,0.85,reference_zone,reference_like,heading_numbered,True,True +11,14,reference_content,"22. Szakmany T, Pugh R, Kopczynska M, et al; Welsh Digital Data Collection Platform collaborators. Defining sepsis on the wards: results of a multi-centre point-prevalence study comparing two sepsis d","[92, 912, 791, 975]",reference_item,0.85,"[""reference content label: 22. Szakmany T, Pugh R, Kopczynska M, et al; Welsh Digital D""]",reference_item,0.85,reference_zone,reference_like,heading_numbered,True,True +11,15,reference_content,"23. Churpek MM, Snyder A, Sokol S, Pettit NN, Edelson DP. Investigating the impact of different suspicion of infection criteria on the accuracy of quick sepsis-related organ failure assessment, system","[92, 984, 757, 1067]",reference_item,0.85,"[""reference content label: 23. Churpek MM, Snyder A, Sokol S, Pettit NN, Edelson DP. In""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,16,reference_content,"24. Peltan ID, Mitchell KH, Rudd KE, et al. Physician variation in time to antimicrobial treatment for septic patients presenting to the emergency department. Crit Care Med. 2017;45(6):1011-1018. doi:","[92, 1078, 791, 1139]",reference_item,0.85,"[""reference content label: 24. Peltan ID, Mitchell KH, Rudd KE, et al. Physician variat""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,17,reference_content,"25. Mellhammar L, Linder A, Tverring J, et al. Scores for sepsis detection and risk stratification—construction of a novel score using a statistical approach and validation of RETTS. PLoS One. 2020;15","[92, 1149, 784, 1212]",reference_item,0.85,"[""reference content label: 25. Mellhammar L, Linder A, Tverring J, et al. Scores for se""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,18,reference_content,"26. Nieves Ortega R, Rosin C, Bingisser R, Nickel CH. Clinical scores and formal triage for screening of sepsis and adverse outcomes on arrival in an emergency department all-comer cohort. J Emerg Med","[92, 1221, 779, 1284]",reference_item,0.85,"[""reference content label: 26. Nieves Ortega R, Rosin C, Bingisser R, Nickel CH. Clinic""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,19,reference_content,"27. Usman OA, Usman AA, Ward MA. Comparison of SIRS, qSOFA, and NEWS for the early identification of sepsis in the emergency department. Am J Emerg Med. 2019;37(8):1490-1497. doi:10.1016/j.ajem.2018.1","[91, 1293, 787, 1335]",reference_item,0.85,"[""reference content label: 27. Usman OA, Usman AA, Ward MA. Comparison of SIRS, qSOFA, ""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,20,reference_content,"28. Bush LM, Kaye D. 'Sepsis' is in the eye of the beholder. Infectious Disease News. October 2017. Accessed June 23, 2022. https://www.healio.com/news/infectious-disease/20171010/sepsis-is-in-the-eye","[92, 1343, 790, 1384]",reference_item,0.85,"[""reference content label: 28. Bush LM, Kaye D. 'Sepsis' is in the eye of the beholder.""]",reference_item,0.85,reference_zone,reference_like,reference_numeric_dot,True,True +11,21,footer,JAMA Network Open. 2023;6(8):e2329729. doi:10.1001/jamanetworkopen.2023.29729,"[91, 1468, 618, 1490]",noise,0.9,"[""footer label""]",noise,0.9,,unknown_like,none,False,False +11,22,footer,"August 25, 2023","[977, 1470, 1078, 1488]",noise,0.9,"[""footer label""]",noise,0.9,,unknown_like,short_fragment,False,False +11,23,footer,Downloaded from jamanetwork.com by guest on 05/18/2026,"[2, 1549, 527, 1569]",noise,0.9,"[""footer label""]",noise,0.9,,unknown_like,none,False,False +11,24,number,11/13,"[1098, 1470, 1128, 1489]",noise,0.9,"[""page number label""]",noise,0.9,,unknown_like,short_fragment,False,False +12,0,header,JAMA Network Open | Health Informatics,"[93, 68, 408, 88]",noise,0.9,"[""header label""]",noise,0.9,,unknown_like,none,False,False +12,1,header,"Sepsis Prediction Model vs SIRS, qSOFA, and SOFA","[813, 69, 1128, 88]",noise,0.9,"[""header label""]",noise,0.9,,unknown_like,none,False,False +12,2,text,"29. Evans L, Rhodes A, Alhazzani W, et al. Executive summary: Surviving Sepsis Campaign: international guidelines for the management of sepsis and septic shock 2021. Crit Care Med. 2021;49(11):1974-19","[92, 116, 763, 179]",reference_item,0.6,"[""reference-like pattern: 29. Evans L, Rhodes A, Alhazzani W, et al. Executive summary""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +12,3,text,"30. Fernando SM, Tran A, Taljaard M, et al. Prognostic accuracy of the quick Sequential Organ Failure Assessment for mortality in patients with suspected infection: a systematic review and meta-analys","[92, 188, 788, 250]",reference_item,0.6,"[""reference-like pattern: 30. Fernando SM, Tran A, Taljaard M, et al. Prognostic accur""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +12,4,text,"31. Wong A, Otles E, Donnelly JP, et al. External validation of a widely implemented proprietary sepsis prediction model in hospitalized patients. JAMA Intern Med. 2021;181(8):1065-1070. doi:10.1001/j","[92, 259, 785, 300]",reference_item,0.6,"[""reference-like pattern: 31. Wong A, Otles E, Donnelly JP, et al. External validation""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +12,5,text,"32. Bennett T, Russell S, King J, et al. Accuracy of the Epic sepsis prediction model in a regional health system. arXiv. Preprint posted online February 19, 2019. doi:10.48550/arXiv.1902.07276","[92, 309, 771, 351]",reference_item,0.6,"[""reference-like pattern: 32. Bennett T, Russell S, King J, et al. Accuracy of the Epi""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +12,6,text,"33. Habib AR, Lin AL, Grant RW. The epic sepsis model falls short—the importance of external validation. JAMA Intern Med. 2021;181(8):1040-1041. doi:10.1001/jamainternmed.2021.3333","[92, 359, 772, 400]",reference_item,0.6,"[""reference-like pattern: 33. Habib AR, Lin AL, Grant RW. The epic sepsis model falls ""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +12,7,text,"34. von Elm E, Altman DG, Egger M, Pocock SJ, Gøtzsche PC, Vandenbroucke JP; STROBE Initiative. The Strengthening the Reporting of Observational Studies in Epidemiology (STROBE) statement: guidelines ","[92, 409, 749, 472]",reference_item,0.6,"[""reference-like pattern: 34. von Elm E, Altman DG, Egger M, Pocock SJ, G\u00f8tzsche PC, V""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +12,8,text,"35. Rhee C, Zhang Z, Kadri SS, et al; CDC Prevention Epicenters Program. Sepsis surveillance using adult sepsis events simplified eSOFA criteria versus Sepsis-3 Sequential Organ Failure Assessment cri","[92, 481, 793, 543]",reference_item,0.6,"[""reference-like pattern: 35. Rhee C, Zhang Z, Kadri SS, et al; CDC Prevention Epicent""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +12,9,text,"36. Kluberg SA, Hou L, Dutcher SK, et al. Validation of diagnosis codes to identify hospitalized COVID-19 patients in health care claims data. Pharmacoepidemiol Drug Saf. 2022;31(4):476-480. doi:10.10","[91, 553, 787, 595]",reference_item,0.6,"[""reference-like pattern: 36. Kluberg SA, Hou L, Dutcher SK, et al. Validation of diag""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +12,10,text,"37. Quan H, Li B, Couris CM, et al. Updating and validating the Charlson comorbidity index and score for risk adjustment in hospital discharge abstracts using data from 6 countries. Am J Epidemiol. 20","[92, 603, 789, 665]",reference_item,0.6,"[""reference-like pattern: 37. Quan H, Li B, Couris CM, et al. Updating and validating ""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +12,11,text,"38. Sloan SNB, Rodriguez N, Seward T, et al. Compliance with SEP-1 guidelines is associated with improved outcomes for septic shock but not for severe sepsis. J Intensive Med. 2022;2(3):167-172. doi:1","[92, 674, 758, 736]",reference_item,0.6,"[""reference-like pattern: 38. Sloan SNB, Rodriguez N, Seward T, et al. Compliance with""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +12,12,text,"39. Glas AS, Lijmer JG, Prins MH, Bonsel GJ, Bossuyt PM. The diagnostic odds ratio: a single indicator of test performance. J Clin Epidemiol. 2003;56(11):1129-1135. doi:10.1016/S0895-4356(03)00177-X","[91, 746, 755, 787]",reference_item,0.6,"[""reference-like pattern: 39. Glas AS, Lijmer JG, Prins MH, Bonsel GJ, Bossuyt PM. The""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +12,13,text,"40. Diaz N. Epic overhauls sepsis algorithm. October 6, 2022. Accessed June 20, 2023. https://www.beckershospitalreview.com/ehrs/epic-overhauls-sepsis-algorithm.html","[92, 796, 716, 837]",reference_item,0.6,"[""reference-like pattern: 40. Diaz N. Epic overhauls sepsis algorithm. October 6, 2022""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +12,14,text,"41. Rhee C, Wang R, Zhang Z, Fram D, Kadri SS, Klompas M; CDC Prevention Epicenters Program. Epidemiology of hospital-onset versus community-onset sepsis in U.S. hospitals and association with mortali","[92, 846, 790, 929]",reference_item,0.6,"[""reference-like pattern: 41. Rhee C, Wang R, Zhang Z, Fram D, Kadri SS, Klompas M; CD""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +12,15,text,"42. Baghdadi JD, Brook RH, Uslan DZ, et al. Association of a care bundle for early sepsis management with mortality among patients with hospital-onset or community-onset sepsis. JAMA Intern Med. 2020;","[92, 940, 746, 1003]",reference_item,0.6,"[""reference-like pattern: 42. Baghdadi JD, Brook RH, Uslan DZ, et al. Association of a""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +12,16,text,"43. Baghdadi JD, Wong MD, Uslan DZ, et al. Adherence to the SEP-1 sepsis bundle in hospital-onset v. community-onset sepsis: a multicenter retrospective cohort study. J Gen Intern Med. 2020;35(4):1153","[92, 1012, 791, 1074]",reference_item,0.6,"[""reference-like pattern: 43. Baghdadi JD, Wong MD, Uslan DZ, et al. Adherence to the ""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +12,17,text,"44. Page B, Klompas M, Chan C, et al. Surveillance for healthcare-associated infections: hospital-onset adult sepsis events versus current reportable conditions. Clin Infect Dis. 2021;73(6):1013-1019.","[92, 1084, 781, 1125]",reference_item,0.6,"[""reference-like pattern: 44. Page B, Klompas M, Chan C, et al. Surveillance for healt""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +12,18,text,"45. Rhee C, Klompas M. Should hospital-onset adult sepsis event surveillance be routine... or even mandatory? Antimicrob Steward Healthc Epidemiol. 2022;2(1):e32. doi:10.1017/ash.2022.16","[92, 1133, 779, 1175]",reference_item,0.6,"[""reference-like pattern: 45. Rhee C, Klompas M. Should hospital-onset adult sepsis ev""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +12,19,text,"46. Cárdenas-Turanzas M, Ensor J, Wakefield C, et al. Cross-validation of a Sequential Organ Failure Assessment score-based model to predict mortality in patients with cancer admitted to the intensive","[92, 1183, 778, 1246]",reference_item,0.6,"[""reference-like pattern: 46. C\u00e1rdenas-Turanzas M, Ensor J, Wakefield C, et al. Cross-""]",reference_item,0.6,reference_zone,reference_like,reference_numeric_dot,True,True +12,20,paragraph_title,SUPPLEMENT 1.,"[92, 1272, 202, 1290]",unknown_structural,0.6,"[""unnumbered paragraph_title, inferred level section_heading: SUPPLEMENT 1.""]",section_heading,0.6,tail_nonref_hold_zone,unknown_like,short_fragment,False,True +12,21,text,"eFigure 1. Diagnostic Odds Ratios for Sepsis, by Model","[92, 1294, 431, 1314]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,tail_nonref_hold_zone,unknown_like,none,True,True +12,22,text,eFigure 2. Kaplan-Meier Analysis of Time to Threshold Positivity vs Time Zero,"[93, 1317, 573, 1335]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,tail_nonref_hold_zone,unknown_like,none,True,True +12,23,text,"eFigure 3. Plot of Criteria Time With Respect to Time Zero for Each EHR-Confirmed Admission, PSS Only","[92, 1339, 732, 1358]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,tail_nonref_hold_zone,unknown_like,none,True,True +12,24,text,eTable 1. Missing Data Stratified by Admission Type,"[93, 1361, 414, 1380]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,tail_nonref_hold_zone,unknown_like,none,True,True +12,25,text,eTable 2. Additional Performance Metrics for Classification of Admission,"[92, 1383, 540, 1402]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,tail_nonref_hold_zone,unknown_like,none,True,True +12,26,footer,JAMA Network Open. 2023;6(8):e2329729. doi:10.1001/jamanetworkopen.2023.29729,"[92, 1469, 618, 1489]",noise,0.9,"[""footer label""]",noise,0.9,,unknown_like,none,False,False +12,27,footer,"August 25, 2023","[974, 1470, 1075, 1489]",noise,0.9,"[""footer label""]",noise,0.9,,unknown_like,short_fragment,False,False +12,28,footer,Downloaded from jamanetwork.com by guest on 05/18/2026,"[2, 1550, 527, 1569]",noise,0.9,"[""footer label""]",noise,0.9,,unknown_like,none,False,False +12,29,number,12/13,"[1096, 1470, 1128, 1489]",noise,0.9,"[""page number label""]",noise,0.9,,unknown_like,short_fragment,False,False +13,0,header_image,,"[95, 0, 252, 19]",unknown_structural,0.2,"[""unrecognized label 'header_image'""]",unknown_structural,0.2,,unknown_like,empty,False,True +13,1,header,JAMA Network Open | Health Informatics,"[92, 67, 409, 89]",noise,0.9,"[""header label""]",noise,0.9,,unknown_like,none,False,False +13,2,header,"Sepsis Prediction Model vs SIRS, qSOFA, and SOFA","[813, 69, 1128, 88]",noise,0.9,"[""header label""]",noise,0.9,,unknown_like,none,False,False +13,3,text,eTable 3. Performance Metrics for Admissions With Respect to Organ Dysfunction Among 1324 EHR-Confirmed Admissions,"[90, 115, 780, 157]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,,unknown_like,none,True,True +13,4,text,"eTable 4. Performance Metrics for Admissions With Respect to Time Zero for 1324 EHR-Confirmed Admissions, PSS Only","[90, 159, 778, 201]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,,unknown_like,none,True,True +13,5,text,SUPPLEMENT 2.,"[91, 225, 206, 246]",unknown_structural,0.3,"[""short text, uncertain role""]",unknown_structural,0.3,,unknown_like,short_fragment,False,True +13,6,text,Data Sharing Statement,"[92, 249, 253, 269]",body_paragraph,0.6,"[""default body_paragraph for text label""]",body_paragraph,0.6,,unknown_like,none,True,True +13,7,footer,JAMA Network Open. 2023;6(8):e2329729. doi:10.1001/jamanetworkopen.2023.29729,"[92, 1468, 619, 1490]",noise,0.9,"[""footer label""]",noise,0.9,,unknown_like,none,False,False +13,8,footer,"August 25, 2023","[974, 1470, 1075, 1489]",noise,0.9,"[""footer label""]",noise,0.9,,unknown_like,short_fragment,False,False +13,9,footer,Downloaded from jamanetwork.com by guest on 05/18/2026,"[2, 1549, 528, 1570]",noise,0.9,"[""footer label""]",noise,0.9,,unknown_like,none,False,False +13,10,number,13/13,"[1095, 1470, 1128, 1489]",noise,0.9,"[""page number label""]",noise,0.9,,unknown_like,short_fragment,False,False diff --git a/tests/test_ocr_document.py b/tests/test_ocr_document.py index 6a04d13a..0da1d87a 100644 --- a/tests/test_ocr_document.py +++ b/tests/test_ocr_document.py @@ -4802,6 +4802,8 @@ def test_same_page_reference_boundary_is_resolved_upstream_not_in_renderer() -> by_id = {b["block_id"]: b for b in normalized} assert by_id["body_1"]["role"] == "body_paragraph" assert by_id["ref_1"]["role"] == "reference_item" + assert by_id["body_1"].get("zone") != "reference_zone" + assert by_id["ref_1"].get("zone") == "reference_zone" def test_page_text_coverage_flags_low_ratio_when_pdf_text_dominates() -> None: diff --git a/tests/test_ocr_figures.py b/tests/test_ocr_figures.py index 4dace76e..7b36eb23 100644 --- a/tests/test_ocr_figures.py +++ b/tests/test_ocr_figures.py @@ -2246,3 +2246,29 @@ def test_partition_assets_by_caption_bands_keeps_assets_local_to_caption_band() assert [a["block_id"] for a in parts["101"]] == [1, 2] assert [a["block_id"] for a in parts["102"]] == [3] + + +def test_sequential_fallback_can_match_previous_page_asset_for_next_page_caption() -> None: + from paperforge.worker.ocr_figures import build_figure_inventory + + blocks = [ + {"block_id": "a1", "page": 39, "role": "media_asset", "raw_label": "image", "bbox": [220, 1153, 556, 1443]}, + { + "block_id": "cap3", + "page": 40, + "role": "figure_caption", + "text": "Fig. 3. Magnetic actuation characterization of MR-SCS.", + "bbox": [73, 99, 1032, 548], + "zone": "post_reference_backmatter_zone", + "style_family": "legend_like", + "marker_signature": {"type": "figure_number", "number": 3}, + }, + ] + + inventory = build_figure_inventory(blocks) + matched = [m for m in inventory["matched_figures"] if m.get("figure_number") == 3] + ambiguous = [a for a in inventory["ambiguous_figures"] if a.get("figure_number") == 3] + + assert len(matched) == 1 + assert matched[0]["matched_assets"][0]["block_id"] == "a1" + assert ambiguous == [] diff --git a/tests/test_ocr_health.py b/tests/test_ocr_health.py index 12aeafc0..3eda5483 100644 --- a/tests/test_ocr_health.py +++ b/tests/test_ocr_health.py @@ -613,3 +613,84 @@ def test_ocr_health_role_gate_degraded_forces_overall_red() -> None: ) assert health["overall"] == "red" + + +def test_ocr_health_exposes_completeness_signals_for_runtime_blocks() -> None: + from paperforge.worker.ocr_health import build_ocr_health + + report = build_ocr_health( + page_count=1, + raw_blocks_count=2, + structured_blocks=[ + { + "role": "body_paragraph", + "text": "short text", + "page": 1, + "pdf_text": "short text plus a much longer native PDF segment that should dominate coverage", + "pdf_region_text": "short text plus a much longer native PDF segment that should dominate coverage", + }, + {"role": "section_heading", "text": "Intro"}, + {"role": "section_heading", "text": "Methods"}, + {"role": "reference_item", "text": "[1] Ref"}, + {"role": "abstract_body", "text": "Abstract."}, + ], + figure_inventory={}, + table_inventory={}, + ) + + assert report["page_text_coverage"][1]["page_text_coverage_status"] == "low" + assert report["text_completeness_summary"]["short_vs_pdf"] == 1 + + +def test_ocr_health_marks_missing_pdf_baseline_without_perfect_ratio() -> None: + from paperforge.worker.ocr_health import build_ocr_health + + report = build_ocr_health( + page_count=1, + raw_blocks_count=1, + structured_blocks=[ + {"role": "body_paragraph", "text": "Body text", "page": 1, "pdf_text": ""}, + {"role": "section_heading", "text": "Intro"}, + {"role": "section_heading", "text": "Methods"}, + {"role": "reference_item", "text": "[1] Ref"}, + {"role": "abstract_body", "text": "Abstract."}, + ], + figure_inventory={}, + table_inventory={}, + ) + + coverage = report["page_text_coverage"][1] + assert coverage["page_text_coverage_status"] == "missing_pdf_text" + assert coverage["page_text_coverage_ratio_chars"] is None + + +def test_rendered_text_coverage_normalizes_whitespace_and_case() -> None: + from paperforge.worker.ocr_health import audit_rendered_text_coverage + + result = audit_rendered_text_coverage( + rendered_markdown="In Vivo methods are summarized here.", + pdf_segments=["in vivo methods are summarized here."], + ) + + assert result["rendered_text_gap_count"] == 0 + + +def test_ocr_health_reports_rendered_gap_count_when_markdown_is_provided() -> None: + from paperforge.worker.ocr_health import build_ocr_health + + report = build_ocr_health( + page_count=1, + raw_blocks_count=1, + structured_blocks=[ + {"role": "body_paragraph", "text": "Short OCR body.", "page": 1, "pdf_region_text": "A long methods segment that is missing from render."}, + {"role": "section_heading", "text": "Intro"}, + {"role": "section_heading", "text": "Methods"}, + {"role": "reference_item", "text": "[1] Ref"}, + {"role": "abstract_body", "text": "Abstract."}, + ], + figure_inventory={}, + table_inventory={}, + rendered_markdown="Only the introduction survived.", + ) + + assert report["rendered_text_gap_count"] == 1 diff --git a/tests/test_ocr_pdf_text_fallback.py b/tests/test_ocr_pdf_text_fallback.py index 97d56ae8..d90680a2 100644 --- a/tests/test_ocr_pdf_text_fallback.py +++ b/tests/test_ocr_pdf_text_fallback.py @@ -2,7 +2,6 @@ from pathlib import Path import fitz -import pytest from paperforge.worker.ocr_pdf_spans import ( _words_to_text, diff --git a/tests/test_ocr_real_paper_audit_contracts.py b/tests/test_ocr_real_paper_audit_contracts.py index 8c9fd4ca..42a20e36 100644 --- a/tests/test_ocr_real_paper_audit_contracts.py +++ b/tests/test_ocr_real_paper_audit_contracts.py @@ -33,3 +33,11 @@ def test_layout_class_manifest_has_named_representatives() -> None: assert by_tag["same_page_ref_body_split"] assert by_tag["post_reference_biography"] assert by_tag["review_callout"] or by_tag["special_structure"] + + +def test_every_audit_paper_has_at_least_one_layout_tag() -> None: + manifest = _load_manifest() + + missing = [paper["paper_key"] for paper in manifest["papers"] if not paper.get("layout_tags")] + + assert missing == [] diff --git a/tests/test_ocr_real_paper_regressions.py b/tests/test_ocr_real_paper_regressions.py index 67332a30..6c534dd3 100644 --- a/tests/test_ocr_real_paper_regressions.py +++ b/tests/test_ocr_real_paper_regressions.py @@ -474,10 +474,6 @@ def test_dwqqk2yb_ownership_no_longer_mega_merges_same_page_assets(tmp_path: Pat assert 3 in matched or 3 in ambiguous, "Fig 3 should be captured" -@pytest.mark.xfail( - strict=True, - reason="Gate 2 remaining work: DW Fig 3 caption scores ambiguous on its mixed-layout page; needs group-first scoring refinement", -) def test_dwqqk2yb_figure3_is_fully_owned_not_merely_captured(tmp_path: Path) -> None: """Gate 2 regression: DW Figure 3 must be strictly matched, not left ambiguous.""" result = replay_production_pipeline("DWQQK2YB", tmp_path)