Skip to content

Column diff emits delete-all + insert-all, can leave table with 0 columns mid-batch #61

@sripathikrishnan

Description

@sripathikrishnan

Context

Discovered while live-testing the tableStartLocation index-shift fix (commit f6f198e).

Symptom

When the reconciler diffs a table whose columns changed, diffmerge/table_diff.py sometimes emits a strategy of "delete every existing column, then insert every new column". In a 3-column table this means deleteTableColumn is issued 3 times in sequence, taking the table through a 2-col → 1-col → 0-col intermediate state.

Google Docs API rejects the third delete:

Cannot remove the last column from a table.

The whole batch aborts, no changes land.

Reproduction

Hit during one combined test in the f6f198e live verification (combined edits + column append path). The requests themselves had correctly shifted indices — the bug is purely in the column diff strategy choosing an illegal intermediate state.

Root cause

extradoc/src/extradoc/diffmerge/table_diff.py column diff path — when it can't cleanly map existing columns to desired columns via LCS, it falls back to delete-all + insert-all. This is a legal transformation for the data but an illegal one for the API.

Fix direction

Column diff must guarantee the table always has ≥1 column at every intermediate step. Options:

  1. Insert new columns first, then delete old ones (keeps column count ≥ desired during transition).
  2. Interleave inserts and deletes so count never drops below 1.
  3. Fall back to keeping at least one "bridge" column to preserve via content update rather than delete+reinsert.

Same concern applies to rows: can't delete the last row of a table either.

Tests to add

  • Diff where all columns replaced: verify batch never takes the table below 1 column.
  • Diff where all rows replaced: verify batch never takes the table below 1 row.
  • Small table with full column replacement: assert column count at every intermediate step ≥ 1.

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