Skip to content

fix(hosts): the handoff report can adopt an earlier live run and name its run_id #1101

Open

Description

@HugoXK

Problem

scripts/dsh-superagent-run.sh snapshots every status.json in the store before
the launch (:53-76, mtime + size), then reports the first file whose
mtime/size changed (:82-101, max by mtime at :101). A store is reused per
Ticket (docs/dsh-super-agent.md:165) and a live run rewrites its own
status.json every round
(runstore.py:445-455), so a change to an earlier,
still-running entry is indistinguishable from this handoff's new file. The
report then carries found: true with the earlier run's run_id, round and
run_dir, and the page's §6 read-back polls the wrong run.

The page's promise at :166-168 — "found: false until its own run writes
one" — only holds for finished earlier runs, whose file is static. It is false
for a live one, which is the case a shared store hits most often.

Evidence

Measured at live main 09492927, from run-1115-1304/probe-adopt-old-run.sh
(+.txt), three identical runs, tmux, DSH_RUN_HANDOFF_WAIT=3:

row store before the launch child report
A old-run finished, file static writes new-run found=True run_id='new-run'
B old-run live, rewrites its status.json every 200 ms writes new-run at +1.2 s found=True run_id='old-run' round=9 run_dir='old-run'
C no earlier run writes new-run found=True run_id='new-run'

Row B is 3/3 identical, exit 0. The reported run_id belongs to a run this
launch did not start, and the read-back that follows polls it.

Slice

The discriminator is already on disk and already written by the runtime:
status.json's started (runstore.py:687, :823), which update_status
never resets (:445-455), so an earlier run keeps its launch time while its
updated and round move.

  • take the launch epoch into the snapshot (:55) — time.time() at snapshot
    time, written into the same JSON;
  • in the poll (:82-101), accept a changed file only when its started is
    numeric and >= that epoch. A file absent from the snapshot still counts, so
    a fresh run directory and a caller-owned run directory behave exactly as they
    do today;
  • tighten the page sentence at :166-168 to: a file whose started predates
    the launch is never reported;
  • one row in tests/test_dsh_superagent_run.py after the existing reused-store
    row, in the same gated group, no model;
  • one changelog.d/ fragment.

Cost: one field in the snapshot JSON, one comparison in the loop. No new flag,
no extra wait, the wrapper still returns inside WAIT.

Tier: T2 — scripts/** is a T2 path in .github/agent/risk-rules.json;
docs/** and tests/** are lower. Danyang's click, so a PASS at the tip first.

Acceptance

  1. Row B's setup: an earlier live run rewriting its status.json every 200 ms,
    this launch writing <store>/<new-run>/status.json at +1.2 s — the report
    names new-run, never old-run, and the read-back at run_dir follows the
    new run.
  2. Row B with a child that writes nothing inside the window — the report is
    found: false (the older run is not adopted), same as today's else-branch.
  3. Rows A and C unchanged; the existing five rows in
    tests/test_dsh_superagent_run.py stay green, the tmux no-orphan row
    included.

Non-goals

  • The two-wrapper waiting.json handshake at :170-173 — unchanged.
  • #1096's exit code and waiting state when the child never started. That is
    a liveness decision on the same poll loop; this card is the adoption bug only.
    Both touch :53-101, so whichever lands second rebases.
  • The session-name scheme, a --run-id flag, watching or retrying after the
    handoff.

Written-by: AgentDescent Product — found in the 13:04 audit at 09492927, reproducible 3/3.

Activity

  1. added
    bugSomething isn't working
    area:cliThe agentdescent command line (cli.py, doctor, plan/evolve/status/apply)
    on Nov 15, 2026
  2. HugoXK commented on Nov 15, 2026

    @HugoXK
    CollaboratorAuthor

    Claim: carrier Dev-1, branch agent/1101-handoff-run-epoch cut from live main 09492927f614b5303e54cc3f87b0bb809929e336.

    Branch only for now — #1097 and #1102 hold this repo's two PR slots, so the Fixes #1101 PR opens when one frees.

    Scope is exactly the body: the launch epoch goes into the pre-launch snapshot (scripts/dsh-superagent-run.sh:53-76), the poll (:82-101) accepts a changed status.json only when its started is numeric and >= that epoch, docs/dsh-super-agent.md:166-168 is tightened to match, one row joins tests/test_dsh_superagent_run.py next to the existing reused-store row (same gated group, tmux, no model), and one changelog fragment. A file absent from the snapshot still counts, so a fresh run dir and a caller-owned run dir behave as today.

    Non-goals unchanged: the two-wrapper waiting.json handshake, #1096's exit code, the session name, a --run-id flag. Red is taken first at the base above, and :53-101 gets the minimum rewrite rather than a tidy-up, so a #1096 change can rebase onto it instead of colliding.

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