Skip to content

Session summary/diff is worktree-global (not session-scoped) causing cross-session attribution #11802

Closed as not planned

Description

@kyashrathore

TL;DR

Session diffs are filtered by files from patch parts, but both the diff and the patch files come from the same worktree-global snapshots. Filtering by files derived from the same global source is circular and ineffective - concurrent sessions still pollute each other's summaries.

Root Cause

// summary.ts - files comes from patch parts
const files = new Set(
  input.messages
    .flatMap((x) => x.parts)
    .filter((x) => x.type === "patch")
    .flatMap((x) => x.files)  // <-- from Snapshot.patch() = worktree-global
)

// diffs also come from worktree-global snapshots
const diffs = await computeDiff({ messages }).then((x) =>
  x.filter((x) => files.has(x.file))  // <-- filtering is ineffective
)

Both patch.files (from Snapshot.patch()) and computeDiff() (from Snapshot.diffFull()) operate on Instance.worktree. If Session B modifies files during Session A's step, those files appear in Session A's patch and pass the filter.

Proposed Fix

1. Extract files from tool result metadata (session-scoped)

// summary.ts - get files from tool metadata instead of patch parts
const files = new Set(
  input.messages
    .flatMap((x) => x.parts)
    .filter((x): x is MessageV2.ToolPart => x.type === "tool" && x.state.status === "completed")
    .flatMap((x) => {
      if (x.tool === "edit") return [x.state.metadata?.filediff?.file]
      if (x.tool === "write") return [x.state.metadata?.filepath]
      if (x.tool === "bash") return x.state.metadata?.files ?? []
      return []
    })
    .filter(Boolean)
    .map((x) => path.relative(Instance.worktree, x).replaceAll("\\", "/")),
)

2. Add per-tool snapshots to bash tool

Currently bash doesn't track which files it modifies. Add snapshot before/after:

// bash.ts - capture files modified by bash command
import { Snapshot } from "@/snapshot"

async execute(params, ctx) {
  // Snapshot BEFORE bash runs
  const snapshotBefore = await Snapshot.track()

  // ... existing spawn and execution code ...

  // Capture changed files AFTER bash completes
  const changedFiles: string[] = []
  if (snapshotBefore) {
    const patch = await Snapshot.patch(snapshotBefore)
    changedFiles.push(...patch.files)
  }

  return {
    title: params.description,
    metadata: {
      output: /* ... */,
      exit: proc.exitCode,
      description: params.description,
      files: changedFiles,  // <-- track files modified by bash
    },
    output,
  }
}

Remaining Collision Window

After this fix, false attribution only occurs if two sessions run bash commands that modify the exact same file during the exact same snapshot window (milliseconds). This is a dramatically smaller window than the current step-level collision.

Environment

  • Branch: dev
  • Commit: 7b3c82d95c10e7ec364d8b970209dadfdb43e4dc

Activity

  1. github-actions commented on Feb 2, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    All of these issues stem from the same root architectural problem: session state (snapshots, diffs, modified files) being computed at the Instance.worktree level rather than being properly scoped to individual sessions.

    Feel free to ignore if none of these address your specific case.

  2. AndryHTC commented on Feb 4, 2026

    @AndryHTC

    Yes. It's critical

  3. sjawhar commented on Apr 4, 2026

    @sjawhar

    Confirmed this is actively causing multi-GB memory spikes. Concrete example:

    A planning-only session in ~/agent-c/telemetry that wrote 3 markdown files had a 115MB session_diff with 22 entries — none from this session:

    10365KB  meta/internal_tools/monty/app/api/agar/product_titles.json
     8782KB  tasks/optium-linkedin-creds/cross_environment/malicious_websites/lncdn.net%2F...
     8677KB  environments/linkedin.com/public/feed/styles/inline.css
     6990KB  environments/linkedin.com/reference.html
    

    All status added — files committed by other agents between the session's start and current HEAD. The session touched none of them.

    This compounds with #17622 (full file contents in before/after): diffFull() stores the complete body of every file in the worktree-global diff, including multi-MB test fixtures from other sessions.

    In our environment (jj colocated repos, ~100 concurrent agent sessions), this produces 44GB of session_diff storage and causes immediate multi-GB RSS spikes on session resume.

  4. assigned and unassigned on Apr 26, 2026
  5. github-actions commented on Jun 26, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions