fix(issue-19): close the trap race, select daemons by argv shape, align docs with the polling fallback

Review round 1 (claude PASS, agy PASS) recommendations:
- reconcile.sh: `_sub_stop` falls back to `$!` for the window before
  `_sub_pid=$!` (R-1); lock identity compared as (st_dev, st_ino).
- lib.sh legacy start_watchdog prints nothing when opted out and no daemon
  runs, byte-identical to the python body.
- conftest/test: `_is_subscriber_daemon_argv` selects only
  `bash|sh <root>/.../reconcile.sh --subscribe`, never a brief-carrying
  wrapper that quotes the words or a prefix-colliding sibling root (R-2);
  one new unit test for the selector.
- monitor SKILL.md / VERSIONS.md: the polling fallback checks the lock
  file's presence only and does not exclude a second daemon; measurement
  wording uses the argv-shape criterion instead of raw pgrep.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
This commit is contained in:
GodopuandClaude Fable 5.1 committed 2026-09-13 16:11:53 +09:00
1 parent 4da4a50682
commit c096cb455b
6 files changed
+69 -16

No files matched your search

+18 -1
View File
@@ -29,6 +29,23 @@ SKILLS_DIR = str(Path(__file__).resolve().parent.parent / ".agents" / "skills")
if SKILLS_DIR not in sys.path:
sys.path.insert(0, SKILLS_DIR)
def _is_subscriber_daemon_argv(args, root):
"""True only for the daemon's own command line: `bash|sh <root>/.../reconcile.sh
--subscribe ...`. A process that merely *quotes* those words (a loop driver
or delegate wrapper carrying a brief in argv) and a sibling root that is a
string prefix of `root` (`.../pytest-9` vs `.../pytest-99`) are excluded
(review R-2) - the reaper kills what it selects."""
argv = args.split()
if len(argv) < 3:
return False
if os.path.basename(argv[0]) not in ("bash", "sh"):
return False
script = argv[1]
return (script.startswith(str(root).rstrip(os.sep) + os.sep)
and script.endswith(os.sep + "reconcile.sh")
and argv[2] == "--subscribe")
def _subscriber_daemon_pids(root):
"""PIDs of `reconcile.sh --subscribe` daemons whose command line lives under
`root`, plus their children (the python subscriber the bash wrapper waits on).
@@ -40,7 +57,7 @@ def _subscriber_daemon_pids(root):
root = str(root)
found = []
for pid in _run_real(["pgrep", "-f", "reconcile.sh --subscribe"]).split():
if root in _run_real(["ps", "-o", "args=", "-p", pid]):
if _is_subscriber_daemon_argv(_run_real(["ps", "-o", "args=", "-p", pid]), root):
found.append(int(pid))
kids = []
for pid in found: