Commit graph

564 commits

Author SHA1 Message Date
LLLin000
d50ea4631b fix: final cleanup before merge — stale test, helper, RGB safety
1. Stale test: test_extract_objects_renders_same_page_once_for_multiple_crops
   now asserts render_pdf_page_cached is NOT called (new PageRenderContext path)
   and pages/page_001.jpg is NOT created.

2. Extract _resolve_object_crop_pdf_path() helper with 4-case test.
   Phase 4 now uses it instead of inline fallback logic.
   Test locks in: Phase 1 resolved path beats stale meta['source_pdf'].

3. PageRenderContext: use fitz.csRGB in get_pixmap() for safe CMYK/n>3 handling.
   Removed fragile pix.n mode detection.

4. Added test_extract_and_write_objects_with_use_disk_page_cache_false_and_valid_pdf
2026-07-05 21:36:17 +08:00
LLLin000
643052489a fix: determinism + correctness fixes before further optimization
6 fixes bundled:

1. Cleanup: remove accidental empty file '6s}'

2. Rebuild determinism: add 'use_disk_page_cache' gate to _crop_asset_from_pdf.
   Rebuild (use_disk_page_cache=False) never reads or writes pages/page_XXX.jpg.
   All rendering goes through PageRenderContext in-memory.
   Legacy callers keep backward-compat behavior (default True).

3. PageRenderContext safety:
   - Only use when page_width>0 and page_height>0 and not rotation_deg
   - Fix Pixmap.n mode detection (L/RGB/RGBA -> convert to RGB)
   - Fix Image.Resampling name bug (Image -> PILImage)
   - Rotated crops always fall back to direct PDF clip path

4. Phase 1+2a merged traversal: always extract PDF lines on every page,
   regardless of whether span_metadata already exists. Fixes figure inventory
   data gaps when pages have pre-existing span coverage.

5. Phase 4: use resolved source_pdf_path from Phase 1, not ocr_meta['source_pdf']
   fallback (which may be stale or missing).

6. Added 5 determinism tests
2026-07-05 21:32:05 +08:00
LLLin000
83b516aac0 fix: optimize span backfill with per-page rawdict batching and incremental re-extraction
- Group raw_blocks by page, call get_text('rawdict') once per page instead of once per block
  (1433 calls → 60 calls, 68s → 16.6s for a 60-page paper)
- Add _spans_from_rawdict() helper to filter cached rawdict by char bbox center overlap
- Keep extract_pdf_spans_for_block() as single-block compatibility API unchanged
- Add incremental backfill: skip pages where all blocks already have span_metadata
- Remove figure_title from _is_text_like_raw_block() coverage count (figure titles are
  typically image-rendered, not selectable PDF text)
- Add regression test: fast path output matches single-block API
2026-07-05 20:39:52 +08:00
LLLin000
c34ebbce81 feat: rebuild speed-up with parallel rebuild, threaded asset cropping, maintenance refactor, embed progress, CLI docs 2026-07-05 19:43:07 +08:00
LLLin000
7931f658bd feat: add rebuild-time fulltext backups and provenance 2026-07-05 17:00:41 +08:00
LLLin000
cdf969d85e feat: persist machine fulltext hash for initial OCR 2026-07-05 16:58:23 +08:00
LLLin000
1474c061a8 feat: surface drift state and invalidate maintenance cache 2026-07-05 16:52:17 +08:00
LLLin000
d8451947ad refactor: separate render artifact writes from user fulltext commit 2026-07-05 16:50:58 +08:00
LLLin000
0a3e40fd92 feat: add fulltext writeback state helpers 2026-07-05 16:45:31 +08:00
LLLin000
c534e481c7 fix: close 4 review gaps — registry, body/object-unit schemas, compat vector arm
Changes:
- Add paper-lookup, content-discovery, scoped-fetch to command registry
- Add unit_kind column to body_units schema (DEFAULT 'body')
- Replace object_units schema: object_role -> object_kind, object_label,
  caption_text, nearby_body_text; drop unit_text, drop object_role
- Bump schema version to 3 with deterministic drop/recreate migration for
  derived tables (body_units, body_units_fts, object_units)
- Update _upsert_body_units / _upsert_object_units for new columns
- Update build_object_units to emit object_kind, object_label, caption_text,
  nearby_body_text fields
- Add vector secondary arm to _run_compat_content_discovery: runs paper_fts
  primary, conditionally adds vector_retrieve secondary when vector is healthy
- Add/fix tests for schema columns, builder output, registry, and compat
  content-discovery vector-secondary path
2026-07-05 15:13:43 +08:00
LLLin000
1e6edad7ba feat: route Layer 4 gateway through body units and structure tree 2026-07-05 14:39:19 +08:00
LLLin000
9d332980f0 feat: add body units, manifest, and body-unit FTS 2026-07-05 14:21:27 +08:00
LLLin000
e23f35114c feat: add LanceDB evaluation backend scaffold 2026-07-05 14:19:37 +08:00
LLLin000
9d5a008df1 feat: make paper lookup decomposed and coverage-scored 2026-07-05 14:14:03 +08:00
LLLin000
b9f4d6c4f0 refactor: add vector backend adapter with Chroma compatibility 2026-07-05 14:13:59 +08:00
LLLin000
bc27ac2255 feat: add Layer 4 gateway command surface 2026-07-05 14:12:50 +08:00
LLLin000
12b930c31d feat: add structure tree builder for paper navigation 2026-07-05 14:12:36 +08:00
LLLin000
7917858e24 feat: PR D — Base view cleanup
- Removed "重做OCR" view from build_base_views() (3 standard views)
- Changed ocr_status displayName to "状态"
- Removed ocr_redo property from _build_base_yaml()
- Removed deprecated ocr_redo insertion from _sanitize_base_file()
- Updated tests (28 passed)
2026-07-05 01:26:34 +08:00
LLLin000
b585e42ab2 fix: 3 micro-patches from 9.4/10 review
1. show_in_base — separate hidden from maintenance vs hidden from both
2. Unified is_degraded check — done_degraded and health yellow/red
   share one entry point
3. Hash error_summary in manifest instead of raw string
4. Fix Promise.withResolvers for ES compat
2026-07-05 01:19:59 +08:00
LLLin000
63915e98a4 feat: PR B — CLI manifest/keys + keyed redo
- list_parser: added --manifest and --keys args
- redo_parser: added positional keys args
- _run_ocr_list: manifest mode (calls compute_maintenance_manifest)
  and keys filtering
- _run_ocr_redo: keyed redo for specific papers + fallback to old path
- 11 new tests (56 total, 0 failed)
2026-07-05 01:02:55 +08:00
LLLin000
e15edb1161 feat: PR A — backend display contract + manifest
- 7 display fields on OCRMaintenanceRow (display_action, label, label_key,
  reason, reason_key, group, severity, visible_in_maintenance)
- _compute_display_fields() pure function with 12 mapping rules
- compute_maintenance_manifest() lightweight hash function
- Integration in collect_maintenance_rows()
- 45 tests covering all mapping rules + manifest
2026-07-05 00:53:18 +08:00
LLLin000
96fd9771ef fix(ocr-quality): contract polish — 4 P0/P1 fixes
1. compute_use_cases output uses status/gates/reasons (not recommended/gate_results)
2. write_feedback validates every mark has result_hash and fulltext_hash
3. load_readiness_policy(policy=...) bypasses user override (reproducible)
4. confidence_and_fallbacks yellow for degraded=True or weak span coverage

Minor: append_mark doesn't mutate caller dict; has_figure_evidence includes
unmatched_legend_count and held_count.
2026-07-04 23:38:03 +08:00
LLLin000
8aba7feb48 Merge branch 'feat/ocr-quality-feedback' 2026-07-04 23:28:29 +08:00
LLLin000
c76f1569aa feat(ocr-quality): add evaluate_readiness() with YAML policy evaluator
Adds readiness policy evaluator layer:
- load_readiness_policy() with deep-merge, importlib.resources, user override
- _resolve_field() dotted path resolver, _apply_op() operator matcher
- _check_hard_red() for hard-red rule evaluation
- evaluate_readiness() with weighted scoring + status determination
- compute_use_cases() for all 4 use cases (reading, qa, figure_table, chunking)
- Default YAML policy with weights, hard_red rules, use_case gates
- pyproject.toml: policies/*.yaml added to package-data
- 7 tests (B1-B7) covering all paths

17/17 tests pass (A10 + B7).
2026-07-04 23:28:06 +08:00
LLLin000
3299c606d7 feat(ocr-quality): add human feedback sidecar
Standalone module (ocr_quality_feedback.py) with:
- read_feedback / write_feedback (atomic via temp+rename)
- append_mark (auto-injects hashes, preserves old marks)
- resolve_human_validation (unreviewed/confirmed/disputed/stale)
- Hashes stored per-mark, stale detection via latest_mark hash comparison

5 unit tests covering roundtrip, hash validation, append, stale, and
resolve without UI dependency.
2026-07-04 23:24:06 +08:00
LLLin000
51e16066c9 feat(ocr-quality): add build_quality_indicators() with 5 normalizers
Creates new ocr_quality.py module as pure function layer between
build_ocr_health() and evaluate_readiness(). All 5 indicators:
- rendered_text_integrity
- body_reference_structure
- figure_table_integrity
- metadata_frontmatter_quality
- confidence_and_fallbacks

10 unit tests covering shape, thresholds, applicability,
health_profile spelling, inventory precedence, and run_integrity.
2026-07-04 23:23:51 +08:00
LLLin000
2216ac7d87 fix(pr3): remove same-column guard from body_blocks, fix _page_width_for_zone_block arg 2026-07-04 22:36:46 +08:00
LLLin000
53a5336b6a feat(pr3): add column-aware same-page reference boundary 2026-07-04 22:36:46 +08:00
LLLin000
59b303dd66 feat(pr4): add column compatibility check for figure caption-asset pairing 2026-07-04 22:35:57 +08:00
LLLin000
2375f3c984 feat(pr2): tighten figure caption heuristic with inline mention escape 2026-07-04 22:35:50 +08:00
LLLin000
17b02b8abf fix(pr4): handle column_band=0 falsy correctly in score_legend_to_group 2026-07-04 22:35:28 +08:00
LLLin000
08a52dcb48 feat(pr4): add column compatibility check for figure caption-asset pairing 2026-07-04 22:28:43 +08:00
LLLin000
f97365b5b4 fix(pr2): return body_paragraph for inline figure mentions regardless of zone 2026-07-04 22:28:23 +08:00
LLLin000
30cc7c3f7b feat(pr2): tighten figure caption heuristic with inline mention escape 2026-07-04 22:28:17 +08:00
LLLin000
a138100398 fix(pr1): remove has_any_author gate from affiliation/email fallback 2026-07-04 22:17:43 +08:00
LLLin000
57bb73556b feat(pr1): add frontmatter render fallback for empty metadata 2026-07-04 21:57:04 +08:00
LLLin000
26715a9d7e feat: layout-category truth audit complete
Workstream X: 11 papers audited across 6 layout classes,
5 verified via vision subagents. 6 real bug patterns identified
with code locations and root cause analysis.

Bug patterns:
A - _is_obviously_formal_figure_caption heuristic too aggressive
    (ocr_roles.py:193)
B - Cross-column figure asset mis-assignment ignores boundaries
    (ocr_figures.py:704)
C - Render frontmatter skip silently drops authors/affiliations
    (ocr_render.py)
D - Two-column same-page boundary body→backmatter zone pollution
    (ocr_document.py)
E - Frontmatter headings misclassified (ocr_roles.py)
F - Supplementary-only PDFs not handled

Full report: docs/superpowers/analysis/2026-07-05-layout-truth-audit-findings.md

Also: codebase-memory-mcp 0.8.1 installed and repo indexed.
2026-07-04 21:26:15 +08:00
LLLin000
914acd6aa5 feat: enable OCR_PIPELINE_V3 by default
The V3 pipeline has been tested against the full 555-paper vault vs legacy:

  No diff: 547 (98.6%)
  Diff:     5 (0.9%) — all in v3's favor: 3 papers find 1 more figure
                        (v3 softens role before matching), 2 papers shift
                        1 block boundary (body_paragraph vs frontmatter_noise)
  Error:    3 (0.5%) — 2 missing raw data, 1 pre-existing v3 pipeline edge case

The 5 diffs are marginal improvements, not regressions. V3's shadow-normalize
approach defers role commit until after figure/table matching, so captions
that legacy normalize prematurely hardened to 'body_paragraph' are correctly
found as figure captions.

Changes:
- _ocr_pipeline_v3_enabled() defaults to True now; set OCR_PIPELINE_V3=0 to
  revert to legacy normalize-then-match order
- Test updated to assert default=True
- Full vault corpus diff script + result report archived
2026-07-04 20:04:39 +08:00
LLLin000
6f479ae46d test: widen v3 parity to six replay fixtures
- add fixture-backed replay parity for 37LK5T97, 8CCATQE3, and 5MAW65YD
- keep existing DWQQK2YB, VAMSAZMG, and PJBMGVTF gates
- update OCR project status and queue to 105-test state
2026-07-04 19:15:26 +08:00
LLLin000
ae3b5ce07c test: expand v3 replay parity corpus
- add fixture-backed parity replay for VAMSAZMG and PJBMGVTF
- keep DWQQK2YB as existing replay gate
- sync post_match render/index defaults to legacy contract
- update OCR status docs to 102-test state
2026-07-04 19:11:31 +08:00
LLLin000
2a4cda9782 test: add DWQQK2YB v3 parity gate
- sync render/index defaults in post_match_normalize to match legacy
- add fixture-backed real-paper parity test for DWQQK2YB
- update OCR project status and active queue to 100-test state
2026-07-04 18:33:32 +08:00
LLLin000
7856d6e202 fix: pre-merge blockers A-D
- A: page-qualified object writeback lookup (key by (page, block_id))
- B: contained figure text routed through ownership evidence
- C: v3 path rescue_roles_with_document_context equivalence
- D: regression tests for all 4 blockers
  - test_apply_object_writebacks_respects_page_for_duplicate_block_ids
  - test_contained_figure_text_stamps_ownership_evidence
  - test_post_match_normalize_runs_rescue_roles
  - test_v3_synthetic_parity_with_legacy_reference_boundaries
2026-07-04 16:40:53 +08:00
LLLin000
1a81817d7f test: add OCR pipeline v3 parity gate 2026-07-04 16:06:06 +08:00
LLLin000
cb685f73d0 C2: Implement post_match_normalize with shadow plus tail settlement, wire full v3 orchestration
- Rewrite ocr_post_match_normalize.py: shadow-normalize rows, commit final
  role/role_source/role_confidence/role_candidate/render_default/index_default
  from shadow, then run settle_tail_and_backmatter
- Wire v3 path in ocr.py postprocess_ocr_result:
  - seed_only normalize mode when OCR_PIPELINE_V3 is truthy
  - pre_match_normalize call (import inside if block)
  - post_match_normalize call (import inside if block)
  - Legacy path unchanged
- Add C2 test test_post_match_normalize_commits_shadow_role_back_to_public_role
2026-07-04 16:05:03 +08:00
LLLin000
d82949ee58 C1: shadow normalize in pre_match_normalize + _match_role helpers
- Rewrite pre_match_normalize to run a shadow normalize pass that
  populates role_candidate on each block while preserving public role
- Add _match_role(block) helper to ocr_figures.py (role_candidate > role > seed_role)
- Port matching-time role reads in figure builders to use _match_role
- Add _match_role(block) helper to ocr_tables.py (same priority)
- Port matching-time role reads in table builders to use _match_role
- Port role reads in FigureCorpus.from_blocks and TableCorpus.from_blocks
- Add 3 C1 tests to test_ocr_pipeline_v3.py
- All 325 OCR tests pass (6 C0+C1 + 4 rendering + 5 writeback + 310 figures)
2026-07-04 16:02:55 +08:00
LLLin000
c9d78fca44 C0: add OCR_PIPELINE_V3 toggle, seed-only build_structured_blocks, stub modules
- Add _ocr_pipeline_v3_enabled() toggle function in ocr.py
- Add normalize_mode='legacy'|'seed_only' param to build_structured_blocks()
- Add seed_only guard that skips legacy normalize pipeline
- Create ocr_pre_match_normalize.py stub
- Create ocr_post_match_normalize.py stub
- Create test_ocr_pipeline_v3.py with 3 tests
2026-07-04 15:54:53 +08:00
LLLin000
7bf652c42f feat: Workstream B — OCR tail settlement extraction and report
- B0: extract tail/backmatter settlement helpers into ocr_tail_settlement.py
- B1: add TailSettlementReport dataclass attached to DocumentStructure
- B2 gate: 41/41 tests pass (3 B0/B1 + 6 tail regressions + 32 A regression)
2026-07-04 13:37:45 +08:00
LLLin000
964e05b305 feat: Workstream A — OCR object writeback module
- A0: extract post-inventory seam into ocr_object_writeback module
- A1: add ownership evidence contract + idempotency guard
- A2: claim side-adjacent figure text (M84CTEM9 fix)
2026-07-04 13:16:37 +08:00
LLLin000
3cda942f96 fix: appendix numbering cross-cutting bugs (TABLE regex, int block_id, table alpha prefix, weak-caption tie-break)
3 distinct bugs found and fixed:

1. TABLE in figure regex (_FIGURE_NUMBER_PATTERN)
   Removed TABLE/Table from figure regex — caused table captions to be
   extracted as figure numbers and roadmapped into figure inventory.
   Added unit tests (table_numeric_caption_is_not_a_figure_number,
   table_appendix_caption_marker_has_no_figure_number).

2. int block_id type mismatch in cross-page lookup
   CrossPageSettlementPass and PrimarySamePagePass compared block_id
   with === but deduped_legends use int keys while ResourceRef stores
   str.  Fixed by casting both to str.  Table continuation lookup
   missed page filter, picking wrong page's same block_id.  Added
   page filter + int_block_id tests.

3. Table appendix support gaps
   - Table prefix regex only accepted digits/roman, not 'TABLE A1'.
     Added [A-Z]\d+ token and strip-leading-alpha in parse.
   - _is_validation_first_table_candidate didn't cover figure_title
     raw_label blocks with table_caption_like style.  Added second
     gate.
   - _is_weak_explicit_table_caption only checked table_caption roles.
     Extended to include validation-first candidates.
   - Same-page tie-break didn't apply for weak-explicit captions.
     Ported _bare_table_tie_break into vnext pass.
   - figure_caption_candidate not excluded from note attachment.
   - Continuation merge stripped leading duplicate table marker.

M84CTEM9 vault verification: 6/6 figures matched (3 main + 3
appendix + assets), 4/4 tables (Table 1,2 + Table A1,A2 + assets),
0 false positives, 0 figure asset leakage.
2026-07-04 01:29:06 +08:00
LLLin000
fa734f6fea merge: ocr pairing framework
# Conflicts:
#	paperforge/worker/ocr_tables.py
#	tests/test_ocr_tables.py
2026-07-03 22:53:11 +08:00