Clean em-dashes and smart quotes from markdown files

This commit is contained in:
Nyx Nimmerverse
2026-08-02 16:09:49 +00:00
parent 2cb32029a6
commit 192ac8f702
13 changed files with 177 additions and 177 deletions

View File

@@ -1,6 +1,6 @@
# Bugs and Fixes Running Log
# 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.
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
@@ -9,24 +9,24 @@ Each entry has:
- **Discovered:** absolute date
- **Symptom:** what the user/we saw
- **Root cause:** what's actually broken
- **Fix:** what we changed (or "none see notes")
- **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]`
## 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.
**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.
- `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.
@@ -36,7 +36,7 @@ Plus belt-and-braces: `tts.maxSegmentWords: 24 → 4` so even residual races los
---
## Bug #1 Recursive farewell loop in continuous mode `[REVERTED fix preserved, see notes]`
## 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.
@@ -53,32 +53,32 @@ Plus belt-and-braces: `tts.maxSegmentWords: 24 → 4` so even residual races los
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.
- **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 activelydon'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.
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 actiondo not select None" with "Prefer action over inaction. **However, recognize natural endings.**"
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.
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.
**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).
**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]`
## 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.
@@ -100,40 +100,40 @@ OPENTRADE"
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` ...
- `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.
**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:**
**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.
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.
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.
**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]`
## 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.
**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.