158 lines
11 KiB
Markdown
158 lines
11 KiB
Markdown
# 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.)
|