mirror of
https://github.com/Yeachan-Heo/oh-my-claudecode.git
synced 2026-09-03 06:25:33 +08:00
fix: eliminate session state leakage across parallel Claude Code sessions
When multiple Claude Code sessions run in the same directory, mode state (ultrawork, ralph, autopilot, etc.) leaked between sessions because state files fell back to legacy shared paths. This change enforces session-scoped state isolation: when a session_id is known, legacy state is invisible. Key changes: - persistent-mode.cjs: add isSessionMatch() helper, remove legacy fallback in readStateFileWithSession() when sessionId present, apply to all 8 modes - session-start.mjs: restore only from session-scoped paths when session_id exists, legacy paths only when session_id absent - ralph/loop.ts: readRalphState() and isUltraQAActive() no legacy fallback - ultraqa/index.ts: readUltraQAState() no legacy fallback - autopilot/state.ts: readAutopilotState() session-scoped only, thread sessionId through cancel.ts, validation.ts, enforcement.ts; fix missing sessionId propagation in transitionRalphToUltraQA -> updateExecution - state-tools.ts: warnings added for missing session_id, schema descriptions updated - Tests updated to match session-first behavior Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -292,19 +292,21 @@ async function main() {
|
||||
}
|
||||
|
||||
// Check for ultrawork state - only restore if session matches (issue #311)
|
||||
// Check session-scoped path first, then legacy paths
|
||||
// Session-scoped ONLY when session_id exists — no legacy fallback
|
||||
let ultraworkState = null;
|
||||
if (sessionId && /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,255}$/.test(sessionId)) {
|
||||
// Session-scoped ONLY — no legacy fallback
|
||||
ultraworkState = readJsonFile(join(directory, '.omc', 'state', 'sessions', sessionId, 'ultrawork-state.json'));
|
||||
}
|
||||
if (!ultraworkState) {
|
||||
// Validate session identity
|
||||
if (ultraworkState && ultraworkState.session_id && ultraworkState.session_id !== sessionId) {
|
||||
ultraworkState = null;
|
||||
}
|
||||
} else {
|
||||
// No session_id — legacy behavior for backward compat
|
||||
ultraworkState = readJsonFile(join(directory, '.omc', 'state', 'ultrawork-state.json'));
|
||||
}
|
||||
if (!ultraworkState) {
|
||||
ultraworkState = readJsonFile(join(homedir(), '.omc', 'state', 'ultrawork-state.json'));
|
||||
}
|
||||
|
||||
if (ultraworkState?.active && (!ultraworkState.session_id || ultraworkState.session_id === sessionId)) {
|
||||
if (ultraworkState?.active) {
|
||||
messages.push(`<session-restore>
|
||||
|
||||
[ULTRAWORK MODE RESTORED]
|
||||
@@ -321,16 +323,21 @@ Continue working in ultrawork mode until all tasks are complete.
|
||||
}
|
||||
|
||||
// Check for ralph loop state
|
||||
// Check session-scoped path first, then legacy paths
|
||||
// Session-scoped ONLY when session_id exists — no legacy fallback
|
||||
let ralphState = null;
|
||||
if (sessionId && /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,255}$/.test(sessionId)) {
|
||||
// Session-scoped ONLY — no legacy fallback
|
||||
ralphState = readJsonFile(join(directory, '.omc', 'state', 'sessions', sessionId, 'ralph-state.json'));
|
||||
}
|
||||
if (!ralphState) {
|
||||
// Validate session identity
|
||||
if (ralphState && ralphState.session_id && ralphState.session_id !== sessionId) {
|
||||
ralphState = null;
|
||||
}
|
||||
} else {
|
||||
// No session_id — legacy behavior for backward compat
|
||||
ralphState = readJsonFile(join(directory, '.omc', 'state', 'ralph-state.json'));
|
||||
}
|
||||
if (!ralphState) {
|
||||
ralphState = readJsonFile(join(directory, '.omc', 'ralph-state.json'));
|
||||
if (!ralphState) {
|
||||
ralphState = readJsonFile(join(directory, '.omc', 'ralph-state.json'));
|
||||
}
|
||||
}
|
||||
if (ralphState?.active) {
|
||||
messages.push(`<session-restore>
|
||||
|
||||
Reference in New Issue
Block a user