fix(sessions): stop pinning the w1-myapp placeholder as Claude's /resume title (#457)

* fix(sessions): stop pinning the w1-myapp placeholder as Claude's /resume title

Local claude spawns passed the tab name as `--name`. That flag is not only the
cross-session peer name: it is also the prompt-box label, the `/resume` picker
entry and the terminal title, and a pinned title stops Claude generating its own
(`customTitle ?? aiTitle`). So every conversation of a case was listed in
`/resume` as the same `w1-myapp`, and none of them got a generated title. On one
workspace, 34 of 34 conversations spawned with `--name` had no ai-title, while
every conversation spawned without it had one.

Only a name the user chose is pinned now: `Session.cliPinnedName` is the name
when `nameSource === 'manual'`, carried to the builders as a separate `cliName`
so the tab/mux name is untouched. Placeholder and auto names let Claude title
the conversation again.

A rename in Codeman also reaches `/resume`: the new name is appended to the
conversation's transcript as the `custom-title` row `/rename` writes (never
creating the file, never writing an empty title). For a pane spawned without
`--name` this holds immediately; a pane spawned with one re-appends its own
title each turn, so there the new name holds from the next spawn.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(sessions): skip no-op renames and docker sessions when syncing the /resume title

A same-name PUT (the Session Options field saves on blur and recomposes the
unchanged placeholder) no longer flips nameSource to manual or appends a
custom-title row, and docker sessions skip the host transcript scan since their
transcript lives in the container. The skill pages no longer use a w<N>- name
as the peer-name example, and the changeset notes the re-append caveat.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* docs: record that nameSource decides --name and renames reach /resume

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: codeman-local <codeman@local>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
shenlvkang-collab
2026-09-24 01:48:33 +02:00
committed by GitHub
co-authored by Claude Opus 5.5 codeman-local
parent 7485afecaf
commit 69a71287e6
17 changed files with 326 additions and 23 deletions
+25
View File
@@ -176,6 +176,7 @@ import {
selectLastAnsweredTurn,
} from '../response-viewer-transcript.js';
import { readDeepSeekLastResponse } from '../../deepseek-transcript.js';
import { appendClaudeCustomTitle } from '../../claude-session-title.js';
// Path to linked-cases registry (same file used by case-routes resolveCasePath)
const LINKED_CASES_FILE = dataPath('linked-cases.json');
@@ -1162,10 +1163,14 @@ export function registerSessionRoutes(
const session = findSessionOrFail(ctx, id, req);
const name = String(body.name || '').slice(0, MAX_SESSION_NAME_LENGTH);
// A no-op rename (the Session Options name field saves on blur and recomposes the same
// string) must not flip nameSource to 'manual' or append a custom-title row to the transcript.
if (name === session.name) return { name: session.name };
session.name = name;
// Also update the mux session name if applicable
ctx.mux.updateSessionName(id, session.name);
persistAndBroadcastSession(ctx, session);
await syncClaudeTitle(session);
return { name: session.name };
});
@@ -2392,6 +2397,26 @@ export function registerSessionRoutes(
return full ? { text: lastText, timestamp: lastTimestamp, messages } : { text: lastText, timestamp: lastTimestamp };
}
/**
* Mirror a rename into the conversation's `/resume` title (claude-session-title.ts).
* Local Claude-format transcripts only: a remote pane's transcript lives on the remote host and a
* docker pane's inside the container (HOME=/home/agent), never under the host's projects dir. Best
* effort: the tab rename has already happened and must not fail on this.
*/
async function syncClaudeTitle(session: Session): Promise<void> {
if (getCli(session.mode)?.capabilities.transcript !== 'claude-jsonl' || session.remote || session.docker) return;
try {
const projectsDir = join(process.env.HOME || '/tmp', '.claude', 'projects');
const hookPath = ctx.getTranscriptPath(session.id);
const transcript = hookPath
? { sessionId: basename(hookPath, '.jsonl'), path: hookPath }
: await findClaudeTranscript(projectsDir, session.claudeSessionId || session.id, session.id);
if (transcript) await appendClaudeCustomTitle(transcript.path, transcript.sessionId, session.name);
} catch (err) {
console.warn(`[Session] Could not carry rename into the Claude transcript for ${session.id}:`, err);
}
}
/** Locate a top-level Claude transcript, including recovered tmux sessions. */
async function findClaudeTranscript(
projectsDir: string,