Repository navigation
Sessions in one folder no longer overwrite each other's tasks; a blocked prompt gives answers back - #5
Merged
Conversation
…folder can't write over each other's tasks Fixes #3. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR9Scezktm9yfS27HZMNgr
A prompt the user types while answers wait marks them delivered and passes on. When a hook further down drops that prompt, or the call fails, the answers now wait again for the next send, as the mod's own send already did. Fixes #4. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR9Scezktm9yfS27HZMNgr
tzafrir
marked this pull request as ready for review
October 15, 2026 12:47
… same write Review of the per-session keys found ways a task could still be lost or shown twice, a count that could go back, and store work too slow or too fragile for the paths that wait on it. Now: - Each session writes only its own key. Taking on another session's tasks is one write of the taker's slot, which notes what it took; the slots it took from leave after that, only if unchanged since read. A crash anywhere in between leaves nothing to take on twice. A session back from sleep, or resumed, lets go of what the notes name, before a press, a prompt or a tool call acts on its tasks. - Each slot carries its session's task count, and a slot leaves the store only once a later one carries it, so numbers never go back. - 0.1.4's value is read as it stands, and changed only to take its sessions' slots out and, while a 0.1.4 session lives there, to raise the count it numbers from; each change is made to the value read just before. - Ending is one write and no read, waited for through half the end's budget at most. After /clear or a resume in place, the session joins the project again. - A press or a prompt writes the slot and reads nothing, unless the session has slept. The mod's sends go one at a time. - A root too long for the store's 256-character keys goes by its hash. - An answer riding a typed prompt counts as sent only if its opening line entered; else it waits again. A toast says so, but for a prompt of the user's own that a hook dropped: the engine shows why. - The live check seeds the store by its file name, so no session has to write one first. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR9Scezktm9yfS27HZMNgr
A third review found a typed prompt that could carry an answer Send now was already sending, store work that could hold up a prompt for good, a /clear that could hide the tasks it meant to take on, and notes that could leave with a month-old slot. Now: - An update is claimed, marked delivered in one change of the task list, before any prompt or row carries it, so no other path sends it meanwhile. A row the running turn refuses gives it back, to go when the turn ends, or at once if the turn ended meanwhile. - A prompt carries an update if the update's head entered (`#5 <title>:`); a hook beneath that takes some out gives back those alone. An answer typed in the prompt box whose task left meanwhile goes as typed. - A press, a prompt or a tool call waits on the store 5 seconds at most; the work finishes on its own, in its turn, and until it does nothing else waits on the store. - After /clear or a resume in place, the session takes on what the one it left holds, from memory and ended, whether or not its end write landed. - A slot that leaves after a month hands its own notes of what it took to the session that removes it, so none of those is taken on twice. - An entry in 0.1.4's value that isn't a slot or a note is skipped. - A project gathers its slots, and the tasks each session's were taken on from by others, once as read. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR9Scezktm9yfS27HZMNgr
A fourth review found a /clear whose first read failed could hold its tasks twice, a session resumed then that could write over its own slot, store work that could still wait or pile up, a 0.1.4 note that could leave before the slot let its tasks go, and typed answers that could go twice, or count as carried when they weren't. Now: - A session that can't read the project as it joins writes nothing until it has; the heartbeat, assigning and its end read it again. It then takes on what the store holds for it, with what it held meanwhile: the tasks of the session it left, or those it assigned. - Raising 0.1.4's count when assigning waits on the store 5 seconds at most, and while store work given up on is out, the heartbeat asks for no more. - A slot that lets go of tasks a 0.1.4 note names is written before the note leaves, assigning too. - An answer typed in the prompt box while Claude is idle carries the responses a batch was about to send; when another send took it as it was saved, the prompt is dropped, saying it went. - A digest's heads are looked for beside a prompt's words, a message's in its words. - list_tasks marks what it shows heard through the same claim as every send. - A note in a slot that isn't one is skipped, and a key that goes on with `#` past the prefix is another folder's. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR9Scezktm9yfS27HZMNgr
…task A fifth review found that taking the project on replaced the task list with a copy taken a moment before, that a hot reload before a failed read was retried forgot whose tasks the process held, that one stored task that isn't one could stop a session reading its project (and so, now, writing), and that a slot leaving could leave a 0.1.4 copy of it to pass for it. Now: - A session takes the project on in one change of the task list, against the list as it stands, so a press meanwhile is kept. - The session whose tasks the process holds is the one it last took the project on as (`loadedFor`, in the session's state): a hot reload keeps it, and moving to another folder lets it go. - Anything in a slot that isn't a task, a count or a note of tasks taken is skipped, in 0.1.4's first layout too. - A slot that leaves takes with it any copy of it in 0.1.4's value. - While a 0.1.4 session lives in the project, the heartbeat raises its count only when the value just read is behind, with no second read. - A slot's notes are carried over in one place, and the store's shapes checked by one test. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR9Scezktm9yfS27HZMNgr
…t assigned A sixth review found that /clear in a session that never read the project dropped what it had assigned (round five's `loadedFor` names no session then), and that a session taking its own task back from a quiet session that had taken it let the task go again at its next heartbeat, leaving it in no pane. Now: - When no session of the process has read the project, what its sessions assigned is taken on beside what the store holds for each, the sessions before this one ended. - A session resumed into another folder holds none of this folder's tasks, and counts none of its sessions'. - A note of tasks taken is outweighed by a later note of the session it took them from taking them back; the taker drops it when it comes back and lets them go. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR9Scezktm9yfS27HZMNgr
…lder A seventh review found that a session taking its task back let it go again when the write noting it failed, that /clear in a session that never read the project wrote over the slot of a session resumed elsewhere meanwhile, that a session resumed into another folder kept this folder's count, open answer and drafts, and that a session whose task was taken back while it was gone still wrote an empty note of it. Now: - The heartbeat reads the project with the session's own slot as it holds it, so a note the store doesn't have yet still counts. - When no session of the process has read the project, a session of it that another process has resumed since keeps its slot: what it assigned goes on in this one's. - A session resumed into another folder starts there as a new one would: its count, the answer it was typing and its drafts stay behind. - A note emptied by a take-back is dropped, so a session that holds nothing else writes nothing as it joins. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR9Scezktm9yfS27HZMNgr
An eighth review found that a store stuck as a session started held the start up for as long as it stayed stuck, that /clear in a session that never read the project took a session resumed elsewhere, quiet there, for ended (so its tasks could wait in two panes once it woke), that a session resumed into another folder could raise a 0.1.4 count there before reading that project, and that a session whose writes failed while it held a task never stored its count once the task was answered. Now: - Joining waits on the store 5 seconds at most, as a press, a prompt or a tool call does: what the session takes on shows once it answers. - A session of the process that the store shows quiet keeps its slot, and the note of what was taken from it waits for it to come back. - A session that hasn't read the project changes nothing of 0.1.4's. - A session has a slot from its first write, landed or not, so a later write carries its count. The tests now type an answer without sending it where they mean to, and aim the slow read at the read they stage; a session that never held a task is checked to leave no slot. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR9Scezktm9yfS27HZMNgr
… the end A ninth review found three gaps in a session that reads the project only after joining: it dropped its own tasks it was done with, so a task it took on could get the number of one its Claude had withdrawn; read only as it ended, it took on the tasks of a quiet session that might come back, leaving them in the slot of a session that had just exited; and read on the heartbeat, it opened no pane for what it took on. Now: - A session's own tasks it is done with stay in its list as it takes the project on, and no task it takes on gets one of their numbers. - Taking the project on as it ends, a session takes nothing from a quiet session; what it holds of a session of its process that the store shows quiet, or alive, goes on in its own slot. - A first read on the heartbeat, assigning or waking shows the project as joining does: the pane opens for what was taken on. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR9Scezktm9yfS27HZMNgr
A tenth review found: - A session that first read the project on its heartbeat and then failed to write it showed none of what it took on: the status line and the pane stayed as they were. Its write now fails quietly, as the heartbeat's own does, and the next heartbeat makes it. - A 0.1.4 note of this session's tasks names numbers only, and a 0.1.4 session's write can put it back after it was read: a task taken on with one of those numbers was then let go. Such a task now gets a new number. - Resumed in another folder, the session kept its selected task and its notes of the first folder until it read the new one. It now starts there with neither. The id lists in toasts come from one helper, and the comments on keys, notes and the folder change say what the code does. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR9Scezktm9yfS27HZMNgr
An eleventh review, and the reverts that no test caught, found: - The start and the heartbeat waited on opening the pane as on the store: a surface slow to open it counted as a stalled store, and turned the heartbeat off until it opened. The redraw now waits apart, 5 seconds at most, and never stalls the store. - A Send now row the last conversation had not read could wake Claude in the next one, and a batch of presses or a send held for the turn's end crossed into another folder. Each now stays with its conversation. - Two fixes had no test of their own once the folder change started clean: resumed into another folder, the session takes on the slot the session it left has there; and after /clear, a session that hasn't read the project leaves 0.1.4's value alone. Both have one now. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR9Scezktm9yfS27HZMNgr
A twelfth review found that a resumed session holding nothing left its slot in the store as its last run left it, ended, until its first heartbeat: joining read the slot but didn't count it as the session's, as the heartbeat does. Joining now does, so the session writes its slot as it joins, its count in it. The design doc says that a session that never read the project reads it at its end before the write that marks its slot ended. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR9Scezktm9yfS27HZMNgr
A thirteenth review found two gaps while the store is slow after /clear, when the new session's join waits in the queue behind the old one's end write: - A session that ended before its join came up lost its end mark: the join cleared it, so the session read the project as a live one, taking on quiet sessions' tasks, and its end write left its slot alive until it went stale. A count of ends now tells the join whether its session ended meanwhile, and keeps the mark if so. - A task Claude assigned before the join was stamped with the old session's id, so it showed as from an earlier session in the pane and in Claude's context for good. Tasks now take the conversation's id from the engine, and the pane and context compare against it. The design doc says what a slow join does, and that the 5-second bound applies to each piece of store work a press or prompt waits on. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR9Scezktm9yfS27HZMNgr
A fourteenth review found: - A hook beneath that rewords a response's head (masks a secret in a task's title, say) made its prompt enter without it, so the response went back as not sent; the turn that prompt started then ended with it waiting, and it went again, turn after turn. A response given back once now counts as heard the next time, with a toast saying so. - A session that ended after /clear before its join had even started (the end coming before the clock ran the join) was taken for alive: the join now gets the count of ends from the end that scheduled it. - An assign refused for repeating an open task, or for the open limit, wrote nothing, so tasks let go of in its read stayed in the stored slot until the next heartbeat. The read now writes the slot when it lets a task go. - Resumed into another folder while the store was slow, a task Claude assigned before the join was dropped there and could land in the folder it left. Claude can't assign until the join is done, and is told to try again in a moment. The live check fails with a clear message if its folder's path is too long for the store keys it seeds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR9Scezktm9yfS27HZMNgr
A fifteenth review found that round 14's fixes went too far: - A response given back once counted as heard the next time, so one a hook beneath took out entirely was lost on the second try. Responses are given back every time again, as before; one given back by a prompt that entered is held from the send at the end of a turn, which is what went round and round, and goes with Send now or the next prompt. - The assign check compared the session's root with the one it joined, but /cd, a host's directory change and a worktree move change the root too, with no join: after one, no task could be assigned. A join into another folder now raises a flag until it has read that folder, and while it is up the session acts on none of the tasks it holds of the folder it left: Claude can't assign, list or withdraw a task, no response goes to Claude, and no prompt or context carries one. - A session that ended before its join ran still got a heartbeat, and the pane opened for it. Neither happens now. A 0.1.4 note whose task was let go of in a write that failed is checked to leave only once the slot is written without it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR9Scezktm9yfS27HZMNgr
A sixteenth review found: - list_tasks marked heard every response of a task it listed, one that came in after it read the list among them: Claude saw the task open and never heard the answer. It now marks only the responses it shows, as main did, and a later one goes as any other. The design doc and comments say exactly what a response a hook gave back does as a turn ends: it goes along with others, never alone. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR9Scezktm9yfS27HZMNgr
A seventeenth review found: - Resumed into another folder and straight back while the store was slow, the session measured the second move from where the queue had got to, not from the folder the first move was for. It saw no move, so between the two joins Claude listed and assigned in the middle folder, and the second join dropped what it assigned. A move is now measured from the folder the latest join is for, set as it is asked. - A response held for Send now was still held after a move to another folder: a task there with the same number and time waited as if a hook had kept it out. The move now clears the held responses with the rest of what the session tracked of the folder it left. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR9Scezktm9yfS27HZMNgr
This was referenced Oct 18, 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.
Requested by Tzafrir · project thread
Before: two sessions open in one folder shared one store value, and each heartbeat wrote the whole value back. If they interleaved, one session could put back a stale copy of the other's slot and lose an answer it had just saved (#3). Separately, answers riding a prompt the user typed were marked delivered before the prompt went on, so if a hook further down blocked that prompt (an organization's
UserPromptSubmithook, say), they were lost (#4).After: each session's slot has a store key of its own, and only that session writes it, so no session can write over another's tasks. A session that takes on another's tasks notes them in its own slot, in the same write, so a crash at any point leaves nothing to take on twice, and a session back from sleep lets go of what another took meanwhile, unless the session it took them from has taken them back since. A session writes nothing until it has read the project, so a store that fails as it joins can't make it write over tasks of its own it hasn't taken on yet. After
/clearor a resume in place, the session it becomes takes on what the one it left held; resumed into another folder, it starts there as a new session would, and acts on no task until it has read that folder. Answers riding a typed prompt that a hook drops, or takes them out of, or whose call fails, go back to waiting and are sent with the next prompt or Send now; a toast says so where the engine doesn't already.How:
hooks/project.tsholds the layout:keysOf(a root too long for the store's 256-character keys goes by its hash),projectOf(every slot, and what other sessions took from each, gathered once as read, a note less what was taken back since),adopt(what a joining session takes on) andprune(which notes and leftover slots can go).register.tsxruns its store work one piece at a time, writes only the session's own key, and reads the project only when joining, on the heartbeat, on assigning, or when a press, a prompt or a tool call finds the session asleep; no piece of store work holds up a press, a prompt, a tool call or the start more than 5 seconds. A session takes the project on in one change of its task list, so a press meanwhile is kept, andloadedForin its state names the session whose tasks the process holds, so a hot reload keeps them. Updates are claimed in one change of the task list before any prompt or row carries them, andpassOngives back those whose head didn't enter. 0.1.4's one-value layout is read as it stands, and changed only to take its sessions' slots out and keep its count ahead while a 0.1.4 session lives there. Anything stored that isn't a slot, a task, a count or a note is skipped. Bumped to 0.1.5 and updated docs/DESIGN.md.Tests: 125 kit tests pass (
claude plugin test .), among them writes and presses landing between a read and a write, a crash between taking tasks on and cleaning up, sleep, resume and/clear(with a failed end write, a store that failed as the session joined, and a resume into another folder), tasks taken back, a store that stops answering, 0.1.4 sessions alive beside this version, and hooks beneath that drop, strip or rewrite a prompt. Each fix was reverted on its own (122 reverts) to check that a test fails, and every one does.tsc -p .,claude plugin validate .andtests/live/test_pane.pyagainst Claude Code 2.1.296 pass.Fixes #3
Fixes #4
🤖 Generated with Claude Code
https://claude.ai/code/session_01DR9Scezktm9yfS27HZMNgr