Split out of the BR-TEXT-2 copy sweep (#83), which closed its other four categories but left this one deliberately untouched. It is the largest item in that issue and the only one that cannot be done as a sweep, so it gets its own thread with its own measurement.
The distinction
Chat is the user-facing word for a conversation with the agent. Session is the technical name for the same object where it is a stored, addressable record: a row in the store, a path segment in the HTTP API, a subcommand in the CLI, a schema name in OpenAPI. Both are correct. The defect is that the two are used interchangeably in copy, so the same object is announced under two names on adjacent surfaces, and a third word, conversation, appears as well.
The sharpest example is inside one file. ui/desktop/src/components/BaseChat.tsx:1877 names a new chat 'New chat'; :2346 names the same thing 'New Session'. Across the UI there are two default names for a new chat, at seven sites:
| default |
sites |
'New Session' |
utils/sessionNameSync.ts:31 (DEFAULT_SESSION_NAME), components/chatGroups/chatGroupsReducer.ts:77 (DEFAULT_TAB_TITLE), components/BaseChat.tsx:2346, hooks/chatStreamStore.tsx:604 |
'New chat' |
App.tsx:388, components/BaseChat.tsx:1877, components/BioRouterSidebar/AppSidebar.tsx:84 |
Measured scope
Measured for this issue rather than carried over. Method: strip comments (line, block and JSX), then match chat / session / conversation as whole words inside string literals and JSX text only, with a boundary that rejects sessionId, session_id, session-id and chatState. Tests, __mocks__ and the generated ui/desktop/src/api/ client are excluded.
ui/desktop/src, 445 whole-word occurrences in 107 files:
| term |
occurrences |
| chat |
306 |
| session |
128 |
| conversation |
11 |
Those 445 are not all copy. Sorted by what they are: 258 in 83 files sit in multi-word user-visible strings (chat 226, session 30, conversation 2), 25 are developer diagnostics on console.* lines, 59 are expression fragments the JSX-text matcher picked up, and 103 are bare tokens, route strings and object keys.
crates/biorouter-cli/src, same method: 462 occurrences in 21 files (session 348, chat 82, conversation 32). Most of that 348 is the session subcommand surface and its help text, which stays.
The actionable set is 42 distinct user-visible strings across 21 files that say session or conversation where the object being named is the user's chat. That is the list to work, not the 445. A representative sample:
components/sessions/SessionListView.tsx 406, 522, 535, 550, 593, 999, 1099: row action labels plus Try a different name, folder, or session ID.
components/schedule/ScheduleDetailView.tsx 111, 178, 346, 474 and SchedulesView.tsx:425: New session: ${id}, Session: ${id}, Current session ID:
utils/sessionNameSync.ts:138 Session name cannot be empty
hooks/useDiverge.ts:90 Diverge did not return a new session id
components/McpApps/McpAppRenderer.tsx:113 Session not initialized for MCP request
renderer.tsx:385 No terminal session. and terminalSessionRegistry.ts:141 (terminal sessions are a different object; likely correct as-is)
types/message.ts:8 biorouter is compacting the conversation...
components/settings/privacy/PrivacyPanel.tsx:243 for every conversation
Several of those name a session ID, which is the case #83 recorded as legitimately session. Each needs a read, which is the point of the issue.
Surfaces where session is the name and must not move
| surface |
scope |
evidence |
| store |
the sessions table in the SQLite session store, plus messages, checkpoints, classification_audit and token_events keyed off it |
crates/biorouter/src/session/ |
| HTTP routes |
25 distinct path literals under /sessions* and {session_id} |
crates/biorouter-server/src/routes/ |
| OpenAPI |
21 schema names, 19 paths, 18 operationIds containing session |
ui/desktop/openapi.json |
| generated TS client |
110 distinct exported symbols containing Session, hand-editing forbidden |
ui/desktop/src/api/ |
| CLI |
11 biorouter session <verb> subcommands (attach, cancel, declassify, diagnostics, diverge, export, list, remove, rename, send, watch) plus --session-id and --no-session |
crates/biorouter-cli/src/cli.rs |
| deep links |
the biorouter://sessions/ URL scheme |
ui/desktop/src/sessionLinks.ts:18 |
Warning
A find-and-replace across the tree breaks the product. Renaming session to chat in any of the six rows above changes an API path, an operationId, a CLI subcommand, a column name or a URL scheme, and every one of those is a compatibility surface with clients outside this repo. The biorouter://sessions/ scheme is already installed on user machines. The CLI's session list is in the docs and in muscle memory.
Do this file by file, deciding per occurrence, with the rule: chat in prose the user reads; session where the session ID or the stored record is the visible object, and everywhere it is an identifier, a path, a column, a flag or a type name.
If anything under routes/ is renamed
Regenerate the client, or the TS side silently drifts from the server:
just generate-openapi && cd ui/desktop && npm run generate-api
Also re-run scripts/check-version-consistency.sh if openapi.json is rewritten, since the version is embedded in it.
Reproducing the counts
The scanner used here was a throwaway. Anyone repeating the measurement should strip comments first: a raw grep -c session over ui/desktop/src counts imports, types and identifiers and lands far above the 128 that are words in copy.
Out of scope
Terminal sessions (renderer.tsx, terminalSessionRegistry.ts) are a different object and probably keep the word. The 11 spellings under conversation should be settled at the same time so a third word does not survive the pass.
Split out of the BR-TEXT-2 copy sweep (#83), which closed its other four categories but left this one deliberately untouched. It is the largest item in that issue and the only one that cannot be done as a sweep, so it gets its own thread with its own measurement.
The distinction
Chat is the user-facing word for a conversation with the agent. Session is the technical name for the same object where it is a stored, addressable record: a row in the store, a path segment in the HTTP API, a subcommand in the CLI, a schema name in OpenAPI. Both are correct. The defect is that the two are used interchangeably in copy, so the same object is announced under two names on adjacent surfaces, and a third word, conversation, appears as well.
The sharpest example is inside one file.
ui/desktop/src/components/BaseChat.tsx:1877names a new chat'New chat';:2346names the same thing'New Session'. Across the UI there are two default names for a new chat, at seven sites:'New Session'utils/sessionNameSync.ts:31(DEFAULT_SESSION_NAME),components/chatGroups/chatGroupsReducer.ts:77(DEFAULT_TAB_TITLE),components/BaseChat.tsx:2346,hooks/chatStreamStore.tsx:604'New chat'App.tsx:388,components/BaseChat.tsx:1877,components/BioRouterSidebar/AppSidebar.tsx:84Measured scope
Measured for this issue rather than carried over. Method: strip comments (line, block and JSX), then match
chat/session/conversationas whole words inside string literals and JSX text only, with a boundary that rejectssessionId,session_id,session-idandchatState. Tests,__mocks__and the generatedui/desktop/src/api/client are excluded.ui/desktop/src, 445 whole-word occurrences in 107 files:Those 445 are not all copy. Sorted by what they are: 258 in 83 files sit in multi-word user-visible strings (chat 226, session 30, conversation 2), 25 are developer diagnostics on
console.*lines, 59 are expression fragments the JSX-text matcher picked up, and 103 are bare tokens, route strings and object keys.crates/biorouter-cli/src, same method: 462 occurrences in 21 files (session 348, chat 82, conversation 32). Most of that 348 is thesessionsubcommand surface and its help text, which stays.The actionable set is 42 distinct user-visible strings across 21 files that say
sessionorconversationwhere the object being named is the user's chat. That is the list to work, not the 445. A representative sample:components/sessions/SessionListView.tsx406, 522, 535, 550, 593, 999, 1099: row action labels plusTry a different name, folder, or session ID.components/schedule/ScheduleDetailView.tsx111, 178, 346, 474 andSchedulesView.tsx:425:New session: ${id},Session: ${id},Current session ID:utils/sessionNameSync.ts:138Session name cannot be emptyhooks/useDiverge.ts:90Diverge did not return a new session idcomponents/McpApps/McpAppRenderer.tsx:113Session not initialized for MCP requestrenderer.tsx:385No terminal session.andterminalSessionRegistry.ts:141(terminal sessions are a different object; likely correct as-is)types/message.ts:8biorouter is compacting the conversation...components/settings/privacy/PrivacyPanel.tsx:243for every conversationSeveral of those name a session ID, which is the case #83 recorded as legitimately
session. Each needs a read, which is the point of the issue.Surfaces where
sessionis the name and must not movesessionstable in the SQLite session store, plusmessages,checkpoints,classification_auditandtoken_eventskeyed off itcrates/biorouter/src/session//sessions*and{session_id}crates/biorouter-server/src/routes/sessionui/desktop/openapi.jsonSession, hand-editing forbiddenui/desktop/src/api/biorouter session <verb>subcommands (attach, cancel, declassify, diagnostics, diverge, export, list, remove, rename, send, watch) plus--session-idand--no-sessioncrates/biorouter-cli/src/cli.rsbiorouter://sessions/URL schemeui/desktop/src/sessionLinks.ts:18Warning
A find-and-replace across the tree breaks the product. Renaming
sessiontochatin any of the six rows above changes an API path, an operationId, a CLI subcommand, a column name or a URL scheme, and every one of those is a compatibility surface with clients outside this repo. Thebiorouter://sessions/scheme is already installed on user machines. The CLI'ssession listis in the docs and in muscle memory.Do this file by file, deciding per occurrence, with the rule:
chatin prose the user reads;sessionwhere the session ID or the stored record is the visible object, and everywhere it is an identifier, a path, a column, a flag or a type name.If anything under
routes/is renamedRegenerate the client, or the TS side silently drifts from the server:
Also re-run
scripts/check-version-consistency.shifopenapi.jsonis rewritten, since the version is embedded in it.Reproducing the counts
The scanner used here was a throwaway. Anyone repeating the measurement should strip comments first: a raw
grep -c sessionoverui/desktop/srccounts imports, types and identifiers and lands far above the 128 that are words in copy.Out of scope
Terminal sessions (
renderer.tsx,terminalSessionRegistry.ts) are a different object and probably keep the word. The 11 spellings underconversationshould be settled at the same time so a third word does not survive the pass.