diff --git a/packages/mosaic/framework/tools/fleet/test-start-agent-session.sh b/packages/mosaic/framework/tools/fleet/test-start-agent-session.sh index 67f4911b..eca95fdf 100755 --- a/packages/mosaic/framework/tools/fleet/test-start-agent-session.sh +++ b/packages/mosaic/framework/tools/fleet/test-start-agent-session.sh @@ -23,8 +23,26 @@ index=0 if [ "${args[0]:-}" = -L ]; then index=2; fi case "${args[$index]:-}" in has-session) + # The holder always answers. MOSAIC_TEST_HELD_SESSIONS lets a case add + # other targets that should answer too — without it there is no way to + # model "tmux still reports the session" for a non-holder agent, and the + # launcher's pane-pid-unresolved branch is unreachable from this harness. + # + # A listed target answers only AFTER new-session, because the launcher asks + # this question twice about the same name: once before launching, where a + # yes means "already running, nothing to do, exit 0", and once after, where + # a yes means "the session survived". A shim that answered yes to both + # would short-circuit at the first and never reach the branch under test — + # it would look like coverage and measure the idempotency path instead. for argument in "${args[@]}"; do [ "$argument" = '=_holder:0.0' ] && exit 0 + case " ${MOSAIC_TEST_HELD_SESSIONS:-} " in + *" $argument "*) + if tr '\0' '\n' < "${MOSAIC_TEST_TMUX_CALLS:?}" | grep -qxF new-session; then + exit 0 + fi + ;; + esac done exit 1 ;; @@ -125,6 +143,7 @@ run_start() { local agent="$2" HOME="$home" PATH="$FAKE_BIN:$PATH" MOSAIC_TEST_TMUX_CALLS="$TMUX_CALLS" \ MOSAIC_TEST_PANE_PID="${MOSAIC_TEST_PANE_PID:-}" \ + MOSAIC_TEST_HELD_SESSIONS="${MOSAIC_TEST_HELD_SESSIONS:-}" \ MOSAIC_TEST_HOME="$home" \ MOSAIC_TEST_FLEET_OWNER=123e4567-e89b-12d3-a456-426614174000 \ MOSAIC_HOME="$home" "$START" "$agent" @@ -475,6 +494,33 @@ fi tr '\0' '\n' < "$TMUX_CALLS" | grep -qF new-session || \ fail "dead-pane case did not reach the launch it is measuring" +# #1241, the other way a pane fails. Above, tmux destroyed the session and +# has-session said so. Here the session is still there and no PID comes back +# after the retries — a different fault (the pane is alive but unusable, or +# tmux is answering inconsistently) that an operator has to be told apart from +# a runtime that died on startup. +# +# This case exists because the branch that handles it shipped with nothing able +# to reach it: the shim answered has-session only for the holder, so every +# non-holder agent landed in the session-is-gone branch no matter what. A +# defensive branch nothing exercises is the same shape as the bug this whole +# change is about, one layer down. +: > "$TMUX_CALLS" +HOME_NO_PID="$ROOT/pane-no-pid" +write_generated "$HOME_NO_PID" "coder-no-pid" +if output=$(MOSAIC_TEST_PANE_PID='' MOSAIC_TEST_HELD_SESSIONS='=coder-no-pid:0.0' \ + run_start "$HOME_NO_PID" coder-no-pid 2>&1); then + fail "launcher reported success over a session with no resolvable pane PID" +fi +echo "$output" | grep -qF 'code=pane-pid-unresolved' || \ + fail "session-present/no-PID was not reported as pane-pid-unresolved: $output" +if echo "$output" | grep -qF 'code=pane-did-not-survive'; then + fail "a session tmux still reports was diagnosed as a destroyed session" +fi +if echo "$output" | grep -qiF 'heartbeat'; then + fail "an unresolvable pane PID is still being reported as a heartbeat-sidecar problem" +fi + # Exact stop derives the socket exclusively from the validated generated # projection and ignores an ambient socket supplied by the caller. : > "$TMUX_CALLS"