`_admit_prompt_turn` is the gate every turn source crosses (prompt.submit,
auto-continue, heartbeat/loop ticks, bot mailbox, compute host). When the
record's agent is None (a deferred build that attached nothing, see previous
commit) it used to hand the None agent through; `_invoke_agent` raised
AttributeError, `_recover_turn_exception` emitted a frame, and then the
`finally` dereferenced `st.agent.interim_assistant_callback` again — the
turn thread died before `running = False`, so the prompt vanished and the
session stayed "busy" for every later prompt.
Now the gate releases `running`, emits the same retryable
`agent_init_failed` terminal frame `_run_after_agent_ready` uses (carrying
the recorded build reason when there is one), logs the refusal, and returns
None so `_run_prompt_submit` reports the turn as not started. The turn body
therefore never sees a None agent and needs no guard in its `finally`.
Live probe (real `_run_prompt_submit`, inline thread, agent None):
before: `CRASH out of _run_prompt_submit: AttributeError ... running: True`
after: `returned: False ... error_surface {'layer': 'runtime', 'code':
'agent_init_failed', 'retryable': True} running: False`
Fixes#111531
Co-authored-by: zqy1-1 <185413950+zqy1-1@users.noreply.github.com>