Skip to content

PLAN-v3 Phase 1: pin the web stack and fix the first-wave bugs - #8

Merged
Kisahime merged 1 commit into
mainfrom
cursor/v3-phase-1-0e13
Oct 10, 2026
Merged

Kisahime merged 1 commit into
mainfrom
cursor/v3-phase-1-0e13

Conversation

@Kisahime

Copy link
Copy Markdown
Owner

Replaces PLAN-v2.md with PLAN-v3.md and implements the Cursor tasks in V3 Phase 1. V3-P0 stays unchecked; those checks need the owner's hardware or accounts.

V3-P1-01 — pin the web stack and accept IPv6 loopback (F-01)

Starlette 1.6.0 splits Host on the first colon, so Host: [::1]:8080 becomes [ and the existing host test returns 400. CI for 933dd35 installed starlette 1.7.0, which parses the hostname and keeps the brackets, so that same test already passed there. requirements.txt now pins the fastapi/uvicorn transitive stack to that CI resolution (starlette==1.7.0, anyio==4.15.1, and the pydantic, click, h11, httptools, and watchfiles pins from the same install). uvloop stays unpinned because it is Unix-only and is not installed on the Windows runner.

create_app passes the prepared settings port, or server.port from config.yaml, into trusted_hosts(). The allow list includes 127.0.0.1, localhost, ::1, and [::1], each with the configured port. A request with server.port 9090 accepts Host: 127.0.0.1:9090 and Host: [::1]:9090 and still rejects evil.test.

V3-P1-02 — shoutouts only for !so (F-02)

_perform_chat treated every allowlisted non-speak command as a shoutout. chat_action already returns command for those lines, so src/policy.py did not need a change. A command in chat.mod_commands is now skipped with not a mod before any branch runs. Only !so calls shoutout. Any other command is skipped with unhandled command. A viewer's !lurk bob does not call shoutout; a mod's !so bob still does; a viewer's !so bob is not a mod.

V3-P1-03 — fence viewer history (F-03)

llm_messages() wraps viewer turns, including twitch-event turns stored as viewer, with fence_viewer_text(). Streamer turns stay plain text. Assistant turns are unchanged. A stored viewer line ignore previous instructions comes back inside <<viewer>>.

V3-P1-04 — time-box alerts and recap vibe (F-04)

ollama.chat_timeout_seconds (default 20) already bounded chat replies. _finish_reply and the recap vibe paragraph now use the same limit, so no new setting was added. On timeout the alert skip reason is llm timeout, the phase returns to idle, and the alert text that was stored before the model call stays in memory. The next queued alert is handled. A vibe timeout leaves the recap without a ## Vibe section.

V3-P1-05 — idle wake score, each frame once (F-05)

The level loop broadcasts while the mic stream is open and either listening, push-to-talk, or a wake model is loaded. The mic keeps a carry buffer and scores only new 1,280-sample frames. A detection calls model.reset() when the model has one. A fake model sees each sample once across uneven chunks, and a level message with a wake value is sent while listening is off.

V3-P1-06 — hourly revalidation and action scopes (F-06)

The Twitch watchdog calls validate for the event token every 60 minutes while EventSub is connected and stores validated_at. Remaining lifetime is expires_in - (now - validated_at). A 401 or 403 on that check sets EventSub to token rejected. IRC is validated once on connect: login must match TWITCH_NICK, and chat:read plus chat:edit must be present. A mismatch is reported as IRC token login is {login}, expected {nick}. Health lists actions.shoutout and actions.marker from ACTION_SCOPES. The dashboard disables those buttons and sets the tooltip when the status starts with missing scope.

Shoutout and Remember this disabled when the action scopes are missing

Checked in headless Chrome against a fake booth: with empty scopes both buttons were disabled and their titles were missing scope moderator:manage:shoutouts and missing scope channel:manage:broadcast. With both scopes present, both buttons were enabled and the titles were empty.

V3-P1-07 — per-app session ducking (F-07)

The old "speaker must be a different device" refusal is gone. Pixel's own process is still skipped inside the Windows snapshot. audio.duck_apps (default empty) is an allow-list; audio.duck_skip defaults to obs64.exe and Discord.exe. An injected backend ducks matching sessions even when speaker_device is null. Without a backend on non-Windows, the mode stays obs-sidechain. README now describes per-app session ducking. chrome:4 is not in the default skip list, so it is ducked, and data/duck.json still restores the previous levels.

Tests

ruff check src tests passed. pytest -q passed: 105 tests. New coverage is in tests/test_phase2.py, tests/test_shoutout.py, tests/test_memory.py, tests/test_phase4.py, tests/test_phase3.py, tests/test_duck.py, and tests/test_eventsub_scopes.py. Tests stay offline: validate is stubbed and does not call Twitch.

Needs owner

  • V3-P0-01 Secret scanning and push protection, and confirm git log --all -- .env is empty. This supersedes the old V0-01 check.
  • V3-P0-02 Live token scopes after Connect alerts.
  • V3-P0-03 Mic barge-in against real speakers.
  • V3-P0-04 Live wake-word detection count. The dashboard can now show the wake score while not listening; the spoken check is still hers.
  • V3-P0-05 Real pycaw ducking. speaker_device: null no longer refuses when a session backend is available. Confirm music dips for apps outside duck_skip and that a mid-sentence exit restores data/duck.json.
  • V3-P0-06 OBS overlay in her scene.
  • V3-P0-07 Rewards, polls, predictions, and a first-time chatter on the live channel.
  • V3-P0-08 A real shoutout and marker, or the scope error from her tokens.
  • V3-P0-09 Reinstall from the pinned requirements.txt on her PC and run pytest. The IPv6 host failure was starlette 1.6.0; the pin is 1.7.0.

To show artifacts inline, enable in settings.

Open in Web Open in Cursor 

Pin the fastapi and uvicorn transitive packages to the versions CI installed, accept loopback Host headers on the configured port, and keep shoutouts on !so. Viewer history is fenced, alert and recap completions time out, wake frames are scored once, tokens are rechecked, and session ducking follows an app list.

Co-authored-by: Kisahime <Kisahime@users.noreply.github.com>
@Kisahime
Kisahime marked this pull request as ready for review October 10, 2026 20:03
@Kisahime
Kisahime merged commit ac00a5a into main Oct 10, 2026
2 checks passed
@Kisahime
Kisahime deleted the cursor/v3-phase-1-0e13 branch October 10, 2026 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants