Skip to content

feat: same-file collision trails across agents and sessions - #84

Open
tcsenpai wants to merge 8 commits into
patoles:mainfrom
tcsenpai:feat/file-collisions
Open

tcsenpai wants to merge 8 commits into
patoles:mainfrom
tcsenpai:feat/file-collisions

Conversation

@tcsenpai

Copy link
Copy Markdown

Stacked on #79 (backfill), which it needs for the replay-aware clock.

With many sessions running in parallel, the one-canvas-per-session model cannot show the one thing that actually bites: two agents working on the same file at the same time.

  • A process-wide FileCollisionIndex records every file-touching tool call per normalized path in a 90s sliding window. When two distinct parties (session + agent) touch the same file and at least one of them writes, a file_collision event is emitted to every involved session. Read-vs-read is deliberately ignored (agents grepping the same config all day is noise).
  • Same session: a dashed, glowing trail between the involved agent nodes with the file name at the centroid, fading over the window.
  • Different sessions: a strip under the top bar naming the file and the sessions (✎ wrote, 👁 read); click a session to jump to it.
  • Unit tests for the index in scripts/file-collisions.test.ts.

https://claude.ai/code/session_01AGs5TZo6yxv7NHARHawyJf

Attaching to a session that already had activity only showed the text of the
current turn: tool calls, subagent spawns and earlier messages were pre-scanned
for dedup and never emitted.

- prepareBackfill() splits existing content: older turns are pre-scanned only,
  the last BACKFILL_TURNS (50) user turns are kept as raw lines.
- replayLines() runs those lines through the normal live path, with the
  session clock pinned to each entry's timestamp (WatchedSession.replayNow),
  so durations and the timeline reflect real timing. Session start is set to
  the first transcript entry for the same reason.
- Applies to both the VS Code watcher and the standalone relay. Codex already
  drained the full rollout on attach and is unchanged.
- Audio stays quiet during a replay burst.

Claude-Session: https://claude.ai/code/session_01AGs5TZo6yxv7NHARHawyJf
…ssion to new SSE clients

- Session clock now starts at the first transcript line that carries a
  timestamp; ai-title/mode/summary lines have none, which left the clock at
  'now', made elapsed() negative and collapsed every replayed event onto one
  instant in the frontend.
- The relay replayed buffered events only for the most recent session on SSE
  connect, so every other tab opened empty. It now sends each session's buffer;
  the frontend already buffers per session and flushes on selection.

Claude-Session: https://claude.ai/code/session_01AGs5TZo6yxv7NHARHawyJf
Real timestamps made a week-old session produce a 10000-minute timeline that
was almost entirely dead time. Gaps between replayed entries longer than
MAX_REPLAY_GAP_MS (30s) are now shortened to that length; the removed time is
tracked in WatchedSession.compressedMs and subtracted by elapsed() for live
events too, so the timeline stays continuous after the replay.

Claude-Session: https://claude.ai/code/session_01AGs5TZo6yxv7NHARHawyJf
… live events

- The context_update emitted before the replay used an uncompressed elapsed()
  ('now' minus a days-old start), which pushed the frontend clock past every
  replayed event and collapsed them again. It is now emitted after the replay.
- advanceClock() replaces the ad-hoc gap logic: it is called per replayed entry,
  once at the end of the replay (gap between last entry and now), and whenever
  live lines arrive, so idle pauses are compressed the same way live.

Claude-Session: https://claude.ai/code/session_01AGs5TZo6yxv7NHARHawyJf
Long sessions replay only the last BACKFILL_TURNS, but the clock starts at the
first transcript timestamp; the gap up to the first replayed entry was never
compressed because the compression clock had no previous event. Seed it at
session start.

Claude-Session: https://claude.ai/code/session_01AGs5TZo6yxv7NHARHawyJf
Audit findings applied:
- lineTimestamp() now parses the top-level timestamp of user/assistant/
  progress/system lines only. file-history-snapshot lines carry a nested,
  later timestamp that pinned the session start hours off and gave the whole
  replay negative times.
- Replay clock never moves backwards (transcript lines are not strictly
  ordered) and advanceClock() ignores timestamps in the future.
- elapsed() is monotonic per session so timer- and subagent-driven events
  cannot land before the last transcript event.
- No per-line context_update during replay; one is emitted at the end.
- The relay ships a replay as a single agent-event-batch frame instead of
  thousands of SSE messages that trickled onto the canvas like playback.
- Buffer trimming keeps agent_spawn/subagent_dispatch/model_detected so a
  late client never receives events for unknown agents.
- SSE frames carry ids; a reconnecting client (Last-Event-ID) gets only what
  it missed, or a reset when the relay was restarted, instead of duplicated
  history and doubled token totals.

Claude-Session: https://claude.ai/code/session_01AGs5TZo6yxv7NHARHawyJf
A shared FileCollisionIndex records every file-touching tool call per
normalized path in a 90s sliding window. When two distinct parties (session +
agent) touch the same file and at least one writes, a file_collision event
is emitted to every involved session. Read-vs-read is ignored on purpose.

- Same session: a dashed pheromone-style trail between the involved agent
  nodes with the file name at the centroid, fading over the window.
- Different sessions: a strip under the top bar naming the file and the
  sessions, with ✎/👁 for write/read; click a session to jump to it.
- Replay-aware: uses the replay clock during backfill so historical
  collisions are detected with the right timing.

Stacked on the backfill branch (needs WatchedSession.replayNow).

https://claude.ai/code/session_01AGs5TZo6yxv7NHARHawyJf
@cla-assistant

cla-assistant Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant