fix(issue-23): an unacknowledged onboarding marks the job and keeps the session

Two defects in the started-based verdict, both found by the full suite.

The first was destructive. No `started` within T exited 1, which fired the
rollback trap and killed the session. That is worse than the bug being fixed:
create_session already has a rule for "the agent is not responding but the
process is alive" one step earlier - a TUI that never signalled ready is
preserved for diagnosis - and an onboarding that is merely slow is the same
situation. The job is marked `error` and warned about loudly; the session, its
row and the pane stay. MULTI_AGENT_RULES §6 has the GM gate on this job
anyway, so an unacknowledged onboarding still blocks real work.

It also broke 21 tests across 8 modules (create/sanity/tier2/tier3/tier4/
issue15/issue19/issue21) and added roughly an hour to the suite: no sandbox
has an agent to publish `started`, so every create_session test sat out the
full 180 s and then failed on the rollback. MAM_ONBOARD_ACK_S shortens that
wait, and the sandbox fixture sets 3 s. It is deliberately not the wrapper's
MAM_INJECT_ACK_GRACE_S - that is a different wait whose own tests assert on
the value they pass, and reusing it made issue18's acceptance2 read "within
3s" instead of "within 4s".

The second: the delivery failure published `error` and then the rollback
published `cancelled` on the same job, which the registry refuses
("conflicting cancelled after terminal error"). Terminal events are published
exactly once, so the trap now skips a job this run already closed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
GodopuandClaude Opus 5 committed 2026-09-22 06:24:06 +00:00
1 parent 2cd20423ac
commit 33b91fe0c8
3 files changed
+43 -12

No files matched your search

@@ -331,8 +331,12 @@ cleanup_herdr_on_error() {
# Release the onboarding job: `cancelled` is a terminal transition the
# registry knows, and it says what happened - the job was never really
# dispatched. Best effort; a failure here must not stop the rollback.
if [ -n "${DELEGATE_JOB_ID:-}" ]; then
# `completed`/`error`/`cancelled` are terminal and published exactly once, so
# a job this run already closed is left alone - a second one is refused by
# the registry anyway ("conflicting cancelled after terminal error").
if [ -n "${DELEGATE_JOB_ID:-}" ] && [ "${JOB_TERMINALIZED:-0}" != "1" ]; then
delegate_publish_event "$DELEGATE_JOB_ID" cancelled "rolled back by create_session" "${DELEGATE_JOB_TOKEN:-}" "${DELEGATE_JOB_ATTEMPT:-}" || true
JOB_TERMINALIZED=1
fi
if [ "$alive" = "1" ]; then
@@ -645,6 +649,7 @@ Task: $SUBMIT_JOB_PROMPT"
# text never appeared - none of which a later `started` can repair.
if [ "$rc" -ne 0 ] && [ "$rc" -ne 4 ]; then
delegate_publish_event "$DELEGATE_JOB_ID" error "instruction injection failed (prompt-lock, rc=$rc)" "$DELEGATE_JOB_TOKEN" "$DELEGATE_JOB_ATTEMPT"
JOB_TERMINALIZED=1
exit 1
fi
if [ "$rc" -eq 4 ]; then
@@ -654,10 +659,26 @@ Task: $SUBMIT_JOB_PROMPT"
# ISSUE-23: this script no longer publishes `started` on the agent's behalf.
# It used to, which made the registry show a started job even when the agent
# never saw the prompt, and left any started-based verdict meaningless.
if ! delegate_wait_started "$DELEGATE_JOB_ID" "${DELEGATE_JOB_ACK_S:-180}" "$DELEGATE_JOB_TOKEN"; then
delegate_publish_event "$DELEGATE_JOB_ID" error "onboarding delivery unconfirmed: no started within ${DELEGATE_JOB_ACK_S:-180}s" "$DELEGATE_JOB_TOKEN" "$DELEGATE_JOB_ATTEMPT"
echo "ERROR: '$SESSION_NAME' did not acknowledge the onboarding prompt within ${DELEGATE_JOB_ACK_S:-180}s" >&2
exit 1
#
# How long to wait for the handshake: MAM_ONBOARD_ACK_S, else the wrapper's
# MAM_INJECT_ACK_GRACE_S, else the job's own ack timeout. The create-specific
# variable comes first so this wait can be shortened - in a sandbox with no
# real agent, for instance - without also retuning the delegate-job
# wrapper's injection ack, which is a different wait with its own tests.
ONBOARD_ACK_S="${MAM_ONBOARD_ACK_S:-${MAM_INJECT_ACK_GRACE_S:-${DELEGATE_JOB_ACK_S:-180}}}"
if ! delegate_wait_started "$DELEGATE_JOB_ID" "$ONBOARD_ACK_S" "$DELEGATE_JOB_TOKEN"; then
# The job failed, the session did not: the pane is up and the row is valid,
# only the handshake never landed. Killing a live agent over a slow or
# silent onboarding would destroy more than it protects, and it would
# contradict this script's own rule for the same situation one step
# earlier - a TUI that never signalled ready is preserved for diagnosis,
# not rolled back. So: mark the job, warn loudly, keep the session.
# MULTI_AGENT_RULES §6 has the GM gate on this job's `completed` anyway,
# so an unacknowledged onboarding already blocks real work.
delegate_publish_event "$DELEGATE_JOB_ID" error "onboarding delivery unconfirmed: no started within ${ONBOARD_ACK_S}s" "$DELEGATE_JOB_TOKEN" "$DELEGATE_JOB_ATTEMPT"
JOB_TERMINALIZED=1
echo "WARNING: '$SESSION_NAME' did not acknowledge the onboarding prompt within ${ONBOARD_ACK_S}s." >&2
echo " The session is preserved and job $DELEGATE_JOB_ID is marked error; re-send the onboarding with multi-agent-mux-delegate-job submit once the agent is up." >&2
fi
# Trigger immediate priority reconcile cycle asynchronously
# Issue #21 phase 2 (A8): the control root's reconcile/watchdog, never the