Skip to content

Give native chat names one source for tabs, sidebar and AI Vault (list and search) - #25986

Merged
brennanb2025 merged 11 commits into
mainfrom
brennanb2025/ai-vault-name-feed
Oct 7, 2026
Merged

brennanb2025 merged 11 commits into
mainfrom
brennanb2025/ai-vault-name-feed

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 19 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​591 $\color{#cf222e}{\Huge{\mathbf{−}}}$​1703 $\color{#cf222e}{\Huge{\mathbf{−}}}$​1112
Prod 49 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​442 $\color{#cf222e}{\Huge{\mathbf{−}}}$​1125 $\color{#cf222e}{\Huge{\mathbf{−}}}$​683

ELI5

Native Claude and Codex chats now get a name after their first message (#25724). The chat's tab and sidebar row showed that name cleanly, but AI Vault (the session history panel) needed a large pile of extra code to keep the name from disappearing, and Vault's search results never showed the name at all. This PR gives the app one place to learn a chat's name from the computer that runs it, makes the tab, the sidebar row, and Vault's list and search all read that place, and deletes the extra Vault code.

The problem

A chat's name is stored in exactly one place: the chat's saved record on the computer running it (AgentSessionRecord.conversationName, written only by compareAndSetConversationName). The tab strip and sidebar read it through the tab, which the host retitles from that record.

AI Vault had no such path. To make a name appear and stay in Vault, #25724 copied the name into Vault's cached list results, into the list currently on screen, and into list requests still in flight, then added code to repair those copies when:

  • the chat's tab closed (the tab was where Vault first saw the name, so closing it could flash "Claude Chat" back),
  • an older history response arrived after the name did,
  • a cached Vault result was evicted,
  • a "name saved" notice riding on the tab updates was coalesced away.

That was about 720 lines of renderer code (ai-vault-structured-title-projection.ts, ai-vault-structured-title-recovery.ts, ai-vault-pending-title-projection.ts, plus additions to the result cache, applied-scan, refresh hook, tab-title sync gate, both tab-snapshot apply paths, a host title-lookup branch, and an extra field on tab snapshots). The root cause: a name that changes on its own was stored inside caches whose lifetime belongs to transcript scans and open tabs.

Separately, Vault's indexed search ignored native chats completely: a search hit showed the transcript's first-prompt title, never the generated name or the user's manual tab rename.

What Changed

What you see

  • Vault list rows, Vault search results, the tab strip and the sidebar row all show the same name for a native chat, in the same order: your manual tab rename, then the generated name, then the plain "Claude Chat"/"Codex Chat" label.
  • Closing a tab right after it was named keeps the name in Vault (no flash back to "Codex Chat").
  • Searching Vault now shows the generated name for native chats, and your manual rename while the tab is open, and those hits open the native chat like list rows do.
  • A chat closed before an app restart still shows its name in Vault (from the host's list answer, as before).

How

  • The host already publishes a per-chat summary over the structured session status feed (one stream per host, keyed by the durable chat id, capability-negotiated, reconnecting, and it keeps a closed chat's summary for the life of the host process). The summary now carries an optional conversationName read from the record. When a name is saved, the host republishes that chat's summary, including for a chat whose tab already closed.
  • The renderer keeps the existing per-host mirror of that feed. A new hook, useStructuredConversationName(hostId, chatId), reads the name from it. The tab strip, the sidebar row, and the Vault row (list and search) all use it with one shared display order (structuredChatDisplayName).
  • Vault rows no longer carry renderer-maintained copies of native names. The host's list answer (projectStructuredAiVaultSessions, unchanged in behaviour) stays as the fallback for chats the feed has not published this run.
  • Indexed search: the host that owns the index attaches the native owner and saved name to each matching hit (projectStructuredAiVaultSearchResponse), sharing one owner index with the list projection (which previously rebuilt the record list once per row). A paired client that does not advertise structured-session support gets its hits unchanged, as before.
  • Deleted: the three Vault projection/recovery/pending modules, their cache/applied-scan/refresh wiring, the tab-to-Vault lane in the title sync gate, the saved-title hooks in both tab-snapshot apply paths, the native branch of the host title lookup, and the transient structuredConversationTitle field on tab snapshots. Those files are back to their pre-Name native Claude and Codex chats after their first message #25724 form.

Line count (production code on the naming path, excluding tests): +381 / −1059, net −678. Tests: net −1338 (the old repair tests go with the code they tested; new tests below).

Why this approach

  • Reuse before a new channel. The status feed already is a host→renderer stream of per-chat state, keyed by the durable chat id, with capability negotiation, reconnect, and "never retract a summary" semantics. Carrying the name there needed one optional field, one republish method, and one hook. The earlier estimate of 450–700 lines assumed a brand-new metadata feed with its own census and ordering; none of that is needed because this feed already has them.
  • Why not just refetch Vault on rename? A refetch cannot stop the rollback: the tab closes before the response returns, so "Codex Chat" flashes back. A pushed name in a store that outlives the tab avoids that without blocking close.
  • Why not keep reading the tab label in Vault? Tabs disappear when closed; that was the original bug.

Compared with the common pattern (a per-environment stream of thread metadata that the client folds into one map, which every thread list reads):

Difference Label Why
The renderer's map holds the chats its host has published this host run, not every chat ever stored; older closed chats get their name from the host's list answer instead Intended Stored records are never deleted, so a complete census would grow without bound; the host's list answer reads the same record at list time, and a closed chat's name cannot change until it is reopened, at which point the feed publishes it
The manual rename is a tab alias (customLabel), not a saved rename of the conversation Intended (unchanged by this PR) Existing product behaviour; this PR keeps it winning everywhere the tab is open
Search hits get their owner and name from the indexing host at query time, rather than from the index itself Intended Keeps the index transcript-only; search does not yet match on generated names (separate feature)

Older paired hosts

Assumption, stated explicitly: #25724 has not shipped in any release yet (latest is v1.4.221), so no released host or client depends on the removed structuredConversationTitle tab-snapshot field or the native title lookup, and removing them is not a wire break.

Situation Behaviour Label
Host without the status feed (very old) The feed's existing capability check gives up after one answer, with no retry loop; Vault shows the host's list title Temporary: remove once hosts older than this release fall below about 1% of installs (roughly 6 weeks after it ships, from current update rates)
Host with the feed but without conversationName (any released host) The optional field is absent, so every surface falls back to the host-projected title: Vault rows update on the next Vault refresh, tabs on the next tab update. No probing at all Temporary: remove once hosts older than this release fall below about 1% of installs (roughly 6 weeks after it ships, from current update rates)
Older desktop client talking to a new host The new optional summary and search-hit fields are ignored by older parsers Intended

How "older host" is detected: the new summary field is optional, so its absence is the signal; no separate capability or probe is needed, and an older host is never re-asked. Version data at the time of writing: about 80% of installs run a release from the last ~10 days and about 4–5% are a month or more behind. No new stream opcode or RPC method was added; only optional fields (see docs/reference/remote-wire-compatibility.md).

Linked Issue

Follow-up to #25724.

Visual Proof

Hidden Electron run on an isolated profile, with stand-in Claude/Codex CLIs and a stand-in naming command, head eb99352a423. Every frame below was inspected. There is no "before" frame for search: before this PR a search hit showed the first-prompt text and never the chat name, which is the bug described above.

Tab closed right after naming: no flash back. Named (tab, sidebar row and Vault row all say "QA Feed plum") → tab closed, Vault row keeps the name:

scenario-codex-feed-plum-generated.png
scenario-codex-feed-plum-vault-closed.png

Tab closed before naming finished. Closed while still "Codex Chat" → the same Vault row becomes "QA Feed cedar" by itself (no tab, no Vault refresh):

scenario-codex-feed-cedar-vault-closed-pending.png
scenario-codex-feed-cedar-vault-closed-generated.png

Manual rename wins in the Vault list and search, and the generated name returns in search once it is cleared:

scenario-3-manual-rename-list-indexed-final.png
scenario-3-manual-rename-search-indexed-final.png
scenario-3-cleared-title-search-indexed-final.png

Indexed search shows the generated name of a closed chat (searched "Feed plum", hit titled "QA Feed plum"):

scenario-4-closed-plum-search-indexed-final.png

Closed chats keep their names in the Vault list, including ones named before this app launch ("QA Main willow", "QA Main quartz", "ALT Settings fern"):

scenario-5-existing-closed-names-final.png

Testing

  • Automated tests added/updated (each one checked by breaking the code it covers and seeing it fail):
    • Host feed: a held chat publishes its saved name once; a chat whose tab closed before naming finished is still named and later subscribers get it; a closing chat keeps the name; unknown chats publish nothing.
    • Vault row: name appears with the tab open and stays after the tab closes; manual rename wins in list and search rows and the name returns once it is cleared; a search hit shows the published name; a chat the feed has not named keeps the host's row title (restart case); another host's name for the same id is never shown; an older paired host keeps the host title and is probed once.
    • Host search: hits are named and owned like list rows; a paired client without structured support gets plain hits.
    • Sidebar: the published name beats the tab label; a manual rename beats both.
  • 169 affected test files (1461 tests) pass locally; tc:node, tc:cli, tc:web, full oxlint, and the changed-code quality gate pass.
  • I manually tested these changes locally (hidden Electron run, frames above).
  • Platforms: macOS locally. The change is renderer/host logic with no platform-specific paths; SSH workspaces have no native chats, so their Vault rows are unaffected.

Review

Agent skill upstream boundary

  • Not applicable.

Notes

Security: no new inputs beyond optional, bounded fields from a paired host. Performance: one more optional string on an existing summary; the list ownership projection now builds one index per request instead of re-listing records per row.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • Screenshots attached (after only; the "before" is the bug described above)
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered
  • CI passes

…ir copies

The host's saved conversation name now rides the structured session status
feed, which already exists per host, is keyed by the durable session id, and
keeps a closed chat's summary. Tab strip, sidebar rows and AI Vault (list and
search) read it through one hook and one display order (tab alias, saved name,
host label). Vault no longer copies names into its cached results, so the
projection, recovery and pending-title modules and their tab-snapshot lanes are
removed. Indexed search hits now carry the native owner and saved name from the
host that indexed them.
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Status: ready for review (not merged)

What this fixes: AI Vault could flash a native chat's name back to "Claude Chat"/"Codex Chat" when its tab closed, and Vault search never showed chat names or manual tab renames. The name now comes from one place (the host's per-chat status summary), read the same way by the tab strip, the sidebar row, and Vault's list and search. About 720 lines of Vault-only name-repair code are deleted (production code on this path: +381 / −1059).

Checked on head eb99352a423:

  • CI: every check finished and passed (unit shards 1–5, relay integration, cross-version wire compatibility, both package jobs, static analysis/typecheck, Mobile Checks).
  • Local: tc:node, tc:cli, tc:web, full oxlint, and the changed-code quality gate pass; 169 affected test files (1461 tests) pass. Each new test was checked by breaking the code it covers and seeing it fail.
  • Code review: a same-model review came back clean with no changes needed. Readiness checklist run at the start and the end of review: all items pass.
  • Hidden Electron run on an isolated profile (frames in the PR body, each inspected): name survives an immediate tab close; a chat closed before naming finished gets its name in the same Vault row with no refresh; manual rename wins in the Vault list and search, and the generated name returns once it is cleared; search shows the generated name of a closed chat; chats named before the app launched keep their names.

Not verified: a live paired remote host or an older paired host at runtime (covered by unit tests only), Windows and Linux runs, and generation by a real model (a stand-in naming command was used).

Known limits, labelled in the PR body: hosts that predate the new optional field show the host's list title, refreshed when Vault refreshes, with no repeated probing (temporary until version data says those hosts can be dropped). Search still does not match on generated names; that is a separate feature.

@brennanb2025
brennanb2025 marked this pull request as ready for review October 6, 2026 23:08
@coderabbitai

coderabbitai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c2bf61c6-edb9-4aac-a452-3b6d43bfd6d1
📥 Commits

Reviewing files that changed from the base of the PR and between 9807f04 and 0946766.

📒 Files selected for processing (5)
  • src/renderer/src/components/dashboard/use-agent-row-conversation-name.ts
  • src/renderer/src/components/right-sidebar/AiVaultSessionRow.structured-title.test.tsx
  • src/renderer/src/components/right-sidebar/use-ai-vault-session-display-title.ts
  • src/renderer/src/lib/structured-chat-tab-index.test.ts
  • src/renderer/src/lib/structured-chat-tab-index.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

Structured-session hosts now publish saved conversation names through status feeds. AI Vault search results can include structured-session ownership and projected names. Renderer views read names from the owning host and apply custom-label or session-title fallbacks. The change also removes snapshot-based title publication, renderer title recovery and projection code, and related request fields.

Priority: ➖ Normal

Merge Risk: 🔵 Low · up to 09467

Search responses that need no ownership data may still wait for chat-host setup. The change is mergeable with awareness of that bounded delay.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 55 functions across 44 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: using one source for native chat names across tabs, the sidebar, and AI Vault.
Description check ✅ Passed The description covers the change, rationale, testing, and visual proof. It identifies #25724 as a follow-up, but leaves the template’s Fixes # field blank and the CI checklist unchecked despite lat…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…-name-feed

# Conflicts:
#	src/main/native-chat/agent-session-wire/structured-agent-session-status-feed.ts
Naming and owning search hits is bookkeeping: if the native chat host fails to
install, return the plain hits instead of failing the search. The runtime RPC
only installs the host for clients that will receive the owners.
A failed feed publication no longer skips retitling the open tab. The publish
now lives in the naming deps, where a test covers it.
Drop a published chat name the record store would refuse, and cap a search
hit's owner workspace id at the same length the list row and record use.
A paired host's search hits carry no resume command, so the row disabled
every open action even for a native chat it can open through its owner, as
its list row does.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e3c9fae2-b979-46be-a42a-6f5a6a1f5fbb
📥 Commits

Reviewing files that changed from the base of the PR and between eb99352 and 9807f04.

📒 Files selected for processing (19)
  • config/scripts/vitest-node-runtime-files.mjs
  • src/main/ai-vault/structured-session-ownership.ts
  • src/main/ipc/ai-vault-search.test.ts
  • src/main/ipc/ai-vault-search.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-client-delivery.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-host.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-status-feed.ts
  • src/main/runtime/orca-runtime-get-worktree-ps.ts
  • src/main/runtime/orca-runtime-prune-mobile-session-tab-group-layout.ts
  • src/main/runtime/rpc/methods/ai-vault-structured-ownership.test.ts
  • src/main/runtime/rpc/methods/ai-vault.ts
  • src/main/runtime/structured-chat-naming-runtime.test.ts
  • src/main/runtime/structured-chat-naming-runtime.ts
  • src/renderer/src/components/right-sidebar/AiVaultSessionRow.structured-title.test.tsx
  • src/renderer/src/components/right-sidebar/AiVaultVirtualRow.test.tsx
  • src/renderer/src/components/right-sidebar/ai-vault-search-session.test.ts
  • src/renderer/src/components/right-sidebar/ai-vault-search-session.ts
  • src/renderer/src/runtime/structured-conversation-name.ts
  • src/shared/ai-vault-search-contract.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.

)
: true
const response = await search
return (await hostReady) ? projectStructuredAiVaultSearchResponse(response) : response

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

sed -n '35,115p' src/main/ai-vault/structured-session-ownership.ts
sed -n '125,180p' src/main/ipc/ai-vault-search.ts
sed -n '22,50p' src/main/runtime/rpc/methods/ai-vault.ts

Repository: stablyai/orca

Length of output: 6199


🏁 Script executed:

printf '%s\n' '--- helper/call references ---'
rg -n -F -- 'ensureStructuredAgentSessionHostUnlessRefused' src/main
rg -n -F -- 'searchWithStructuredOwners' src
rg -n -F -- 'ensureStructuredSessionOwnership' src/main
printf '%s\n' '--- response type and consumer references ---'
rg -n -F -- 'AiVaultSearchResponse' src/main shared
printf '%s\n' '--- scoped PR diff ---'
git diff --unified=12 f6448a1e26c0bb0f17998bb5e8865de7bfd4949e 9807f0472604f62472a458a3e29f4674471372a8 -- src/main/ai-vault/structured-session-ownership.ts src/main/ipc/ai-vault-search.ts src/main/runtime/rpc/methods/ai-vault.ts

Repository: stablyai/orca

Length of output: 20818


🏁 Script executed:

printf '%s\n' '--- host-refusal helper ---'
sed -n '1,85p' src/main/runtime/structured-agent-session-host-refusal.ts
printf '%s\n' '--- host ensure declarations and bindings ---'
rg -n -F -- 'ensureStructuredAgentSessionHost' src/main
printf '%s\n' '--- RPC search caller contract ---'
sed -n '1,60p' src/main/runtime/rpc/methods/ai-vault.ts
printf '%s\n' '--- search response contract ---'
sed -n '1,180p' src/shared/ai-vault-search-types.ts
printf '%s\n' '--- local search registration and invocation ---'
sed -n '210,244p' src/main/ipc/register-core-handlers/register-core-handlers.ts
sed -n '135,170p' src/main/ipc/ai-vault-search.ts
printf '%s\n' '--- local/all-host search response handling ---'
sed -n '1,160p' src/main/ipc/ai-vault-search-all-hosts.ts

Repository: stablyai/orca

Length of output: 38985


🏁 Script executed:

printf '%s\n' '--- bound runtime method ---'
nl -ba src/main/runtime/orca-runtime-get-worktree-ps.ts | sed -n '150,184p'
printf '%s\n' '--- host installer implementation ---'
nl -ba src/main/runtime/structured-agent-session-runtime.ts | sed -n '145,215p'
printf '%s\n' '--- response kinds and schema ---'
rg -n -C 5 -- 'stale-cursor|unavailable|AiVaultSearchResponseSchema' src/shared/ai-vault-search-contract.ts src/main/ai-vault-search/session-search-service-registry.ts src/main/ai-vault-search/session-search-instance.ts
printf '%s\n' '--- search service registry ---'
nl -ba src/main/ai-vault-search/session-search-service-registry.ts | sed -n '1,100p'
printf '%s\n' '--- search instance cursor handling ---'
nl -ba src/main/ai-vault-search/session-search-instance.ts | sed -n '70,130p']]>

Repository: stablyai/orca

Length of output: 20545


🏁 Script executed:

printf '%s\n' '--- bound runtime method ---'
nl -ba src/main/runtime/orca-runtime-get-worktree-ps.ts | sed -n '150,184p'
printf '%s\n' '--- host installer implementation ---'
nl -ba src/main/runtime/structured-agent-session-runtime.ts | sed -n '145,215p'
printf '%s\n' '--- response kinds and schema ---'
rg -n -C 5 -- 'stale-cursor|unavailable|AiVaultSearchResponseSchema' src/shared/ai-vault-search-contract.ts src/main/ai-vault-search/session-search-service-registry.ts src/main/ai-vault-search/session-search-instance.ts
printf '%s\n' '--- search service registry ---'
nl -ba src/main/ai-vault-search/session-search-service-registry.ts | sed -n '1,100p'
printf '%s\n' '--- search instance cursor handling ---'
nl -ba src/main/ai-vault-search/session-search-instance.ts | sed -n '70,130p'

Repository: stablyai/orca

Length of output: 22437


🏁 Script executed:

printf '%s\n' '--- complete runtime setup method ---'
nl -ba src/main/runtime/orca-runtime-get-worktree-ps.ts | sed -n '168,225p'
printf '%s\n' '--- installer location ---'
rg -n -- '^(async )?function install\\(' src/main/runtime/structured-agent-session-runtime.ts
printf '%s\n' '--- installer body ---'
nl -ba src/main/runtime/structured-agent-session-runtime.ts | sed -n '1,145p'

Repository: stablyai/orca

Length of output: 13765


Return non-result and empty searches before awaiting host setup.

searchWithStructuredOwners waits for hostReady even when the response is unavailable, stale-cursor, or has no hits. These responses need no ownership projection, so this wait can delay the search response until lazy host installation completes. Return them first; the setup rejection handler remains attached.

Suggested fix
   const response = await search
+  if (response.kind !== 'results' || response.hits.length === 0) {
+    return response
+  }
   return (await hostReady) ? projectStructuredAiVaultSearchResponse(response) : response
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (await hostReady) ? projectStructuredAiVaultSearchResponse(response) : response
if (response.kind !== 'results' || response.hits.length === 0) {
return response
}
return (await hostReady) ? projectStructuredAiVaultSearchResponse(response) : response

A paired host's row or search hit could carry a workspace id such as
"constructor", which read an Object.prototype member as a tab list and broke
the render. The shared tab index now reads only own workspace entries.
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review and QA summary (head 0946766240c)

The problem

A native Claude or Codex chat gets a generated name after its first message (#25724). The tab and sidebar showed it, but AI Vault (the session history panel) needed a large set of renderer-side copies of the name to keep it from flashing back to "Claude Chat"/"Codex Chat" when the tab closed. Vault's indexed search never showed the generated name or a manual tab rename at all.

What changes for you

  • The tab strip, the sidebar row, the Vault list and Vault search all show the same name for a native chat, in one order: your manual tab rename, then the generated name, then "Claude Chat"/"Codex Chat".
  • Closing a tab, before or after the name arrives, no longer brings back "Codex Chat" in Vault. A chat named after its tab closed updates in Vault by itself.
  • Vault search shows native chats by their name. A native search hit now opens the native chat, like a list row does, including hits from a paired host. That also means search hits for native chats no longer offer "Copy resume command" or Delete, which matches list rows.
  • orca search in the terminal now also prints the native chat's name (or "Claude Chat"/"Codex Chat") instead of the first-prompt text, the same way the session list already treats the CLI.

Fixed during review

Item What Commit
Merge Merged main and resolved one conflict in the status feed (kept both sides) 7805a2a7383
Search depended on the chat host If the native chat host fails to start, Vault search now returns plain results instead of failing. Clients that can't open native chats no longer wait for it e761f169163
Naming publish The status feed and the open tab's title are updated independently, so one failing can't skip the other. Test added for the real naming path 7962c5dc9a6
Unchecked remote input A name from a paired host is checked with the same rule the record store uses, and the search hit's workspace id limit now matches the list row's (512) 3c4f9f04abc
Paired search hits couldn't open Hits from a paired host carried the chat owner but had no resume command, so the row disabled opening. They now open the native chat 9807f047260
Crash from a malformed paired host A workspace id such as constructor made a Vault row throw while rendering. Tab lookups now read only real entries 0946766240c
Comment Noted why the status feed must keep closed chats' entries: Vault names depend on it 4a3431e8394

Also checked and not a problem: a name saved on a live chat counts as a status change and re-runs that change's handlers. Each one is already guarded, so there is no second naming run, duplicate mail, second workspace rename or extra notification.

Deferred (P2 and below, not blocking)

  • Pre-existing native chats stay "Claude Chat"/"Codex Chat". Naming only runs on a chat's first message, so chats created before Name native Claude and Codex chats after their first message #25724 never get a name. The Vault list already shows them generically on main; this PR extends that to search and orca search, where the first-prompt text used to tell them apart. This is an open product decision.
  • Short flash on Vault reopen. If a chat is named while the Vault panel is hidden and no tab from that host is open, reopening Vault shows the cached "Codex Chat" until the status feed reconnects. That is about one local round trip, longer for a paired host. Fixing it without a renderer copy of the name means keeping the local status feed always on; left as a follow-up.
  • Offline paired hosts are retried. Vault rows from a paired host keep that host's status connection open while they are on screen, including retries every few seconds when it is offline. An open tab from that host already does the same, and it stops when the rows unmount.
  • Release timing. This PR removes a field Name native Claude and Codex chats after their first message #25724 added to tab snapshots. That is safe only because Name native Claude and Codex chats after their first message #25724 has not shipped: no release tag contains it, including the v1.4.222 draft. This should land before the next release cut that includes Name native Claude and Codex chats after their first message #25724. The body's "latest is v1.4.221" is now out of date.
  • Minor: while a chat host keeps failing to install, each (debounced) search logs a warning. The tab's drag label still uses the tab label. A rename on a live chat only notifies from the one naming call site.

Verified

  • Review: an adversarial review of the whole PR (host side, renderer side, deleted code and tests, plus an independent design comparison), two follow-up reviews of the fixes, and a release readiness pass (remote and cross-platform, crash/security/performance, compatibility). No P0 or P1 was found. The design comparison reached the same design independently and recommends keeping it as one PR. Splitting would bring back the flash on close.
  • Performance: measured with a script. With 2,000 chats in the feed and 400 rows subscribed, one status event costs about 0.5 ms, and only rows whose name changed re-render.
  • Electron QA: hidden app on a separate Mac, with stand-in Claude/Codex CLIs and a stand-in naming command, at head 9807f047260. The final head differs only by the tab-lookup guard and was not re-run. All six scenarios passed:
    1. Closed right after naming (Codex and Claude): the same Vault row keeps the name, with no refresh.
      s1-codex-named-tab-sidebar-vault.png s1-codex-closed-vault-keeps-name.png s1-claude-named-tab-sidebar-vault.png s1-claude-closed-vault-keeps-name.png
    2. Closed while naming was still running: the row shows "Codex Chat", then the name by itself.
      s2-closed-while-pending-codex-chat.jpg s2-closed-then-named-by-itself.jpg
    3. A manual rename wins in the tab, sidebar, Vault list and search, and clearing it brings the generated name back.
      s3-manual-rename-list.jpg s3-manual-rename-search.jpg s3-cleared-list.jpg s3-cleared-search.jpg
    4. Search names a closed chat, and the hit's Resume button opens the native chat.
      s4-closed-chat-search-hit.jpg s4-search-hit-opened-native-chat.jpg
    5. Names survive an app restart.
      s5-after-restart-names-kept.jpg
    6. Paired host: the chat's name shows in the client's Vault list and search, and the hit opens the chat.
      s6-paired-host-vault-list.jpg s6-paired-host-vault-search.jpg s6-paired-hit-opened-chat.jpg
  • Tests and checks: targeted test files pass, and each new test was checked to fail without its fix. tc:node, tc:web, tc:cli and the changed-code quality gate pass. CI: all checks pass on 0946766240c (20 passed, 18 skipped). One test shard was rerun once after its runner was shut down mid-job, with no failing test.

Not verified

  • Windows, Linux and WSL: no platform-specific code changed, and none was run.
  • Mobile: the phone app reads none of the changed fields, so it wasn't run.
  • SSH workspaces have no native chats.
  • Opening a paired-host chat that is closed on that host.
  • Mixed versions (an older desktop with a newer host): checked in code only.
  • After a "Change Title", the Vault row's preview briefly read "No conversation preview available". This wasn't compared against main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant