Skip to content

Content-type-aware diff: tables (CSV/Excel) and PDF #117

Description

@gregoryfoster

Scope

Content-type-aware normalizers and renderers for the diff surface:

  1. Tables — CSV, Excel
  2. PDF — page-aware

Dependencies rewritten 2026-08-19 in a backlog staleness pass. The design below is unaffected by the service-boundary rework and still wanted; the blocker chain in the original was stale in both directions. See Dependencies — this is now build-from-scratch behind #222, not an extension of an existing module.

Tables

  • Row-key-aware normalization: when the extractor identifies a key column, diff by row-key (row add/remove/modified) instead of line-by-line.
  • Render in dashboard as a data-table with adds highlighted green, removes red, per-cell changes yellow with old → new values visible.
  • Requires: pandas or equivalent; extractor metadata to carry key-column hints.

PDF

  • Page-aware diff: show which pages changed, diff extracted text per-page rather than as one flat blob.
  • Integrate with page-level screenshot comparison where available.

Dependencies

Blocked by #222, not #115.

The original read "Blocked by #115 Phases A/B (needs src/core/diff/ module, normalization layer, unified-diff core)". That is stale twice over:

So there is no diff layer to extend. The real chain is:

#117 (this)  →  #222 (restore unified diff + chunk semantics)  →  a persistent content store

ChangeRevision stores a content fingerprint and nothing else, so at dispatch time there is no previous content to diff against — for any content type. Watcher is currently a fingerprint-shift notifier: it reports that an item changed, not what changed. Content-type-aware diffing is a refinement of a capability that does not presently exist, so this cannot start until #222 lands.

Consequence for scoping: the acceptance criterion "src/core/diff/normalize.py gains normalize_table and normalize_pdf_text" presumes a file that must first be recreated. Whoever does #222 should decide whether to reintroduce src/core/diff/ as a package — if they do, this issue extends it as originally imagined; if they restore only the notification-side helpers from the 89d64eb anchor named in #222, this issue also owns creating the module.

Legacy .xls — updated

The 2026-06-23 note from #168 still holds, with one change in cost:

  • CsvExcelExtractor is backed by openpyxl, which reads .xlsx (OOXML) only — it cannot parse the old binary .xls.
  • application/vnd.ms-excel is therefore absent from _DEFAULT_EXTRACTOR_MAP (src/core/registry.py) and .xls is absent from the URL-extension tiebreaker (src/core/media_type.py); both resolve to the HTML fallback rather than mis-parsing. Verified still true.
  • What changed: extraction moved to co-core — the map now imports co_core.pure.extract.csv_excel.CsvExcelExtractor. Adding a legacy .xls reader (xlrd, or an upstream convert-to-.xlsx step) is now a cannobserv change, not a local one, and per the cross-repo policy needs an issue filed there rather than an edit here. That raises its cost enough that it should be an explicit decision, not a silent prerequisite.

.xlsx, .csv, and .pdf dispatch correctly.

Acceptance

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions