# Bugs and Fixes — Running Log A chronological record of bugs we've found in SkyrimNet behavior, what we did about them (or chose not to do), and the latent issues that surface as a result. **Entries stay even after fixes** — they document why a code path looks the way it does. ## How to read this log Each entry has: - **Status:** `[FIXED]`, `[PARTIAL FIX]`, `[KNOWN, UNFIXED]`, `[REGRESSION]`, `[OBSERVED, NOT REPRODUCED]` - **Discovered:** absolute date - **Symptom:** what the user/we saw - **Root cause:** what's actually broken - **Fix:** what we changed (or "none — see notes") - **Side effects:** anything the fix exposed or changed downstream - **Files touched:** absolute paths --- ## Bug #0 — First sentence not played in TTS `[FIXED]` **Discovered:** 2026-04-18 (early in this session) **Status:** Fixed by user via in-game UI before we instrumented the cause. **Symptom:** The first sentence of any NPC dialogue line was generated by the LLM (visible on the web UI debug view) but never spoken out loud. Subsequent sentences played fine. **Root cause:** Race in the DBVO (Dialogue Background Voice Over) pipeline. SkyrimNet routes TTS audio through vanilla Skyrim's dialogue topic system; the first audio chunk would arrive before the dialogue topic was fully "mounted," and the audio buffer dispatched into a non-listening topic — silent drop. Subsequent chunks landed on the now-live topic and played fine. **Fix:** User enabled two settings via in-game UI (later persisted to YAML): - `enableNPCNodeUpdates: false → true` (`SkyrimNet.yaml:26`) — forces audio node re-binding when a new line begins, so the first buffer has a live node to attach to. - `dbvo.fixSubtitleDuringTTS: false → true` (`SkyrimNet.yaml:31`) — extends subtitle (and carrier line lifetime) to cover the actual TTS audio. Plus belt-and-braces: `tts.maxSegmentWords: 24 → 4` so even residual races lose at most 4 words instead of a full sentence. **Side effects:** The default-off settings have small visual costs (skin texture flicker with mods that inject textures at runtime; brief subtitle flicker). Worth the trade for this user. **Files touched:** `SkyrimNet.yaml` (in `overwrite/.../config/`). --- ## Bug #1 — Recursive farewell loop in continuous mode `[REVERTED — fix preserved, see notes]` **Discovered:** 2026-04-18 **Status:** Fix was applied to `gamemaster_action_selector.prompt` and verified working (GM returned `ACTION: None` in subsequent traces). **Reverted later same day** because the fix exposed Bug #2 (malformed action markers were no longer being masked by the GM's retry loop). The reverted edit is preserved at `mods/SkyrimNet/SKSE/Plugins/SkyrimNet/prompts/gamemaster_action_selector.prompt.edit` for reference. Active prompt is back to upstream baseline. Plan: re-apply the fix paired with a sister edit to `submodules/user_final_instructions/0750_embedded_actions.prompt` that prevents Bug #2. **Symptom:** `conversation_log.log:79-106` shows Severio Pelagia delivering 5+ farewells over ~1m50s after player said "Please leave me alone": ``` [2026-04-17 00:53:12] Severio: As you wish, lad. I'll be on my way then. Take care of yourself out here. The roads can be dangerous... [2026-04-17 00:53:22] Severio: Farewell, lad. May the road be kind to you. I'll be heading back to the farm now. Take care of yourself out here. [2026-04-17 00:53:30] Severio: As you wish, lad. I'll take my leave then. The road can be treacherous... [2026-04-17 00:53:39] Severio: I'll be on my way then, lad. Take care of yourself out here. The roads can be dangerous... [2026-04-17 00:53:48] Severio: Muttering to myself as I turn back towards home... [2026-04-17 00:53:55] Severio: These young'uns, always in such a hurry... ``` Same intent, different paraphrasings, every ~8-10 seconds. **Root cause:** Structural in `prompts/gamemaster_action_selector.prompt`: - **Line 9 (pre-fix):** `{% if not is_continuous_mode %}- ACTION: None — no action needed{% endif %}` — the `None` action was *removed from the action menu* when in continuous mode. - **Lines 75-77 (pre-fix):** "You are actively directing this scene. **You must select an action**—do not select None." — explicit prohibition. - **Lines 109-114 (pre-fix):** the wise `None` description ("a conversation just concluded naturally, silence serves the mood better") was gated to non-continuous mode only. So in continuous-roleplay mode (which user keeps on), the GM had **no syntactic way** to signal "the scene is done." Its only choices were `StartConversation`, `ContinueConversation`, or fail. Combined with line 763's bias ("Shape the world actively—don't just facilitate dialogue; make things happen") and the `ContinueConversation` reason at line 791 ("the dialogue needs another beat to conclude naturally"), the GM was structurally pushed to keep firing `ContinueConversation`. Each `ContinueConversation` re-prompted the Dialogue agent with the prior farewell in context, and the Dialogue agent dutifully restated. **Fix:** Edited `prompts/gamemaster_action_selector.prompt` with seven changes: 1. Line 9: Removed `{% if not is_continuous_mode %}` guard so `ACTION: None` is always in the format help. 2. Line 57: Qualified "needs another beat" with "NOT applicable if prior beat was a conclusion." 3. NEW Anti-Restatement Rule under ContinueConversation: explicit instruction that topic must drive *new* content, never rephrasing. 4. Lines 75-77: Replaced "you must select an action—do not select None" with "Prefer action over inaction. **However, recognize natural endings.**" 5. NEW Concrete signals list: explicit triggers for `ACTION: None` ("farewell," "goodbye," "I'll be on my way," "take care," "no more words," "leave me alone"). 6. Lines 150-152: Removed `{% if not is_continuous_mode %}` guard so `None` is always listed in Available Actions. 7. Line 161: Continuous user message mentions `ACTION: None` as a valid choice. Backup naming convention: the upstream baseline (now active again after revert) had been kept at `gamemaster_action_selector.prompt.backup` while the fix was active. After revert, the user renamed our edited version to `gamemaster_action_selector.prompt.edit` — semantically clearer than `.backup` (it labels "this is OUR edit," not "this is what was here before"). This `.edit` suffix is the convention going forward for preserving reverted-but-not-discarded edits. **Side effects:** **Exposed Bug #2** — the over-firing GM loop had been masking the malformed-marker bug by giving the Dialogue model multiple retries to emit a parser-compatible action line. With the loop stopped, malformed markers go through to TTS uncorrected. **This is the reason for the revert** — we want both fixes shipped together, not a partial regression. **Files touched:** `mods/SkyrimNet/SKSE/Plugins/SkyrimNet/prompts/gamemaster_action_selector.prompt`. **Verification:** Web UI trace screenshot at 04:43 showed three consecutive `ACTION: None` returns from the GM after Arcadia's `OPENTRADE` greeting — exactly the desired behavior (no spurious continuation). --- ## Bug #2 — Malformed action markers spoken aloud `[KNOWN, UNFIXED]` **Discovered:** 2026-04-18 (immediately after Bug #1 fix exposed it) **Status:** Known, unfixed. Was previously masked by Bug #1. **Symptom:** The Dialogue model emits a bare uppercase token (e.g., `OPENTRADE`) at the end of its dialogue text without the required `ACTION: ` prefix. The C++ parser ignores it as a non-match, and `FilterActionLines` doesn't strip it (it only strips successfully-parsed action lines). The token then falls through to TTS and gets spoken verbatim. No in-game action fires. **Concrete example** (from web UI screenshot, 04:42:47): ``` Dialogue agent output: "Welcome, Davies Nullshari. My name is Arcadia. How may I assist you today? OPENTRADE" → Arcadia speaks: "Welcome, Davies Nullshari. My name is Arcadia. How may I assist you today? OPENTRADE." → No trade UI opens. ``` **Root cause:** Two-layer: 1. **Parser is strict** (`ActionManager.cpp:1783` "No ACTION: line found in response"). Requires literal `ACTION: ` prefix at start of line. No fuzzy matching, no fallback. 2. **Prompt format encourages the mistake.** `submodules/user_final_instructions/0750_embedded_actions.prompt:6-9` lists actions as bullets: ``` **Available Actions:** - `OPENTRADE` — Use ONLY if ... - `OFFERQUEST` — ... ``` The model sometimes emits the bare bullet name instead of the full `ACTION: OPENTRADE` line. **Why it became visible after fixing Bug #1:** The over-firing GM loop gave the Dialogue model 3-5 retries per scene. Eventually one retry would emit the marker correctly and the action would fire. With the loop stopped, the first malformed emission is the only one — no retry, no recovery. **Fix candidates** (none applied): A) **Prompt-side enforcement.** Edit `submodules/user_final_instructions/0750_embedded_actions.prompt:6-9` to add a "WRONG vs RIGHT" example: ``` **CRITICAL — common mistake:** - ✗ WRONG: "Welcome to my shop. OPENTRADE" - ✓ RIGHT: "Welcome to my shop.\nACTION: OpenTrade" The literal `ACTION: ` prefix at start of a new line is REQUIRED. Bare action names will NOT fire. ``` B) **Parser-side leniency.** Modify the C++ DLL to recognize bare uppercase tokens at end of dialogue as candidates. **Not feasible** — closed source. C) **Add an "action-marker recognition" rule to the GM** so the GM sees the orphaned marker in recent dialogue and fires the corresponding action on its next tick. Brittle — adds responsibility to the wrong agent and depends on GM tick timing. **Recommendation:** Option A is the durable fix. Cheap, additive, treats the cause not the symptom. **Files touched:** none yet. Sister edit would land at `mods/SkyrimNet/SKSE/Plugins/SkyrimNet/prompts/submodules/user_final_instructions/0750_embedded_actions.prompt`. **User's stance:** "all fine dear it's just a game B." — willing to live with this for now while we map the architecture systematically. --- ## Bug #3 — `OPENTRADE` reflects an action-name uppercasing surprise `[OBSERVED, NOT REPRODUCED]` **Related to Bug #2**, deserves its own note. **Observation:** Papyrus-registered action names are **uppercased in the action registry** (`OpenTrade` → `OPENTRADE`). YAML-registered actions preserve case. So `OPENTRADE` IS the correct registry name when emitted via the Papyrus path — the only thing the Dialogue agent gets "wrong" is the missing `ACTION: ` prefix. **Implication:** Any prompt-side fix to bug #2 should reference `ACTION: OPENTRADE` (uppercase, matching the registry), not `ACTION: OpenTrade`. Mismatch would cause the parser to also reject the corrected form. **Verified:** `SkyrimNet.log:16393` `ActionLibrary::RegisterPapyrusAction for action: OPENTRADE`. --- ## Pattern observation: "two bugs canceling out" Bug #1 + Bug #2 form a classic interaction pattern: - The visible bug (recursive farewell) was *caused by* Bug #1. - Bug #2 (malformed markers) had been silently present but **statistically masked** by Bug #1's retry loop. - Fixing Bug #1 exposed Bug #2. This is worth remembering when planning future fixes: **a clean fix can reveal latent bugs that were invisible while the bigger bug was running**. Always consider what the broken behavior was inadvertently working around. --- ## Future-bug log slots (Reserved for entries we haven't discovered yet. When you land here from a future session having found a new bug, add an entry following the format above and update the README's "Last verified pass" date.)