LifeLab · lens · substrate
The bar you set is the right one. Making a block costs ⏎: no modifier, no target, no name, nothing to know in advance, and ⌫ undoes it. Summoning can't beat that with a better shortcut, because the problem was never the shortcut — it's that ![[ is a prefix. A prefix demands you decide what kind of thing this line is before you've typed a word of it. Enter demands nothing.
So don't add a gesture. Move the decision to the end. Press ⏎ like always, type like always — and if what you typed turns out to name a place, one more key turns the line you just wrote into a window onto it. ⏎ type ⇥. Same start, same typing, one keystroke of divergence, taken after you already know what you meant.
And the model underneath stays honest: any block can be summoning. A lens becomes a state a block is in, on top of its own text and children. ![[mcp]] as a standalone block is then just the degenerate case — a summoning block whose own text happens to be empty. Draft 2's grammar doesn't move an inch.
Pull that thread all the way and it lands on the storage: blocks are one table, pages are another, and the only interesting operation in the product is the trip between them — promote and summon are the same crossing in opposite directions. Pull it once more and the nouns collapse too: lens, tag and page are one thing with different amounts of ownership.
today · a lens is a block you author
![[ → picker → pick → ]] → maybe {filter}proposed · a lens is a state you toggle
The grammar doesn't move. ![[SOURCE]]{TERM*} is still the durable, round-tripping truth (Draft 2, invariant 3). What changes is that text becomes a receipt of an action you took rather than a thing you had to type to take the action. It's still there, still editable in place, still the thing that syncs.
| property of a new block | ⏎ new block | ![[ today | ⏎ type ⇥ |
|---|---|---|---|
| no modifier | a key under your finger | a three-character prefix | two keys you already press constantly |
| no target | happens at the cursor | at the cursor | at the cursor |
| starts empty | blank line, cursor in it — you needn't know what it's for | the picker opens demanding a name you must already have | blank line — the name is discovered by typing, as always |
| decided late | a block is whatever you end up typing | you declare the line's kind before word one | the last keystroke, not the first |
| undone by ⌫ | empty block deletes itself | delete the block | unbinds back to plain text you already typed |
| nobody taught you | it's what ⏎ does everywhere | must be read in docs | the offer appears under the line, at the moment it applies |
The last row is the one that decides it. A shortcut can be fast without ever being obvious. ⌥S would have been fast; you'd still need to be told it exists. Tab-to-accept teaches itself, because the suggestion is on screen while you type the thing it matches — the same reason nobody has to be taught shell completion.
01 press ⏎ — nothing new here
A blank block, cursor in it. No mode has been entered. Whatever happens next, this is still just a line in your document — which is exactly why the gesture costs nothing to start.
02 type — the line quietly resolves
One dim line under the caret — not a popup, not a menu, and it never steals a key. Keep typing and it follows or disappears. Press ⏎ and it's an ordinary block containing the word "mcp", which is what the same keystrokes have always done.
03 ⇥ — the line becomes a window
facepage · 2 subpages · 7 linked
Your text snaps to the full name, the gutter picks up ◈, and the face unfurls behind a dotted rail. The block still owns its own text and children — above the rail. ⌫ here unbinds and leaves the words you typed.
variant nothing matched — same key
+ nothing here yet — type to start it
This is the block-becomes-a-page gesture, and it needed no second mechanism. ⇥ always means "turn what I typed into a place I can look at" — existing or not. It runs promote in place (slug from the headline, nothing moves) and summons the result in the same beat.
variant you don't know the name — ⏎ on an empty line
summon — recent · ↑↓ then ⇥
The one thing ⏎ on an already-empty block doesn't currently do anything useful with. Now it offers what you'd most likely pull in — recents, pins, yesterday. Type to filter and you're back in frame 02; press ⏎ once more and you get the extra blank line you may have been after.
Filters ride along the same line. Type mcp {todo} before pressing ⇥, or add {todo} to the block afterwards — it's the block's own text either way, so there's no second construct to learn and no second place to look. Capabilities stay per Draft 2: computed rows are read-only unless the plan yields an unambiguous write.
the gates · what has to be true before a suggestion appears
Three gates, all required: the line is headline-shaped (short, no trailing punctuation, not a sentence), the match is whole (not a substring of a longer name), and the caret is still in it after a brief pause. ⏎ always wins and always means “plain block”. The only thing that ever binds a lens is you pressing ⇥.
A section heading and a page name are the same act of naming. When you type a headline into an outline you are declaring the topic of the region below it — which is exactly the moment you would want to know what already exists under that topic.
So the “instasummon on titles” worry inverts. Titles are the only place the offer belongs, because a title is a claim about subject matter and everything else is just words. The system isn't guessing what you meant — it noticed you reused a name, which in an outliner is never a coincidence.
The failure mode to watch is the opposite one: a heading that innocently collides with a page name (“notes”, “inbox”, “today”) offering itself over and over to someone who never wants it. Two mitigations, both cheap: the offer is a dim line that never takes a key, and a name you decline three times stops offering on that page. The suggestion has to be forgettable, or it becomes the new ![[.
| mechanism | the move it makes | where the result sits | write into it? | survives leaving? |
|---|---|---|---|---|
| link | takes you to the page | you're now somewhere else | yes — by leaving | n/a |
| zoom / breadcrumb | narrows to one subtree | replaces the view | yes | n/a |
| transclusion | pastes a copy of the page into yours | inline, permanent | usually | yes — you wrote a line to get it |
| mirror / synced block | same block in two places | inline, permanent | yes | yes — same cost |
| saved query | a computed list | inline, permanent, usually configured | rarely | yes — same cost |
| side panel | parks a page beside the one you're on | a separate region — outside the document | often | no — and that's its virtue |
| summon | brings the page to the line you're writing on | inline, at the depth you're thinking at | yes — and additions land in the source | not until you keep it |
the gap all six share
Every mechanism that puts another page inside your document charges you a permanent line for it. Every mechanism that's free puts it outside the document — a panel, an overlay, a second window — where it can't be reordered, nested, folded, or interleaved with what you're writing. Nobody offers a cheap, throwaway view that lives in the outline itself. That's the whole opening, and it's one bit wide: view state that isn't file state.
the resulting difference in kind
In an outliner the page is where you are and everything else is a destination. Here the page is a place other pages come to, for as long as a thought lasts, and the unit of work shrinks to the local region — a parent block with three summons under it. That's why the outline verbs matter so much: reorder, indent, fold and ⌫ already are a window manager, so a workspace costs nothing to build and nothing to throw away.
Honest prior art: the shift-click side panel is the closest existing thing — it's genuinely free and genuinely ephemeral, and it's the mechanism people actually use for this. The claim here isn't that it's bad; it's that it's the right idea in the wrong place, stuck in a region with no outline verbs, one nesting level, and no way to write a note between two summoned things. Pull that panel into the outline and it inherits everything.
Infinite nesting means you can express anything. It doesn't mean you can express it at the right depth — and today every annotation on a lens costs a level, because the lens has already spent the block's only naming slot on a page title.
today — the reference is the line, so prose has to become a parent
Four levels to say two things. Depth is being spent on plumbing rather than meaning, and the only tool available to compose lenses is nesting — which is the outliner's generic tool. Of course it feels like an outliner: structurally, that's all it is.
proposed — the line is yours; the source is a property of it
Two levels, and a plain sibling note can sit between two views — impossible today without inventing a level to hold them. Depth now means what it always meant in an outline: containment. Nesting is freed to be structure again because it's no longer doing double duty as the annotation mechanism.
Three things unlock the moment text and reference stop being the same field:
a · same source, twice
Today two views of mcp in one workspace are two identically-titled blocks — indistinguishable in the outline and in search. Named lines make them different objects to a reader while sharing a source, which is the ordinary case in a workspace, not an exotic one.
b · fold becomes useful
Collapsed, today's lens shows ![[mcp]]{todo} — a path. Collapsed, a summoning block shows “why the push is blocked”. Fold is the minimise button, and minimised windows are only worth having if what remains is a human name.
c · the headline outlives the source
Point the same line at a different page and your framing survives; the source is a property, not the label. Conversely a page rename doesn't silently rewrite your prose — the two stop being coupled by accident.
This forces a correction to the ladder above. If keep wrote ![[mcp]]{todo} as the block's text, it would hand the name slot straight back — the exact bug. So keep appends the reference to the line you wrote: why the push is blocked ![[mcp]]{todo}. Text stays the truth, Draft 2's grammar is unchanged, it still round-trips — and a line whose reference is the whole text is simply the degenerate case we started from. While unkept, the binding lives in visit state, so rewriting the headline never breaks it.
A block has exactly two writable fields. Turning it into a lens leaves you zero: the title goes stuck (page name plus filters, derived not authored) and the content becomes a list you don't own. That's the whole cost, and it's paid in one keystroke.
| title · content | what it is | you can | you can't |
|---|---|---|---|
| yours · yours | a plain block — the thing ⏎ makes | everything | see anything you didn't write here |
| stuck · dynamic | today's lens — a viewport wearing a block's clothes | look; contribute through the plan | name it, annotate it, order it, or note a conclusion without inventing a parent |
| stuck · yours | a shortcut with local notes — the shape nobody built | annotate | see the source at all |
| yours · yours + face | the proposal — the face is a third region, not a substitution | everything, plus look | reorder or edit the face by hand — that's what the plan is for |
three regions, one block — additive, not substitutive
facemcp {todo} · borrowed · 3
Solid rail = yours, dotted rail = borrowed. Nothing you could write before is taken away; one region is added. This is also the container answer: a lens looks like a container today precisely because other people's blocks are sitting in its content slot. Give the content slot back and the ambiguity has nowhere to live.
what the content slot was buying you
recovered by keeping it
The stuck title is the tell. A field that was writable becoming read-only is how you know a mechanism has taken something rather than added something — and it's why today's lens can't be casual. You don't hand over both of a block's fields on a hunch.
The ordering question this opens: reading order argues for face-then-notes (evidence, then verdict). Editing predictability argues for notes-then-face — otherwise ⏎ on the title drops your cursor below forty result rows. I've kept yours above, borrowed below for that reason, but it's the one place the three-region layout reads backwards, and it may want to flip once the face is folded.
If pages and blocks exist for indexing and retrieval, then the question isn't whether users can make managed structure — it's whether the managed kind is the path of least resistance. Today it isn't, so people produce hierarchy the system can't read.
| structural act | costs | what the index learns | retrievable by |
|---|---|---|---|
| indent | ⇥ — free, reflexive | b sits under a — why is unrecorded | nothing but position |
| write prose | free | a string | full-text, i.e. luck |
| tag | cheap | a typed membership | the tag's set |
| name a page | a decision, a dialog, a relocation | an address | name, links, backlinks |
| summon | highest — authoring a permanent line | a typed edge: this region of page A is about page B, filtered thus | both directions, with intent attached |
Read that column top to bottom: cost rises exactly as informativeness rises. We charge the most for the only act that produces a machine-readable relation, and nothing for the one that produces opaque depth. Users are behaving correctly — they take the free option, so the notebook fills with hierarchy that means something to them and nothing to the system. Cheapening the summon isn't a convenience; it's how the index gets fed.
the payoff — the inverse view, which only exists if summons are common
looked at bysystem-maintained · 4
Not “mentioned on 4 pages” — “used as a working view, with these filters, by these contexts.” That is a retrieval surface no outliner has, and it can only exist if summoning is something people do dozens of times a week rather than a handful.
what “manages for them” can then mean
system-side moves the index enables
lens/friday-mcp, or place it automaticallyEach of these is the system proposing structure from evidence the user generated by working — which is the only kind of automatic organisation that survives contact with a real notebook.
And this is what the ephemeral tier is actually for. It isn't a convenience — it's the filter on the index. Six summons thrown at a page while thinking never enter it; the one you kept working inside does. Keep is the indexing act, and it's triggered by evidence of use rather than by asking someone to predict, at the moment of curiosity, whether a thing deserves to be permanent.
Where this bites my own proposal: the free-form content slot in the block above is unindexed by construction, and a workspace of summons plus loose prose could still become a junk drawer. I don't think the answer is to restrict it — unindexed thinking has to be allowed somewhere or people write nothing — but it does mean the promotion path out of prose has to stay one keystroke. Prose → named → indexed, at ⇥ per step. If any step in that chain costs a dialog, the notebook silts up exactly where it does today.
| rung | gesture | lives for | writes to the file | ends by |
|---|---|---|---|---|
| glance | hover a link or tag → peek card | the hover | nothing | moving the mouse |
| summon | ⇥ on a line you just typed | this visit — per viewer, per page, in the existing visit-config path | nothing | ⌫, or leaving and coming back |
| keep | ⇥⇥ (a second Tab on a bound line), or edit anything inside the summon | forever, and it syncs | ![[mcp]]{todo} appended to the line you wrote | deleting the reference text |
| name | ⌥N on a kept summon | forever, reusable everywhere | a lens/… page + a live reference here | detach, or delete the definition |
Rungs 2 and 3 are the same step today, and that is the entire friction. Splitting them is what makes a scratch workspace possible: you throw six summons at a page while thinking, and five of them evaporate when you leave. The one you kept working inside kept itself — editing a summoned result is the strongest possible signal that this window earned permanence, so it writes its own text at that moment rather than asking. Note the shape of the ladder: ⇥ once to look, ⇥ twice to keep — the same key, pressed as many times as you mean it. Read the other way, this ladder is the index's admission policy: rungs 1–2 stay out of it, rungs 3–4 go in.
Don't force one. Force the separation instead: a tag is the only way to be a member of something; a page is the only thing that owns a block. Two different axes, so two mechanisms can't compete — and the write destination follows from which one you summoned.
the workout case — two routes, one stored result
route A · tag in place. Two keystrokes. You already knew what you did.
marked setnew lines land here, tagged
route B · summon the tag, type inside. More keystrokes, and you can see the week while logging.
both store the same thing
A block on 2026-08-01 carrying :workout:. A tag-set has no home to move things to, so writing into its face can only mean “write here and mark it” — which is precisely route A. Route B isn't a second way to log a workout; it's route A with the set visible while you do it. Nothing to choose between, so nothing to get wrong.
the contrasting case — summoning a page, where typing genuinely relocates
pagenew lines land on mcp integration
This todo belongs to the project; today is only when you happened to think of it. So it should live on mcp integration and merely appear here — the opposite of the workout, which belongs to the day and is merely gathered elsewhere.
The discriminator is never “which feature?” — it's “does this belong to where I am, or to what I'm looking at?” And the user answers it by choosing what to summon, which they had to do anyway.
A named lens over several sources has no unambiguous home, so it stays read-only — Draft 2's existing rule, now visibly the same rule as the other two rather than an exception.
The one thing this design owes the user is a sentence, not a decision. Both faces look identical — a dotted rail with rows under it — so the caption has to say where keystrokes will land before they type: “new lines land here, tagged” versus “new lines land on mcp integration”. This is the one place in the whole design where verbose UI is correct; a wrong guess silently moves someone's writing to another page, which is the worst outcome available.
Remaining confusion I can't design away: a page and a tag can share a name. Summoning workout and :workout: then look nearly the same but behave oppositely. Either the two namespaces must be visibly distinct at every mention, or the system should refuse the collision at creation — I lean toward refusing, because a notebook with both is one where the caption is the only thing standing between the user and a misfiled block.
No. If projects/mcp implied an outline position, a page's address would depend on where its block sits — so dragging a block would rename a page and break every reference to it. That kills promote-in-place, kills the free round trip, and re-introduces the page-vs-block anxiety in its worst form.
reading of a/b | implies | consequence |
|---|---|---|
| folder · outline | b is contained by a | moving a block re-addresses a page; two competing hierarchies must be kept in sync forever; “where does this really live” becomes unanswerable |
| pure string | nothing | Safe but wasteful — users will type projects/ expecting it to gather, and be right to. |
| derived membership | b is a member of the set a, computed from the name | no new concept — slash is a tag you get for free from the name, and it lands on the axis that already exists for exactly this |
both facts true at once, no conflict
namespacenew lines create projects/mcp/…
Its home is under “friday push”. Its membership is projects/mcp. Those never contend, because one is ownership and the other is gathering — the same separation as tags. A page appears once in the outline and in as many faces as gather it.
the write-destination rule extends to three, unchanged
what typing inside a face does
Typing “auth” into the projects/mcp face makes the page projects/mcp/auth, homed at the block you typed. The rule didn't need a special case — destination still follows from the source kind, which is the sign the axis was drawn in the right place.
The honest finding: slash is nearly redundant with tags. projects/mcp is close to a page mcp carrying :projects:. Its one real property is that the qualifier is inseparable from identity — you can't untag it, and it disambiguates two pages that would otherwise collide. So the rule is: slash when the qualifier is part of what the thing is (lens/overdue is not the topic “overdue”), tag when it's something the thing has. If we can't hold that line in the UI, we should drop slash entirely rather than ship two membership systems.
Two costs to accept. Users will read the slash as a folder no matter what we write in docs — so there must be no “new subpage” command, no tree in a sidebar, nothing that rewards the metaphor; it stays characters in a name. And it complicates the ⇥ offer: typing “transport” no longer whole-matches projects/mcp/transport. Leaf matching has to count as a whole match when the leaf is unique, and fall back to the ↑↓ list when two namespaces share one — which is the recents list already in the design, doing a second job.
This collapses the vocabulary rather than adding to it. A topic is a name you can mark things with. A page is a topic that also owns content. A composition is several topics at once. There is no fourth thing — and “lens” survives only as the act of summoning one, which is what it should have been from the start.
| was | is now | what actually changed |
|---|---|---|
| tag | a topic | nothing — this was already the pure case |
| page | a topic with a home | nothing stored — the page-table row is “has a home” |
| namespace | a topic derived from a name | nothing — last section's finding, restated |
| lens · filter grammar | a composition of topics, summoned | {todo} stops being a filter language and becomes another topic in the list |
| named lens | a named composition — itself a topic | the loop closes: you can mark a block with a saved view's name |
composition is just more topics on the line
composednew lines land here, marked ×3
homednew lines land on mcp integration
Same gesture, same rail, same ⇥. The only difference a user perceives is how many topics they named — and the caption tells them what that bought.
and the three destination rules become one
where a line you type inside a face goes
The old three-case table was three instances of this. Zero homes and many homes behave identically, so the read-only rule for multi-source lenses disappears — it was never a restriction, just a missing default. Draft 2's “unambiguous write plan” requirement is now the literal statement of the rule.
Why this is worth the rename: “lens” is a noun, and every argument in this document has been a fight against people treating it as an object — a container, a pane, a thing you create and manage. “Summon a topic” has no noun to reify. The user's model becomes: everything is named; naming is how the system finds things; summoning brings a name here to work near. Three sentences, no new concepts, and the indexing motive is visible in the vocabulary rather than hidden behind a feature name.
Where the word breaks down — and it does. 2026-08-01, sam 1:1, and transport doc are not topics; they're an occasion, a meeting, and an artifact. “Topic” smuggles in aboutness, and a notebook is at least half made of things that merely happened. If we ship the word, journal pages will feel mislabelled forever. The safer version is to keep the model exactly as above and call the noun something that means only “a name the system knows” — the unification is the real proposal; “topic” is one candidate word for it, and probably not the right one.
Second cost: collapsing tag and page into one noun makes the home distinction invisible in the vocabulary at the exact moment it decides where your writing lands. Today “that's a tag, not a page” does real explanatory work. If both are “topics”, the caption is carrying the entire distinction alone — so the homed/unhomed difference has to be as visible as ◆ versus ◈ already is, or we've traded a clean model for a confusing one.
river — the default, an ordinary subtree
Every verb you already have does workspace management: drag to reorder panes, indent to nest one inside another, fold to collapse, ⌫ to close. The second child is folded — that's a minimised pane, and it cost one keystroke because it's the same fold you use everywhere else.
columns — ⌘⇧L on the parent · a projection, not a mode
Same tree, laid sideways for the session. It is visit state, like fold — not a block type, not a stored layout, nothing to migrate. Drag between columns is the same re-home drag as in the river; close a column and the block is merely folded. Leave the page and you're back to a document, which is what it always was.
The workspace is not a thing you create. It is three lines you typed and Tabbed, under a parent — a block with children, the cheapest object in the system. If "make me a workspace" ever needs its own command, we've added a container and lost the argument in Lens Is Not a Container.
| substrate | what it is | outline verbs | cost to make | verdict |
|---|---|---|---|---|
| dedicated block | today — a block whose whole content is a reference | all of them | high — authoring | Keep, as the degenerate case. Right for a lens you meant to publish into a document. |
| block state | a viewport bound to any block, on top of its own text and children | all of them, inherited | one key, at the end of a line you were typing anyway | The proposal. Generalizes the dedicated block instead of competing with it; nothing new in the data model but a visit-state flag and, on keep, the same text as today. |
| pane / tmux | a first-class window outside the outline | none — needs its own set | medium | Rejected as the model, kept as a projection (⌘⇧L). A layout system that isn't the outline is a second app. |
| overlay / peek | a hovering card over the page | none — read-only by nature | free | Keep as rung 1. Can't contribute into the page, which is half of what a lens is for. |
| buffer | the ephemeral ⌘K v agenda | partial | free | Keep. It's the notebook-wide sibling of a summon — same ladder, different implicit source. |
text, parent, order. Addressable only by where it sits. Cheap, disposable, positional — the thing you make with ⏎ without thinking.
a name, a face, a plan. Addressable from anywhere, gathers from anywhere. The thing you currently have to decide to create.
Every feature in this document is one of those four arrows. Promote-in-place is left-to-right. A summon is right-to-left, borrowed, free. Keep is the borrow written down. Demote is the exit. There is no fifth operation and no third table — which is a good sign that lens, page, tag-set and workspace aren't four concepts.
the unification — a block with a face, own or borrowed
Two blocks, one rendering path. Both show a face below the rail; the only difference is whose page-table row it came from. Filled ◆ = this block is that page. Hollow ◈ = this block is looking at one. Everything else — fold, drag, indent, ⌫ — behaves identically, because there is nothing else to distinguish.
The anxiety this kills is “should this be a page?” — the question every outliner makes you answer at the worst possible moment, on incomplete information, with an expensive undo.
If promote is a name added to a row that doesn't move, and demote removes it, then the trip is a round trip and the question stops being load-bearing. Promote generously; the cost of being wrong is one keystroke, not a reorganisation. That is only true because nothing relocates — the moment promotion moves content, the arrow stops being reversible and the anxiety comes straight back.
Two implementation payoffs fall out of the split, not from extra work:
— The ⇥ suggestion is a page-table lookup, not a search. Exact name hit or nothing. That's why the gates can be strict and the offer instant, and why it never behaves like fuzzy autocomplete.
— A named lens, a tag-set and an ordinary page are the same row, differing only in whether they carry content, a plan, or both. lens/overdue is a page row with a plan and no content of its own. No new storage, no new concept to explain.
The one rule the crossing needs: a block has at most one face. Summoning onto an already-promoted block doesn't stack two — it makes a child that bears the summon, which is what you meant anyway. Without that rule, “whose children are these?” becomes unanswerable and the block quietly turns into a container.
1 · ⇥ already means indent
Proposal: ⇥ accepts only while a suggestion is showing — which is only on a line you are actively typing, where indent is least likely to be what you meant. The instant you press →, ⏎, or click away, ⇥ is indent again. It's the ghost-text convention, and it's the same bet shell completion makes. If that bet loses in testing, → at end-of-line is the fallback with no collision at all.
2 · children vs. results
Rule: your children are above the rail, results below it, never interleaved. ⏎ and ⇥ always make your child. Dragging into the results region is refused with the reason. If that hairline ever gets ambiguous, the summon has become a container.
3 · a summon that doesn't sync
Rung 2 lives in the existing per-page config path, so a summon you left open isn't there on your phone. Correct by the Draft 2 sharing stance (owner-local), but it means "keep" is the only cross-device gesture — so its affordance has to be visible, not just a second Tab.
The transfer's own open question: demote is only clean while the page has no inbound references. Once three other pages point at it, dropping the name has to either break them or refuse — so the round trip is genuinely free only for a while, and the UI shouldn't imply otherwise. Proposal: demote stays available but reports the count first, and a page with inbound links demotes to a named block rather than vanishing from the table.
Also unresolved: how often the suggestion line may appear before it feels like the editor is watching you — my instinct is strong matches only, after a brief pause, never on the first character · whether folding a summoning block should hide results first and children on a second press, since one verb is currently doing two jobs · what ⇥ does when the text names several things (proposal: the top match, since unions are deferred anyway) · and whether "editing inside a summon auto-keeps" is delightful or spooky — it silently writes text into the document, which is exactly what Draft 2 says never to do behind the user's back. A one-line undoable toast is probably the price.