9493d24a53
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.