Skip to content

fix(csv): do not read prose as a table - #697

Merged
andiwand merged 2 commits into
mainfrom
fix/csv-prose-detection
Aug 18, 2026
Merged

fix(csv): do not read prose as a table#697
andiwand merged 2 commits into
mainfrom
fix/csv-prose-detection

Conversation

@andiwand

@andiwand andiwand commented Aug 17, 2026

Copy link
Copy Markdown
Member

A plain text file whose lines each contain a comma is, by field count alone, a consistent two column table. So prose was detected as csv and rendered as a spreadsheet.

This came out of a user report about the Android app: "does not work usefully any more with larger TXT files". The app hands the engine a cached copy named cached-file.tmp, so detection is content-only and every text file goes through this probe.

What the probe said before

Measured with Odr.mimetype on real samples:

sample detected
prose, no punctuation text/plain
prose, one comma per line text/csv
German business prose text/csv
prose with semicolons text/csv
prose with tabs text/csv
one paragraph, no newlines text/csv
an actual csv text/csv

A comma before a subclause is ordinary punctuation in German, French and English alike, so this is most prose, not an edge case.

The rules added

Both ask for positive evidence of a table rather than the absence of evidence against one.

A separator used as punctuation is not a delimiter. If almost every field after a separator opens with a space and the fields average long enough to be sentences, the separator is punctuation. Both halves are required, which is what keeps a spaced-out csv working: name, age, city has the spacing but its values are short.

One record is a line, not a table, however many separators it holds. That is the single-paragraph case.

Direction of the doubt

Deliberate: plain text is a readable rendering of a table, and a table is not a readable rendering of text. A 2-column csv with long free-text values written Ada Lovelace, mathematician and writer will now fall back to text. Both thresholds are named constants (punctuation_spacing, prose_field_length) if that balance wants moving.

Tests

Three new cases in csv_file_test.cpp — prose with commas and with semicolons, spaced-out short-value csv, and the single record. Full suite: 883 passed, 8 skipped, 0 failed, including the reference-output comparisons.

🤖 Generated with Claude Code

A comma between clauses splits every line of prose in two, which by field
count alone is a consistent two column table. Any text whose lines share a
separator was detected as csv and rendered as a spreadsheet.

Measured against the probe, all of these came back `text/csv`: prose with one
comma per line, German business prose, prose with semicolons or tabs, and a
single paragraph with no newlines at all.

Two rules, both requiring positive evidence of a table:

- A separator that almost every following field opens with a space, in fields
  long enough to be sentences, is punctuation rather than a delimiter. Both
  halves are needed: a csv written `name, age, city` has the spacing but not
  the length, and stays a csv.
- One record is a line, not a table, however many separators it holds.

The doubt breaks towards text on purpose. Plain text is a readable rendering
of a table; a table is not a readable rendering of text.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TouAQNfktsp9THcceennEX

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c29864633c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/odr/internal/csv/csv_util.cpp Outdated
Comment thread src/odr/internal/csv/csv_util.cpp Outdated
Two ways the punctuation rule could reject a real csv.

The prose threshold is stated in characters but `trimmed_length` counted
bytes, so four cjk characters or three emoji read as a sentence. Count
the bytes that open a character instead.

And when the probe's sample ended mid-record, `score` dropped that record
from the field counts but left its fields in the spacing and length
statistics, so where the 64 KiB bound happened to fall could decide a
file. The record now comes out of both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XTfEEeZdHs1qEzXbtycGJ5
@andiwand
andiwand merged commit 50e0058 into main Aug 18, 2026
36 checks passed
@andiwand
andiwand deleted the fix/csv-prose-detection branch August 18, 2026 05:48
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