Skip to content

fix(sessions): guard continueRecent/list against cwd collisions in shared session dir#96881

Closed
moguangyu5-design wants to merge 2 commits into
openclaw:mainfrom
moguangyu5-design:fix/96542-session-cwd-collision
Closed

fix(sessions): guard continueRecent/list against cwd collisions in shared session dir#96881
moguangyu5-design wants to merge 2 commits into
openclaw:mainfrom
moguangyu5-design:fix/96542-session-cwd-collision

Conversation

@moguangyu5-design

@moguangyu5-design moguangyu5-design commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Closes #96542

What Problem This Solves

getDefaultSessionDir encodes the cwd by replacing path separators (/, \\, :) with -, which is not injective. Two genuinely different project directories such as /home/alice/dev/client/app and /home/alice/dev/client-app collapse to the same session directory.

When SessionManager.continueRecent or SessionManager.list later reads that directory, it used to pick the newest file by mtime without checking the cwd stored in the session header. As a result, resuming a session from one project could silently load the conversation from the other project, carrying unrelated history into the current working directory.

Why This Change Was Made

  • findMostRecentSession now parses the session header and filters by cwd.
  • SessionManager.continueRecent passes the requested cwd to findMostRecentSession, so it only resumes a session whose header cwd matches the current project.
  • SessionManager.list filters its results by the requested cwd as well, so listing sessions for a colliding directory only returns sessions that belong to the requested project.
  • Project-scoped resume/list now requires an exact cwd match; cwd-less/malformed files are not silently adopted as belonging to an arbitrary project. Shipped session headers (e.g. v2026.5.28, v2026.6.10) always record cwd, so no compatibility fallback is needed.

User Impact

Users working in directories whose paths differ only by a path separator are no longer at risk of continue resuming the wrong project's conversation. The fix is fully backward-compatible with existing session files that have a recorded cwd.

Evidence

Added regression tests in src/agents/sessions/session-manager.test.ts that reproduce the collision and the wrong-project resume:

  • Confirmed that getDefaultSessionDir("/home/alice/dev/client/app") and getDefaultSessionDir("/home/alice/dev/client-app") currently map to the same directory.
  • Wrote two session files in that directory, made the file for client-app strictly newer, and verified that continueRecent(cwdA) now resumes the client/app session instead of the newer client-app session.
  • Verified that SessionManager.list(cwdA) and SessionManager.list(cwdB) each return only their own session.
  • Added a test with a newer cwd-less session file to confirm it is ignored when resuming/listing for a specific cwd.

Local verification:

pnpm test src/agents/sessions/session-manager.test.ts -- --run
pnpm exec oxlint src/agents/sessions/session-manager.ts src/agents/sessions/session-manager.test.ts
pnpm format:check src/agents/sessions/session-manager.ts src/agents/sessions/session-manager.test.ts
pnpm tsgo:core

All passed.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels Jun 26, 2026
@clawsweeper

clawsweeper Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

This PR is now superseded: current main already has the same compatibility-preserving cwd-aware session resume/list fix from the merged canonical branch, and this branch only reworks the same two files with alternate helper shape and duplicate coverage.

Canonical path: Keep the current-main implementation from #97785 and close this duplicate branch; any further session-collision work should be a narrow follow-up against current main.

So I’m closing this here and keeping the remaining discussion on #97785.

Review details

Best possible solution:

Keep the current-main implementation from #97785 and close this duplicate branch; any further session-collision work should be a narrow follow-up against current main.

Do we have a high-confidence way to reproduce the issue?

No, current main no longer reproduces the bug: the session manager now filters recent-session lookup and project-scoped list results by exact header cwd. The latest release still has the old behavior, so this is fixed on main but not yet shipped in v2026.6.11.

Is this the best way to solve the issue?

No, this PR is no longer the best way to solve the issue because #97785 already merged the same compatibility-preserving fix with real on-disk proof. The best path is to keep that implementation and avoid landing duplicate session-selection changes.

Security review:

Security review cleared: The diff only changes local JSONL session-header parsing/filtering and tests; it adds no dependencies, workflow changes, secret handling, downloads, or new code-execution paths.

AGENTS.md: found and applied where relevant.

What I checked:

  • Current main source implements cwd-aware recent selection: Current main parses the JSONL session header during recent-session discovery and filters candidates to candidate.header.cwd === cwd when a cwd is supplied; continueRecent now passes the requested cwd into that selector and SessionManager.list passes cwd into list filtering. (src/agents/sessions/session-manager.ts:1191, fc5ba0e58bb4)
  • Current main has regression coverage for colliding cwd directories: The current test suite creates two session files for /home/alice/dev/client/app and /home/alice/dev/client-app, makes the other cwd newer, and asserts unfiltered lookup returns the newer file while cwd-scoped lookup, continueRecent, and SessionManager.list return only the matching cwd session. (src/agents/sessions/session-manager.test.ts:156, fc5ba0e58bb4)
  • Merged canonical PR provenance: Live GitHub data shows fix(sessions): avoid cross-cwd recent resumes #97785 merged on 2026-07-01 and closed the same canonical session cwd-collision issue, with the same header-cwd filter strategy and real on-disk proof in the PR body. (e2dd5a0309a3)
  • Fix commit provenance: The merged fix commit is e2dd5a0309a3e35325ae9fb3e099475276de438c, authored by qingminlong and committed at 2026-06-30T17:46:40-07:00 with subject fix(sessions): avoid cross-cwd recent resumes (#97785). (e2dd5a0309a3)
  • Latest release does not yet contain the fix: The latest release tag v2026.6.11 still has the old cwd-blind findMostRecentSession(sessionDir) path and SessionManager.list call without cwd filtering, so the superseding fix is current-main-only until a later release ships it. (src/agents/sessions/session-manager.ts:1188, e085fa1a3ffd)
  • This branch is now duplicate implementation work: The live PR diff and compare show this branch is two commits ahead and four behind current main, still touching only src/agents/sessions/session-manager.ts and src/agents/sessions/session-manager.test.ts to implement the same cwd-aware selection with an alternate options-object helper signature and extra tests. (a7eced069c9f)

Likely related people:

  • qingminglong: Authored the merged superseding cwd-aware session resume/list fix that is now on current main. (role: recent area contributor; confidence: high; commits: e2dd5a0309a3; files: src/agents/sessions/session-manager.ts, src/agents/sessions/session-manager.test.ts)
  • vincentkoc: Merged the superseding session cwd-collision fix and also merged the earlier long-header recent-session fix in the same session-manager area. (role: merger and adjacent reviewer; confidence: high; commits: e2dd5a0309a3, 695cea68f546; files: src/agents/sessions/session-manager.ts, src/agents/sessions/session-manager.test.ts)
  • rohitjavvadi: Authored the merged long-header recent-session fix that changed the same findMostRecentSession seam and adjacent tests shortly before this cwd-collision work. (role: recent area contributor; confidence: medium; commits: 695cea68f546; files: src/agents/sessions/session-manager.ts, src/agents/sessions/session-manager.test.ts)

Codex review notes: model internal, reasoning high; reviewed against fc5ba0e58bb4; fix evidence: commit e2dd5a0309a3, main fix timestamp 2026-06-30T17:46:40-07:00.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. labels Jun 26, 2026
@harjothkhara

Copy link
Copy Markdown
Contributor

I think the cwd-less fallback in this PR needs either a cited shipped-state reason or removal.

Two-lens read:

  • Runtime/correctness: sessionFileMatchesCwd() returns true when header.cwd is missing/empty, and SessionManager.list() keeps !session.cwd. In the exact shared-directory collision this PR is fixing, a cwd-less JSONL file remains eligible for both projects; if it is newest, continueRecent(cwdA) can still adopt a transcript that is not proven to belong to cwdA.
  • Contract/compat: I checked the first shipped tag that contains this session manager (v2026.5.28) and the latest stable tag I found in this lane (v2026.6.10); both define SessionHeader.cwd: string and write cwd: this.cwd for new session headers. I do not see a tagged upgrade contract for cwd-less session headers.

If there is a real legacy state this preserves, please cite the version/path and add a regression test showing why those files should match every cwd. Otherwise, I think the cwd-scoped paths should require a string header cwd matching the requested cwd, leaving cwd-less/malformed files discoverable only through explicit open/list-all style flows rather than project-scoped resume/list.

@moguangyu5-design

Copy link
Copy Markdown
Contributor Author

@harjothkhara Good catch. I removed the cwd-less fallback entirely.

  • sessionFileMatchesCwd() now requires header.cwd === cwd when a cwd is requested; missing/empty cwd no longer matches.
  • SessionManager.list() now filters with session.cwd === cwd only.
  • Added a regression test that writes a newer cwd-less session file alongside a matching-cwd file and verifies the cwd-less file is ignored by both continueRecent and list.

You are right that shipped headers (v2026.5.28, v2026.6.10) always record cwd, so there is no cited upgrade contract that warranted the fallback.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Jun 26, 2026
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 🛠️ actively grinding The PR author has acted after the latest ClawSweeper review and work remains. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 27, 2026
@moguangyu5-design

Copy link
Copy Markdown
Contributor Author

Real behavior proof for the cwd-collision fix.

I verified the regression tests reproduce the bug on main and pass with this fix.

Repro steps

  1. Checked out a temporary branch from main.
  2. Applied only the new tests from this PR (src/agents/sessions/session-manager.test.ts) without the source changes.
  3. Ran the regression tests.

Before the fix (on main, tests only)

git checkout -b temp-repro main
git checkout fix/96542-session-cwd-collision -- src/agents/sessions/session-manager.test.ts
pnpm test src/agents/sessions/session-manager.test.ts -- --run

Result: 3 failed, 47 passed

  • does not resume a session from a different cwd that shares the encoded session dir
    • Expected session-A.jsonl for /home/alice/dev/client/app, but got session-B.jsonl (the newer file belonging to /home/alice/dev/client-app).
  • lists only sessions that belong to the requested cwd when dirs collide
    • SessionManager.list(cwdB) returned both session-A.jsonl and session-B.jsonl instead of only session-B.jsonl.
  • ignores cwd-less sessions when resuming or listing for a specific cwd
    • Expected matching.jsonl, but got cwdless.jsonl because the cwd-less file was newer.

This confirms the bug: when two project paths collapse to the same encoded session directory, a pure mtime-based pick loads the wrong project's session.

After the fix (on fix/96542-session-cwd-collision)

git checkout fix/96542-session-cwd-collision
pnpm test src/agents/sessions/session-manager.test.ts -- --run

Result: 50 passed, 0 failed

I also ran the broader session test suite:

pnpm test src/agents/sessions/ -- --run

Result: 16 test files passed, 120 tests passed, 0 failed.

Static checks

npx oxfmt --check src/agents/sessions/session-manager.ts src/agents/sessions/session-manager.test.ts
npx oxlint --tsconfig config/tsconfig/oxlint.json src/agents/sessions/session-manager.ts src/agents/sessions/session-manager.test.ts

Both pass with 0 warnings and 0 errors.

@moguangyu5-design

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed status: 🛠️ actively grinding The PR author has acted after the latest ClawSweeper review and work remains. labels Jun 28, 2026
@moguangyu5-design
moguangyu5-design force-pushed the fix/96542-session-cwd-collision branch from b2fd3aa to a7eced0 Compare July 1, 2026 05:51
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Jul 1, 2026
@clawsweeper

clawsweeper Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper applied the proposed close for this PR.

  • Action: closed this PR.
  • Close reason: duplicate or superseded.
  • Evidence: durable ClawSweeper review.
  • Coverage proof: PR B clearly implements and merged the same concrete session cwd-collision fix that PR A was trying to land, including the relevant continueRecent/list behavior and proof surface. Any differences in helper shape or extra tests are incidental and can be handled on the canonical merged PR/main follow-up path. Covering PR: fix(sessions): avoid cross-cwd recent resumes #97785.

@clawsweeper clawsweeper Bot closed this Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P2 Normal backlog priority with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: S status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]sessions: project cwds differing only by a path separator share one session dir, so continue resumes the wrong project

2 participants