Pre-existing, found while adding the insert/delete reference transform (#972 / PR #974). Independently confirmed by that PR's reviewer. Left untouched there — it is a defect in a sibling transform, not in the new one.
The defect
Engine::translate_formula("=Sheet1!A1", -5, 0) -> "=Sheet1!#REF!"
That output does not parse:
parse_formula("=Sheet1!#REF!") -> Err
parse_formula("=#REF!:B5") -> Err
So a public transform produces formula text the engine's own parser rejects. Whatever consumes that string — storing it, re-evaluating it, round-tripping the document through it — is holding something that cannot be read back.
Why it happens
translate.rs replaces a reference per corner. When a corner goes out of grid it substitutes #REF! for that corner alone, leaving the sheet qualifier — or the other half of a range — attached. #REF! is an error token, not an address, so it cannot carry a qualifier and cannot be a range endpoint.
How #974 avoids it
The new shift_refs_for_grid_edit replaces a removed reference whole, sheet qualifier included, producing =SUM(#REF!) — which does re-parse. That shape was chosen by probing the parser rather than by convention, precisely because the per-corner form fails.
So the two transforms now disagree about what a removed reference looks like. That divergence is the reason to fix this rather than leave it: two answers to "what does a dead reference render as" is the same shape as the bug behind #969, where two implementations of "which references does this touch" disagreed.
Suggested direction
Align translate_formula on whole-reference replacement — when any part of a reference leaves the grid, the entire reference including its qualifier becomes #REF!.
Worth confirming the intended product behaviour first. A spreadsheet's own rule for a partially-out-of-grid range on a fill or paste is establishable ground truth, and neither transform's rule for it was verified against the pipeline. See also #972's PR body, which lists eight semantics asserted in the same area without fixture verification — this belongs with them.
Scope note
translate_formula is public on Engine and exposed on the wasm surface, so any change here is observable to npm consumers.
Pre-existing, found while adding the insert/delete reference transform (#972 / PR #974). Independently confirmed by that PR's reviewer. Left untouched there — it is a defect in a sibling transform, not in the new one.
The defect
That output does not parse:
So a public transform produces formula text the engine's own parser rejects. Whatever consumes that string — storing it, re-evaluating it, round-tripping the document through it — is holding something that cannot be read back.
Why it happens
translate.rsreplaces a reference per corner. When a corner goes out of grid it substitutes#REF!for that corner alone, leaving the sheet qualifier — or the other half of a range — attached.#REF!is an error token, not an address, so it cannot carry a qualifier and cannot be a range endpoint.How #974 avoids it
The new
shift_refs_for_grid_editreplaces a removed reference whole, sheet qualifier included, producing=SUM(#REF!)— which does re-parse. That shape was chosen by probing the parser rather than by convention, precisely because the per-corner form fails.So the two transforms now disagree about what a removed reference looks like. That divergence is the reason to fix this rather than leave it: two answers to "what does a dead reference render as" is the same shape as the bug behind #969, where two implementations of "which references does this touch" disagreed.
Suggested direction
Align
translate_formulaon whole-reference replacement — when any part of a reference leaves the grid, the entire reference including its qualifier becomes#REF!.Worth confirming the intended product behaviour first. A spreadsheet's own rule for a partially-out-of-grid range on a fill or paste is establishable ground truth, and neither transform's rule for it was verified against the pipeline. See also #972's PR body, which lists eight semantics asserted in the same area without fixture verification — this belongs with them.
Scope note
translate_formulais public onEngineand exposed on the wasm surface, so any change here is observable to npm consumers.