T85.1 — JSON-build audit (chat/state, chat/services, chat/eventlog):
no findings. Every JSON column write in those modules already uses
``json.dumps`` (chat/state/events.py, world.py, edges.py, group_node.py,
meanwhile.py, manual_edit.py, entities.py, chat/services/snapshot.py,
chat/eventlog/log.py); chat/state/meanwhile.py:48-49 even carries an
explicit comment about the ``json.dumps`` choice for safety against
quote/backslash injection. No production changes.
T85.2 — meanwhile cancel route-level coverage:
* ``test_meanwhile_turn_cancellation_via_route`` — pins the
end-to-end shape produced when /turns/cancel fires mid-meanwhile-
beat: assistant_turn lands with truncated=True (and the right
meanwhile_scene_id + speaker_id), no memory_written events fire, no
post-turn edge_update events fire, and _in_flight_tasks is empty
post-flight. Drives the cancel by hijacking client.stream to raise
CancelledError on first iteration — same pattern proven by
test_cancelled_turn_still_closes_scene_when_user_prose_signals_close
in tests/test_turn_flow.py. The synchronous TestClient can't issue
a second POST mid-stream from the same thread, and driving via
task.cancel() trips GeneratorExit-on-dependency that prevents the
conn from committing the partial; the inline-raise mirrors what
cancel_turn produces (CancelledError delivered on next await) and
is the standard idiom in this codebase. Combined with the existing
test_meanwhile_turn_registered_in_in_flight_tasks (registration
pin), the full Stop-button lifecycle for meanwhile beats is now
covered.
* ``test_meanwhile_cancel_route_no_op_after_turn_completes`` — runs
a meanwhile turn to completion, then POSTs /turns/cancel; asserts
204 no-op, no error, registry stays empty. Pins the cancel
endpoint's robustness against the racy "Stop just after stream
finished" sequence.
Suite: 334 -> 336 passing.