Repository navigation
Record who changes a session, and who deletes it - #517
Merged
pufit merged 7 commits intoOct 17, 2026
Merged
Conversation
This was referenced Oct 17, 2026
alex-clickhouse
added this pull request to stack #521
October 17, 2026 07:20
Session changes had no attribution, and a delete removed the session's events, so nothing told who deleted a session. Each change that a principal makes is now a session event with an actor and a cause. - v054 adds session_events.actor_id and caused_by_actor_id, and the table session_deletions (last title, time, actor, cause). The four columns reference actor_refs and are in the actor column registry. - REST PATCH records title_changed, starred, unstarred, model_changed and parent_changed for each field that changes, in the transaction of the write. Archive (with its cascade), unarchive, resume and delete through REST, and stop and resume through the WebSocket, record the person as actor and cause. - A delete writes its session_deletions row in the same transaction. - The placeholder and the generated title are the system actor's change for the cause of the turn. The title task gets the cause as an argument, so a later turn of another person cannot change it. - Automatic archives, the Codex sync archive, the cron rotation retitle and the workflow stop are the system actor's own work. Channel commands record no actor and no cause. External tool call events take the attribution of their tool context. - Events that record the runtime state (started, idle, error, a stopped status after a cancel) stay unattributed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Describe the session event types, their details, their actor and cause, and the session_deletions record. The API docs show the two new fields on session events, and the WebSocket stop and resume events. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An archive of an archived session wrote another archived event, so a cascade could record that Alice archived a child that Bob had archived before, and two archives at the same time recorded two people for one change. The same applied to a restore. change_session_status reads the status, writes it and appends the event in one transaction, and writes nothing when the change has no effect. Archive, unarchive and the Codex sync archive use it. A cascade counts an archived descendant as archived and records no event for it. A restore changes only an archived session. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Also use plain punctuation instead of dashes in the new text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two resumes at the same time each wrote a created event for one status change, and two callers that created one session ID each wrote a created event, although the row names one creator. A stop that only interrupted an idle client recorded a stop request with nothing to stop. - transition uses change_session_status, so a resume writes its event only when its own write changes the status, in one transaction. - create_session takes created_event and appends the created event in the transaction of the insert, only when this insert made the row. _create_session uses it. - stop_session records stop_requested only when a turn or a registered task of the session runs (SessionManager.has_running_work). Tests hold two REST or WebSocket resumes, and two creates of one ID, until both callers have read the session. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A resume of a resumed session, a create of an existing ID and a stop when no turn runs record nothing, like an archive of an archived session. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each session now has one fixed cause, and every change that the agent or Nerve makes in a session is the system actor's change for that cause. - The placeholder and the generated title record the system actor and the session's cause, whoever sent the message. The title task no longer takes a cause argument; the stored cause does not change. - The created event records the creator and the session's cause from the creation cause, in the transaction of the insert. - The retitle of a rotated cron session and the stop of a workflow run record the system actor and the session's cause. - Automatic archives and the Codex sync archive stay the system actor's own housekeeping. Changes that a person makes are unchanged. - Tests: a title in Alice's session after Bob's message names Alice; the created event of a session that Nerve creates for Alice names Alice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alex-clickhouse
force-pushed
the
alex/attr-03-session-changes
branch
from
October 17, 2026 11:42
46699d1 to
253e2ec
Compare
alex-clickhouse
marked this pull request as ready for review
October 17, 2026 12:06
pufit
approved these changes
Oct 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Nerve records who changed a session and on whose behalf. Each change is a session event with
actor_idandcaused_by_actor_id. A delete removes the session's events, so Nerve records each delete in a new table,session_deletions.How
session_events.actor_id,session_events.caused_by_actor_id, and the tablesession_deletions(session ID, last title, time, actor, cause). The columns are nullable foreign keys toactor_refs. Old rows stay NULL.title_changed,starred/unstarred,model_changedorparent_changed, with the old and the new value. The event is written in the same transaction as the change.session_deletionsrow are one transaction.createdevent names the creator and the session's cause./new,/stop,/star) record no actor and no cause.started,idle,error) stay unattributed.Why
Session changes and deletes had no record of the person. This PR is part of the series that records the actor and the cause of every change.
Behaviour changes
reason: "resume"to itscreatedevent.stop_requestedevent.Testing
tests/test_session_attribution.py(new), including concurrency tests held at a barrier. Every commit passes its tests.Stack
main)🤖 Generated with Claude Code