Skip to content

Codex status treats a denied tasklist query as proof that a live bridge is dead #612

Description

@suiren

What happened

On Windows, inside a restricted agent sandbox, a live Codex bridge was reported
as stale by:

scripts/delivery.sh status codex <project>

The bridge process was still running. The native PID in its pidfile and metadata
matched, but the sandbox denied the tasklist query used by
_agmsg_pid_alive. Because the probe suppresses tasklist stderr and only
checks whether the PID appears in its output, query failure is indistinguishable
from a successful query that found no process.

The resulting status line was equivalent to:

Codex bridge: demo-team/demo-agent stale pidfile (pid 4242 not running)

Names and PID above are placeholders. No host, project, team, or agent
identifier from the observed machine is included here.

Environment and preconditions

  • agmsg v1.1.13 liveness path (reproduced on 2026-08-02).
  • Host agent: Codex CLI — the status command ran inside its restricted sandbox.
  • Windows with Git Bash / an MSYSTEM environment.
  • A live Codex bridge whose pidfile contains its native Node process PID.
  • The status command runs in a sandbox that allows the pidfile and metadata to
    be read but denies tasklist process enumeration.

This is different from the MSYS-PID provenance bug fixed around #567/#582/#584.
The bridge PID is correctly routed to the generic/native probe. The failure is
that the native probe has only two outcomes.

Reproduction outline

  1. Start monitor delivery and wait until the Codex bridge is live.

  2. Read the bridge PID from run/codex-bridge.<team>.<role>.pid and verify that
    the corresponding process is still running from a context that can inspect
    it.

  3. From the restricted Git Bash context, run the same native query used by the
    helper:

    MSYS_NO_PATHCONV=1 tasklist /FI "PID eq <pid>"

    The observed query failed with an access-denied error instead of returning a
    process table.

  4. In that same restricted context, run:

    scripts/delivery.sh status codex <project>
  5. Observe that the live bridge is printed as a stale pidfile.

Why this happens

The Windows branch of _agmsg_pid_alive is effectively:

MSYS_NO_PATHCONV=1 tasklist /FI "PID eq $pid" 2>/dev/null | grep -q "$pid"

Both of these cases return false from the pipeline:

  1. tasklist ran successfully and the PID was absent.
  2. tasklist could not enumerate processes at all.

scripts/drivers/types/codex/_delivery.sh then maps every false result to
stale pidfile (pid ... not running). The diagnostic therefore states a fact
that the probe did not establish.

Expected behavior

A failed process-table query should not be treated as positive evidence of
death.

At minimum, status should distinguish:

  • alive: the requested PID was positively found;
  • dead: a successful query positively established that it was absent;
  • unknown: the query could not be performed or interpreted.

For the observed case, a message such as bridge liveness unverifiable would
be accurate. Destructive or ownership-changing callers should preserve records
and avoid kill/reclaim actions when the result is unknown.

Possible direction

This issue is primarily an observation report, not a proposal for the broader
Windows PID-generation design. A follow-up could add a tri-state result at the
existing liveness seam, or provide a separate detailed probe while retaining a
boolean compatibility wrapper.

Whichever shape is chosen, regression coverage should distinguish all three
native-query outcomes:

  1. successful output containing the PID;
  2. successful output not containing the PID;
  3. nonzero/access-denied tasklist execution.

The third case must not produce a definitive not running status or authorize
destructive cleanup. PID reuse, process start markers, and wider ownership
generation work are intentionally out of scope for this issue.

Impact

The immediate observed impact is a false status report, which can lead an
operator to restart or tear down a healthy route. The same binary helper is also
used by lifecycle code, so preserving the distinction between dead and
unverifiable is important before a failed probe is used as authority for
cleanup.


🤖 Issue text drafted with Claude Code; the
observation and reproduction are from a real deployment.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions