Skip to content

# Bug/UX: Custom chat names are not persisted to the server and are lost on /resume #388

@ovistoica

Description

@ovistoica

Summary

When a user renames a chat with eca-chat-rename (C-c C-S-r), the new name is
stored only in the Emacs buffer as a client-side buffer-local variable. It is
never sent to the server. As a result:

  • /resume lists chats by the server's auto-generated title (e.g.
    "Fixing eca API key config"), ignoring any user-assigned name
  • After /resume opens the chat in a new buffer, the custom name is gone entirely
  • The server's remote API (GET /chats) also returns only the original
    auto-generated title

Expected behavior: Renaming a chat should persist the new name so that
/resume (and any other chat listing) reflects it.


Steps to Reproduce

  1. Start an ECA session and send a prompt — the chat gets an auto-generated title
    (e.g. "Debugging API endpoint")
  2. Rename the chat: C-c C-S-r → type "My important investigation" → RET
  3. Observe: the tab, header line, and workspaces tree all show the new name ✅
  4. Kill the ECA session or start a new one
  5. In the new session, type /resume to list available chats
  6. Observe: the chat appears as "Debugging API endpoint" — the custom name is
    completely absent ❌
  7. Run /resume N to resume that chat
  8. Observe: the opened buffer has no custom title — the rename is permanently
    lost ❌

Root Cause Analysis

The rename is purely client-side and ephemeral

eca-chat-rename only writes to a buffer-local variable:

;; eca-chat.el
(defun eca-chat-rename ()
  "Rename last visited chat to a custom NEW-NAME."
  (interactive)
  (let ((new-name (read-string "Inform the new chat title: ")))
    (eca-assert-session-running (eca-session))
    (with-current-buffer (eca-chat--get-last-buffer (eca-session))
      (setq eca-chat--custom-title new-name))))   ; ← buffer-local only, no RPC call

There is no chat/rename RPC method, no chat/setTitle notification, nothing.
The server is never informed of the new name.

The server has a single title field, set only once

The server DB schema (db.clj) has exactly one field for the chat name:

:chats {"<chat-id>" {:id :string
                     :title (or :string nil)   ; the only title field
                     ...}}

This field is populated once — asynchronously on the first prompt, via a background
LLM call using the chatTitle prompt. After that it is never updated by any
user-facing mechanism
.

/resume reads :title from the server DB

The /resume command in commands.clj builds its chat listing using only
(:title chat):

;; features/commands.clj — /resume listing
(format "%s - %s - %s\n"
        (inc (.indexOf ^PersistentVector chats-ids chat-id))
        (shared/ms->presentable-date (:created-at chat) "dd/MM/yyyy HH:mm")
        (or (:title chat)                             ; ← server :title only
            (format "No chat title (%s user messages)" msgs-count)))

It has no knowledge of eca-chat--custom-title.

The display priority on the client hides the problem

eca-chat-title correctly prioritises the custom name over the server name:

;; eca-chat.el
(defun eca-chat-title ()
  (cond
   (eca-chat--custom-title ...)   ; custom wins if set
   (eca-chat--title ...)          ; server title fallback
   (t "Empty chat")))

This makes the rename appear to work perfectly within the active buffer session.
The illusion breaks the moment the buffer is killed — because eca-chat--custom-title
dies with it.

Full disconnect diagram

User: C-c C-S-r → "My important investigation"
          │
          ▼
  eca-chat--custom-title = "My important investigation"   (buffer-local)
  [NO server call, NO RPC, NO persistence]
          │
          ▼
  Tab / header / tree show "My important investigation"   ✅ (looks correct)

  [Session ends / buffer killed]
          │
          ▼
  eca-chat--custom-title = nil                            (gone forever)
  Server DB: :title = "Fixing eca API key config"         (unchanged)

  User: /resume
          │
          ▼
  Server sends: "3 - 30/03/2026 09:25 - Fixing eca API key config"  ❌

Proposed Fix

Option A — Persist the custom title server-side (recommended)

Add a chat/rename (or chat/setTitle) RPC method on the server that updates
[:chats chat-id :title] and flushes the cache.

Server (handlers.clj):

(defn chat-rename [{:keys [db* metrics]} {:keys [chat-id title]}]
  (when (get-in @db* [:chats chat-id])
    (swap! db* assoc-in [:chats chat-id :title] title)
    (db/update-workspaces-cache! @db* metrics)
    {}))

Server (server.clj):

"chat/rename" (handlers/chat-rename components params)

Client (eca-chat.el) — eca-chat-rename:

(defun eca-chat-rename ()
  "Rename the current chat, persisting the new name to the server."
  (interactive)
  (let ((new-name (read-string "New chat title: ")))
    (eca-assert-session-running (eca-session))
    (with-current-buffer (eca-chat--get-last-buffer (eca-session))
      ;; Update locally for immediate display
      (setq eca-chat--custom-title new-name)
      ;; Persist to server so /resume and other sessions see it
      (eca-api-notify (eca-session)
                      :method "chat/rename"
                      :params (list :chatId eca-chat--id
                                    :title new-name)))))

Client — on chat/contentReceived "metadata" frame:

Since lifecycle/send-content! already sends {:type :metadata :title ...} to the
client after the server writes the title, the client could also update
eca-chat--title (not eca-chat--custom-title) to keep the server and client
display names in sync:

("metadata"
 (unless parent-tool-call-id
   (setq-local eca-chat--title (plist-get content :title))))

This is already done — it sets eca-chat--title. With Option A the rename would
update :title on the server, which would then push a "metadata" frame back,
which would set eca-chat--title on the client. At that point
eca-chat--custom-title could be cleared (the server is now the source of truth):

("metadata"
 (unless parent-tool-call-id
   (let ((title (plist-get content :title)))
     (setq-local eca-chat--title title)
     ;; If server confirmed our rename, the custom title is now redundant
     (when (string= title eca-chat--custom-title)
       (setq-local eca-chat--custom-title nil)))))

This is optional — keeping eca-chat--custom-title as a local display override
is also fine.

Option B — Two-field schema: auto-title + user-title (alternative)

Keep the LLM-generated title in :title (untouched) and add a separate
:custom-title field that the rename operation writes:

;; db.clj
:chats {"<chat-id>" {:title (or :string nil)         ; LLM-generated
                     :custom-title (or :string nil)  ; user-assigned
                     ...}}

/resume and the remote API would then display (or :custom-title :title).

Advantage: The original auto-generated title is never destroyed, which can be
useful for debugging or if the user wants to revert.

Disadvantage: More schema surface and slightly more logic everywhere a title is
read.


Impact

Feature Current behaviour With fix
/resume listing Shows LLM-generated title Shows user-assigned name
After /resume, new buffer eca-chat--custom-title is nil (name lost) Server sends correct title via "metadata" frame
Remote HTTP API (GET /chats) Returns LLM-generated title Returns user-assigned name
Tab-line / header in active buffer Shows custom name correctly ✅ No change
Server cache (db.transit.json) Only :title saved :title updated by rename and saved
Name survives ECA server restart ❌ No (custom title is gone) ✅ Yes (written to disk cache)

Files to Change

Server

File Change
src/eca/handlers.clj Add chat-rename handler
src/eca/server.clj Register "chat/rename" in the dispatch table
src/eca/db.clj Optional: add :custom-title field to schema doc (Option B only)

Client (eca-emacs)

File Change
eca-chat.el eca-chat-rename Add eca-api-notify call after setting eca-chat--custom-title

That is the minimal fix — two changed files, one new server handler, one added
eca-api-notify call in the client.


Classification

This is could be either a bug or ux improvement in the rename feature: eca-chat-rename presents itself as
persisting a name change (C-c C-S-r with an explicit "Inform the new chat title"
prompt) but silently discards the change the moment the buffer is killed. A user
who renames a chat has a reasonable expectation that the name will be remembered.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    Status

    Ready

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions