Ali Rathore.

May 2025

When one line of code becomes five things

The drag-to-rearrange proof passed seven of seven and meant nothing, because a model that writes your interface turns one expression into a screen full of echoes, and you cannot point at the third echo until you give it a name.

The drag-to-rearrange proof passed seven of seven, and it was meaningless. I had built a canvas where a model generates the interface and you drag elements to rearrange them, and the test that proved the dragging worked seeded its elements by hand: three paragraphs I had typed as literals, A and B and C, each its own line of code. Three separate things, three separate names, identity for free. Then the model rendered a real list, four rows from one loop, and grabbing the third row and grabbing the first row did the same thing, because to my code they were the same thing. The test had never once tested a list. The bug was not caught by the suite going red. It was caught by my noticing what a loop does to the names.

Here is what it does. The compiler sees one expression, the loop, and assigns it one identifier. The screen shows four rows. So one name now belongs to four things, and a handle that resolves what you grabbed by walking up to the nearest name finds the same name under every row and can only ever act on the first. The pixel under your cursor is not the thing. It is the fourth printout of one sentence of code, and there is no fourth printout in the source to grab. There is a loop, and a screen full of its echoes.

This is not a fact about dragging. Drag was just the first handle to hit the wall. Any direct way of pointing at the screen hits the same wall the instant the screen is generated rather than authored: a click, a context menu, a keyboard cursor, all of them resolve a pixel to a source, and the source fanned out the moment it ran. The realization that reorganized the whole thing is that when a model writes the interface, you no longer have a one-to-one map between what you see and what it wrote, and every interaction you want to offer the human has to be rebuilt on top of that missing map.

one loop expressionsource name: u-1compile time: one nameu-1 : 0u-1 : 1u-1 : 2u-1 : 3render time: a name per instance
The compiler knows one name. The screen has four things. Pointing needs a name the source never had.

So I stopped trying to fix the handle and built the missing map instead. After the page renders, a pass walks the committed DOM, counts how many nodes carry each source name, and stamps a second name that is unique per rendered node: where one loop produced four rows, they become the first, second, third, and fourth instance of that source, in order. Now a click has something real to resolve to. The human points at one instance; the system records which one as an ordinary row in the document, the same way it records everything else; and when it tells the model what “this” refers to, it can say which instance of which expression, so the model can tell “make this card red,” which edits one row of data, from “make the cards red,” which edits the one template. That distinction is the entire game, and drag never had access to it, because drag only ever held the template.

The gesture barely changed, which is the tell that the gesture was never the point. You still hover, you still click. What changed is what a click resolves to. The swap from dragging to pointing is the story; the idea underneath it is that a generated interface forces you to invent a naming scheme for things the source code does not know exist, and none of the pointing works until you have one. I deleted the drag code, five dependencies and about five hundred lines, within two hours of its proof going green.

The name I mint is the weak point, and I knew it when I shipped it. It is positional: the third instance means the third one in document order, not the row that happened to be third. Reorder the underlying data, move an item up, and the screen nodes are the same but the indices slide, so the name now points at a different row than the one the human meant. The stable identity is the data row itself, its key, not its position on the screen, and that is the version I have not built. For now the names are honest only until something moves, and I have not yet had to find out how often that bites, because I never wired the harness that would let a live model act on a selection at all. The whole disambiguation works in the one place I have only tested by reading it.