LifeLab · lens · correcting my own framing
I said "one tree, three child-producers", which reads as though a lens becomes a node that has children. It shouldn't. A lens appears in the outline like a node so the verbs work, but it owns nothing — it has no children, only results. The distinction that keeps it ephemeral is ownership, not appearance: a container holds blocks; a lens points at blocks that are held elsewhere.
Deleting a lens deletes nothing. Every block it showed still exists, unchanged, in the same place, with the same parent. If a gesture ever makes that false, that gesture has turned the lens into a container and must be redesigned.
container · what we're avoiding
lens · what it is
![[page]]{todo} is the entire object.Where the container smell was creeping in: drag-to-reparent. "Drag a block into a lens" sounds like putting it in — and if the lens is computed there's nowhere to put it. The honest reading, which you already have in the model: dropping on a computed lens makes the block match (tags it, sets its status), and dropping on a referenced lens re-homes it to that page. In both cases the change lands on the block, and the lens just re-runs. The lens is never a destination — the page or the predicate is.
what's actually in the file
Three lines. The third has no children in the file and never will — it's a sentence, editable as text, diffable, greppable, pasteable into another page where it means the same thing.
what you see, recomputed each paint
The two results are borrowed for the duration of a render. Their rail says where they really live. Delete the lens line and they don't move, don't change, and don't notice.
Concretely: a lens is a node in the render tree and not in the document tree. That's what lets it keep the outliner's verbs — fold, zoom, keyboard nav all operate on the render tree — while owning nothing in the document. Fold state, scroll, and focus are view-local, not written back; if a lens ever needs to persist something, that something belongs in its expression, as text.
The gradient is the feature. A question starts as a keystroke, and only becomes text if it earns it — and even then it's still just a sentence you can retype, not a structure you've committed to. Contrast with a folder or a saved search, both of which are objects you have to manage. Nothing here is ever managed; it's written or it's forgotten.
| test | must be true | what failure means |
|---|---|---|
| delete | Removing the lens changes zero blocks. | It owned something. Find out what and move it back to a page. |
| retype | Typing the same expression elsewhere gives an identical lens. | It has hidden state — an id, an order, a stashed fold map. Put it in the sentence or drop it. |
| double | The same lens can appear twice on one page without conflict. | It's an object with identity, not a projection. |
| drop | Dragging in changes the block (its home or its attributes), never the lens. | You've built a bucket. The predicate should be the target, not the frame. |
| offline | Deleting every lens in the vault leaves the content complete. | Structure has leaked into views — the vault is no longer plain pages of blocks. |
The one I'd watch hardest is double. It's the cheapest way to catch container drift: the moment a lens needs to remember something per-instance, someone will add an id, and the day it has an id it's a thing you own rather than a question you asked.