using leaf
Threads
A comment attaches a conversation to a passage. Replies stay beside that passage, so the discussion and the page can be read together. This page also explains how Leaf keeps each thread on its passage as the page changes.
Try a thread
This fictional exchange shows an agent's reply after revising the importer policy. Open the marker beside the sentence to read the exchange, or open Threads in the sample's top row. Select the sentence to try a comment. The sample has its own temporary log; no agent answers its practice comments. Reset restores the fictional exchange.
Importer queue policy
When the queue is full, the importer discards the oldest queued job and admits the new one.
Anchors
A thread's anchor is the place on the page it is about. The first comment records it in the event log, and that record never changes; a reply or a revision can later move the thread to a new anchor. A comment can be anchored in these ways:
| Anchor | How the user makes it | What the log stores |
|---|---|---|
| Passage | Select words, then press c |
section, the id of the nearest element around the words;
quote, the words; and prefix and
suffix, up to 24 characters on each side
|
| Element | ⌥-click (Alt-click) an element, such as an image or a table, or press s and choose one | section alone |
| Part of a picture | ⌥-click a box that a diagram declares commentable | section and visual, the part's name |
| Data value | Select words inside one value a widget drew from a data source |
section and datum, the value's key, and for a
value from an external source, that source's revision
|
| Drawing | Draw on the page in Draw mode (w) | An element anchor, plus the strokes |
| None | Press c with nothing selected, or write in the Threads panel's comment box | No anchor: the thread is about the whole page |
An agent opens threads with the same anchors, using
leaf thread create with --quote,
--section, --part, or none of them.
Finding a passage
In the browser, a passage is an ordered list of segments, each
{node, start, end}: a text node and a range of characters in it.
A selection that crosses a bold word, a link, or two paragraphs is several
segments. Text nodes exist only while the page is loaded, so the log stores
the passage by its words instead, as the table above shows.
Each time the page is drawn, Leaf searches the anchor's section for the quote. If exactly one occurrence has the stored prefix and suffix around it, that occurrence is the passage. If none has them but the quote occurs only once, that one is the passage. In every other case the anchor finds nothing. Leaf never takes the first of two identical passages, because a comment made on the second would then appear on words it was never made on.
Leaf reads a page's text in two ways. says is the text the
rendered page shows, including words a widget draws and declares as part of
the page, such as a tab's label. wrote is only the text the
author put in the HTML. Anchors and quotes use says, because the
user selects what they see. Comparing two versions uses wrote, so
words a widget draws do not appear as edits.
The browser makes an anchor from a selection. leaf thread create
makes one in Python, from the HTML file. Both apply the same rules: the same
whitespace characters collapse to one space, the same elements count as
separate blocks, the context is the same 24 characters, and the search is the
same.
The file holds the author's HTML, not the words a widget's script draws
between its parts, such as a board column's heading or a diagram drawn from
its source. A quote may not cross such words on either side.
leaf thread create refuses a quote that crosses them, that occurs
more than once in the section, or that sits in content the user's decision
removed, and says what to quote instead. An agent's anchor is therefore one
the user's page can find.
Anchors across revisions
Before publishing a new revision, Leaf checks each open thread's quoted passage against the revised content. The results are shown below.
When the quoted words change but their element survives, Leaf moves the thread
to the element its anchor names in section and records the move
in the log as a reanchor event, without adding a message.
Clicking the quote scrolls to the element. The same happens when a revision
repeats the words without context that tells the copies apart.
When the element is gone too, Leaf refuses the revision and names the thread.
The agent keeps the element, moves the thread with a reply that names a new
passage, or detaches it with a reply passing --detach, which says
the subject has left the page. Leaf refuses a revision that removes the
element an open element-anchored thread is on in the same way. Only the agent
chooses a new passage for a thread, in a reply that says why; Leaf's own move
goes only to the element the anchor already named.
A detached thread stays open in Threads, and its first comment keeps its original anchor in the log. Its quote is greyed out and cannot be followed; hovering it shows “This passage is no longer in the version shown”. Threads' No longer here filter lists detached threads. A later reply can move one to a replacement passage.
Anchors are found in whichever version the user is viewing. Opening an older version from the version menu shows threads on passages that version lacks as “This passage can't be identified in the version shown”, also listed under No longer here.
Drawings
In Draw mode, which w turns on and off, the user draws freehand on the page and writes a comment beside the ink. The drawing is anchored on the element under the start of its first stroke, or the nearest one. The comment stores up to 32 strokes as positions within that element's box, the box's size, the words the ink covers, and the window size and colour scheme it was drawn in.
The page draws the ink at the element's current size, so it stays on the
element in a narrower window, though text that reflows inside the element can
move out from under it. The agent reads the covered words in the comment, and
leaf page picture renders that revision again in the same window
with the ink over it.
Widgets in messages
An agent's reply can carry a small widget, typically a question with options
(--markup on leaf thread reply). Leaf validates the
markup when it is posted and stores it in the log with the reply, so the
thread shows the same widget in every version of the page. A later
leaf page init refuses a vocabulary that could no longer render
it.
A widget on the page takes its state from the version being viewed: that
version's markup plus the actions taken up to it, minus any a stamped version
marked restated. A widget in a message belongs to no version. Its
state is its markup plus every action taken on it, whichever version is open,
and no revision can retract one. Choosing the answer to its question hands the
thread back to the agent.
Titles
A title names a thread in Threads, in a few words of plain text. The agent
sets one with --title on the command that first handles the
thread, and can rename it later with leaf thread edit --title.
The agent's reply can take minutes, so when the user opens an untitled thread,
Leaf also asks the harness's model for a title at once, in a separate request:
a claude -p request to Haiku in Claude Code, and an App Server
request in Codex; in Pi, the agent names the thread. The request holds only
the thread's passage and first message, nothing from the agent's session, and
runs in parallel with the agent's work. Leaf uses the first title it receives.
While the agent is working on an untitled thread, the panel shows animated
dots in place of the title; a thread nobody named shows its opening words.
Thread readings
A thread appears in several places at once: its card in Threads, its entry in the margin, its row in the Page Map, its row in On me, and its line in an activity feed. Each place says the same thing about it, so a thread never goes by two names or shows two statuses.
| What it says | Where it shows |
|---|---|
| Its name: the title, else the words of its opening message, else Drawing | Threads, On me, the margin, the Page Map, an activity feed |
| Its status: Your turn, On it, Still to do, Waiting, or how far the agent has got with the latest update | Threads and the floating panel, as the card's status; the margin and the Page Map, beside its name |
| How far the agent has got with an update: Queued for Claude, Up next, Replying, Couldn't answer | the message itself; the margin and Page Map entries |
| How many of the agent's messages the user has not read | Threads, the margin, the Page Map |
| An unsent reply or rewrite | its box, wherever the thread is open |
Summaries
leaf thread summarize folds a run of consecutive messages in one
thread under a disclosure labelled “Earlier discussion” or a label the agent
chooses, with a summary of their decisions, reasoning, and open questions. The
original messages stay in the log and unfold from the disclosure. A summary
answers nothing and closes nothing.
Once a thread is long, Leaf suggests a range to the agent with the user's next message, leaving the newest exchange outside it; the agent decides whether to summarize. Editing a message inside a written summary's range removes the summary, so an outdated summary cannot hide the correction. Progress messages an agent posts while working fold on their own behind “Show N progress messages” when its next ordinary reply arrives.
How a thread ends
The user usually closes a thread with Resolve thread once they have read the answer. Its marker and ink leave the page, and it moves from Threads' Open list to Resolved. Reopen brings it back, as does z when resolving was the user's latest action.
The agent resolves a thread only when the user asks, or when no review could change the outcome; the thread then reads “Resolved by” and the agent's name. A new message from either side reopens a resolved thread. A reaction, a progress message or a failure notice does not.
A reaction is the only message the user can take back. Its mark's undo
control, or z while it is the user's latest action, withdraws it:
the log gains an undo naming it, and the mark and any thread it
started leave the page. This works only while the thread is open and nothing,
not even another reaction, has answered it. A message with words cannot be
withdrawn.
Words typed but not sent are kept after their box closes. If a new version removes the content they were about, Resume writing (g i) opens them, unsent, in Comment on the page, after any words already there.
Comments and replies
Select a passage, then use the comment control or press c to write a comment. The thread keeps that passage as its anchor. Its marker opens the conversation beside the passage, where the agent's reply and the reply box appear.
The Threads tab of Leaf's On view, which the panel's On me mark opens, lists the threads above the panel's row. Select a thread to read its messages and reply in the panel's comment field. Its passage link returns to the anchor on the page.