PA.f125 (blocker): execute_dispatch persisted a dispatched Persona's own
turn before execute_round ever recorded the Dispatch call's own body. A
crash in that window left persona_turns durable with no call record
naming it; on resume the already-durable turn was misread as a
different call's completed dispatch, so the resumed call's own tool
result became "<id> already acted" and the Persona's prose never
reached the agent (PA.f64). execute_round now records and persists the
call's own body before execute_dispatch runs any persona turn;
execute_dispatch tells its own not-yet-finished work apart from a
different call's completed dispatch through the new
PlayTurn::dispatch_owner (first call, in call order, to name a
subject). Replaced PA.f123's own test with
a_resumed_dispatch_call_recovers_its_own_persona_prose_not_already_acted,
which guards both the live durability ordering (before_submit_hook off
disk) and the resumed-call outcome (hand-built crashed turn).
Cleanup:
- PA.f128: closed a fourth turn in the ledger retry test so the
retried key's own entry sits at neither end of the ledger deque,
catching a `self.closed.back()` mutation the prior two-turn shape let
through.
- PA.f129: handle_for derives its handle from the library's own
id_text instead of a second hand-written debug-paren strip.
- PA.f130: deleted PlayError::NoPlayerOpportunity — no production site
raises it.
- PA.f131: removed the unneeded #[allow(dead_code)] on
WorldFixture::owner (the abort tests already read it).
- PA.f132 (ghostlight-persona-projection): corrected SourceSpan::locate's
doc comment, which claimed the first-byte check changed the worst
case rather than being a constant factor over non-matching
candidates; replaced the guard test's quote (which never called
starts_with at all) with a shared-prefix quote that forces the real
worst case.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>