Skip to content

feat(table): StrategyAuto for one-axis-ruled tables (opt-in; negative benchmark result recorded) - #21

Merged
hallelx2 merged 1 commit into
mainfrom
halleluyaholudele/hal-568-horizontal-ruled-tables
Aug 2, 2026
Merged

feat(table): StrategyAuto for one-axis-ruled tables (opt-in; negative benchmark result recorded)#21
hallelx2 merged 1 commit into
mainfrom
halleluyaholudele/hal-568-horizontal-ruled-tables

Conversation

@hallelx2

@hallelx2 hallelx2 commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Tests the hypothesis from the ICDAR 2013 evaluation. The hypothesis came back negative. Shipping the feature opt-in and recording why.

Hypothesis

28 of 125 benchmark documents had no table detected at all. Cause confirmed by measurement — every one is ruled on a single axis, and lines builds cells from intersecting rulings:

us-017:  218 H,   0 V     lines=0  →  6 tables with the other axis inferred
us-018:  226 H,   0 V     lines=0  →  7
us-024:  135 H,   0 V     lines=0  →  4
us-025:  225 H,   0 V     lines=0  →  3

So infer the unruled axis from word alignment. Expectation: recall up, precision holds.

Result — it doesn't work

system precision recall F1
lines 0.865 0.229 0.362
auto 0.797 0.231 0.358

Recall moved 0.229 → 0.231. Precision fell. Net slightly worse.

Why — this corrects the earlier conclusion

lines auto
documents with no table found 28 (22%) 23 (18%)
tables detected 306 331
F1 on docs where one was found 0.556 0.400

Detection improved exactly as predicted. The tables it now finds are gridded badly.

The earlier report said "the bottleneck is detection, not structure", reasoning from precision 0.865 on detected documents. That population was self-selected by being fully ruled. On the hard ones, structure is weak too — knowing where a table is doesn't tell you where its columns are.

Corrected: both are weak on one-axis-ruled tables, and fixing detection alone converts almost nothing.

What's shipped

StrategyAuto, per axis, not a default (TestAutoIsNotTheDefault pins that). It's correct for its stated case and a caller who knows their corpus is booktabs-ruled gets a real improvement.

The conservative rule is load-bearing and stays regardless:

this axis ruled          → lines
not, but other axis is   → text
neither ruled            → lines  (find nothing)

That last branch is why this isn't a naive fallback. Falling back to text on an unruled page scores 0.223 precision — prose has word alignment too, and text will happily report a table for it.

Implication for next steps

Don't spend more on heuristics for finding the table. The measurement says the missing piece is row and column structure where rules don't supply it — which is precisely what layout models (Table Transformer et al.) output, as opposed to a bounding box.

Revised hybrid split:

  • layout/VLM → rows, columns, spans
  • pdftable text layer → cell contents and coordinates (exact values, citation geometry)

Verification

  • go build, go vet, go test ./... -count=1 -race — green.
  • All existing goldens unchanged; defaults untouched.
  • Negative result recorded in docs/evaluations/ so it isn't re-run blind.

Relates to HAL-568

Summary by Sourcery

Introduce an opt-in StrategyAuto for table edge detection on one-axis-ruled pages, wire it into the table-finding pipeline and ICDAR 2013 benchmark harness, and record the negative evaluation result in docs while ensuring defaults and strategy support remain unchanged.

New Features:

  • Add StrategyAuto table strategy that chooses between line-based and text-based edges per axis based on observed rulings.
  • Expose new benchmark modes (mixed, lines-then-mixed, auto) to evaluate one-axis-ruled table handling.
  • Allow diagnostic benchmarking to select the table strategy via an environment variable.

Enhancements:

  • Ensure page-level table edge finding resolves StrategyAuto after inspecting drawn edges and fetches text only when needed.
  • Treat StrategyAuto as line-like for object scanning while leaving existing strategies unchanged.

Documentation:

  • Add a detailed evaluation document describing StrategyAuto’s negative benchmark result and link it from the docs index.

Tests:

  • Add unit tests covering StrategyAuto resolution rules, non-fallback behavior on unruly pages, pass-through for other strategies, and verifying it is not a default strategy.

The ICDAR 2013 evaluation found 28 of 125 documents where pdftable
detected no table at all, and blamed tables ruled on one axis only:
"lines" builds cells from INTERSECTING rulings, so a horizontally-ruled
table yields none. Measurement confirmed it exactly -- us-017 has 218
horizontal rules and 0 vertical, us-018 226 and 0, us-025 225 and 0, and
all of them go from 0 tables to 3-7 with the other axis inferred.

StrategyAuto picks per axis: lines where that axis is ruled, text where
it is not BUT the other axis is, and lines when neither is. That last
branch is the load-bearing one. Falling back to text on an unruled page
is what makes a naive lines->text fallback score worse than lines alone
-- precision 0.223 against 0.865 -- because prose has word alignment too
and the text strategy will report a table for it. Rulings on the other
axis are the evidence that a table is actually present; without that
evidence Auto declines to guess.

It is NOT a default, because the hypothesis it was built to test came
back negative and the number is honest about it:

  lines   precision 0.865  recall 0.229  F1 0.362
  auto    precision 0.797  recall 0.231  F1 0.358

Detection improved as predicted -- documents with no table found drop
from 28 to 23, tables detected rise from 306 to 331. The tables it finds
there are gridded badly, so quality on the scored set falls from 0.556
to 0.400 once those harder documents enter it, and recall barely moves.

That corrects the earlier conclusion. "The bottleneck is detection, not
structure" was inferred from precision on the documents lines could
already see -- a population self-selected by being fully ruled. On the
harder ones structure is weak too: knowing where a table is does not tell
you where its columns are. Both are weak on one-axis-ruled layouts, and
fixing detection alone converts almost nothing.

Shipped anyway because it is correct for its stated case and a caller who
knows their corpus is booktabs-ruled gets a real improvement. The
negative result is recorded in docs/evaluations so the next person does
not re-run the experiment blind.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @hallelx2, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@hallelx2, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 23 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b469230e-057e-41c8-8027-6a6d98290484

📥 Commits

Reviewing files that changed from the base of the PR and between 05c0c92 and 5f2ccce.

⛔ Files ignored due to path filters (1)
  • bench/icdar2013/__pycache__/score.cpython-313.pyc is excluded by !**/*.pyc
📒 Files selected for processing (9)
  • bench/icdar2013/diag.py
  • bench/icdar2013/extract.go
  • bench/icdar2013/score.py
  • docs/README.md
  • docs/evaluations/2026-08-02-strategy-auto-negative-result.md
  • finder.go
  • page.go
  • strategy_auto_test.go
  • table.go

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai

sourcery-ai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Reviewer's Guide

Introduce a new opt-in table edge detection strategy (StrategyAuto) that infers one axis from text when the other axis is ruled, wire it into the page edge finder and ICDAR 2013 benchmark harness, and record the (negative) evaluation result with tests that pin behavior and non-default status.

Sequence diagram for page.findTableEdges with StrategyAuto resolution

sequenceDiagram
    participant Page as page
    participant Layout as layout
    participant Text as text_layer

    Page->>Page: findTableEdges(TableSettings)
    Page->>Page: resolveAuto(VerticalStrategy, Vertical, lineLikeEdges)
    Page->>Page: resolveAuto(HorizontalStrategy, Horizontal, lineLikeEdges)
    alt vStrategy or hStrategy is StrategyText
        Page->>Text: Words(WordOpts)
        Text-->>Page: words
    end
    Page->>Page: baseEdges(vStrategy, Vertical, lineLikeEdges, words, TableSettings)
    Page->>Page: baseEdges(hStrategy, Horizontal, lineLikeEdges, words, TableSettings)
    Page-->>Layout: edges
Loading

Flow diagram for resolveAuto table edge strategy selection per axis

flowchart TD
    A[StrategyAuto input for axis] --> B{strategy == StrategyAuto?}
    B -->|no| C[return strategy]
    B -->|yes| D{edges on this axis >= minEdgesForAxis?}
    D -->|yes| E[return StrategyLines]
    D -->|no| F{edges on other axis >= minEdgesForAxis?}
    F -->|yes| G[return StrategyText]
    F -->|no| H[return StrategyLines - neither axis ruled]
Loading

File-Level Changes

Change Details Files
Add StrategyAuto and its resolution logic to choose per-axis "lines" or "text" based on observed rulings, without degrading precision on non-table pages.
  • Define StrategyAuto as a new TableStrategy with documentation of the one-axis-ruled use case and conservative decision rules.
  • Implement resolveAuto to turn StrategyAuto into a concrete per-axis strategy using oriented edge counts and a minimum edge threshold.
  • Extend isLineLike to treat StrategyAuto as requiring line-like primitives so drawn objects are loaded before resolution.
table.go
page.go
Integrate StrategyAuto into the table edge finding pipeline so it resolves after line-like edges are known and fetches words only when needed.
  • Resolve vertical and horizontal strategies via resolveAuto in findTableEdges once line-like edges are available.
  • Conditionally load words when either resolved axis is StrategyText and words were not pre-fetched.
  • Pass the resolved per-axis strategies into baseEdges instead of the raw settings strategies.
page.go
Expose StrategyAuto to callers and benchmarks while keeping it strictly opt-in and updating diagnostic tooling.
  • Allow StrategyAuto in ensureSupportedStrategies validation for TableSettings.
  • Add new benchmark strategies (mixed, auto) and CLI modes, including lines-then-mixed and auto, in the ICDAR 2013 extractor.
  • Update ICDAR 2013 scoring harness to report AUTO variants instead of the old fallback modes and make the diagnostic script strategy configurable via DIAG_STRATEGY.
finder.go
bench/icdar2013/extract.go
bench/icdar2013/score.py
bench/icdar2013/diag.py
Add tests that lock in StrategyAuto’s decision table, its conservative behavior, pass-through semantics, and non-default status.
  • Create unit tests for resolveAuto covering fully ruled, single-axis ruled, and unruly pages, plus minimum edge threshold behavior.
  • Add tests ensuring resolveAuto leaves non-auto strategies unchanged.
  • Add a test that DefaultTableSettings does not use StrategyAuto on either axis.
strategy_auto_test.go
Document the StrategyAuto experiment, its benchmark results, and conclusions in the evaluations docs and index.
  • Add a new evaluation document describing the hypothesis, measurements, and negative outcome for StrategyAuto on ICDAR 2013.
  • Link the new evaluation from the docs README with a concise headline summarizing the negative result.
docs/evaluations/2026-08-02-strategy-auto-negative-result.md
docs/README.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@hallelx2
hallelx2 merged commit 00c4c12 into main Aug 2, 2026
5 checks passed
@hallelx2
hallelx2 deleted the halleluyaholudele/hal-568-horizontal-ruled-tables branch August 2, 2026 22:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant