Fix #94: watch unwatch order #103

Merged
crueber merged 1 commit from fix/issue-94 into main 2026-09-05 03:08:43 +00:00
Owner

Reverses SetWatch(off) to list-CAS-then-record-delete so a CAS failure/crash between leaves a fail-closed record-without-membership (self-heals on next toggle) instead of a phantom watcher stranded in watcher_list. Regression: TestSetWatchUnwatchCASFailureNoPhantom (fault-injected 412 CAS failure; verified it fails on the old order). Suite: go test ./internal/notify/ -race green, coverage 97.3% (>=95%), gofmt/vet clean. Doc Decisions entry in docs/features/06_notifications.md (law 12). Fixes #94.

Reverses SetWatch(off) to list-CAS-then-record-delete so a CAS failure/crash between leaves a fail-closed record-without-membership (self-heals on next toggle) instead of a phantom watcher stranded in watcher_list. Regression: TestSetWatchUnwatchCASFailureNoPhantom (fault-injected 412 CAS failure; verified it fails on the old order). Suite: go test ./internal/notify/ -race green, coverage 97.3% (>=95%), gofmt/vet clean. Doc Decisions entry in docs/features/06_notifications.md (law 12). Fixes #94.
SetWatch(off) deleted the watch record before the social.json
watcher_list CAS; a CAS failure/crash between stranded a phantom
watcher (record gone, principal still in watcher_list, still fanned
out). Reverse the order: list CAS first, then record delete. A crash
between now leaves a record without membership: fail-closed
(fan-out consumes the list) and self-healing on the next toggle.

Regression: TestSetWatchUnwatchCASFailureNoPhantom (fault-injected
412 CAS failure asserts no phantom + retry convergence; fails on the
old order). Doc Decisions entry per law 12.
Sign in to join this conversation.
No description provided.