63be8ce863
Running the suite could take over the machine it ran on. Two real effects, both now closed: - **The suite spoke through the speakers.** Once any test drove the `voice.toggle` RPC with `action="tts"`, the handler set `HERMES_VOICE_TTS=1` in the *live process environment*, and the flag outlived that test. Every later test that drove a turn to completion then fed its final response text to `hermes_cli.voice.speak_text` on a background thread - real synthesis, real playback, no API key needed (the default `edge` provider is keyless). A developer heard the fixture string "partial answer complete" out loud. Because the flag is set from inside the process, `scripts/run_tests.sh`'s `env -i` never protected against this. `tests/conftest.py` now blanks `HERMES_VOICE`/`HERMES_VOICE_TTS` per test, and a new autouse `_audio_playback_guard` stubs `speak_text` and its playback binding outright, so the speakers stay shut even inside the test that sets the flag itself. `@pytest.mark.real_audio_playback` opts out. - **The suite launched Chrome.** `tests/tools/test_browser_supervisor.py` spawned a real browser on any machine with Chrome on `PATH`. Its docstring promised a `HERMES_E2E_BROWSER=1` gate that existed nowhere in the code. That gate is now real, and the file is marked `integration` so the default marker filter excludes it. `scripts/run_tests.sh` forwards `HERMES_E2E_BROWSER` so the documented manual run still works. Miscellanea - `tests/test_audio_playback_guard.py`: regression cover for both defences, driving the real `voice.toggle` handler rather than a stand-in.