Thread titles
How an unnamed thread gets a label, and why we don’t ask a model to invent one.
The problem
Thread creation is fresh by default, so a house soon holds several unnamed threads with the same people in them. A list shows who is in each one and the latest line, and that is not enough: five threads with ada, all ending in “ok, done”, look identical.
What a title is here
A thread has one real title: the name a person typed. It is stable, addressable as #name in chat, and it decides whether a new conversation reuses an old one. Nothing derived from messages ever touches those three things. Whatever else a list prints is presentation.
How would you title a chat that keeps evolving?
You wouldn’t. A chat has people (its identity) and an “about” that drifts. Slack names a DM by its people forever, gives a channel a topic that is expected to change, and shows a thread as its opening message plus the latest reply. Coding-agent tools (opencode, t3code) title a session once from the opening message and never retitle it. Both answers are the same: the stable thing about a conversation is how it started.
A model-written title from message two goes stale by message twenty; a title that tracks the conversation changes under you. Once the label is “how it started”, neither problem exists.
What arbe does
The opening line. The first previewable message is stored on the thread next to the latest one, written once and never regenerated. It is the instant, free seed: the moment someone speaks, a list can tell this thread from its siblings. arbe thread view prints it as started:.
The polished title. When the thread director finishes a pass and finds an unnamed thread that has started but has no title, it rewrites the opening line once into a short phrase — “can we ship the release on friday?” becomes “Friday release” — on a cheap model, paid by arbe rather than the house, capped at 50 characters and kept in the message’s language. The prompt follows opencode’s title generator and t3code’s subject-and-outcome rule (drop “hey, can someone…”, keep names, numbers and dates); the output is sanitized the same way (first line, quotes off, clamped). It is written only while the title is still empty and the thread still unnamed, so a name typed meanwhile wins.
It is never regenerated on its own. A person can ask for a fresh one — “Regenerate title” in the thread menu, arbe thread title <id> --regenerate, or PATCH /api/threads/:id { "generated_title": null } — which forgets the title and wakes the director to write it again. That is the whole regeneration story: forgetting is the request, and the director is the only writer.
Display order
Every surface uses the same helper (@arbe/core/thread-display): explicit name → generated title → participants → short id for the row label. The opening line shows underneath as the differentiator until a title has been written from it, and never when it is also the latest line. The house palette is a people picker, so it labels unnamed threads by their people regardless.
See threads for the row, reuse, and the participant filter.