fix(locks): free every root on release --all - #199
Merged
Merged
Conversation
lockm release --all only freed the holder's rows under the root of the checkout it ran from. The write hook claims in whichever checkout a write lands in, so a session that wrote into a second worktree or a scratch directory kept those claims until the TTL or session end, though the using-devkit skill promised "drop everything you hold". Release matches on the exact holder alone. Holder ids are globally unique, so the root filter bought nothing. Sub-agent rows (holder/agent) still stay for SubagentStop, as locks.md already documented. The daemon ReleaseAll request keeps its root field for wire compatibility: the daemon ignores it, and an older daemon still parses the request and releases in the caller's root, instead of failing the handshake and refusing every lock write until it restarts. MCP locks.release with all=true gets the same scope through the shared function. Closes #197 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
AbysmalBiscuit
force-pushed
the
197-fix-locks-release-all-frees-every-root
branch
from
September 27, 2026 20:03
4f65041 to
8ed351b
Compare
AbysmalBiscuit
deleted the
197-fix-locks-release-all-frees-every-root
branch
September 27, 2026 20:13
AbysmalBiscuit
added a commit
that referenced
this pull request
Sep 27, 2026
🤖 I have created a release *beep* *boop* --- ## [0.14.7](v0.14.6...v0.14.7) (2026-09-27) ### Features * **brief:** add a rules section ([#160](#160)) ([7ba41e2](7ba41e2)) * **brief:** list tasks by app and trim the brief ([#162](#162)) ([d991929](d991929)) * **command:** model permission changes ([#174](#174)) ([4aea742](4aea742)) * **config:** describe task args and variables ([#159](#159)) ([34e0076](34e0076)) * **docm:** treeless clones and per-library excludes ([#157](#157)) ([a4f4410](a4f4410)) * **forge:** support GitLab and Forgejo alongside GitHub ([#183](#183)) ([1926908](1926908)) * **harness:** claim bounded shell write sets ([#158](#158)) ([a72acf1](a72acf1)) * **hook:** parse Cursor and Antigravity payloads with pabal 0.2 ([#191](#191)) ([00e2918](00e2918)) * **hooks:** add before_worktree_remove ([#140](#140)) ([1ea3aed](1ea3aed)) * **issue:** add dashboard chart aggregation modes ([#144](#144)) ([61ad36a](61ad36a)) * **issue:** label the y-axis of dashboard charts ([#143](#143)) ([99e7b29](99e7b29)) * **issues:** create issues from enforced templates ([#185](#185)) ([3a38874](3a38874)) * **issue:** take the summary from the tracker ([#138](#138)) ([30425fb](30425fb)) * **rules:** add rules add, edit and remove ([#150](#150)) ([7052dd4](7052dd4)) * **rules:** honor repo-rules-agent.toml in query ([#151](#151)) ([e7a1d1f](e7a1d1f)) * **template:** render templates on demand ([#165](#165)) ([2da3c36](2da3c36)) * **templates:** add variable descriptions ([#152](#152)) ([918f594](918f594)) ### Bug Fixes * **command:** end the fresh-path exemption on placement ([#125](#125)) ([40ae328](40ae328)) * **common:** keep live blocks from climbing over output ([#141](#141)) ([2e2b919](2e2b919)) * **git:** fail fast when ssh needs a prompt ([#142](#142)) ([8fe7ae3](8fe7ae3)) * **guard:** note each unresolved command once ([#200](#200)) ([2bcd2fa](2bcd2fa)) * **guard:** redirect only commands a task covers ([#198](#198)) ([f6c0488](f6c0488)) * **hooks:** keep enforcement on past a broken layer ([#190](#190)) ([8846a35](8846a35)) * **issue:** allow setup --slug without an issue id ([#127](#127)) ([7c28ba7](7c28ba7)) * **issue:** dashboard spinners, x labels and status colors ([#149](#149)) ([b35e2ed](b35e2ed)) * **issue:** hold worktrees past their merged PR ([#189](#189)) ([a27a28b](a27a28b)) * **issue:** make status, info, end and MCP agree ([#193](#193)) ([cd9b46a](cd9b46a)) * **issue:** report ambiguous branch PRs in review finish ([#192](#192)) ([ff2cf62](ff2cf62)) * **locks:** free every root on release --all ([#199](#199)) ([d55ba01](d55ba01)) * **tasks:** refuse a blank required arg ([#173](#173)) ([d1b32b3](d1b32b3)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
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.
TL;DR (human written)
Closes #197
Agent summary
lockm release --allonly freed the session's locks in the checkout it ran from. The write hook claims in whichever checkout a write lands in, so a session that wrote into a second worktree or a scratch directory kept those claims until the TTL ran out or the session ended. The using-devkit skill calls--all"drop everything you hold".release --allnow frees every lock held by exactly that holder, in every root. Holder ids are globally unique, so the root filter protected nothing.holder/agent) stay, asreferences/locks.mdalready documented. They have their own cleanup inSubagentStop, and a top-level--alltaking them would pull locks out from under a sub-agent that is still running.ReleaseAllrequest keeps itsrootfield for wire compatibility, and the lock proto stays at 4. The daemon ignores the field. An older daemon still parses the request and releases in the caller's root, the old behavior. A proto bump would have failed the handshake, and the fallback file store refuses every lock write while a daemon holds the registry, so edits in enforced checkouts would have been denied until the daemon restarted.release_all_keeps_its_wire_shapepins the serialized shape.locks.releasewithall: truegets the same scope, since it calls the same function.release --all -h,SKILL.md,references/locks.mdand the MCP field description now describe the same scope.release_all_frees_the_holder_in_every_rootdrives the reallockmbinary across two git checkouts withalice,alice/agentandbobholding locks in each. Before the fix it released 1 lock instead of 2.resolved_fns_roundtrip_via_flock_pathwrites to the real registry, so its fixedholder-aid became a per-process id: a cross-root release could otherwise free another concurrent run's rows.Not checked locally: macOS and Windows. Nothing here is platform-specific.
🤖 Generated with Claude Opus 5.5 via Claude Code