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:
- Insert new columns first, then delete old ones (keeps column count ≥ desired during transition).
- Interleave inserts and deletes so count never drops below 1.
- 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.
Context
Discovered while live-testing the
tableStartLocationindex-shift fix (commitf6f198e).Symptom
When the reconciler diffs a table whose columns changed,
diffmerge/table_diff.pysometimes emits a strategy of "delete every existing column, then insert every new column". In a 3-column table this meansdeleteTableColumnis 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:
The whole batch aborts, no changes land.
Reproduction
Hit during one combined test in the
f6f198elive 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.pycolumn 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:
Same concern applies to rows: can't delete the last row of a table either.
Tests to add