← All posts

Product update4 min read

Absence of return is not evidence of absence

A requested feature - deleting furniture an update finds gone - was built, measured against glass, and reverted before it shipped. Plus an undo for map updates.

An update replaces what it re-captured

Update Map re-enters the scanning view seeded with a place's existing map and merges what you capture into it. The rule governing that merge is deliberately narrow: the replacement mask is the set of cells the update observed densely — at least three triangle hits — so old geometry is only replaced where the update actually looked, and the update's own sparse walk-by geometry is dropped rather than merged.

Three changes were made to that merge in one recent commit. Two shipped. The third was built, measured, and thrown away (e0ae434).

Updates are now reversible

The merged mesh used to overwrite the place with no way back. One generation is now kept as a .mesh.prev file, and Places → ⋯ → Undo Last Update puts the previous mesh in place of it. Floor area is derived from the mesh, so it is restored alongside it.

The world map is deliberately not reverted. It only ever gains relocalization data, so keeping the newer one means the place still recognises the areas the update visited. Undoing the shape does not undo the ability to be found there.

Ceilings no longer authorise deletion

The second fix was a defect in the shipped merge. Replacement is bounded by a per-cell maximum height, and that height was being built from all of the update's dense geometry — including ceiling triangles — while the observation grid that gates replacement excludes ceilings entirely, mapping the ceiling class to none and skipping it.

The damage showed up along walls. A wall cell clears the three-hit density test easily, because many near-coplanar vertical triangles cover a single cell centre. So a glance at the ceiling set that cell's maximum height to roughly 2.5 m, and every old triangle below 2.9 m in that column was deleted — including the wall the update had only seen the top of. Ceiling triangles no longer contribute to that height.

The feature that was asked for

The third change is the one that did not ship. The request was removal of vanished objects: when a table is carried out of a room, an update should take it off the map rather than leave it there blocking routes. Saved furniture is a real obstacle — routing subtracts each saved object's footprint, plus a clearance margin, from the walkable floor — so stale furniture costs something.

The rule is easy to state and looks obviously right. The update meshed floor in this column and nothing above it, therefore whatever used to stand here is gone.

It was built, then measured against the real declutter pass rather than reasoned about. That pass promotes unclassified clusters into furniture: a cluster at object height, wide enough, and either densely meshed or dwelled on, is reclassified as a table as far as the map is concerned. Run against glass, it promoted 1584 of 1716 faces.

What a glass stair guard measured

Then the physics. A LiDAR sensor works by timing the return of an infrared pulse, and a transparent panel lets that pulse through instead of returning it. Glass does not re-mesh on the second scan. A glass partition or balustrade therefore meshes as floor with nothing above it — the exact signature the rule reads as this has vanished.

In a harness replicating the merge block, 754 of 754 faces of a glass stair guard were deleted. The barrier ceased to exist in the map, and routing then went straight through where it stood. Frameless glass was worst; a framed panel survived only because its top rail happened to mesh. Thin vertical surfaces get no protection from the density filter either — their footprint rarely covers a cell centre, and only 2 of 35 tested configurations were protected by it.

A second failure was confirmed independently of glass. Nothing in the rule required the update to have looked above the floor at all. Sweeping the ground — which the app's own Help actively teaches — deleted wall shelves and stair soffits standing over open floor. A margin already exists in the merge to prevent exactly that regression; the new rule reintroduced it under a two-metre cap.

Absence of return is not evidence of absence

The note left at the call site names the flaw in one line: the rule treats absence of return as evidence of absence. A sensor failing to see something is not the same as the thing not being there. The app's own Help already says as much, listing glass and mirrors among surfaces that scan "as holes or ghosts".

What deletion would need is positive evidence that the space is empty — the device physically passing through the column, or a camera ray carved through it and reaching past. Until that exists, an update only ever replaces what it re-captured.

The reasoning is recorded in the source at the point where the temptation recurs, rather than in a commit message somebody would have to go looking for. The merge callback was also changed to hand over each old triangle's surface class along with its position, because deciding that something vanished is only ever safe for furniture — the caller has to be able to tell a table from a load-bearing wall.

This is a different decision from the removals of the past few weeks, which took out shipped features that had proved unreliable in the field. This one never reached a build. The measurement happened first.

Where this is

None of this changes what leaves the device, which is nothing. ICIDO has no accounts, no sign-in, no analytics and no network connections; scans, tagged spots and settings stay on the iPhone.

The work described here is in TestFlight builds. ICIDO is coming to the App Store, and scanning requires the LiDAR sensor — iPhone 12 Pro and later Pro models.

Related reading