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
TL;DR
Session diffs are filtered by files from
patchparts, but both the diff and thepatchfiles 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
Both
patch.files(fromSnapshot.patch()) andcomputeDiff()(fromSnapshot.diffFull()) operate onInstance.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)
2. Add per-tool snapshots to bash tool
Currently bash doesn't track which files it modifies. Add snapshot before/after:
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
dev7b3c82d95c10e7ec364d8b970209dadfdb43e4dc