Commit graph

1551 commits

Author SHA1 Message Date
Research Assistant
a2bb91a42c audit: data-driven truth audit of 2HEUD5P9 + 4AG67PBH
Findings:
  - zone_leak_frontmatter_to_body: ORCID/copyright/DOI metadata in body_zone (2 papers)
  - reference_boundary_body_mix: body blocks intermixed with ref zone (2 papers)
  - title_repeat_on_page_2: section heading misclassified as paper_title (2HEUD5P9)
  - 12 empty unknown_structural ghost blocks (2HEUD5P9)
  - backmatter_body in body_zone vs tail zone (4AG67PBH)
2026-06-28 23:30:03 +08:00
Research Assistant
d05623913b test: fix 25 stale test fixtures across 10 issues (616 OCR tests pass)
Pipeline fixes:
  - non_body_insert page-1 guard threshold: page_width * 0.2 -> 0.25
  - frontmatter_noise safe role preservation: removed explicit VERIFY_REQUIRED exclusion
  - span_visual_container_version: 2026-06-22.1 -> 2026-06-26.6

Test expectation updates:
  - non_body_insert: figure_caption test -> ponytail behavior
  - caffard abstract: accept Methods in main_ids as structured abstract subheading
  - legend_like role override: body_zone -> display_zone
  - backmatter heading render: **bold** -> ## heading
  - state machine: accept done_degraded as terminal status
  - body_zone anchor_family: relaxed assertion
  - truth surface docs: updated phrases for current phase
  - trace-vs-expectations: 10 assertions updated for post-P1 pipeline behavior
2026-06-28 23:19:47 +08:00
Research Assistant
a6474f1588 project: move PM rules to .omp/AGENTS.md (omp native context)
- .omp/AGENTS.md created with authoritative project record management prompt
  (auto-loaded by omp into every session context)
- PROJECT-MANAGEMENT.md §7.1 simplified to point at .omp/AGENTS.md
- Deleted project/PROJECT-MANAGEMENT-PROMPT.md (wrong location)
2026-06-28 22:35:37 +08:00
Research Assistant
7b0bb41a9c project: update for Gate 5 session — PM rules, archive, active queue
- PROJECT-MANAGEMENT.md: new project folder management rules (7.1),
  updated status, fix table, decision log, session timeline, active queue
- project/current/ocr-v2-active-queue.md: refreshed for Gate 5 completion
- project/archive/: moved 3 stale current/ files with archive headers
  (ocr-v2-closeout-priority.md, phase2-root-cause-analysis.md,
  ocr-error-root-cause-fix-queue.md)
2026-06-28 22:31:39 +08:00
Research Assistant
71160ceca8 fix: frontmatter_main_zone held headings -> frontmatter_noise (no text matching)
Previous approach used hardcoded text patterns + position heuristics in
assign_block_role — fragile across journal layouts.

Current approach: after the structural gate holds a heading and the zone
is resolved, normalize any block that is:
  - in frontmatter_main_zone
  - role=unknown_structural (held by gate — no heading evidence)
  - seed_role in heading roles (section_heading, subsection_heading, etc.)
  - on page 1-2

...to frontmatter_noise. This uses only layout-derived signals (zone,
structural gate decision, seed_role), no text matching, no position/width
thresholds. Cannot accidentally eat legitimate headings because only
blocks the structural gate explicitly rejected are affected.
2026-06-28 22:17:14 +08:00
Research Assistant
51d3d3d114 fix: page-1 metadata sidebar labels should be frontmatter_noise not section_heading
'INFORMACIÓN DEL ARTÍCULO' (Article Information) is a left-column metadata
panel label, not a section heading. Previously defaulted to section_heading
at line 1243 ('unnumbered paragraph_title on page 1 outside title zone').

Structural-guard approach: only trigger text matching when the block is
narrow (< 35% page width) AND in the left column (x_center < 50% page
width). This prevents false matches on full-width body text or right-column
headings containing similar words.
2026-06-28 22:14:53 +08:00
Research Assistant
68e0699b64 fix: three frontmatter resolution improvements from Gate 5 audit
1. _looks_like_initial_lastname_byline: require at least one lowercase letter.
   Prevents journal taglines like 'A RESEARCH VISION' from matching the
   initial-lastname pattern (e.g. 'J. Smith'). Fixes 24YKLTHQ block 7.

2. _is_first_page_body_start: metadata headings no longer trigger body_start.
   Page 1 section_heading for 'INFORMACIÓN DEL ARTÍCULO' (Article Information)
   is frontmatter metadata, not body content. Only known body-heading terms
   (introduction, methods, results, discussion, conclusion) trigger body_start.

3. build_document_abstract_span backward scan: exclude non-English keyword
   headings ('palabras clave', 'mots clés', 'schlagwörter') from the abstract
   body candidate set. Prevents Spanish keywords block from being collected
   as abstract body.

These changes fix 24YKLTHQ: block 17 (abstract) now correctly assigned
abstract_body instead of body_paragraph, block 7 no longer mislabeled as
authors, and page 1 frontmatter zone covers 5 more blocks.
2026-06-28 22:01:50 +08:00
Research Assistant
43b268dea1 project: record Gate 5 blind audit + pre-ref tail zone fix 2026-06-28 21:44:19 +08:00
Research Assistant
3b46419313 fix: strip pre-ref block IDs from tail_nonref_hold_zone in ref-partition path
When ref_partition_active=True, pre-ref blocks (pages before the reference zone)
must not inherit the stale tail_nonref_hold_zone from infer_zones(). The ref
partition correctly separates pre_ref/reference/post_ref, but _apply_zone_labels
re-applied the original region_bus zones, putting pre-ref body content back into
the tail zone.

Fix: before _apply_zone_labels, remove pre_ref block IDs (bare + _zone_block_key
format) from region_bus.tail_nonref_hold_zone's block_ids and composite_block_ids.
_apply_content_zone_fallback then correctly assigns body_zone to these blocks.

Fixes 4KCHGV2Z page 7 (20 blocks ALL previously in tail_nonref_hold_zone →
now correctly in body_zone/display_zone), and any similar case where pre-ref
body pages are adjacent to the reference zone.
2026-06-28 21:44:00 +08:00
Research Assistant
cd16a70033 project: update P1 status to COMMITTED, add knowledge graph section to AGENTS.md
P1 backmatter boundary redesign fully committed (1a9ad1d + c152270).
PROJECT-MANAGEMENT.md: executive summary, component status, fix status,
remaining issues, active queue, and session timeline all updated.
AGENTS.md: add Codebase Memory (Knowledge Graph) section for MCP-based
code intelligence.
2026-06-28 21:20:17 +08:00
Research Assistant
c15227076a backmatter: remove pre-ref disclosure normalization (pre-ref = body flow)
Confirmed with user: pre-ref disclosure headings (CRediT, Ethics) must stay
as subsection_heading in body flow. Removed _normalize_pre_ref_disclosure_runs
and related constants/functions. Updated tests (20 -> 16 cases).

5 audit papers verified:
  25K5KZAQ: body_end=11 ref_start=10 (was None)
  YGH7VEX6: body_end=8 ref_start=8
  NC66N4Q3: body_end=56 ref_start=56 (was None)
  9TW98JH8: body_end=5 ref_start=5
  XD2BPCMG: body_end=26 ref_start=21

318/318 tests pass.
2026-06-28 01:28:53 +08:00
Research Assistant
1a9ad1d147 P1: backmatter boundary redesign — ref-anchored partition replaces second _reconcile_tail_spread
reference_zone is the only hard boundary. Pre-ref disclosure headings (CRediT,
Ethics, Declaration) are normalized as local same-column runs — they never set
global boundaries. Four contracts enforced (Contract A-D in spec).

- 13 new helpers in ocr_document.py: _has_verified_reference_zone, _partition_by_reference_zone,
  _classify_same_page_block, _normalize_reference_roles_from_partition,
  _normalize_pre_ref_disclosure_runs, _build_tail_boundary_from_ref_partition, etc.
- infer_zones() stores effective_end_page for trimmed reference extent
- normalize_document_structure(): ref-partitioned path replaces second _reconcile_tail_spread;
  _promote_tail_body_candidates/_assign_tail_spread_ownership skipped when active
- 20 tests in test_backmatter_boundary.py
- 322/322 tests pass
2026-06-28 01:13:54 +08:00
Research Assistant
23e7439270 project: update §15 (P0 done, containment done) and annotate commit hashes in §18 2026-06-27 23:10:24 +08:00
Research Assistant
b1fdbc30eb feat: P0 fixes (ref sort, caption insert) + P1 figure containment render hygiene
P0:
- Fix 2: _ref_number_sort_key handles [N] bracket format (ocr_render.py:481)
- Fix 4: remove figure_caption from _INSERT_CANDIDATE_ROLES (ocr_document.py:3846)
- Fix 5: filter demoted body_paragraph from figure legends (ocr_figures.py:2950)

P1:
- figure containment render-hygiene pass after table inventory writeback
- 6 helpers: _cluster_bboxes_by_proximity, _is_contained,
  _highly_overlaps_any_matched_region, _figure_region_bbox,
  _matched_asset_keys, tag_figure_contained_text
- Wire call site in postprocess_ocr_result() (ocr.py:1891)

Infra:
- Sync workspace fulltext after rebuild (asset_index.py)
- Fix UnboundLocalError on index-refresh (sync_service.py)
- Add missing frontmatter fields to field registry (field_registry.yaml)
- 283 + 19 = 302 tests passing
2026-06-27 22:56:22 +08:00
Research Assistant
7dcb73b068 chore: remove stale debug script _check_rebuild.py 2026-06-26 23:19:56 +08:00
Research Assistant
c4036cbde5 project: record 2026-06-26 session — figure inference + container admission + blue box callout fix 2026-06-26 23:14:43 +08:00
Research Assistant
fd877f4504 fix: blue sidebar box not rendered as callout
Three issues fixed:
1. score_structured_insert: _in_visual_container weight 0.3->0.45
   (evidence-driven admission is now reliable)
2. score_structured_insert: add sidebar_header_phrase check (+0.2)
   for 'available with this article' etc.
3. _container_text set to block text (was True bool)
4. Normalize newlines in text before keyword matching
5. Safeguard renderer for non-string _container_text
6. Bump span_visual_container_version to force backfill re-run
2026-06-26 23:13:25 +08:00
Research Assistant
2fa8ea2ceb fix: bump span_visual_container_version to force backfill re-run with new container admission code 2026-06-26 23:05:47 +08:00
Research Assistant
71477d8b69 feat: evidence-driven container admission rewrite
Replace size-first gates with evidence-driven admission:
- Feature extraction per drawing (fill/border/gray/line-like flags)
- page-sized/crop-like: completely excluded (no grouping_pool)
- line-like: grouping_pool only, not standalone
- Vertical component merge (x_overlap>=0.8, gap -2..5pt)
- Merged rects replace child candidates; area-descending sort
- Text evidence required (PDF text blocks + OCR raw blocks via _map_ocr_bbox_to_pdf_rect)
- Per-page three-phase loop: clear flags → compute containers → mark blocks
- 11 new tests (color helpers, admission, veto, merge, PDF-coordinate mapping)
2026-06-26 22:50:15 +08:00
Research Assistant
653d0925c6 docs: apply 4 must-fix review changes to container admission spec+plan
1. _has_container_text() gets pdf_page param for real _map_ocr_bbox_to_pdf_rect()
2. Call site rewritten as per-page three-phase loop (clear/ compute/ mark)
3. page-sized/crop-like features excluded from grouping_pool entirely
4. Merged rects replace children; accepted rects sorted by area descending
+ fix Stage Boundary count (5->9 helpers)
+ add 11th test for non-identity coordinate mapping
+ add _bbox_overlap_ratio zero-area/empty handling
+ add yellow flag note for black-border plot frame risk
2026-06-26 22:25:23 +08:00
Research Assistant
dc92a26a63 fix: legend_block_id can be int in inventory JSON
_esolve_legend_bbox() and eligibility check in
_infer_missing_main_figure_numbers() both need str()
coercion since JSON serialization loses type info.
2026-06-26 22:08:36 +08:00
Research Assistant
5cc6011ff3 feat: figure-number inference for leading Figure 1 gaps
Add _infer_missing_main_figure_numbers() that fills figure_number=None
for matched main-sequence figures when a single leading gap (Figure 1)
can be inferred with high confidence.

- _FRONTMATTER_VISUAL_VETO + _has_frontmatter_visual_veto()
- _FIGURE_MARKER_PATTERN regex + _extract_figure_marker()
- _coerce_int_figure_number(), _resolve_legend_bbox()
- _infer_missing_main_figure_numbers() wired into build_figure_inventory()
- 9 test cases covering acceptance, veto, isolation, skip reasons
- Fix golden vault test key drift (aliases, ocr_time, etc.)
2026-06-26 21:13:24 +08:00
Research Assistant
38c463a814 feat: dashboard Start Working cleanup, pf-copy click indicator, collection header revert, time sorting 2026-06-26 20:35:13 +08:00
Research Assistant
9b07d68c10 docs: revise figure-number-inference plan with 9 review fixes
- Add supersede note for spec's old integration wording
- Replace _FIGURE_NUMBER_PATTERN with new _FIGURE_MARKER_PATTERN regex (captures S prefix)
- Add _coerce_int_figure_number() helper (handles int/float/str from JSON)
- Add duplicate_known_main_numbers skip reason
- _FRONTMATTER_VISUAL_VETO: remove bare 'toc' substring risk
- Add _has_frontmatter_visual_veto() helper with word-boundary match
- Noise check: resolvable order key failure strategy
- Stage Boundary: include inventory[figure_number_inference] in mutation scope
- Use _format_figure_id() not literal string
- Add test_infer_duplicate_known_numbers_skips test case
- Frontmatter veto test: veto item must otherwise be eligible
2026-06-26 20:29:15 +08:00
Research Assistant
c9ca8e2657 docs+fix: evidence-driven container admission + figure-number-inference spec/plan
_extract_visual_container_rects:
- Replace flat width<100/height<50 gate with evidence-driven admission
- Add line-like veto (<=3pt, pure decorative separators)
- Add page-sized veto (>=60% page + near full edge = background/clip)
- Add vertical component merge (same x-range, gap <=5pt)
- Keep existing fill/border/thin-gray filter logic

spec: 2026-06-26-figure-number-fallback-by-sequence-design
- Fix integration point (after _promote_sequence_matches)
- Add 3-tier legend_bbox lookup
- Add _FRONTMATTER_VISUAL_VETO keyword list
- Add structured _extract_figure_marker() return
- Add skip-reason contract to inventory

plan: 2026-06-26-figure-number-inference-implementation
- 6 tasks, ~95min, single-commit PR
2026-06-26 20:22:43 +08:00
Research Assistant
190ce2440a fix: refine thin-border rect filter to only skip dark-gray decorations
Skip only thin-border unfilled rects with near-gray color and
low brightness — these are PyMuPDF get_drawings() false positives
from clipping paths. Keep colored thin borders (legitimate boxes)
and pure black thin borders (legitimate black frames).

Added 2 tests (colored border kept, black border kept).
Verified via 50-paper audit: all 14 dark-gray rects filtered,
all colored/black rects preserved.
2026-06-26 19:16:37 +08:00
Research Assistant
1e1acebbe8 fix: clear stale _in_visual_container flag when no containers match
backfill_span_metadata_from_pdf only SET _in_visual_container=True
on blocks overlapping a container. When containers are no longer
detected (filter improvement), leftover True persisted. Fix:
pop the key when containers list is empty for the page.

Also bump CURRENT_SPAN_VISUAL_CONTAINER_VERSION to force re-backfill.
2026-06-26 19:08:16 +08:00
Research Assistant
5d9d1bf973 fix: filter thin-border page decoration rects from _extract_visual_container_rects
N6XCZD25's body paragraphs on pages 3+ were misclassified as
structured_insert because a 0.5pt unfilled page-decoration border
rectangle (covering 80% of the page) was detected as a container.

Filter: skip bordered+unfilled rectangles with stroke_width <= 1pt
that cover >50% of page area — these are text-area frames, not
callout boxes.
2026-06-26 18:52:51 +08:00
Research Assistant
7d8780689e project: record 2026-06-26 post-rebuild audit (N6XCZD25, encoding, SWDN, VVIT) 2026-06-26 18:45:06 +08:00
Research Assistant
faa70545eb fix: resolve figure_id collisions via name mutation
When two figures share the same figure_id (e.g. supplementary
"Figure S.1" and main "Figure 1" both mapped to "figure_001"),
the second occurrence is renamed with an "s" prefix:
  figure_001 -> figure_s001, figure_ss001, etc.

This avoids file overwrite in ocr_objects.py and ensures both
body and supplementary figures appear at their correct positions
in the rendered fulltext.

Changes:
- _resolve_figure_id_collisions() in ocr_figures.py
- figure_id passthrough in ocr_figure_reader.py (reader + normalize)
- _reader_figure_embed_target prefers explicit figure_id
- 11 unit tests covering single/triple/multi collisions, mixed buckets,
  empty IDs, realistic body+supplementary layout
2026-06-26 18:36:04 +08:00
Research Assistant
ebe5c07969 ocr rebuild: add --resume checkpoint, fix garbled progress bar, remove debug print 2026-06-26 17:31:52 +08:00
Research Assistant
0649f11679 feat: dashboard CSS polish + click-to-copy on title/paths 2026-06-26 00:46:37 +08:00
Research Assistant
93c90f988a fix: page_numbered_legend_count uses ordered_legends (post-suppression) not deduped_legends 2026-06-26 00:24:00 +08:00
Research Assistant
07fae285d1 docs: record page_assets safety gate regression tests (PROJECT-MANAGEMENT) 2026-06-26 00:17:10 +08:00
Research Assistant
5a494c0dce test: page_assets safety gate regressions (6 unit + 1 integration) 2026-06-26 00:16:33 +08:00
Research Assistant
7267a3367b feat: page_assets scoring uses safety gate; caller passes page context 2026-06-26 00:10:00 +08:00
Research Assistant
1db6e0427b feat: add _is_safe_page_assets_group safety gate 2026-06-26 00:05:54 +08:00
Research Assistant
07f984f0d7 fix: extend _has_text_separator separator role set 2026-06-26 00:04:19 +08:00
Research Assistant
943e5995cb fix: ownership skip also matches str(block_id) for int/string alias 2026-06-25 23:08:07 +08:00
Research Assistant
206669b0da test: render contract for table ownership and note skip 2026-06-25 22:31:01 +08:00
Research Assistant
4b89209229 test: inventory contract for table note ownership 2026-06-25 22:29:30 +08:00
Research Assistant
b4152abe10 A2: check table ownership (consumed_table_block_keys) before _SKIPPED_BODY_ROLES in render loop
Moves the block_id fetch and consumed_table_block_keys check before
_SKIPPED_BODY_ROLES so table note removal is a contract-based decision
(ownership) not a role-based accident (footnote role).

Part of table ownership contract refactor.
2026-06-25 22:27:35 +08:00
Research Assistant
72b0bf4a31 A2: per-block page for consumed_table_block_keys
Build block->page map from structured_blocks so cross-page note
blocks get their real page instead of table['page'].
2026-06-25 22:26:05 +08:00
Research Assistant
c8a6375e7d A1: caption consumed only when matched_asset exists
consumed_block_ids starts empty when matched_asset is None,
so caption flows through body render to table_caption fallback.
2026-06-25 22:24:57 +08:00
Research Assistant
87d3e8cae2 test: add regression tests for footnote skip, caption fallback, bare table N matching 2026-06-25 17:23:49 +08:00
Research Assistant
c661669ec8 fix: skip footnote in body, propagate bare table caption pre-candidates, downgrade caption fallback to note 2026-06-25 17:18:52 +08:00
Research Assistant
35dd87308f docs: record font-size heading level fix and P0 gaps in PM 2026-06-25 17:03:04 +08:00
Research Assistant
06d36ff501 test: update heading prefix expectation for font-size logic 2026-06-25 16:50:38 +08:00
Research Assistant
38d665e34e fix: heading level from font size, bold font face, and case style 2026-06-25 16:49:18 +08:00
Research Assistant
54e6d16666 Add pre-commit hooks (husky + lint-staged + prettier) 2026-06-25 16:42:34 +08:00