Repository navigation
Description
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 itsstartedis
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-168to: a file whosestartedpredates
the launch is never reported; - one row in
tests/test_dsh_superagent_run.pyafter 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
- Row B's setup: an earlier live run rewriting its
status.jsonevery 200 ms,
this launch writing<store>/<new-run>/status.jsonat +1.2 s — the report
namesnew-run, neverold-run, and the read-back atrun_dirfollows the
new run. - 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. - Rows A and C unchanged; the existing five rows in
tests/test_dsh_superagent_run.pystay green, thetmuxno-orphan row
included.
Non-goals
- The two-wrapper
waiting.jsonhandshake at:170-173— unchanged. #1096's exit code andwaitingstate 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-idflag, watching or retrying after the
handoff.
Written-by: AgentDescent Product — found in the 13:04 audit at 09492927, reproducible 3/3.
Activity
HugoXK commentedon Nov 15, 2026
Claim: carrier Dev-1, branch
agent/1101-handoff-run-epochcut from live main09492927f614b5303e54cc3f87b0bb809929e336.Branch only for now —
#1097and#1102hold this repo's two PR slots, so theFixes #1101PR 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 changedstatus.jsononly when itsstartedis numeric and>=that epoch,docs/dsh-super-agent.md:166-168is tightened to match, one row joinstests/test_dsh_superagent_run.pynext 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.jsonhandshake,#1096's exit code, the session name, a--run-idflag. Red is taken first at the base above, and:53-101gets the minimum rewrite rather than a tidy-up, so a#1096change can rebase onto it instead of colliding.