fix(actas): recover abandoned reclaim mutexes safely - #522
Open
yui-stingray wants to merge 3 commits into
Open
Conversation
yui-stingray
marked this pull request as ready for review
July 29, 2026 00:01
6 tasks
yui-stingray
force-pushed
the
fix/actas-reclaim-orphan-gc
branch
from
August 2, 2026 10:27
a5a1d66 to
e513f20
Compare
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.
Summary
Rebase the abandoned-reclaim-mutex fix onto current
main(v1.1.13,545973d) and make the related actas teardown paths fail closed.The original age-based
.reclaim.dcleanup proposed in #68 is unsafe: a livemutator can be paused past any threshold, and a delayed collector can then
remove a newly created mutex at the same pathname. This change therefore never
age-deletes a legacy marker.
The rebase also closes teardown gaps found while reviewing the checked-release
contract. A watcher that claimed the same active name in multiple teams now
tears down its frozen startup set in a deterministic, retryable order instead
of dropping one team and leaving the others without a receiver.
What changed
(lock_key, holder_pid, 128-bit holder_generation).exact lock state, and ends with
exec rm, so killing the helper cannot leavean untracked destructive child running after its mutex appears stale.
same final mutation protocol. Reclaim a mutex row only when its complete
PID/generation tuple is still current.
claim/reclaim failures structured and fail closed. Checked multi-pair
rollback reports the exact retained pairs when infrastructure recovery is
incomplete.
explicit about partial failure; a failed drop no longer proceeds to change
the Claude active role or Monitor state.
reset/dropretain their all-team scope and partial-progress reporting; whenchecked release cannot be proven, they now exit nonzero rather than report a
completed drop.
pairs, reset non-target pairs first, and reset the requested target last as
the completion latch. After a committed non-target reset, rebuild the live
SQL subscription and discard the stale fetched batch before processing more
rows.
remains in teardown bookkeeping so a later control can re-prove release and
finish cleanup.
start, success requires its absence. A different-owner replacement is not
treated as proof and cannot cause a successor spawn record to be deleted.
another same-name registration. Single-target force cleanup uses the scoped
machine protocol and retains recovery state on logical reset/release errors.
.reclaim.dmarkers during SessionStart and document thequiescent upgrade/rollback procedure.
shell-minted helper PIDs.
Scope and coordination
This is a same-user concurrency-correctness primitive, not a security boundary
against arbitrary hostile same-UID filesystem or database mutation.
fencing remain in fix(actas-lock): make release and stale GC generation-fenced compare-and-delete #519. This PR does not claim to solve that ABA boundary.
ctrl:despawnstilluses the current message-based control path.
current provider behavior; when integrating fix(despawn): never invoke placement providers from graceful teardown #518, preserve this PR's
target-last reset, readiness gate, and retry behavior.
termination remain in fix(despawn): handle OS-terminal placements #548. That work should retain this PR's multi-team
preflight, scoped reset, and logical failure retention.
set, matching the current static-subscription boundary tracked in watch.sh: reevaluate actas lock ownership after startup (static PAIRS misses mid-session claim/release) #556.
#518 and #548 overlap with this PR in the despawn/watch/test surfaces and require
semantic reconciliation. The recommended integration order is #522, then a
semantic rebase of #518, then the safety redesign/rebase of #548; simple
conflict-marker removal would not preserve all three contracts.
Validation
bounded serial run and continuation groups: 933 passed, 3 failed. The
changed-surface files (
actas_lock,actas_integration,despawn, andtemplate guidance) contain 112 tests; all 112 passed. Two stable
failures reproduce unchanged on clean
v1.1.13:\'escape before thecode under test runs;
/usr/bin:/bincontains nonode, whilethis host has
/usr/bin/node.tracked in bats suite flakes across the process-lifecycle tests: main itself fails ~half its runs, a different test each time #595: the replacement-dispatcher child-count assertion failed in
the serial run and one focused rerun. The test, launcher, and its runtime-lock
path are unchanged by this PR; the same focused command passed on clean
v1.1.13.regressions: passed.
source-following/locale and sourced-variable notices).
git diff --check: passed.GitHub's existing Linux and macOS full-suite jobs remain the authoritative
cross-platform gate. Native Git Bash and Bash 3.2 were not available in this
local environment; the existing Windows jobs cover only their current targeted
helper/runtime legs and do not exercise the full actas/despawn suite. No
workflow expansion is proposed here.
Rollout and rollback
have sourced the old mkdir-based mutator.
.reclaim.dmarker based on age. Remove only a reported,validated empty marker after all old mutators are stopped.
run/actas-reclaim.dbwhile a helper may be active.run/actas-reclaim.dbfor inspection; do not delete it to force rollback.Addresses #68 by replacing unsafe automatic marker cleanup with a fail-closed
recovery protocol. Related: #65, #514, #518, #519, #548, #556, #584, #595.