Commit graph

1538 commits

Author SHA1 Message Date
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
Research Assistant
7b165970a5 docs: record heading merge + level fixes in PM 2026-06-25 16:15:07 +08:00
Research Assistant
88116cf262 fix: normalize OCR heading newlines and fix single-word heading level 2026-06-25 16:14:32 +08:00
Research Assistant
423927545d fix: prevent adjacent heading merge when case styles differ 2026-06-25 16:02:19 +08:00
Research Assistant
d018a68fd6 fix: harden early OCR frontmatter-side zone inference 2026-06-25 15:57:53 +08:00
Research Assistant
5b830d3bfd fix: sort maintenance table by time descending 2026-06-24 01:19:45 +08:00
Research Assistant
1f7bd743ce fix: close out OCR merge-gate figure blockers 2026-06-24 01:09:38 +08:00
Research Assistant
9a19dc5850 fix: maintenance tab timezone, rebuild timestamp, title tooltip; UI: Vercel-inspired polish across all tabs 2026-06-24 01:08:20 +08:00
Research Assistant
692c200a12 fix: sort reference items by parsed number in tail reordering 2026-06-23 20:01:41 +08:00
Research Assistant
fb19d5ba20 fix: sidecar outlier-tolerant narrow column + raw band for far-column panels
- _same_page_narrow_caption_column: cluster by x-center when global
  spread check fails, keep largest aligned column with >=2 captions.
  Singleton right-column outliers (misclassified body mentions) dropped.
- Sidecar promotion uses raw_band_assets directly (band partition is
  the ownership unit for sidecar pages, not row-coupled geometry).
- Add asset_block_ids to sidecar entry schema.
- Fixes 6FGDBFQN Fig 3: 2 assets [11,13] via sidecar (was assetless_shell).
  Fig 2: 2 assets [3,4] via sidecar (was 4 via same_page).
- 221 tests, 0 failures.
2026-06-23 19:29:38 +08:00
Research Assistant
c03b95964e fix: same-number distinct legend dedup guard
- _strip_caption_number_prefix + _normalized_caption_body helpers
- When same figure number but different caption body (and not bundle-source),
  retain both as distinct legends instead of deduping
- Fixes 2UIPV93M page 49 appendix figures silently removed by dedup
- Bundle-source dedup (DWQQK2YB pattern) unaffected
- same_number_distinct_legends audit surface added to inventory
- 217 tests passed, 0 failures
2026-06-23 16:56:52 +08:00
Research Assistant
1076cffc82 fix: converge figure ownership accounting and phantom cluster cleanup
- Add _score_dense_parent_candidate_against_local_ownership (Task 5)
- Dense parent built before legend loop to participate in arbitration (Task 7)
- Relax _dense_parent_single_group_ok to fragment_count >= 4
- Dense parents excluded from pages with competing captions
- Dense page unmatched assets -> unresolved_clusters with cluster_source
- Dedup unresolved_clusters against matched_figures after all matching
- Composite parent supports representative cases (2UIPV93M, RKSLQRIM);
  VFS8CBW2 resolved via same_page/cross_page after phantom fix
- 217 tests passed, 0 failures
2026-06-23 15:22:34 +08:00
Research Assistant
147f5b77e1 docs: add convergence architecture spec/plans and dense-parent hardening spec/plan 2026-06-23 14:29:18 +08:00
Research Assistant
d7e7821c9c fix: resolve VFS8CBW2 page-32 Fig. 3 via page-aware reservation keys
- Changed _reserve_cross_page_objects to return set[tuple[int, str]]
  instead of set[str] so reused block_id across pages does not
  overwrite reserved legends (VFS8CBW2 page 33 vs 38 both used block_id=1)
- Updated _settle_cross_page_reserved_objects to key legends_by_id
  by (page, block_id)
- Fix: reserved legends no longer filtered by caption-band assist on
  competing-caption pages, so deferred hypotheses still emit
- Fix: reservation test assertion updated for tuple format
- Fix: competing-caption veto test updated for band-scoped parent
  acceptance
2026-06-23 14:29:05 +08:00