Repository navigation
PLAN-v3 Phase 2: harden local state and split the orchestrator - #9
Merged
Merged
Conversation
Atomic booth and identity saves, a mood override that survives restart, shoutout lines, persona control commands, Piper interrupt cleanup, hermetic test roots, and a coverage floor. Orchestrator behaviour moves into src/core behind a thin facade. Co-authored-by: Kisahime <Kisahime@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#8 merged into main (
ac00a5a) before this PR was opened. CI on #8 was already green (both test jobs). This PR targets main and contains only Phase 2.V3-P2-01 Atomic writes for booth and identity
save_booth()andsave_identity()useatomic_write_text(). A corrupt file is logged and copied to a.badsibling, the same way memory already does.Tested by making
os.replaceraise (the oldbooth.jsonstays intact) and by loading a corruptbooth.json/streamer.json(logged, copied to*.bad, defaults used).V3-P2-02 Piper temp cleanup
An abandoned persistent request keeps its output path in a pending-delete set. The placeholder is removed immediately, and the path stays tracked so a wav Piper writes after the interrupt is deleted on the ready line, the next synth, or close. Temps stay in the system temp directory rather than
data/tmp/, so constructing aPiperTTSin tests does not wipe a repo folder.Tested with a fake Piper that writes the wav after
interrupt(); the next synth leaves no stray file.V3-P2-03 Manual mood across restarts
BoothStatestoresmood(the profile base) andmood_override(a manual change).effective_mood()prefers the override. Applying a profile clears the override. Loading a booth that has no override still applies the profile mood.Tested: profile "Focus game" plus manual
hype, a new orchestrator on the same booth file is stillhype, and switching profile again returns tochillwith an empty override.V3-P2-04 Shoutout lines
chat.shoutout_line(defaultgo check out {user}) is the chat line when the target is not the last raider. A shoutout of the last raider still says{target}, welcome in.chat.shoutout_speak(default true) controls whether that line is spoken. The chat post still happens when speech is off.Tested:
!sowith no raid posts the configured line and speaks it; a raider shoutout keeps the welcome;shoutout_speak: falseposts and does not speak.V3-P2-05 Persona control command
With
chat.control_commandempty, the control command is!plus the first token of the persona name, lowercased.!pixelstays an alias. A setcontrol_commandis used as written and the persona name is not also derived. The streamer-save hint uses the live persona name. The smalltalk fallback default isCohost, and the chat path passes the persona name.chat_action()still treats only!pixelas a control verb when it is called on its own. Live chat goes through the orchestrator, which passes the full command set. That is what!nova muteuses.Tested: persona Nova,
!nova muteand!pixel muteboth work for a mod. In the dashboard, saving streamer Luna with persona Nova showsNova will treat Luna as the streamer.The Twitch nick (the preview usespixelbot) is account data, not the persona string.V3-P2-06 Split the orchestrator
Behaviour moved, unchanged, into:
src/core/chat_flow.py— pick, rate, decide, performsrc/core/event_flow.py— batching, priority, react, recapsrc/core/voice_flow.py— utterance to reply, level loopsrc/core/twitch_link.py— connect, disconnect, watchdog, shoutout, markerssrc/core/queue.py— queue ids and snapshotssrc/core/lifecycle.py— startup, profiles, mood, and settingslifecycle.pyis the extra module. Startup and the settings setters did not belong in the five flows named in the plan, and keeping them in the facade would have blown the 500-line cap.Orchestratoris a thin facade (388 lines).main.pyand the existing tests still call the same methods._spawnis still the onlyasyncio.create_taskinorchestrator.py, with the linetask = asyncio.create_task(coro, name=name). Names tests patch onsrc.orchestrator(update_config,user_id,shoutout,create_marker,ROOT,asyncio.sleep) are looked up at call time.Tested by the existing suite with no call-site edits.
V3-P2-07 Hermetic tests and a coverage floor
conftest.pysetsCOHOST_SKIP_SECRET_CHECK=1and points every already-importedsrc.*module'sROOTat a temp tree with a minimal config and the persona fixture.A deliberately broken repo
config.yamlstill builds a booth:_booth_portreturns 8080 andcreate_app(prepared=...)comes up. Two tests still open the shippedconfig.yamlby an explicit path (test_shipped_config_and_overlay_hooks,test_shipped_config_keeps_optional_feeds_off) so those shipped values stay locked. They are the exception, and they fail if that file is actually broken.CI and
pyproject.tomlrunpytest --cov=src --cov-fail-under=72. Measured coverage on this change is 74% (5410 statements). The floor is that number minus 2.V3-P2-08 Configurable logging
logging.leveldefaults to INFO.logging.debug_modules(for example["cohost.mic"]) turns DEBUG on only for those loggers. The dashboard button "Debug log for 10 min" POSTs to token-protected/api/logging/debugand the task restores the previous level after 600 seconds.Tested:
debug_modules: [cohost.mic]records the mic line and not the tts line. The toggle test fast-forwards the 600s sleep and checks the level returns to INFO. In the browser, the button gains an active border and the hint readsDebug log on for 10 minutes.; a second click readsDebug log off.Debug log button on
Nova streamer hint
debug_log_toggle_and_nova_hint.mp4
Tests
ruff checkclean.pytest113 passed. Coverage 74.12% against a floor of 72.Needs owner
No new owner items. The V3-P0 checklist is unchanged and still unticked.
To show artifacts inline, enable in settings.