Skip to content

feat(delivery): per-identity watcher liveness in status - #436

Open
Masashi-Ono0611 wants to merge 6 commits into
fujibee:mainfrom
Masashi-Ono0611:feat/monitor-status-visibility
Open

feat(delivery): per-identity watcher liveness in status#436
Masashi-Ono0611 wants to merge 6 commits into
fujibee:mainfrom
Masashi-Ono0611:feat/monitor-status-visibility

Conversation

@Masashi-Ono0611

@Masashi-Ono0611 Masashi-Ono0611 commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Extends delivery.sh status so monitor-tool agent types (watch.sh receive path) report per-identity receiver liveness at the same granularity as the existing Codex bridge lines.

  • Exclusive (actas) receivers: ready sentinel + watch.<token>.pid (the watcher process, not the agent CLI pid embedded in composite instance ids) + cmdline match
  • Broad watchers (e.g. Claude Code SessionStart default, no ACTIVE_NAME): no per-identity ready file — report no exclusive receiver (broad watcher alive, pid N) instead of a false not running
  • Aggregate watch processes: N alive, M stale line kept; Codex plug unchanged

Example output

mode: monitor
Watcher: masa-dev/masa-grok alive (exclusive, pid 12345)
Watcher: masa-dev/masa-claude no exclusive receiver (broad watcher alive, pid 67890)
watch processes: 2 alive, 0 stale pidfiles

Credits / related

Test plan

  • bats tests/test_delivery.bats -f 'delivery status' — green (includes exclusive alive, agent-alive/watcher-dead false-alive guard, broad coverage, not running)
  • New receiver-live.sh direct cases for missing ready / agent-only composite
  • CI bats on this PR

Notes

  • CHANGELOG intentionally not updated (caller preference for PR-time decision).
  • Ready-file semantics unchanged (still actas-only); broad watchers are detected via scoped live watch.sh pidfiles, not by writing ready files for them.

Update (2026-07-19): additional review pass, three more fixes

A follow-up /review:self-multi-model pass (Codex + Fugu, independently
converging on two of these) found real gaps before this had any maintainer
eyes on it, now fixed in the latest commit:

  • P1 — broad-watcher false positive: the broad-watcher scan matched any
    watch.sh cmdline containing " <project> <type> " as a substring. An
    EXCLUSIVE watcher's argv (watch.sh <sid> <project> <type> <agent>) also
    contains that substring, so it could misreport an exclusive watcher for a
    DIFFERENT agent as "broad coverage" for an identity that actually has zero
    receivers. Now requires project/type to be the trailing tokens (nothing
    after them), true only for a genuinely broad invocation.
  • P1 — ps-unavailable false negative: in Claude Code sandboxes where
    ps is unavailable, compat_get_cmdline returns empty, so both the
    broad-watcher scan's and receiver-live.sh's mandatory argv checks
    rejected every live watcher — reporting "not running" even though
    watch.sh's own self-clean logic already has a documented fallback for
    exactly this environment. Both now skip argv validation and trust
    kill -0 alone when cmdline can't be inspected (the broad-watcher scan
    falls back to "the sole live watcher, if unambiguous," since it can't
    disambiguate across multiple untyped candidates without argv).
  • P2 — pid-recycling defense too weak: receiver-live.sh only checked
    that a live pid's cmdline generically looked like watch.sh, not that it
    was watch.sh for THIS agent — a recycled pid landing on an unrelated
    watcher (different team/role) could false-alive. Now also requires the
    agent name as a trailing argument.

New regression tests added for all three (tests/test_delivery.bats,
including a mocked-ps-failure harness reused from the existing pattern in
test_team.bats). Full suite re-verified: 737/737 (this worktree's copy of
the pre-existing install: watch.sh self-cleans... flake did not reproduce
on this run).

Extend delivery.sh status for monitor-tool types (watch.sh ready-file
path) so each registered identity reports alive/not-running with the
same granularity as Codex bridge lines. Keeps the aggregate watch
count for non-actas watchers; codex plug unchanged.
Rebuild monitor-tool watcher liveness in delivery.sh status on the
shared ready-sentinel primitive from fujibee#372 (@u-ichi):

- add scripts/receiver-live.sh (credit fujibee#372; composite instance-id
  fallback for today's watch.sh ready tokens plus optional --pid)
- delivery status prints "Watcher: team/agent alive|not running"
  by calling receiver-live.sh per registered identity
- codex bridge status path unchanged; aggregate watch count kept

Tests cover composite tokens, explicit pid= form, missing/dead ready,
and shared use from claude-code identities.
Address card-3 P1s from multi-model review:

- receiver-live.sh probes watch.<token>.pid (the watcher), not the agent
  pid embedded in composite ready tokens, so a live agent with a dead
  watcher is no longer false-alive
- delivery status falls back to scoped broad-watcher detection when an
  identity has no exclusive ready file (Claude Code SessionStart path),
  printing "no exclusive receiver (broad watcher alive, pid N)" instead
  of a hard not-running

Credits fujibee#372 (@u-ichi) for the ready-path primitive.
…e no-ps

Two P1s found by /review:self-multi-model (Codex + Fugu, independently
converging on both), plus one P2 (Codex):

- agmsg_delivery_find_broad_watcher_pid matched any watch.sh cmdline
  containing " <project> <type> " as a substring. An EXCLUSIVE watcher's
  argv (watch.sh <sid> <project> <type> <agent>) also contains that
  substring, so it could be misreported as "broad watcher coverage" for a
  completely different agent that actually has zero receivers. Now requires
  project/type to be the TRAILING tokens (no agent arg after them), which is
  true only for a genuinely broad invocation.

- In Claude Code sandboxes where `ps` is unavailable, compat_get_cmdline
  returns empty, so both this function's and receiver-live.sh's mandatory
  argv checks rejected every live watcher — reporting "not running" even
  though watch.sh's own self-clean logic already has a documented fallback
  for exactly this environment. Both now skip argv validation and trust
  kill -0 alone when cmdline can't be inspected (falling back to "the sole
  live watcher, if unambiguous" for the broad-watcher scan, since it can't
  disambiguate across multiple untyped candidates without argv).

- receiver-live.sh's pid-recycling defense only checked that the live pid's
  cmdline generically looked like watch.sh, not that it was watch.sh FOR
  THIS agent — a recycled pid landing on an unrelated watcher (different
  team/role) could false-alive. Now also requires the agent name as a
  trailing argument.
@Masashi-Ono0611
Masashi-Ono0611 force-pushed the feat/monitor-status-visibility branch from 24261fb to 23f677a Compare July 29, 2026 23:51
Masashi-Ono0611 and others added 2 commits July 30, 2026 09:16
…are kill -0

Rebasing onto current main surfaced fujibee#500's guard ("no shipped script decides
liveness with a bare kill -0"), which landed after this branch was written.
Both call sites this branch adds were offenders: receiver-live.sh's watcher-pid
check and the broad-watcher scan in delivery.sh.

_agmsg_pid_alive is what the guard exists to enforce — it is EPERM-aware and
cross-checks ps, so a sandbox that cannot signal a live watcher no longer reads
as "not running". That subsumes the no-ps handling this branch had hand-rolled
around the bare check. delivery.sh already sourced instance-id.sh and used the
helper everywhere else; receiver-live.sh now sources it too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ujibee#436 conflicts)

upstream/main advanced 17 commits since this PR was opened. scripts/delivery.sh
auto-merged cleanly. tests/test_delivery.bats had one conflict: both branches
independently appended new @test blocks at the same end-of-file anchor. Kept
both blocks in full (PR's watcher-liveness/receiver-live.sh tests, then main's
codex fujibee#579 seat/timeout/dedup-probe tests) with no content dropped from either
side. Verified with `bats tests/test_delivery.bats`: all 175 tests pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant