a chat with no chosen directory runs in the caller's own home
The default was DATA_PATH/<email>/general_chat_sessions, a dedicated directory so /chat sessions formed their own Claude project group instead of cluttering the home. It is a sibling of the home, and confineUserTree makes every sibling the platform's at 0700 because the others are attachments and email_accounts. So it was unreachable for a member: the first live member turn started there and every Bash call failed on its own working directory before doing anything. A per-member copy inside each home fixed the symptom and left two rules to remember. The owner chose one rule instead — the account's own home, whoever they are — and accepted the trade knowingly: /chat sessions now share a project group with anything else run from that home, which was the reason the dedicated directory existed. Removed rather than left dangling: getGeneralChatSessionsCwd, ensureGeneralChatSessionsCwd, ensureMemberChatCwd, and general_chat_sessions from USER_DIRS so new accounts stop getting it. Existing directories are untouched and their transcripts stay where they are — Claude groups by cwd, so the owner's old /chat history remains under its own project slug rather than moving. The UI labels move with it: the default group now reads "home" rather than naming a directory that no longer has a role. ChatIdentity keeps carrying both email and home. The pairing was justified in the comment by general_chat_sessions being email-derived, which is now gone — but the distinction it encodes is real (the email says who, the home says where), so the comment explains that instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -153,7 +153,7 @@ function NewChat(props: NewChatProps) {
|
||||
invalidateClaudeSessions();
|
||||
}, [invalidateClaudeSessions]);
|
||||
|
||||
// context 'chat' tells the backend to run this session from the dedicated general_chat_sessions cwd, so
|
||||
// context 'chat' tells the backend to run this session from the caller's own home, so
|
||||
// its transcript lands in Claude's own store as an isolated project group (source of truth).
|
||||
const chat = useChat(undefined, locationState?.model, {
|
||||
resumeSummary,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useChatPwds } from 'state/useClaudeSessions';
|
||||
import { DirPickerModal } from './DirPickerModal';
|
||||
|
||||
type PwdSelectorProps = {
|
||||
value: string | null; // null = the default general_chat_sessions dir
|
||||
value: string | null; // null = the caller's own home
|
||||
onChange: (cwd: string | null) => void;
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ export const PwdSelector = ({ value, onChange }: PwdSelectorProps) => {
|
||||
const [custom, setCustom] = useState('');
|
||||
|
||||
const isDefaultActive = value === null || value === defaultCwd;
|
||||
const label = isDefaultActive ? 'general_chat_sessions' : basename(value!);
|
||||
const label = isDefaultActive ? 'home' : basename(value!);
|
||||
|
||||
const pick = (cwd: string | null) => {
|
||||
onChange(cwd);
|
||||
@@ -58,9 +58,7 @@ export const PwdSelector = ({ value, onChange }: PwdSelectorProps) => {
|
||||
className={`flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs cursor-pointer hover:bg-muted ${selected ? 'text-duck-teal' : 'text-foreground/80'}`}
|
||||
>
|
||||
<Check className={`h-3.5 w-3.5 shrink-0 ${selected ? 'opacity-100' : 'opacity-0'}`} />
|
||||
<span className="min-w-0 flex-1 truncate">
|
||||
{p.isDefault ? 'Default · general_chat_sessions' : shorten(p.cwd)}
|
||||
</span>
|
||||
<span className="min-w-0 flex-1 truncate">{p.isDefault ? 'Default · home' : shorten(p.cwd)}</span>
|
||||
{p.sessionCount > 0 && <span className="shrink-0 opacity-40">{p.sessionCount}</span>}
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -39,7 +39,7 @@ export const SessionList = () => {
|
||||
const [selected, setSelected] = useSelectedChatSession();
|
||||
// A group path when we're on one; otherwise the open session's own directory, so /chat/<id> shows
|
||||
// that session among its neighbours instead of snapping the list back to the default group. Null =
|
||||
// the default general_chat_sessions dir.
|
||||
// the caller's own home.
|
||||
const activeCwd = cwdFromSplat(splat) ?? selected?.cwd ?? null;
|
||||
const { sessions, isLoading, error, refetch, deleteSession, renameSession } = useClaudeSessions(activeCwd);
|
||||
const [editingId, setEditingId] = useState<string | null>(null);
|
||||
|
||||
@@ -20,7 +20,7 @@ export const GROUP_SEGMENT = 'g';
|
||||
/** Absolute cwd → path suffix. Encoded per segment; React Router decodes it the same way. */
|
||||
const encodeCwd = (cwd: string): string => cwd.split('/').filter(Boolean).map(encodeURIComponent).join('/');
|
||||
|
||||
/** The list for a group. `null` = the default general_chat_sessions group. */
|
||||
/** The list for a group. `null` = the caller's own home. */
|
||||
export const chatListPath = (cwd: string | null | undefined): string =>
|
||||
cwd ? `/chat/${GROUP_SEGMENT}/${encodeCwd(cwd)}` : '/chat';
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ export type ClaudeSessionDetail = {
|
||||
|
||||
export type ClaudePwd = { cwd: string; sessionCount: number; updatedAt: string; isDefault: boolean };
|
||||
|
||||
// Query string for the selected working directory (null/undefined = the default general_chat_sessions dir).
|
||||
// Query string for the selected working directory (null/undefined = the caller's own home).
|
||||
const cwdQuery = (cwd?: string | null) => (cwd ? `?cwd=${encodeURIComponent(cwd)}` : '');
|
||||
|
||||
/** The default /chat dir plus every directory that already has Claude sessions. */
|
||||
|
||||
Reference in New Issue
Block a user