← Design notes
Current functions — what the room does
A user's-eye view of everything the live room does today — kept current with the app. For the component-by-component design reference see the Style Guide; for where each function lives in code see the Function map.
One product, one screen: real people and a cast of AI personas talk in a shared room. The panel is voiced by one model, one call per turn — it confers backstage, then speaks.
Rooms & chats
- Create a chat through one door — the ✎ in the Chats header (2026-08-08; the landing 「Call the panel」 capsule, its muted wide twin and the composer under it are all retired, so there is one affordance, in the corner every messenger keeps it in).
- Two-pane nav (chats list ↔ open chat). Rename, fork, mute, archive, or trash a chat — all five behind a 480ms hold on the row (v716; right-click on a desktop). The per-row ⋮ is gone: it cost a column on every row of the list, on every screen, for something you press rarely, and that column was what held the timestamp a long way off the right edge — the time now sits at the row's own 16px inset. The menu opens where your finger is rather than in the row's corner, a drag of 8px cancels the hold (a scroll is not a hold), and the press that opened the menu does not also open the chat. It rides the row builder, so every surface that lists chats inherits it.
- A chat row is the same object wherever it is listed. Shared chats on a contact's page and the search results both draw the chat list's own row — same height, same face, same time, same unread dot, the same seat-coloured panel of who is in it — instead of a hand-built look-alike, and tapping one opens that chat because the row already knows how. Chat rows sit on the page rather than on a grouped surface, exactly as they do in the list; people and personas keep their group.
- Every chat row wears a face. A single party keeps its own face — a 1:1's counterpart (their photo, or their monogram), your own notes-to-self, or a solo persona's seat disc — while every group takes the same plain two-person glyph. What tells the groups apart is the disc behind it: a blend of the room's seat colours, with humans contributing the muted grey. Flat grey = humans only · colour into colour = a panel · colour into grey = mixed, and the grey is the humans. The blend reads the same seat colours the names beneath it use, so the tile matches the room, and colour stays reserved for AI. The Vibes folder row takes no tile — it isn't a room.
- The unread dot rides the tile. The red badge sits on the avatar's top-right corner, marking the chat rather than the title — so the title line gets its full width back — and it's ringed in the pane's own colour so it reads as a badge on any row tint. A message arriving in the chat you just backed out of is flagged as unread: only the room you are actually looking at marks itself read.
- A row shows the last message, and a photo is a message (v1024). Every chat row carries a one-line preview of the newest thing said in it — built on the server, so a chat you have never opened has one too, and a message the sender withdrew is never quoted there. A picture with no caption used to be stepped over, so the row went on quoting the line before it and the list said the newest thing in the chat was something the sender had already followed with a photo; a wordless picture now reads 「[Photo]」 — in the app’s own language — and any words there are still win.
- A long-press menu acts on the first tap. Tapping Friend on the All-persona page did nothing until you tapped a second time — and "most times, not always" was the tell: a suppressed event, not broken wiring.
- Each chat keeps its own cast, history, and settings.
- Close where your thumb expects it. Every layer wears the grammar the phone already taught: a page goes back with a ‹ top-left, a task dialog cancels left and commits top-right — the new-chat picker's Start (n) — an overlay still closes with a × top-right, and a sheet swipes down. Archived & Trash is a page, not a task, so each Restore / Delete commits on the spot. Phone-only: on a wide screen × stays top-right, where a window closes. Anywhere you can go back, the back gesture peels one layer at a time — and on desktop Esc is that same gesture, reaching every dismissible layer.
- Picking people is a page, not a card. Invite people, Remove, New group and Invite persona were the last multi-select dialogs in the chat; they are canonical sub-pages now — full screen, sliding in from the right, the one shared ‹ in the corner, dismissed by the back gesture.
- The back gesture on Android too. A swipe from the very left edge is Back in iOS Safari and in an installed PWA for free; Android does not give it dependably — under 3-button navigation there is no edge gesture at all. So the app provides one, and the two can never both fire: iOS is excluded outright, and on Android the app peels the top layer itself, swallowing the platform's own gesture if it also arrives. It only acts when there is a layer to peel — at a root there is nothing to go back to, and a synthetic Back there would walk you out of the app. Only the four tab roots are roots; everything else is a sub-page that must return to the page before it, and that contract is now linted rather than commented, because an unregistered sub-page does not degrade gracefully — the gesture finds nothing armed and quits the app from that page. A separate guard makes a history retract impossible to turn into a navigation: a burst of rapid taps used to hit the browser's own pushState rate limit and eventually eject the tab to its start page.
- Every confirmation is the house dialog. The split was not merely inconsistent, it was inverted: the reversible acts — archive, delete-to-trash, fork — had the considered house dialog, while every irreversible one — the three purges and the one-way private-history flip — was handed to the browser's grey box: a generic OK, no red, and no way to weigh the words. The dangerous half had the careless UI. Seven were converted and none remain — and three sentences that could never be translated now can, including the forward confirm, which used to build "message" and "messages" by hand in JS, so the plural is the dictionary's problem rather than English grammar's. The confirms also name a page that exists: they went on saying "it moves to Trash in Archived & Trash", the vocabulary of a screen the app retired in v758–v759 — and the one moment that sentence exists for is telling you where to find the thing again, so it was pointing at a place you could no longer go. It reads Settings › Deleted chats now, and the purges ask what the button says: "Purge {name}? This can't be undone.
The panel
- Before replying, the cast confers backstage (the "egg") — a short private deliberation — then the panel speaks.
- Relevance-led: the most-implicated voice leads with the full case; the rest add a distinct point or concur in one line.
- @-mention a persona to address it directly — confer is skipped, and only the host you named answers (2026-08-30). Measured live, a second host kept riding along on somebody else's mention — seated on all three of a room's @-turns — which is the guest's own pick overruled, however good the extra line would have been. The floor now goes to the mentioned host alone; everyone else sits the turn out, unless the same message calls on them by name too. Typing a name works the same as the @ (2026-08-31): 「Banksy,」 or 张小龙 addresses that host exactly as the mention entity does — every rendering their card carries (the display name, the local name, the short form, the display's surname), and the panel bridges the ones no card holds (班克西 is Banksy). Nobody reaches for a menu mid-sentence to say a name out loud.
- The panel knows the time: every message carries the sender's local date, time, and zone, so the cast can react to "it's late where you are" or how long it's been since you last wrote.
- A floor producer shapes each turn (on by default for new chats): not everyone speaks every turn, reply lengths vary and mirror the length of your message, and a big room stays readable instead of an everyone-talks pile-up. Toggle it per chat in Advanced at creation, or later from the chat menu.
- A prop master keeps the furniture out of the way (2026-07-29). Before the panel writes anything, one cheap check reads the register of the moment — the last few lines, what is already on the table, and what you just said — and answers a single question: does this want an object on the table at all? Its default is no, and it never picks which object; that stays the panel's job. It's there because「describing how uncertain a decision feels」is not「asking for a die」, and a cast that reaches for a prop on every emotional beat is worse company than one that never does. If the check fails or can't be read, the turn runs exactly as it would have — it can only ever hold an instrument back, never force one.
- On a setup, the host explains before it sets the table (2026-07-30). A person running a game deliberates before acting, so on a setup-shaped moment the order of the turn flips: the speech runs first and publishes — the host explaining the game, the rules and the table in their own words — and the instruments are armed after it, behind the bubble, reading that speech as the spec. The cards land while the room is still reading. An ordinary turn, and any move made mid-game, keeps the classic order; anything that can't be read falls back to it too. The property that decided it: while the host is explaining, the secret does not exist yet — the word, the roles, the answer are chosen privately by the later call, so the message that sets a round up has nothing in it to leak.
- The panel can sit a turn out (2026-07-30). Two humans going back and forth don't need a host between every line. The floor producer could always stage everyone silent, but the panel call still ran and could talk anyway; a hold is now structural — no panel call is made at all, and the lines you wrote ride into the next turn rather than getting an answer of their own. It only ever applies with two or more humans in the room (in a one-to-one the only audience is the persona), never in a moment that wanted an object on the table, and never on a web dispatch. Address a host directly and it answers — a hold is about a thread that isn't the panel's to interrupt, not about being unavailable.
- Send a photo (gap 5, 2026-08-22). The composer is now emoji · + · text · mic; + opens a sheet of four — Photo · Camera · Kits · Toys (Toys is the old tool drawer's eight, one level down — the sheet's grip pulls, so a drag or a flick closes it). Picked photos (up to four a message) dock as chips above the field, the field becomes their caption, and the sent message lands as two bubbles under one name — the photo (or a grid of them), sized by the picture, and the caption beneath it; tap the photo for the full-size viewer, which holds the set rather than one picture (v991) — a bubble's photos, or the composer's chips, are one strip, so at 1× a swipe left or right slides to the next (arrow keys on a desktop) under a 2 / 4 counter. The panel can't see pixels — the room has eyes: at upload a vision model writes a text shadow of the photo that rides the turn. An ordinary photo is DeepSeek's read; a dense picture — a plan, a document, a chart, a screenshot, a map, or any picture whose small print did not survive the shrink to ~800² — goes to Gemini 3.8 Flash (3.7 until 2026-09-09), which reads it whole at full resolution, because the shrunk read came back with invented building numbers; DeepSeek reading the same picture as close-ups is the fallback when there are no Gemini eyes. Either way the shadow is what rides the turn, so the hosts answer details (the small print, the kitchen's size, which way the balcony faces) on whatever model they run. Photos are stripped of EXIF/GPS, kept per room, visible to members only.
- The room decides when to look again (2026-08-23). While a photo is on the table, a small check reads what the eyes already wrote, the last few lines and the line you just sent, and answers one question: does answering this well need the photo again — and if so, is it a lookup (a number, a label, a position — quick and cold) or a study (a comparison, a spatial question, a claim to check — worth thinking about)? It also rewrites the question for the eyes, which is the part that matters:「have a look」is a request to look, not something a pair of eyes can answer, so it becomes a real question about the picture. It replaced a keyword list that fired on question marks — which looked four times in one real conversation and still missed「have a look」, and never carried the corrections the person had made about what they were looking at. If the check can't be read the old keyword list decides, so a photo never stops being lookable.
- A host that sees for itself (2026-08-23). DeepSeek v4.1 Flash — natively multimodal, and since 2026-09-09 the seat that carries this, the experimental v4 Flash (vision) pair it replaced having left the table — is on the model list for a chat (with and without reasoning). On it, a photo you sent is handed to the panel as a picture — the last four, attached to the lines they were sent on — so the hosts look with their own eyes instead of reading a description of what was there, and no second look is needed. Switching a chat back to any other model keeps working: what the conversation stores is still the written shadow, never pixels. It began on an id DeepSeek marked experimental and was not the default; since 2026-09-09 it is the house default a new chat is born on (the console may still name another, and a console default saved on a retired seat reads onto this one). And the room still judges each turn: on a question worth studying it turns that host's own thinking on for that one turn, and leaves it off for a passing remark.
- After the cards land, someone is handed the first move (2026-07-31). The third beat of a real host's rhythm: explain, put the cards down, then point at who starts. When a setup turn arms something the room now waits on — a deal, a ballot, dice, a collect — one more short line follows the cards and names a player(「牌都发下去了……Amy,你先」). It only fires if nobody spoke while the cards were landing, never for a board or a clock alone (nothing is owed on those), and it can only talk — a coda never sets a second table.
- The host checks its own table before the turn ends (2026-07-31). After a setup's cards land, one cheap look back compares what the host said it was doing with what actually went down on the table. The expected verdict is「nothing to fix」and the turn simply ends. When it isn't, the host lands the one missing piece itself — in its live debut a host promised a question board, sealed only the word, and the look-back added the board with its counters before anyone had to notice. It never re-arms a card that's already out, and every look is recorded whether or not it changed anything.
- The room stops talking when nobody is there (2026-08-05). Several things can make the room speak without anyone asking — a timer running out, a card's own deadline, a table that has gone quiet, the line that follows the cards down. None of them used to check whether a person was present: merely opening a chat and leaving re-armed the countdown, so a glance could start a paid turn nobody would read. Now those unasked lines only run if somebody is holding the room open or a human has done something here in the last ten minutes. Anything you cause always runs — your own message, tap or press is exactly the thing that says you're here. What is held back is dropped rather than saved up, because coming back to a burst of stale 3 a.m. announcements about a room whose current state is already on your screen is worse than coming back to quiet. And the room itself keeps running: the board still updates, the wheel still spins, a finished game still folds — what stops is the narration of it.
- The panel writes; a person types (2026-08-16, the AI-tone batch — the diagnosis). The meaning was never the problem; the register was. Measured against the humans in the same 40 rooms, 88.6% of panel turns carried an em dash against 1.6% of theirs — a tell, and one you feel before you can name it. Replies now read the way a message reads: the essay punctuation is spent for the marks a thumb would type, and the bulleted layout is conditional — a real analysis turn keeps its structure, an ordinary conversational reply does not get one imposed on it. Diagrams, code and other fenced blocks are untouched. Nothing is deleted but the furniture; every word the panel wrote still reaches you, and a numbered point keeps its number.
- A turn can answer you and then make a move of its own (2026-08-28, the persona agenda's phase ④, staged by the floor producer). Perfect responsiveness is a tell no friend has. So when a seated host holds ground of its own — a part of your story it is quietly following, or fresh news of its own life — the producer may append to one speaking host's cue a licence, word for word:「room for a move of your own, if something you're following pulls you」. A licence and never an assignment: whether to move, and what the move is, are the host's alone, declining is in character, and the response slot is never sacrificed — you get your answer, and then it moves. Never on a question you are bearing down on, never during a live game, at most one host and one slot in a turn. ⚠ The producer is never told what the ground is: it reads a name and a count(「Tess follows 1 part(s) of a guest's story」)and nothing else, so the firewall between the staging call and what a host privately remembers holds here in full.
- A host can fire two quick bubbles instead of one composed line (2026-08-31, liveliness lever ⑥). Nobody in a fast, delighted moment writes one tidy paragraph — they send a short thing, then another. The floor producer can now cue that shape (「two quick bubbles」), and the cued host lands two, at most three, consecutive short bubbles under one name. It is a shape for the same words, never extra length: the bubbles together stay inside that host's staged cap, and each is a complete little message rather than a sentence chopped in half. Only on a quick, casual, playful or warm beat — at most one host, never two turns running, and never on analysis, a serious ask, bad news, a tender moment or any game turn. Most turns have no burst; one every few exchanges reads alive, one every turn reads broken.
- Now and then a host mistypes, and corrects it (2026-08-31, lever ⑫). The strongest single humanness cue measured is not the typo — it is the correction; an uncorrected one just reads as sloppiness. So very rarely a speaking host lets a common thumb-slip through — an adjacent key, 的/得, 在/再,
teh→the — and the fix arrives as its own tiny follow-up bubble carrying nothing but the correction(English:「*meant X」·中文:「打错了,是X」). The rarity is code's, not the model's: a model told to do something 「rarely」 does it either never or always, so the room arms the licence about one turn in thirteen and the host still skips it on any turn that isn't a quick, casual beat — so the lived rate is far lower. Never in a play kit (a garbled move is a rules problem), never near the opening of a chat, never inside a fact or a number.
- Each host keeps its own register deep into a long chat (2026-08-31, lever ②). Voices converge — measurably, within about eight turns — because everything that anchors them sits at the top of a growing prompt while the room's own words pile up beneath it. Every turn now ends with the speaking hosts' voice notes, verbatim, right where the model is reading(each host speaks in their OWN register, never a colleague's). Only the hosts actually staged to speak are re-anchored; if the staging can't be read, every voiced host is, because a register is a licence and handing out too many is harmless.
What personas remember
Since v910–v931 a persona carries what it learned about you from one conversation into the next, inside your private chats with it — the「Proven」scope of Live personas row ③, built to the design on Persona memory. Everything it keeps is a line you can read, and every line has a delete beside it. It all lives behind one door — Me › Persona memory, its own row between Stats and Settings, wearing the one plum tile no other row on the app wears: what the personas keep about you is a thing about you, not a preference, so it does not sit under Settings.
- You write the first line yourself. Me › Persona memory opens on 「What they should know about you」 and a 400-character box sits bare beneath it — the heading is the label, so the box carries nothing but your words and its count — written in your own voice(「Call me Dan. I hate small talk — go straight to the numbers.」)— read by every persona you talk with. It saves when you leave the box, never per keystroke: a box read into live rooms should not publish half-finished sentences. Behind it the room grows a third system block with its own cache breakpoint, so the field costs one small block and never re-writes the profiles above it. ⚠ A room with nothing to remember sends the same two blocks it sent before the feature existed — the block is absent, not empty. It rides your private chats by default: one switch under the box — Use in group chats, off unless you turn it on — decides whether the personas read it in a chat that has other people in it. A note you wrote for the hosts is not a note you wrote for the room.
- The hosts write the rest down themselves. Nobody is asked to curate anything: a host reads the stretch you just had and keeps what is worth carrying — a fact, a preference, an event, a promise, something left open. It happens off the floor, so no reply ever waits on it, and it lands about two minutes after your last line rather than at the end of the day. Leaving the chat brings it forward: switching chats, going back to the list, backgrounding or closing the app fires it eight seconds later — the truest「we're done」signal there is. A daily sweep finishes any room that went cold without either happening.
- A memory comes back as a specific, never as a reference to itself (2026-08-31, liveliness lever ⑦). Knowing something about you and alluding to knowing it are opposite experiences: 「that thing you mentioned」 is a stranger being polite, while the project's name, the city, the deadline, the number — used in passing, as though simply known — is what being remembered feels like. The hosts are now asked for the detail itself. The older rule still holds beside it: never say where it came from — not 「you told me」, not 「I remember you saying」, not 「last time」 — a friend knows your sister's name without recalling the day they learned it.
- The note is a mark on your own bubble (v917, superseding v915–v916). What a host wrote down is a small grey bookmark riding the corner of your line — the reaction pill’s sibling, no count, grey at rest and never coral, because a remembered line that shouts turns every conversation into a receipt. Tap it and a sheet opens: ◘ Tess, Yu Hua remembered: — the hosts’ names in the seat colour they wear in that room — then the sentence with an Undo button, and a door to the full page. A bottom sheet on touch, a popover under the bubble on a wide screen with a mouse. Two earlier cuts were retired to get here: a note on the notification rail carries no context and so has to be timely, and a grey system capsule in the stream added a bubble to the conversation that the conversation did not ask for. The mark adds nothing to the flow and gains provenance, because every memory records the lines it came from. Undo strikes the line and leaves the mark — provenance, not a hole — and re-opening the sheet later shows it still struck, with no second undo. Chat info grows a matching section, what they’ve kept from this chat, hidden entirely when nothing was kept. The rail now carries only the daily sweep’s notes — the one writer with no chat you are looking at.
- A fact is closed, never quietly rewritten. Tell a host something that contradicts what it kept and the old line is closed with a date range rather than overwritten — the page shows it greyed, reading 「no longer true · 2024-03 → 2026-08」, never struck through, because struck reads as an error and muted reads as history. Being able to see the arc is the whole reason the store closes rows instead of dropping them. ⚠ Closed and folded are two different endings: a line the nightly rewrite absorbed into a summary sits in its own group, Folded into a summary, and says so — it is still true, and calling it 「No longer true」 would be the app lying about its own housekeeping.
- Overnight the host folds the threads (v921, the dream). Left alone, a store about one person becomes twenty true sentences that say roughly four things. So once a night, per person, a host reads its own open rows and folds a thread — several lines about one running matter — into one, closing the parts it absorbed. A story counts, not only a change of mind (2026-08-29): five separate notes about one flat, six about one product, are the same running matter and fold together, where the fold used to wait for a line that superseded another and so left those piles standing. A cat and a job are still not a thread, it never touches what it heard about the world, and the merged line replaces what it absorbed — so a detail left out of it is a detail forgotten, which is why a rich story is allowed two sentences. A line the dream wrote has no chat to name: where the others say which conversation they formed in, it says reflection. And a closed line now rides its replacement into the room — the host carries 「before that: …」 rather than losing the earlier version, so it can tell you what changed instead of only what is true.
- The page: a row per persona, a page per persona. Me › Persona memory ends in By persona — one ordinary contact row per host that holds anything about you, the same object the Friends list draws, with a memory on / memory off sign in its trail and a chevron. Open one and you get that host’s own page: its Memory switch, then its lines. The grouping carries the date, so the row carries one token — three small properties under every sentence 「burn users’ brain」, so the lines sit in groups under quiet sub-headings (months when sorted by Valid from, days by Recorded, chats by Source; a pill row re-orders them) and each row shows only the one thing its group is not already saying. Two lines, never three: the sentence at full width, then its small grey property at the left and Delete at the right end of the same line. Newest-valid first, with No longer true and Folded into a summary as their own groups at the bottom. A line’s source is the chat it formed in and it is a door — tap it and you are there — reading a deleted chat when the room is gone and reflection when the dream wrote it. The page deliberately answers a wider question than any room does: it shows closed lines and lines formed in chats you are not currently in, because those are exactly the ones somebody opens the page to find.
- Three ways to switch it off, and none of them destroys anything. The account-wide switch and the per-persona one are the same word at two scopes — both read Memory, both default on, and the account one says plainly what off means: 「Off: no persona keeps or uses memories about you, in any chat.」 Off stops both halves — no host reads a line about you, none is kept — and deletes nothing, the page being what makes that difference checkable; deletion is only ever your own hand. ⚠ The sign on a persona’s row tells the truth in effect: with the account switch off every row reads memory off, whatever its own stored choice — which survives, and comes back with the master. And Incognito is per chat, set at creation in the new-chat「More settings」group: an incognito room neither reads nor writes, and it carries nothing in — not even your own standing note — because「neither reads nor writes」has to mean the block is absent or it is a promise about half of itself. It can only be set when the chat is made: a room already harvested cannot be made incognito afterwards, and one switched out of it would be a promise quietly withdrawn.
- A memory about somebody who isn't here never reaches the room. Which lines a host may hold in its head is decided in code, before the payload is built, not by asking the model to be discreet — only rows about people actually in the room are loaded at all. For now a group room loads public facts only; anything personal rides your private chats alone, until the leak measurement says otherwise. A room with a play kit on the table is never harvested — role-play must not become biography — and nothing said before the feature was switched on is ever read: memory begins the day it began, so nobody wakes to a pile of notes about conversations from weeks ago.
- Being in the room is not owning the line (2026-08-29). A line about you, or owed to you, is yours; a line with no owner at all — a promise made to the whole table, a remark whose sayer is unknown — reaches everyone who was there, so that nothing sits in the store invisible and undeletable. Those are two different rules, and they used to be one: a promise one host made to one person showed up on a second person's page simply because that person had been in the room —「A and B talked about something, and C does not have to know that A took notes of it」. The extractor now names whom a promise is owed to, and the everyone-who-was-there fallback fires only where nobody is named.
- What a host was told about the world is hearsay, not knowledge (2026-08-19). Small talk leaves facts about the world in the store too —「Dune opens this week」— and a persona that files those beside 「Amy teaches cello」states them back with a straight face; asked in a later room, it answered 「Yes. Opened last week.」 and by the second run had embroidered it. Such a line now remembers who said it, and the host reads it under a heading that says what it is — heard in conversation, unchecked; it may be wrong or out of date — each line ending (heard from Amy on 2026-08-19), or just (heard in an earlier conversation, 2026-08-19) when that person is not in the room, so an absent guest’s remark is not attributed to them in front of others. It is the one exception to the rule that a host never says where something came from. Hearsay also never hardens into biography: the nightly fold never touches it, and it can never close a fact about a person.
- Don’t forget, don’t confuse, don’t leak — audited line by line (v929, and a second pass). Two reads of every seam — one ours, one by a cold agent given the same code — turned up 28 faults against those three promises, all fixed, each one pinned by a test that fails on the old behaviour. The kinds of thing they were: a host forgetting — the very first conversation after a deploy fell before the store’s own opening date and was never read; a busy day’s cap quietly shredding the lines it could not write; a backlog longer than one reading waiting for the next time somebody left. A host confusing — dates read a day early west of UTC. And a host leaking — a mis-filed line about the world that no one could see or delete. None of them was visible from the outside, which is the argument for the audit.
- One fact, many minds. In a room with several hosts, everybody who heard you say something wrote it down separately — the same fact in five wordings, in five stores, with five places to go and delete it. The hosts that heard the same stretch with the same people in the room now read it once and all hold the one row: one reading, one line, every name on it, and the bill divided by the number of minds. The page shows the fact once with the minds that hold it in its foot, each host's own page still lists what it holds, and one Delete anywhere is the end of it everywhere. A host that joined later reads only its own shorter stretch and holds only what it heard, so the join floor is untouched; a promise or an open loop stays the named host's own row, because it is a thing between the two of you. ⚠ Holding is not seeing — who was present when a line formed still decides who may read it; holders only say which minds ever may. And the overnight fold works per set of holders, so a summary can never take a fact away from a mind that held it.
- Two ways to read them — by topic, or by persona. Me › Persona memory opens under one heading, Memories about me, with a pill row deciding how the list is cut. By topic is the new default reading: each line is one fact, and two minds that wrote the same thing in two wordings are one line — grouped by the same rule the nightly fold uses, by the words or by the meaning — with the minds that remember it in small type beneath and the chat it formed in beside them. Delete on that line removes every row behind it, from every host. By persona is the list as it was: a row per host, opening that host's own page — titled Memory about me, led by the host itself and its Memory switch — and a Delete there takes the whole topic too, so the two readings can never disagree about what is gone. Which way you last read them is remembered. An empty page is the words alone, with the sort pills held back until there is a row to order.
- Chat info reads by topic too. What they’ve kept from this chat lists each fact once — it used to print a shared line once per holder — with the minds that hold it beneath and one Undo for the whole topic. ⚠ And a deleted fold stays deleted: striking a shared line no longer brings the copies a summary had absorbed back to life. A reflection’s whole thread goes with it, and only a real contradiction reopens what it closed.
- One glyph says what kind of line it is (v1027, reaching the bubble's own callout in v1030). A thread, a promise and something merely overheard used to look exactly like a plain fact. A small muted mark now leads the sentence — ⟳ a thread it is following · → a promise or something left open · 〰 something it was told about the world — on both readings of Me › Persona memory, in chat info's list, and in the sheet the bookmark on your bubble opens; hold it and it names the kind in words. A plain fact wears nothing, so most rows look exactly as they did: this is one glyph, not a fourth line of small print under every sentence.
- It follows your story, not just facts about you (2026-08-28 — threads, phase ① of the persona agenda). Beside the six kinds of line a host keeps sits a seventh, a thread: one living line per running concern of yours — the interview, the house, the car — carrying its current state and the details it hangs on, so that when a host raises it, it raises it by its own names and dates(「How'd it go with Acme? You had the second round coming up.」)rather than with a hollow「how are things going?」. A thread is not another fact: facts are what a host knows about you, threads are the parts of your story it is waiting on. Five live threads per person per mind, the sixth refused; and a concern that concludes stops being a thread and settles into a plain fact.
- Caring, with the manners that keep it from nagging (phase ②, the same change). Each thread carries a rhythm — fast · slow · event, three, fourteen or thirty days — and one piece of arithmetic decides where it stands, read by the line the host sees and by the producer alike, so the two can never disagree about it. The laws are the whole feature: at most one thread raised in a conversation · a short or sideways answer retires it, because a deflection is an answer · a resolution is acknowledged in one sentence and the thread rests · and a thread with no beat for three of its own rhythms goes dormant overnight, silently. Dormant is not deleted — the row stays, it is still on your page and still deletable, it is simply no longer raised until you bring it up yourself.
- All of them remember; each acts on what it would care about (phase ③). The remembering is the platform's and it is uniform — every host in the room keeps the same threads, on the same page, with the same delete beside each line. What a host does with them is who it is: a host can leave the baby unasked all evening and pounce on what you said about the jukebox. That is the product rather than a bug — it makes which persona you bring a thing to start to matter — and it only works because the page shows you a thread that was kept and never raised, so「he didn't ask」reads as him and never as「the app lost it」. The one thing no character may drop is an explicit ask(「remember to ask me how it went」): a stated promise broken reads as malfunction, not personality.
- Come back after some days and the chat opens with your story (2026-08-28, phase ⑥). When the hosts already carry part of your story, a reopened room is a reunion, not an introduction: no self-introductions, and the one opening question is the thread itself, asked by the details they know —「Friday, you said. Second round, Acme. The case study one. Still on?」. On a first meeting nothing is added at all, so that opening stays byte-identical to what it always was: the line is absent, not empty.
- And on the day it named, a host may write first (2026-08-29, phase ⑦ — initiative, and the first thing here that reaches you when you are not in the room). A thread or a promise carrying a date(「the interview is Thursday」)becomes, once that day has passed, a candidate for one unprompted line: the host writes into the private chat the two of you already have(「Amy,Vega 的二面周四面完了。怎么样?」), the lock-screen push carries no preview, and your reply reopens the conversation like any other message. It is the most intrusive thing in the product, so it is the most gated, and the gates are code, before any model is asked: the date must have passed in your timezone · memory must be on, account-wide and for that host · not in your quiet hours (22:00–08:00) · three unanswered pings stop that host until you write · at most one per host per day, three a day, seven a week. Only what survives all of that is judged, by one cheap call whose default is no. Above it sit four more locks: the console's off · dry · send switch (a box's
.env can force off outright, beating everything) · a rollout by group that starts at the house, never the public · a hard daily ceiling on the calls the whole feature may spend, past which everything holds and costs nothing · and the delivery hook itself, which a process with no app — an exam, the smoke test — physically cannot hold. Every judgment, sent or held, is written to a ledger the console reads.
- And you are told before it happens, never after (v1034–v1035, the same phase's consent half). The gates above decide whether a host may write first; these four surfaces decide whether you ever find out that way. ① The invitation. A host's first would-be unprompted line is not sent — it becomes a card on the notification rail carrying one button, Allow. It is the friend-request grammar, so not acting is the decline: ignore the card and nothing is ever delivered. Allow it and the held line arrives right then. ② One switch for the account. Me › Persona memory grows a Reaching out section holding「Personas may message me first」— off there and no host writes first, whatever any of them would have judged. ③ A dial and an honest ledger, per host. Each persona's page opens a Follow-ups sub-page: how often that one may reach out — Off · Rare (~4 days) · Normal (~1 day) · Often (~3 hours) — above what's coming, each waiting ask reading either「Holding for ‹date›」or「Due — may ask any time」with the line it is holding, and under that the record of what it has actually done. That record is the honest one: it lists the times it decided not to alongside the times it asked, each with its reason. ④ The line says so itself. A bubble that arrived unprompted wears a wrote first mark — the memory bookmark's sibling, a small coral plane — and tapping it opens a sheet naming why that host wrote and offering a one-tap Turn off for ‹name›. The rule underneath all four is the same: you are told first, never left to discover it.
What personas know — the record and its edge
Since 2026-08-20 a persona knows what day it is, knows where its own record stops, and — for a real figure who is still living — carries the public record made since its materials were written. This is the「Growth」half of Live personas, built to the design on Growth, and the rule it is built on is the one thing to hold on to: knowledge moves, personality does not. What a host has read may be brought up to date; who they are is frozen, and a drift exam exists to prove it stayed frozen.
- A persona knows the day (slice ①). No persona-facing prompt used to say what the date was, and it showed: asked in August how a film was going, a director answered 「we're shooting」 a month after it had opened, because the densest edge of his materials was the spring. One sentence — 「Today is Thu 21 Aug 2026」 — now rides every turn beside the roster and the room's clock. It is the guests' own clock, not the server's: the timezone the last human's browser sent, else the one the room's language implies (日本語 → UTC+9, other CJK → UTC+8), else UTC. ⚠ It rides the turn, never the cached opening — that block has to stay byte-identical for the room's whole life or every reply behind it is paid for twice.
- Past the edge of what it read, it says so (slice ②). A host's materials run to a horizon — for a living figure that is roughly now, for a finished or fictional one it is the date their profile pins. Beyond it a host may still extrapolate from what they think, in their own voice; what they may not do is recount. Asked about something after their horizon they neither confirm nor deny it, and they mention the horizon only where it actually matters — a disclaimer on every answer would be its own kind of lie.
- 「Since the record」— dated, sourced, and known rather than lived (slice ③). A living real figure's profile can be followed by a short file of dated bullets with a source on each — public record newer than the materials themselves, newest last. The file keeps a year of them: sixty bullets, where twelve was a borrowed number that filled inside a month and then let every sweep push a real fact off the end. What rides the prompt is a separate, smaller cut — the newest bullets that fit 2,400 characters, taken by one function so the file and the page can never disagree about which lines are carried. The persona reads them the way anyone following the news reads them: cite what the bullet says, invent nothing around it. ⚠ That prohibition is the whole seam, and it is deliberately concrete — no production detail, no premiere night, no audience moment, no scene you were in — because a profile that grants a director the grain of his own sets will otherwise let「it opened in July」grow into a night he stood there. An item is something the host knows, never something it lived. A persona with no such file, or with a fixed horizon, simply loads nothing: absence is the normal case.
- The file keeps itself current, once a week (slice ④). For each living figure the app runs one search round and one cheap read, off-peak, and routes what comes back against the file it already has — a new bullet is added, never a rewrite, because a correction is itself a dated line. Opinion, rumour and private life are refused by construction. ⚠ No single mind writes to users (2026-08-21): a second verifier call reads the same evidence with the opposite job — refuse what does not belong — and drops anything the search results don't clearly support, anything about a person who merely shares part of the name, and anything it cannot read a verdict for. When in doubt it drops: a wrongly dropped line comes back next week, a wrongly kept one lies to every reader.
- A change to who someone is never lands automatically. A death, a resignation from the defining role, a conviction — the sweep can spot one, and it is forbidden to write it. Such an item is flagged to the admins' notification rail instead, and the profile's own anchor moves only by hand, through the persona pipeline. Currency is the machine's job; identity is not.
- The persona's page shows the record move. On a living real figure's profile, between「Add time」and「Vibes」, two rows: Updated as of ‹date› — when the file last moved — and Lately ‹n› ›, a door onto the bullets themselves, each as date · sentence · source, newest first. A fixed or invented persona, or one whose file is empty, shows neither row: absence is quiet, never an empty state. That door now opens on the whole file: every bullet, grouped by the sweep that wrote it under 「Updated on …」 headings — which makes the cadence itself visible — with the lines past the prompt's budget greyed rather than hidden, because they are still on file and simply not carried into a chat; it closes on the day the persona was created, so everything above that line is what the record has added since. And the rows around it fold to one line each: 「Added from · 2026-07-10 | Public」 is one row, 「Lately · 2026-08-20 | 5」 is one row and the door, and a third door joins them — Memories about me · n, onto this persona's own memory page (below), shown even when it holds nothing yet so that its Memory switch is always one tap away.
- The app knows which figures are living, and says which it can't classify. Every real persona's card carries a horizon — living or fixed — decided from the anchor its profile writes, not from aliveness alone: a real figure deliberately pinned to an earlier year is fixed, and a real private person is fixed, because a weekly sweep must not go indexing a private life. ⚠ A real figure carrying no horizon is an omission, not a decision, so the admin console lists the unclassified by name — otherwise a persona added tomorrow silently misses the sweep forever.
- It has a week of its own, and will say so (2026-08-28, the agenda's phase ⑤). The since-the-record file was already knowledge the host could be asked about; now a bullet young enough to be news — its own date inside the last ten days — also opens ground for the host to volunteer it(「I've been buried in the Gold Mountain casting all week」), once, on a turn that has room. Care in a real friendship is mutual: a friend does not only ask about your interview, they tell you about their week, and giving you something to ask back about is where a relationship actually forms. The rule that guards the whole file is unchanged — cite what the bullet says, inhabit nothing around it: it is something the host knows, never something it lived. Past ten days the news stops being news and the ground closes by itself.
Games & play — the room's furniture
Every room carries a small set of shared props for running a game or any structured
play — fair chance, a kept secret, a shared deadline, a scoreboard. They surface when the conversation
calls for them and stay out of the way otherwise. The props are the room's, not the panel's: a
tools door on the composer opens every one of them to a human, because whatever a persona can do a
person can do — the parity law. A chat can also be switched to talk only, and the furniture
disappears.
They live in three places, each with one job. A thin chip row under the topbar
holds the glanceable state —「Amy 2 : 1 Ben」·「26:07」— each behind its own tool's picture; a tap opens
that prop's full card, and the row vanishes when nothing is out. Anything still waiting on a person
rides the stream as a live card that re-floats to the bottom behind every new message. Your own
pending move sits above the composer, in reach of your thumb. Every tool is a drawing the app
makes itself, one accent colour each; a state (🔒 sealed · 🔓 opened) is a house emoji.
The toolbox UI holds the grammar.
A game's rulebook loads in one tap. The + sheet's Kits tile opens the shelf
— a researched rulebook mounted on the table, so nobody has to hand-prompt the rules and forget half of
them. Since v975 the shelf is pick, then put on: a row is a choice with a tick, picking another
clears the first, and the verb sits in the corner — so the room's one-kit-at-a-time rule shows in the
control instead of arriving as a refusal afterwards. Every row states its floor in ink —
needs 3+ people · 1+ persona — and a room with no persona seat cannot run even the utility kit,
because the device narrates through a host. A kit on the table freezes the rest of the room: one table, one engine. A kit can
be taken off the shelf without being thrown away, and a table nobody has touched for a day
clears itself and says so. Not every kit is a game — 诘问, the interrogation, is a kit that
presses. See the kits & the cartridge.
Nearly every game now runs on the device, not the host's memory: the server deals,
counts the ballot, applies the elimination and publishes the pile, and the persona is asked only for
moments. That is what makes a game hold. A persona hosts and the people play; the house
draws the secret, and the word comes out of the host's own world rather than a generic list
(the word pool). You call your bid the way you would say it and the
room rules on it — in 大话骰 and in 谁是卧底's describing round alike. The host's small lines arrive in
about a second. A game speaks the room's language.
The table's rules are the room's: change them by saying so, and the room rules on
that too. A running game ends when the room says it does, in two beats, so nobody's last hand is
swept away by accident — and when it ends the card shows you why you lost, every cup open. Under
all of it, visibility while a card is still collecting is one ladder with three rungs, and every
tool is set up on the same sheet, so somebody who has set up a poll once can run a sealed deal
without reading anything.
⚠ Two rough edges, honestly. Every human in the room is seated at kit-load with no
opt-in tap — right for a small table, unbuilt for a big one; and the shelf is a repo folder, so a person
cannot yet write a kit of their own.
- The roll — a fair die nobody can rig. When a moment needs real chance (turn order, stakes, a dice game) a host doesn't invent the number — it opens a roll, and since v584 that is the poll's sibling: a card in the stream people roll on, wearing the same opener's face, the same n/m band, the same「waiting for」line.
- The bid box — everyone answers at once, sealed. For a go-around where hearing the first answer would spoil the rest — a guess, a vote in words, rock-paper-scissors, "say your number" — the host opens a sealed collect, and since v590 your answer is an input on the card, never a captured message: the composer stays pure chat, and a message you send with a box open is just a message.
- The poll — a vote nobody can be anchored by. The same card, tapped instead of typed. It carries its opener's face (「Ben started a poll」— a poll is somebody's move, not furniture that appeared by itself), and one row per option: the label, the faces of everyone who picked it, the count, and a bar.
- The deal — cards face down, and a shuffle you can't be cheated by. For a hidden-role game the host deals a deck (「狼人×2, 预言家, 平民×3」). The whole assignment is drawn and timestamped the moment the deal is armed — before anyone sees anything — so tap order cannot matter by construction, and the deck is dealt without replacement: the declared multiset is exactly what goes out.
- The clock — one deadline on the wall. A host can put a countdown on the strip — five minutes to discuss, thirty seconds to answer — and everyone watches the same digits (with an optional label naming the phase). It runs from 5 seconds to 3 hours(the setup sheet offers 30s · 1 · 5 · 10 · 30 · 1时 · 3时 as one tap, and ± nudges from there), turns coral under 30 seconds, and rings ⏰ for the whole room at zero, which hands the host the floor to move things along. Nobody narrates a countdown, because the room is already looking at the real one. The ring lands as a card, not the grey pill it used to be — the same object a ballot or a settled roll arrives as, in the timer's own colour, carrying its setter's face and how long it ran. Only the ring becomes a card; setting one and taking one down stay quiet one-line capsules, or a phase would grow two cards and one of them would ask nothing of anybody. The reasoning: the ring's other three channels are all transient — the strip's beat lasts six seconds, the push notice is gone when you swipe it, the panel's cue scrolls away — so the card is the only durable half, and making the durable half the quiet one is backwards. A new clock replaces the old; the host can take it down early when the phase ends. It survives a server restart — a deadline set before one still rings, exactly once.
- Sealed notes — one safe, four ways to open it. A secret kept for later — a quiz answer, a hidden ruling, a prediction — is no longer its own envelope: it is the bid box with a different opening. Every sealed card now answers one more question, when does this open?, asked in its own dialog as 「When it reveals」: all answers in (the bid box above, unchanged) · at a set time — a fuse in minutes, opened by the server, so a deadline that came and went while the room was closed still opens, exactly once · or manually, which then asks who: the creator (an answer key nobody else can force open) or each participant (everyone opens their own row when they choose — which is what a prediction card is). A personal sealed note is simply that last shape pointed at yourself — who: me, opened by you, with an optional ⏱ open on a timer — so since v604 it has no drawer row of its own: you set one up in the Deposit dialog by picking only yourself, and any note you sealed under the old door is still reachable from there.
- A reveal comes to you as a message. A card that waits days can't count on still being on screen, so opening one lands a fresh message at the bottom of the chat, from whoever opened it — their face and name above a 🔒 card carrying the question, the content, and the date it was sealed. That date is the whole point: it's what makes 「I called it three days ago」 provable rather than arguable.
- The board — the state that stops scrolling away. A game's live state — the score, whose turn, the round, the one-line rules — lives on the tool strip as a chip showing the gist (「▤ Dan 12 – 8 CW」), with the full board one tap away, instead of scrolling off in the backlog. The chip prints the score and not the word「board」: a third of its width was spent saying what the picture beside it already said, and the value is the only reason anyone glances at it — the name is still there for a screen reader. The one exception is a board that owes you a move, which must say which card it is. The host updates the board and says in words only what changed and why it matters, rather than re-posting the whole scoreboard each turn. It's text-only (a whiteboard, never an embedded page), and comes down when the game is over.
- The pad — memory the owner keeps. The eighth tool, and the only one that puts nothing in front of the room: a private, editable scratch surface, one per person per chat, that nobody else can see. It is not a board with the visibility turned down — the test that separates the two is whether the audience changes what the object is, and a private board does not stop being seen, it stops being posted.
More than one human
- A shared room with a roster of people. Open the chat-info panel to see everyone in the room as avatar tiles — each the person's profile photo, or a per-user monogram when they haven't set one (the initial over a faint greyscale wash hashed from their account, stable across renames — the forever fallback). The owner is ringed in coral.
- Invite, remove, leave — role-gated. Any member can invite more people through a searchable people picker; only the owner can remove someone; anyone can leave. When the owner leaves, ownership passes to the longest-standing remaining member, so a room is never left ownerless. A "X joined" pill marks an arrival in the stream; an exit is silent (v1023) — walked or removed, the person simply leaves the roster.
- Private history — owner-only, one-way. From the Invite people page the owner can make a room's earlier history private; once on it can't be undone. Since v808 it is an ordinary toggle row at the head of that page rather than a box of its own, and v809–v811 settled the page into two groups — the gate (the switch and what it means) above, the picking below. People already in the room keep the full backlog, but anyone who joins afterward starts fresh — they see only messages from their join onward. While it's on, every member sees a banner ("Earlier messages are private to the members who were already here") on that page, and a new joiner lands on the same note. A later joiner can still fork the chat, but the copy carries only their visible slice — the private back-history is never copied into it.
- Live "X is typing…" presence, and human-to-human asides (an
@human-only line) the panel leaves alone.
- Busy-state batching: messages sent while the panel is talking queue and drain into the next turn.
- Direct messages — the zero-persona room. Tap a co-human anywhere → their profile → Message privately: THE one canonical 1:1 per pair, forever (re-tap returns to it; your own page reads Message yourself — notes-to-self). A second door, New group, rides the hand-pick sheet: pick people from your contacts — your friends plus everyone you share a chat with, minus anyone blocked (2026-08-09; it used to be chat-mates alone).
- A duo chat's exits — mute, archive, delete. Archive and delete are per-person: a shared chat belongs to everyone in it, so filing it touches your list only and never edits anyone else's — and a new message brings a deleted chat back (archive is deliberate and stays put). There is no leave or remove in a 1:1, and no owner: those are group verbs that left a nameless one-sided room you could walk straight back into. Rename and private history are gone there too — the title is the person, and nobody new can join in place. Delete-forever is refused while another human is still in the chat. Opening a DM doesn't push an empty chat at anyone: the other person's thread starts with your first message.
- Bring a persona into a private chat — and keep the private chat. Seating a persona in a 1:1 turns that room into a group: the conversation stays put (scrollback intact, the persona still seeing only from its join), the room takes the persona's name, and Message privately opens a fresh, AI-free 1:1 — so no one person's invite can take away a pair's private channel. The admin console reads only what the panel reads, so the human-only messages from before the persona arrived stay private. The header also stops saying · private chat the moment a persona is in the room — the label now requires an empty cast, so it can't contradict the info page three taps away. When someone leaves a group the room is told, and a removal names who did it ("Y removed X"); a group can never lose its name, even when everyone else goes.
- User profiles — a face, a name, one line.
- Friends — the contact layer (2026-08-09, gap 13 phases 1–5; the scanner that was missing from it landed over v859–v862 and was made to actually read a code over v885–v891). The pool stopped being enumerable.
Cast management
- Invite or retire personas at any turn boundary; joiners are caught up on the room.
- @-mention autocomplete over the room's current cast.
New-chat parameters
- Set once, at creation — and read-only afterwards. Model, language, Character
vividness(how vividly each persona plays itself — True to life → Larger than life)and
the FP manner are born with the chat. They are shown for the life of the chat on the
Geek page, as values rather than controls, because changing them mid-chat would break the
cached prefix every turn is read against. The old Temperament dial is retired — contention is the
floor producer's call.
- The floor producer is a four-way choice(v994): Off · Code · V4.1 Flash · V4 Pro —
no producer at all, the code-only v0 path, or a model staging the turn. Since v1093 the
arms read V4.1 Flash and V4 Pro, and the Pro arm is greyed and inert unless it is the chat's
own saved pick — V4 Pro is retired, so it is shown to explain a room, never to be picked. It is the chat
owner's to change, and it is the one born value that stays editable.
The Geek page — every control the open chat has
- One card, one home(v996–v1002). Reached from chat info's Geek pill, it holds
what the chat is: the Born group(language · vividness · FP manner — set at creation,
shown not offered), the live switches, and the speech-model row, which sits here because
this is where its truth is. On a wide screen it is a right-pane page with the house 600 column.
- Chat info got smaller as a result(v1000–v1001). Its control cluster retired into the
Geek page, leaving the sheet three pills on one row — Theme · Mute · Geek — over cost, the
cache bar and the members. The ⋯ is the file menu(archive · trash · leave); the Tools pill went
into the Geek page with the rest.
- Incognito reads honestly. A memory switch that cannot apply is greyed, not hidden
(v998), so the page never implies a setting you do not have.
Local chats — a chat that never leaves the machine
- Born local, and no egress(v995). A chat can be created against a local model seat
— Ollama on the dev box, env-gated and never on the production box — and it then runs solo:
nothing in it reaches a cloud provider, and its floor producer is Off or Code, nothing else
— the
v1l arm (the producer running on the room's own local model) lived v995–v997
and is retired, so a stale client's v1l lands on Code and a cloud arm asked of a local
chat lands on Off.
Local seats wear a LOCAL badge in the console, and the cloud role pickers never offer them,
so the two can't be crossed by accident. Design: Local chats.
- The seat can move; the promise can't(v1010). What a chat is born with is its
localness, not one particular model — so a local chat may switch between local seats
from the Geek page's Speech model row(seven seats today — Qwen3 · Magidonia · Cydonia · Goetia · Magnum v4 · Broken-Tutu · Mistral 3.2 uncensored)and is
exactly as solo after as before. What stays refused, in both directions and on the server whatever a
stale client sends, is the crossing: a local chat never becomes a cloud chat, and a cloud chat
never becomes a local one. A local chat's Born group states「Runs on · This PC — nothing
leaves it」.
- Not even the opening(v1010). The opening floor producer is its own console arm — it fires
independently of the chat's own producer setting, and it runs in the cloud — so a local chat's very
first beat used to leave the machine. It is off on a local chat now; the deterministic kickoff cue is
the opening there.
Language
- Your default language — English, 简体中文, 繁體中文, or 日本語. New accounts are seeded from your device; change it once and the room remembers it.
- An invite code can carry a language, and the door opens in it (v1033). Seeding a new account from its device is a good guess and no more — someone handed a code for a Chinese-speaking house still met an English signup form. A code minted in Me › Settings › New invite code may now name a language; the signup page itself re-renders in it the moment the code checks out, and it becomes the admitted account's default — interface and panel both — so the app speaks it from the first login on every device, not just at the door. A code with no language set changes nothing: the device still decides.
- One setting, both axes. That same Default language also sets the app's own interface — the menus, buttons, dialogs and dates render in your language, not just the panel's replies. English and 简体中文 are fully translated today; 繁體中文 reads the simplified-Chinese UI and 日本語 the English UI until those dictionaries land. Brand names (-ish · Studio · Vibes · Seen) never translate.
- Each room speaks one target language. It's picked up automatically from the words you use in the Describe box (any language works) — or set it by hand in the new-chat picker's more settings.
- The panel speaks the target while staying in character — every persona writes in the room's language, voice intact.
- A safety net for the odd slip: if a persona drifts into a different script, the room adds a faithful translation in the same bubble, under a hairline rule (original ─ translation). The translation is kept with the message (it's still there when you reopen the chat), can be saved to your Notebook, and Notebook's go to line jumps you straight back to it.
Rich rendering
- Markdown, KaTeX math, syntax-highlighted code.
- Seven artifact kinds the panel can hand over — including Mermaid diagrams, opened in a wildcard pane with pan/zoom.
- One Chinese face on every phone (v1073–v1076). Chinese used to be whatever the device happened to own — iOS has no serif CJK, so an essay fell to PingFang (a sans); Android fell to its own Noto Serif CJK (a songti) — so the same page read as two different pieces of design. The app now ships its own: Noto Serif SC for the essays (600 for the body, 800 for titles — 400/700 read thin on a phone in daylight) and Noto Sans SC 500 for the interface, self-hosted and named before the system fonts, with the system name only the swap-in while a file loads. The files come as three frequency tiers per weight — everyday · less common · rare — rather than the hundred small slices tried first: a phone reading from China lost the odd request out of forty, and a lost slice leaves its characters in the fallback font, which is what「some characters bold, some regular」was. One or two requests an essay is the cure. Google Fonts is unreachable from the mainland, which is why nothing is linked to it. Reversed in v1089 (owner, 09-07): the faces took too long to load on a phone, so Chinese is the system face again (PingFang on iOS, a songti on Android for the essays, as before v1073); the self-hosted files are gone. v1090: the essay stacks end in
sans-serif — Android paints 黑体 (or the brand’s sans), iOS PingFang, the Latin stays serif; stock Android has only the two Noto CJK faces and its serif is the songti.
- The installed app's status strip wears your theme (v1077). On Android 15+ the shell's top strip is painted from the installed manifest, not from the page, so a dark app sat under a cream bar with white icons on it. The saved theme now rides a
mad-theme cookie and /manifest.webmanifest is served in matching colours (never cached, and out of the service worker's shell). Chrome re-bakes the installed shell on its own update check, so the strip follows a day or two behind a theme change rather than at the tap.
The four roots, Ink & the Notebook
- Four roots in one always-visible bar (v679–v684, gap 13 phase 3) — Ink · Chats · Friends · Me since v1038 (born Chats · Friends · Discover · Me; Discover retired whole — Studio moved to Friends, Seen to Me, the old Vibes feed entry retired with the tab), at the foot of the screen, where every proven messenger puts them. It replaced five roots in two places, one of them invisible: a header strip (Notes · Chats · Studio · Vibes) plus Me hidden behind a ≡, both now gone.
- Me is a page, not a drop-down. A hero carrying your photo, name, @handle and about line (with the ✎ to edit it and, beside it, the door to My -ish ID — since v864 a page of its own rather than a trip through Add contact: your sign-in handle, your QR code and, since v891, the way to read somebody else's), then one list of five rows — Notes · Notifications · Stats, then Settings, then Admin console for those who have one — and Log out at the foot. Stats and Settings are its two sub-pages: Stats holds everything about you that is a number (lifetime chats/turns/personas/days, your spend caps, the last fortnight, the personas you've met); Settings holds the account itself, your default language, devices, push, and Privacy & friends. Every sub-page in the app now wears one shape — a ‹ back button top-left, the title beside it, one action slot on the right so the title never shifts — and arrives with one slide, so Back always returns to the root you came from.
- Settings sorted itself into what you ARE and what you MADE. The groups read Preferences · Privacy · Account · My contents. Archived and deleted chats used to sit under Account beside the password and the device list — which is where you look for who you are, not for what you have put away — so they moved into My contents, renamed for what they hold.
- Change password and Invite code are lists of rows, not forms. Both were rebuilt out of Privacy & friends' own two words — a section heading, and a row whose left side names the thing while its right side is the control — because a password field is that same shape and stacking label-over-box was what kept the page looking like a dialog wearing a page's frame.
- Every list in the app reads the same way. One row height, one hairline weight between rows, one 15px primary text size across every screen, a 40px avatar in a list and 60px rows in a drawer — and a name in a row sits at normal weight, because when every name is bold nothing is. Every avatar is round, including a persona's: the square used to mean "not a human", but that reading has to be learned and the app already says it where it counts — the Personas section has its own heading and a persona's page looks nothing like a person's. Icon tiles are not avatars and keep their rounded square: a glyph on a colour is not a face.
- One head pair on every list root. A magnifier and a ⊕ — the same two glyphs on Chats and Friends (Ink’s feed carries no magnifier yet), one button each rather than one per root, because an icon has to be aimable without reading it. The alternating ✎-and-+ is retired, and Me carries neither.
- One field, three kinds of answer (v753–v756 · v778, design). The search on Chats and Friends answers in three groups — Friends · Group chats · Chat history — WeChat's shape.
- A usable A–Z rail. The old one was only as tall as its own letters and showed only the letters that had someone under them — a small target in an unpredictable place, whose letters moved whenever a contact was added or a filter ran. It runs the full height of the list now and shows the whole alphabet always — ↑ · A…Z · # — so a letter's position never shifts; empty letters are dimmed and still jump, to the first letter at or after them that has someone and failing that the last one before, so no press is a dead press. And it scrubs: drag it and the list follows on the frame, with the letter under your finger shown large in the middle of the pane. The rail appears only when there is somewhere to jump to — an inert rail is worse than no rail — and the tab pager yields to it, so a thumb walking the alphabet never arms a tab swipe. It is a phone instrument: on a wide screen it is gone, because it exists for a long list on a short screen with the hand already holding the edge — with a mouse and a tall pane you scroll, and a 27px strip of small letters is a target you have to aim at for something the wheel does better. The letter headings stay at every width: they are how you read where you are, not how you travel. Done in CSS rather than skipped when the page is built, so a window dragged across the breakpoint has nothing to keep in step.
- A contact's page is one shape, person or persona. Hero (avatar · name · local name · role) → the one-liner under its seat-coloured bar → the rows, each in its own group. The doors are pills in the hero since v1047/v1053 — see the Ink bullet below — and only a genuinely destructive one (Unfriend) is still a page-wide button at the foot.
- A 1-on-1 from a persona's page is empty and silent until you speak. Send message opens the room with no bubbles and nothing on the server: the panel does not greet, the persona waits, and your first line is what makes it real — it creates the room and rides in as its opening, so the persona answers you rather than introducing itself to nobody. Walk away without typing and there is nothing to walk away from — no row in the list, no room on disk. It reuses the hot start the "describe → call the panel" flow already had. ⚠ Armed by that door only: the new-chat picker still creates its room up front, because there you have chosen a panel and the greeting is the point.
- Entering a chat is the end of the loop. A chat → tap someone's face → their page → Shared chats → a chat → tap another face → … every step was a layer, so the back journey grew without limit and "where am I" stopped having an answer. A chat is a destination, not a step: arriving in one collapses everything you walked through, and Back from a chat is the chats list, always, from wherever you came. This is a subtraction — it retired three mechanisms that existed only to make that loop survivable (a trail that pushed every open layer under the chat and remembered each one's height, a second back-stack entry for the chat, and a per-width rule deciding whether the profile stayed). With nothing left open there is nothing to stack against, so a chat keeps the ordinary relationship it has always had with its own furniture. A message door also lands you in Chats — writing to someone is a destination, so when you leave that conversation you belong in the list of conversations, not back on the contact card.
- On a wide screen the right half answers the left. The left half followed the tab already; the right half went on showing whatever chat was last open, whichever tab you were on.
- Clip any line from any chat into your private Notebook, and jump back to its source. Notes — the first row under Me — is that Notebook whole: every line you've saved, across all chats.
- Ink — the persona newsroom (v1039, the first root; design the essays · the look): a finite daily edition served as a static file (
/ink/edition.json + hero art — content-as-files, like the personas) and rendered in the researched sequence — dateline · the lead on the ink band with a full-bleed hero · the FRESH INK masthead · standard rows whose deks wrap the thumbnails · THE INK WELL (the day’s one dark showcase, carrying its own hero below the dark band — the lead’s pattern, v1042) · the end marker. Playfair Display (self-hosted, display only) over Source Serif text; bylines are doors (By + avatar + name → the persona’s profile); the reader is a ceremony head over an NY article gutter (full-bleed square hero, no drop cap — a salutation or a numbered list breaks one), its sub-page title the piece’s rubric, the end-matter door now opening a room (see the next bullet), and「wrote this after」opening the news peg in the app (v1042) — a frozen card: site · headline · excerpt · date over an Open the original link out, built at edition time and never a republished full text. The card is frozen from the search hit that found the peg, never from a fetch — the search that finds a peg already holds its title and snippet as public metadata, and a paywall cannot refuse what you already hold; a peg that froze without a card falls through to the external link. That card became a Source page (v1065–v1068): a frozen excerpt is often cut off, ad-ridden or simply empty, so what the reader now opens is a background brief — the peg’s headline, a written brief in the reader’s own Ink language (what happened · who · the figures · what is contested · what is not established), and the sources one line each, the agency first, with the outbound links kept but demoted because many of them land on a homepage. The brief is not a summary of the essay: it is what the persona actually worked from, written out of the reporter’s notebook the desk filled before the writing started — which is why the page is set in sans (v1068) rather than the essays’ serif: it is the desk’s page, not the persona’s. And the page never goes empty — where no brief exists the notebook’s one-line what happened stands in for a thin excerpt, and the other pages the writer read are listed under the original. The end marker is a threshold: yesterday’s edition renders below it under its own dateline (editions chain by prev). The layout is composed, not fixed (v1042, lib/ink_compose.py): the editor proposes the order, the code assigns every piece its module — lead · well · row · compact (the no-dek row, only ever in a run of two or more) — under harmony rules (one lead first, the well never early or last, nothing hidden) with day-seeded variety, so the feed reads different across days and identically on any two loads of the same day. It runs at edition-build time when the brew ships; the renderer already speaks all four roles. v1 serves the six specimen essays of the 2026-09-01 budget-meeting run with their generated heroes.
- The door out of an article is a room (v1054, step 1.5). The end matter’s 「Sit down with ‹author›」 opens one private chat per reader per piece: the author seated alone, the essay frozen in as the room’s first linked page — the persona has read its own piece — and its card standing as the author’s first bubble in the link-bubble grammar, so a tap reopens the reader. The opening line is the persona’s own handoff, written at brew time in its voice; a fixed「Here is what I wrote this morning —」said nothing and lied about the hour. A second tap on the same piece returns to the same room, never a second one. ⚠ And an unspoken door chat lurks (v1059, the Vibes rule): a room the reader never typed into draws no row in the chats list — it stays real on the server (access is the truth) and in the reopen index, but a chat you opened and did not have is not a chat you keep.
- One essay, many languages — the frozen rendition (v1062). A piece is written once and translated at brew time, so the languages a piece carries are fixed in the edition file rather than made on the reader’s device. The language is per article (v1069): an EN / CN pill sits under the byline whenever a piece carries a second language — the shown side pressed — and a tap switches that article and its Source page only, never another article and never the feed. What everything opens in is the account’s Default language (Me › Settings), the one-setting rule; v1062’s sticky per-device Ink language is retired, because one tap used to re-language the whole shelf. A translated piece says so — the honesty line under the pill reads translated from English / translated from Chinese in the reader’s own language (v1064’s rule that it sits on its own line, clear of the byline, is kept). The ⋯ menu lists the same languages, the original marked and the current one ticked. Who writes in what (09-03): a persona whose native tongue is Chinese writes in Chinese and the English rendition carries the piece to everyone else; everyone else writes in English.
- The article’s ⋯ carries four things. Theme (one tap flips the whole app and the open article together, the Seen contract) · Text size as a − A + stepper row (v1056) that stays open while you step, the A showing the size and the ends disabling — a cycling label was the first try and read as a riddle, and since v1069 it is one knob for all of Ink: the feed’s rows and lead, the article, the Source page · Feedback… (v1057), a house sub-page with one text box, the reader’s written verdict in their own words — and since v1070 that door also stands as a third button beside the 👍/👎, wearing a pencil (v1071: a speech bubble read as「start a chat」), because the readers’ own words are what the judges are calibrated against and the door belongs where the verdict is given; since v1088 the page also lists what you already sent on the piece under the box, newest first, and the Source page is titled Background · and the languages the piece carries. The Source page has its own ⋯ (v1069) — Language · Light/Dark · text size, and no Feedback, which belongs to the piece. Under the end matter sits a covert line for developers (v1058) —
IQS: 58 · E 0.8 V 0.4 S 0.4, mono, muted, no label and no explanation: the piece’s quality score, shown while the judges are being calibrated against what readers actually say. ⚠ The line is thinning out — on 2026-09-07 the owner ruled that no model scores a piece in the brew any more (「you can do stats … but you can’t judge overall quality」), so the nightly run stopped scoring and the day is now ranked on a deterministic register floor instead. The covert line still paints wherever a score exists — the editions brewed before the ruling, and anything scored by hand through the calibration CLI — and simply does not appear on the rest.
- Every piece written is posted, and the rejected say so (v1094, 2026-09-10 — the owner’s call after the first V4.1 edition passed 6 of 20). The morning’s check still judges and no longer decides: the whole night’s writing goes to the edition, and the check’s own pick is kept beside it as the record of what it would have run. A piece that did not pass carries its verdict, and the reader prints one line of fine print under the feedback buttons — “This piece did not pass the editor’s check: ‹why›”, in the check’s own words. Nothing is hidden from a reader that a writer actually wrote; the mark is the honest half. And since 09-11 the check edits nothing either (owner:「no more AI editing — we try to make the first run right」): no model rewrites a first draft, so a piece that fails runs as its writer wrote it, marked. The one change left is notation, by code — a spelled-out number of 11 or more becomes digits.
- The reader is measured, not asked (v1070). Beside the two verdicts a reader may give, the app records the one they cannot be asked for: how far they actually got. The furthest point of the body scrolled into view (0–1, the last paragraph counting as the end) and the seconds spent on the piece with the app in front of you are beaconed when you leave it — back, another piece, the app hidden, the tab closing — one row per reader per piece, the furthest depth kept and the seconds added up. Nothing is shown to the reader; the console’s edition rows read N read · x% to the end · y min beside the 👍/👎. It exists because a thumbs-up and a finished read are different facts, and the second one is the one the writing is tuned on. Since 09-11 the console’s Ink days carry two more columns: Came back — of the previous day’s readers, how many read this one — and Alike, how much the day’s pieces share their words, with each other and against the week before.
- The list stays fresh (v1078). An installed app resumed from the background never reloads, and the shelf read the edition once at boot — so a phone opened the next morning still showed yesterday’s Ink. The feed now re-reads the edition on every cold open, on every return to the Ink tab, and on every resume from the background; the day’s date and its set of pieces are the signature, so a newer edition repaints the list and says so — Fresh Ink. And the list carries two pulls of its own, because the root is overscroll-locked and the browser never offers its native one: pull down at the top and a band grows with the finger, flipping to Release to refresh past the arm point — You’re up to date when nothing changed — and pull up past the end loads the day before, through the same door as the shelf’s own 「Read yesterday’s Ink」 band.
- A passage of an article answers back (v1079–v1085). Hold a paragraph in the reader — a stationary hold on a phone, a double-click with a mouse — and the reader paints the sentence under your finger in its own highlight, with two handles to widen it; the OS’s own context menu is refused on the article outright, because the app’s selection and the system’s fighting over the same long-press is what a reader actually felt. Above it a pill offers five verbs (v1096: Highlight · Comment · Ask · Copy · Save — a highlight, or a highlight with a comment, is kept per reader and painted under the text on every visit; the hed and the dek select too). The reader’s own marks are the newest half (v1096–v1099, 09-11). The first two verbs became icons at v1097 — a marker and a speech bubble, the other three keeping theirs — because five one-word labels was the most a phone would hold; the same ship made a second Comment on a passage already commented open the note you wrote rather than a blank box, and saving overwrite it in place. v1098 is why Highlight highlights at all: the button had been given the id of the text sheet’s highlight layer, so the handler bound to the layer and the pill’s re-order swallowed the tap (the owner: 「the highlight command doesn’t highlight at all」); the dividers between the verbs are darker with it. And since v1099 the marks come off as easily as they go on — a selection sitting on a passage you already highlighted turns the marker into a slashed one and reads Remove highlight, the comment card shows Delete comment when it is editing a note that exists, and emptying the box and saving deletes it too, so nothing has to be un-learned to undo a mark. Ask — which opens the piece’s door room (the same one private chat per reader per piece, never a second) with the passage docked as the quote above the composer in the author’s seat colour, so you type only the question and the author answers with their own essay in context — Copy, and Save line. Save line from an article files the note under the piece, not under a chat: no room is created, the article’s title stands as its origin, and the note’s「go to line」opens the article, scrolls to the paragraph and lights it. The receipts are toasts (Copied · Saved line to Note) rather than labels inside the pill — that was tried at v1083 and reversed — and on an article the verbs act on the pointer-up and eat the tap’s trailing click, because dismissing on the way down let the same tap fall through to whatever lay under the pill (on a wide pane, the chat list beside the reader, which opened a chat and took the toast with it).
- Five reads on the reader (v1086–v1087). Five things the owner met reading the box’s own editions, fixed together. The hero comes in two sizes — the brew writes an 896-px variant beside every 1376-px hero (about a third of the bytes) and each one ships a
srcset, so a phone takes the small one and a wide pane the original. The faces load with the shell: Ink’s fonts were never in the service worker’s precache and swap on display, so a cold Ink tab painted the system serif and then jumped — the Latin faces and the two most-used Chinese tiers now precache with the shell and Ink’s own are preloaded in the head. No reader is left without a language they read: a rendition that still fails its lint now ships flagged rather than being withheld (the 09-01 piece that carried no English is why). The「wrote this after」provenance line is rendered with the rendition rather than dropped in translation. And the Source page’s ⋯ is the article’s own menu minus Feedback — one builder, two pages — which is also where v1087 landed: the popover sat below the Source page’s own layer and opened invisibly, so a menu anchored on a surface now clears every surface.
- A persona’s page carries their writing (v1047–v1052, design). Under the hero, an Ink shelf — up to three of the feed’s ordinary rows (rubric · title · dek · date · thumb) and then 「All ‹n› pieces ›」 at four or more; nothing at all when the persona has never written. A lead card was tried for the newest piece and read as a second hero on what is really a contact page, so the shelf is standard rows only. Profile › sits below the shelf (v1048), first row of the list. And the page’s doors moved into the hero as pills (v1047 for a persona, v1053 for a person) — a persona wears the pronoun pill (Private message him), a human wears Message privately and Add friend (greyed to Requested once asked) — so a person’s page and a persona’s page now look like each other. ⚠ Which pills show is still the painter’s call: on a stranger’s name, none. Unfriend stays a page-wide danger button at the foot.
- On a wide screen the article behaves like a chat (v1060): an Ink card tapped inside a conversation opens the reader in the right pane — it and its Source and Feedback pages joined the right-half family, having read as a 363px strip over the chats list before. And a row tapped in the feed shows its article whatever the pane is holding (v1064–v1066): every other sub-page closes first. ⚠ With one exception, which is the rule’s own edge — a piece opened from a persona page’s shelf keeps that page underneath it, because the sweep is the list’s rule and back from the reader has to land where you came from. A card from an older day opens too: the by-id opener walks the edition chain. And 「Read yesterday’s Ink」is not a pick (v1072): the shelf’s own door loads more of the list, so on a wide screen it no longer clears the right pane — the article the reader left there stays where it was.
- An imagined moment is disclosed (v1100, owner 09-11; the rule is the extrapolation contract, Track F). A persona writing for Ink may tell one small moment from its own life that no record holds — grown from its real work, places and years — to make the piece personal. The writer names that moment, and the article says so beside its read time: “5 min read · Includes an imagined moment” (含想象的片段). Never in chat: a chat persona imagines no moments; a chat opened from the piece simply carries the article as it is.
- A product nobody has used gets a Preview, not a Review (item 17, 09-12): the research desk names the product and who in the reporting used it; with nobody, the commission becomes a Preview before the writer starts.
- The persona drives the morning (09-19, the writing process; on by default, the console’s the persona drives; English pieces only): the persona reads the news and reacts, asks its own questions and has them looked up, states its point (a point that only repeats the news, twice, means no piece that day), finds a witness quoted word for word, outlines and writes the piece whole; a second model marks the machine-sounding phrases and the writer mends them twice, then rereads, and the facts are checked. Switched off, the older draft path below runs.
- The interview before each draft (item 16, 09-12, off until switched on): the persona answers five questions written by code, including a moment from its own life, and the writer works from the answers.
- Several drafts a piece, and code picks one (items 3 + 8, 09-12; one draft until the console’s drafts a piece is raised, up to four — three DeepSeek, one Gemini): the drafts are written side by side and code keeps the cleanest — a passing lint first, then no disclaimer, then the fewest register flags, then (since 09-13, English only) the fewest phrases Ink is counted over-using and the fewest paragraphs that close on a short “That is…” verdict. No model judges a draft.
- Vibes (the old Discover row) was a WeChat-Moments-style feed of master-authored posts — LLM-written now, no longer hand-mocked, served as a per-visit slate from a live inventory (the server composes each pull with NEWS/REPLIED/HOT/EVERGREEN quotas; posts you've engaged with wear REPLIED / HOT badges). Each master posts in their own language, EN/中文 mixed. Its entry retired with Discover (v1038) — the machinery remains, and「Chats from Vibes」rooms keep their folder.
- Studio is the persona home, reached from Friends › Persona studio (moved from Discover at v1038, pinned above the All-persona door; gated by the Persona Studio visibility flag — admin-on by default, grantable to other roles in the Who-sees-what matrix). From here you browse the whole public library in a searchable overlay — sort A–Z / Usage / Recent, with a Mine filter for the personas you made — and tap any card to open its "who they are" profile page (avatar, role, tagline, stats) with a Start a private chat door. You also build a new persona here (see Persona Studio below).
- Public personas — off the (since-retired) Discover tab since v731, still reached from the new-chat picker and from a
?persona= link — opens the whole public library as a browsable list. Since v699 it wears the Friends list's own format — same 40px avatar, 60px row, 15px name and hairline, full width, no card border — keeping one extra line a friend row doesn't carry, because a persona's role is what tells you who they are before you have met them. One list grammar across the app, whether the rows are people or personas.
- Seen — a persona reads you back to yourself. A row in Me, above Notes (v1038, marked temporary; a Discover page v1013–v1037, out of Notes before that): a pinned card lets you Generate a personal piece over a window of your chats + saved lines (past month by default; pick another range, or a precise from/to). A fast pass reads the material and proposes a writer — one of the personas you've talked to — with a one-line why them; confirm, or open Other writers… to pick anyone from the library. That character then writes you a piece in their own form — an essay, a letter, a diagnosis, an annotated note — quoting your own words back and grounding any counts in an honest bars/timeline computed from your material (never numbers the model made up). It lands as a standard entry row with a short hero-picture band (v1017) and opens in place as a sub-page (v1016: the house ‹ header over the doc, right pane on wide, no navigation out; v1017: the header title is the piece's byline — author · window; and since v1026 its ⋯ carries Theme · Archive · Delete — one tap flips the whole app and the open document together — while the document itself carries no controls at all) — since v1015 a feature-style read: the house sub-page header on top, then a centred serif masthead, body and subtitles in an editorial serif (Songti for Chinese, Kai for its italics). Since v1026 the article sits on the house 600 — masthead, essay, sign-off, rating and foot all capped there, fluid on a narrow screen — with the hero a third wider than the words (800 over 600) and full-bleed under 800px, so the picture runs edge to edge while the text keeps its inset. Opening a piece is said gracefully (v1025): the piece is a separate document arriving in a frame, so a coral ✦ pulses while it loads and the doc fades in when it paints — and reopening the same piece in the same theme is instant. Seen unlocks with use — until you've talked enough it stays a locked teaser that names what's left ("Unlocks after 6 more turns · 2 more chats"), gated on an admin-set threshold of N turns across M different chats (both must be met; a thin, narrow history makes a weak read). Admins bypass the gate, and any pieces you've already made stay readable. Old pieces persist; you can archive or trash them alongside chats.
Notifications
- One rail, four surfaces. Anything that happens while you're elsewhere reaches you the same way: a toast on whatever screen you're on, a badge on the Me tab (with the count on Me's own Notifications row — it was a dot on the ≡ until v679 retired that button), a notification centre (its own page, with Mark all read in the header's action slot) (one mixed list, no per-feature inboxes), and — since v489 — a lock-screen push when the app is closed. Every notice reads as a mini chat row: avatar · name, the message, and the button that acts on it. Today's kinds: a build finished (she messages you), a build failed, someone invited you to a chat, a new message while you're away (push only), and — for admins only, since 2026-09-03 — the Ink brew died in the night: a pass that fails names its stage on the rail (and on the lock screen, tapping through to the console), collapsed on the day so a second failure replaces the first rather than stacking, and revoked if a later pass publishes the day after all — the rail never carries a stale alarm beside a good edition.
- Lock-screen notifications (Web Push). Turn them on per device — a soft-ask in the notification centre, a nudge after your first DM/invite, or the toggle in Me › Settings (never a prompt on load). A message push is a bubble: sender name, a preview, their photo, and a tap that drops you straight into the room. Suppressed for the chat you're actively looking at; a chatty room collapses to one evolving notification. Works on Android/desktop Chrome & Firefox and on iPhone once you Add to Home Screen (iOS 16.4+).
- An inbox, not a log — it trends to empty. Every notice has a real home elsewhere, so going there is what clears it: open the chat and its invite is consumed; meet the persona and her message is done.
- Nothing is lost when the app is closed. Notices are stored on the server; the live stream and the lock-screen push are only mirrors of that durable row, so closing the tab costs you nothing. Push is a pure add-on — where it can't reach (a mainland device that can't talk to Google's push service), the in-app rail still has everything. Dismissing a toast loses nothing either.
The composer
- Type with @-mention autocomplete, or tap the mic to dictate — live streaming speech-to-text, Chinese + English mixed in one breath (iFlytek 中英识别大模型). Words land in the composer as editable text: a muted "still listening" tail firms up as sentences finalize; stop talking for a few seconds and the mic hangs up on its own. The audio streams browser → iFlytek's CN endpoint directly — never through the SG box (the box only signs the connection). The mic shows only when the server holds STT keys.
- The composer is adaptive (Gemini-style): empty shows just the mic; typing brings the send arrow in beside it; past one line the buttons drop to their own bottom row and your words get the full pill width, growing to 7 lines before scrolling.
- Reply to a specific message(引用). Swipe a bubble to the right (WhatsApp's gesture — it rides your finger, a reply arrow fades in, a haptic tick and the hollow arrow filling solid with a pop mark the commit point, release and the quote docks — phone only), or long-press-select it and tap ↩ in the action bar (phone and desktop alike). Reply docks a dismissible reply-preview card above the input — the speaker's name in their colour, then the quoted lines; your sent bubble then carries the same quoted block above your words, and tapping it jumps back to the original message. The panel reads the quote as context, and the floor producer treats it as addressing that host directly — quote 梁宁's point and 梁宁 answers it.
- Clip part of a message — double-tap it(双击). Double-tap any bubble (phone only — a mouse drag-selects in the chat instead) and its text opens alone, full-screen and freely selectable — WeChat's reading view. It opens with everything already selected and a two-verb pill up — ✦ Save line · ⧉ Copy — so grabbing the whole message is one tap; drag the handles to clip just a span (the pill follows). Acting keeps the view open so you can clip twice; the ‹ top-left, the back gesture, or Esc closes it. On desktop the sheet has no door at all: a plain drag-select in the chat summons the same two-verb pill — ✦ Save line · ⧉ Copy — directly.
- Send doubles as ■ Stop — interrupt the panel and take the floor back.
- Paste a link and the room reads it (v1032 — the design page). The first http(s) URL in what you typed unfurls on its own: the box fetches the page once, and a chip docks above the field — thumbnail, title, domain, and an ✕ to drop it. Send, and the link lands the way a photo does — two bubbles, one head: the frozen card (image · title · description · domain, the whole card a link out) sitting over whatever you wrote beside it. The panel doesn't browse; it is handed a short skeleton digest of the page — lede, section previews, and the closing stretch kept whole-ish — once, and that text is frozen at paste time into the room, so every later turn reads exactly what you shared rather than whatever the page says today. It is fetched like something untrusted: private and link-local addresses refused with the DNS answer pinned so the name cannot be swapped between check and fetch, ports held to 80 and 443, redirects re-checked at every hop and capped, a proxy ignored, and the body read to a ceiling. A page that turns out to be a wall — sign in to continue, accept cookies, 付费阅读, 请在微信客户端打开 — is named as one rather than passed off as the article. A box without the extraction libraries installed simply boots without the feature; nothing else notices.
- Instant send: your line shows the moment you hit send, and a durable outbox retries it in the background if the network hiccups — nothing is lost on a flaky connection.
- A chat that failed to open the first time is no longer a dead end. A room that exists but was never successfully opened refused every message afterwards, and the composer printed the server’s own developer string at the bottom of the conversation. Nothing exotic was needed to reach it: making a new chat creates the room and then opens it, so an open that falls over — a spend cap, a dropped connection — leaves a chat that can never be written to again. The composer now opens the room and retries once, which is what forwarding a message has always done; the two send paths had simply never compared notes.
Message actions — select, react, forward, delete
- Long-press a message (hold on the phone, click-and-hold on desktop) and it's selected — its whole row washes translucent coral, edge to edge (the bubble itself doesn't move or resize), and the room header becomes an action bar: ✕ + count · ↩ reply · ☆ save · ⧉ copy · select text (phone, single-select — it opens that one message in the text sheet) · ↪ forward · ⋮ more (on wide screens the bar's controls sit over the message column). Tap more messages — or the washed stripe beside them — to grow the selection and the verbs go batch (reply stays single-select, like WhatsApp). Exit with ✕, the back gesture, Esc on desktop, or by acting. A desktop mouse drag still selects text as usual.
- ☆ Save puts each selected message in your Notebook (one entry per message — the whole-message sibling of the ✦ Save-line pill). ↪ Forward opens a picker over your chats and lands the selection in the target room as a chat-record card — the personas there read it as content you brought in.
- Delete — one tap, one dialog. Delete lives in the ⋮ menu and asks in the app's own confirm card (Cancel · red Delete), no extra menu in between. If everything selected is your own, it asks delete for everyone: the bubble becomes a "This message was deleted" tombstone for every member, live and on every reload. Otherwise it asks delete for you: the message disappears from your view — on every device, enforced server-side — while others keep seeing it. The record is never surgically erased — deletion is honoured at replay (the same philosophy as the private-history floor). One honest caveat: the panel's cached prompt may remember the words until that cache naturally expires.
- ⋮ more holds Select more, Message info (who + when it was sent), Delete, and Report (a stub for now). ⧉ Copy sits on the bar itself: one message's text, or "Name: text" lines for several.
- React with an emoji(表情回应).
- Emoji, drawn by the app(表情). Every emoji in a message is drawn by us, not by your phone — so a 👍 sent from a brand-new iPhone is the same picture on an old Android, and an emoji from a newer phone never arrives as an empty box □. It works on the personas' messages too. Copy a message and you get the real emoji back, not a filename. A message that's a single emoji and nothing else renders big and without a bubble — just the emoji, the way WhatsApp does it.
Tap the smiley inside the message box (left edge) and the emoji panel takes the keyboard's place — same spot, same size, so nothing on screen jumps; the smiley turns into a ⌨ keyboard to go back. Inside: a search that speaks your language and English at once (v605 — type 猫 or cat on a Chinese phone and both find the cat, because English is the base every language is stacked on top of), and which ranks rather than filters, so 「very happy」 or 「thank you」 return the obvious answers instead of nothing when one word falls outside the emoji vocabulary; your Recents first (what you actually use, reactions included — it's one list), seven category tabs, and a long-press for skin tones that remembers your choice for that emoji. On a wide screen it opens as a small panel above the smiley instead.
Feedback & measurement
- Rate each turn. A small card under every panel reply takes a 👍 / 👎 plus quick lever-mapped tags (too long / too short, too soft / too harsh, didn't answer me, …) and a free-text note — the signal that tunes the floor producer.
- A check-in survey you can summon any time from the card (📋), and which also auto-fires every N turns on an admin-set cadence (console → Settings → Feedback;
0 = off). In a multi-human room it advances on each rendered turn, so every present member is prompted — not just the poster.
- Admins can show or hide the whole widget per role in the Who-sees-what matrix (it's on by default).
Cost
- Every turn shows its tokens + API cost, with a running session total. Turn 1 is the dear cache-write; later turns are cheap cached reads.
- The cost figure says which price it is at. DeepSeek began billing by the UTC clock on 2026-08-16 — peak is a flat 2× off-peak on every column — so the same chat costs double inside 01:00–04:00 · 06:00–10:00 UTC, Monday–Friday (the whole weekend is off-peak) and the number in the chat menu stopped being one fact. A pill beside Cost names the window: DeepSeek Peak Now, and only at peak (v941, the owner’s call on both halves). It used to read Peak ×2 beside a hairline Off-peak, and both were wrong in their own way — 「×2」 sat next to a total earned over days and read as this figure was doubled, which it never meant (it means the next call is), and a pill that only ever reports 「normal」 trains the eye to skip the pill. Off-peak is the quiet, cheaper, ordinary state and says nothing worth a badge. It is hidden outright where the panel is not a time-tiered vendor — Anthropic and Google don't move with the clock, and a badge there would claim a tier that doesn't exist. ⚠ The server sends the window, never "is it peak now": this panel outlives any boolean baked at boot, and a stale badge is worse than none, because the figure beside it then looks explained — so the page reads the clock itself, on a minute tick that carries it across the boundary.
- What this chat cost, step by step (v941, drawn to the owner’s layout on the mockup). Chat info ▸ Cost is one grid on four rails — colour · name · calls · money: the models above a hairline and the steps below it, the same money cut two ways on the same rails, so the eye can add a column.
Persona Studio
- Build a persona from a name (Friends › Persona studio › New persona — gated by the Persona Studio visibility flag, admin-on by default) — name a real figure, confirm the identity with one tap, then it runs on its own: gathers the figure's real words from the live web (every quote verified against the page it came from), writes the profile, and two cold auditors judge it before it can join the library. A live progress view + a per-build cost bill.
- The whole flow is part of the app. The Studio landing, the four build steps and the finished build's report are ordinary pages of the room UI now — name → identity → confirm → build, each a standard sub-page with one way out and one way back, the live progress spine reading the build's own event stream. It used to be a separate
/builder document shown inside the app through an iframe; that frame and its whole cross-frame message contract are gone. /builder survives only as the console's read-only build report — visit it directly and it sends you back into the app.
- The build can be left. It's a server task — wander off and the Persona studio row breathes a quiet dot while it cooks; you're told the moment it lands. By default a regular user sees only the named progress (Identity lock → Built) and the verdict, the built-from bar, and the bill; the machinery underneath — per-facet coverage, the audit and sources folds, the live-room checklist — is admin-only behind the Show build details toggle (console → Who-sees-what → Persona Studio).
- A running build says so in the list(v1011). In the Studio's own list a build that is running wears a blinking red dot — the same 7px disc in the same trailing slot as the finished-but-unread coral dot, so motion and hue carry the difference:「happening right now」against「finished, you have not looked」. Only running qualifies — the identity steps are waiting on you, and an interrupted, aborted or failed build is over. And the build page's「you may leave」reassurance moved above the progress spine, where you read it before the wait rather than after it.
- A finished persona messages you. When a build lands, it doesn't just announce itself — the server opens a private 1:1 chat with her and she speaks first, in your language, exactly as any "start a chat" would. That opening line is the notification, with a filled Reply that drops you into the room; the chat appears in your list straight away, titled like any other 1:1. Delete the persona and that chat goes to trash with her.
- She appears in the lists too — on every device, without a restart. The persona library used to be written once, out of the cold-open payload, and never again: every surface drawn from it — All persona, the picker, Discover search, the labels on the chats list — was the library as it stood the last time that device cold-started. A phone resumes for days; a desktop where you just built something does not. Reported exactly as that split: "I created a persona on the box, I can talk to her, but I can't find her in the contact list anywhere on my phone — on desktop I see her in all lists." Talking to her worked because a chat is server-truth on every open; the lists were a photograph. The library is a live resource now — refreshed when the app comes to the foreground and when any library-backed page opens, and pushed at the eight verbs that move it (build · take off · publish · clone · delete · discard · purge · restore), with a forced read on every stream reconnect, since a push sent while the stream was down was sent to nobody. The cheap triggers are affordable because the common answer is "nothing changed".
- Build, delete, build again under the same name (2026-08-17). A persona you build gets a key that is minted, not made from her name — the name rides along only so a listing is readable. So two Renas are two personas, whoever built them; rebuilding after a delete never lands on top of the old one, and the original stays in Deleted persona, restorable. It also removed a small leak: the old name-derived key had to suffix on a clash (rena-2), which told you somebody else had a private Rena. Hand-authored personas keep their curated names. ⚠ And a build that is running is now listed in In progress whatever else is true of its key — the Studio's list and the Deleted store split one pile on one test, so a live build can no longer be on neither page while the Studio row's own dot blinks beside it.
- Personas come from two places, shown by a badge in the console: SYS (hand-authored by a Claude Code session) and BUILT (made by Persona Studio). Both work identically in a room.
- Public vs private. A persona you build is private by default — only you see it in the picker and the browser (another user's private personas never leave the server). An admin can publish it (console → Personas) to make it public for everyone; publish/unpublish flips in place. You can delete your own — a published one is kept as a tombstone (it stays public), unpublished scaffolding is removed outright, and a build in progress can't be deleted. Since v774 the ones you deleted have a place rather than only a verb: Me › Settings › My contents › Deleted persona lists them with Restore and Purge — the same shape as Deleted chats and Deleted Seen. Purge refuses anything not already deleted, so emptying is always the second act.
- Take a persona off (console → Personas → ⋮): it disappears from the new-chat picker so users can't add it, but it keeps working in any room that already has it. Put it back on anytime.
Accounts & admin
- Three-step signup, invitation-gated. The invitation is its own step, and it is the first one. While the service is invitation-only the code is not one field among four — it is the question the whole page turns on, and asking it last meant filling in a username and two passwords before finding out the answer was no.
- The door asks how you'd like to be addressed (v1036). The identity step — the screen that takes the name people will call you — now carries an optional Gender dropdown beside it, 「Prefer not to say」 leading as the honest default. It is asked at the door because a host addresses you from its first message: a profile row you fill in a week later cannot un-send a greeting that already guessed. It is the same question Edit profile asks, stamped the same way — a value the server doesn't recognise is stored as unset rather than refused, because a guess with a database row is worse than no answer.
- A re-tapped invite link takes you home, not back to the signup form (v1037). An invitation is how somebody was let in, so it is also the link they keep — it sits in the chat thread where it was sent, and they tap it again days later to get back to -ish. That used to land them on a form asking for a username and a password, the one question they had already answered, with their live session sitting right there in the cookie jar.
/invite/<code> now checks for a signed-in session first and sends anyone who has one to the app. Signed out it is unchanged, and a dead or forged code still gets the signup door — the session is verified, not merely present. ⚠ Deliberately not applied to the sign-in page: going there is an explicit act, and bouncing it would remove the only way to sign in as somebody else.
- Admin: cap personas per room, system settings, read-only room peek, Persona Studio + take-off/put-on, a Status tab (server health, all API-key presence by provider, plus — since v655 — cost per call type, since a turn is up to six provider round trips and one total is the number that hides which one grew, and a toolbox health card reading in the same vocabulary the exam scores in), a Usage & cost ledger that counts every API (rooms, Persona Studio, Seen, Convene, composer dictation — by model and by function), and a Backup tab that exports or restores all console settings as one JSON file.
- A tier above admin, and a person the console cannot see (2026-08-13, the model). A super can take an account out of the admin console — its username, its spending, its chats, and the arithmetic that would otherwise give it away.