Skip to content

fix(hosts): the handoff wrapper prints waiting and exits 0 when the child never started #1096

Open

Description

@HugoXK

Problem

scripts/dsh-superagent-run.sh (new in #1084) promises at :18-19: "Exit 0 once the run is detached and DIR/waiting.json is written". It does that unconditionally — the final python heredoc prints the report and exits 0 whatever the child did, and die fires only when tmux/setsid refuses the session (:71, :76). A child that refuses to start therefore reads as a successful handoff: the report says "status": "waiting" with found: false, and the exit code says 0. The only trace of the real reason is DIR/worker.log, which nothing in the report points at as a failure.

This is not hypothetical: it is how #1084's own documented-command defect stayed invisible. The page's example omitted --usd-per-call, the CLI exited 2 in the child before creating anything, and the wrapper reported a handoff.

Evidence

Measured at c69767d7 (the tip of #1084), with the page's command minus its --usd-per-call — the exact case the page now warns about:

$ DSH_RUN_HANDOFF_WAIT=3 bash scripts/dsh-superagent-run.sh --store "$S" -- \
    agentdescent --store "$S" evolve spec.json --detach --budget 5
waiting (no run yet) round 0 …/ln-2
report …/ln-2/waiting.json
WRAPPER_EXIT=0

$ cat "$S/waiting.json"
{"command": "... evolve spec.json --detach --budget 5", "driver": "tmux",
 "found": false, "run_id": "", "round": 0, "state": "unknown",
 "status": "waiting", ...}

$ cat "$S/worker.log"
error: budget_usd needs usd_per_call: the engine counts calls, and a tool-using agent's price per call is not known in advance

The same shape reproduces with any child that exits before writing status.json (agentdescent wrong-flag, a typo in the spec path, a missing interpreter). The successful path is unaffected: with --usd-per-call 0.01 the wrapper returns in ~1 s with found: true, a real run_id, and state: running.

Why it needs a decision, not a one-line patch

found: false alone cannot tell "the child is alive but has not written its status yet" from "the child is gone". The wait window exists because the first case is legitimate (:79-81, and the page's "a handoff into a used store reports found: false until its own run writes one"). So the fix has to decide liveness at the end of the window:

  • the driver knows how to answer it — tmux has-session -t =SESSION is false once the session's command ends (the existing smoke test already relies on exactly that, tests/test_dsh_superagent_run.py), and setsid gives a pid to kill(pid, 0);
  • when the child is gone and no fresh status.json appeared, the handoff failed and the wrapper should say so: nonzero exit, a state that is not waiting, and the report naming worker.log as the place the child's own reason sits.

Acceptance

  1. A child that exits 2 before creating anything → the wrapper exits nonzero; waiting.json has found: false, a state that is neither waiting nor running, and a field naming DIR/worker.log.
  2. A child that starts and takes the full window to write its status.json → unchanged: exit 0, found: true, real run_id (the existing fake_run.py smoke already covers the fast case; this row is the slow one).
  3. A handoff into a reused store that writes nothing → found: false with exit 0 (not a failure): nothing was refused, the child just has not written yet. Keep the current row green.
  4. The five rows in tests/test_dsh_superagent_run.py stay green, including the tmux no-orphan row.

Non-goals

  • Watching the run after the handoff, retrying it, or adding a daemon/scheduler.
  • Changing the DSH_RUN_HANDOFF_WAIT default.
  • The stale-tmux-server environment question (a session started by an older PATH runs a different agentdescent than the caller's); that is a host-setup matter, recorded separately.

Activity

  1. added
    bugSomething isn't working
    area:cliThe agentdescent command line (cli.py, doctor, plan/evolve/status/apply)
    on Nov 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:cliThe agentdescent command line (cli.py, doctor, plan/evolve/status/apply)bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions