The feature that passed every test but never left the tab
The assumption that seemed reasonable
Multi-surface editors (a document view, a code view, a diff view, an export pipeline) usually settle on one canonical piece of state and a sync layer that keeps every surface mirroring it. The common shape: listen for the browser's native input event on the editable surface, and on that event, push the new content everywhere else.
This is a completely reasonable design. It's also completely reasonable to assume that "the user edited something" and "an input event fired" are the same fact. They are not, and the gap between them is exactly the set of edits that don't go through typing.
When it failed
I built several toolbar features for a document editor: a column-split control, indent/outdent, a definition-list insert, a callout-box insert, a "format painter" that copies formatting from one selection to another. Every one of them worked, visually, in the tab where I built and tested it. Selected text split into columns. Indent added a visible gap. The callout box appeared with the right styling.
None of it showed up in the other views. Switch from the Doc tab to the raw-HTML editor tab, and the split was gone. The underlying source of truth still held the pre-split content. The document that would actually export was never the document on screen, and I didn't catch it until I happened to click over to check something unrelated.
Every one of these features passed manual testing, because manual testing meant looking at the surface I'd just edited. The bug was invisible from inside the tab where the work happened, and only visible from a tab I hadn't thought to check.
What was actually wrong
The sync layer's assumption was narrower than it looked. It synced on input, and input fires for typing and for document.execCommand (the browser's built-in rich-text commands: bold, italic, insert-list, and so on). It does not fire for direct DOM manipulation: appendChild, insertBefore, Node.remove(), Range.insertNode(), Range.surroundContents().
Every feature I'd just built used direct DOM manipulation, because none of them mapped cleanly onto a built-in execCommand. Splitting selected blocks into column containers, wrapping a selection's computed style into a span, building a <dl> from scratch: these are structural operations you write yourself, node by node. And every one of them mutated the DOM silently, from the sync layer's perspective, with no event to catch.
So what I actually shipped was a feature that's functionally correct and visually confirmed, but structurally invisible to everything downstream of the surface it ran on. The bug isn't in the feature. It's in the boundary between "things that fire events" and "things that don't," and every new hand-authored DOM mutation crosses that boundary by default unless someone remembers to bridge it.
What got deleted
Nothing, honestly. The fix was additive, not corrective in the sense of removing bad code. The bad code was an absence: nowhere in any of these functions was there a call telling the sync layer "something changed, go push it."
What replaced it
A single explicit sync call, added at the end of every function that mutates the DOM directly:
function syncStructuralEdit() {
const el = document.getElementById('primary-surface');
if (el) applyEverywhere(el.innerHTML, el);
}
function applyColumnSplit(cols) { // ...move nodes into column containers... syncStructuralEdit(); }
function insertDefinitionList() { // ...build and insert the <dl>... syncStructuralEdit(); }
Not elegant. Every call site needs to remember to add it, which is exactly the kind of manual discipline that caused the bug in the first place. But it's honest about the actual constraint: the sync layer only knows about events it's told about, and hand-written DOM code doesn't generate those events for free.
The generalizable lesson
When a sync or observer layer is keyed to a specific event type, audit every code path that changes the underlying state, not just the ones you're currently thinking about. "The event fires when the state changes" is a claim about the code that listens, not a law of the DOM. input fires when a human types or when execCommand runs, because browsers wire those two paths to fire it. It does not fire because "the content changed" in any more general sense.
Any time you reach for direct DOM manipulation next to code that relies on execCommand for the same surface, that's the moment to check whether your sync layer can see the new code path, because by default, it can't.