viewer(#972 defense-in-depth): strip OOC authoring-preamble leaks in sanitizeNarration - #979
Conversation
…sanitizeNarration
The 2026-06-17 craft audit found the first-person OOC AUTHORING-PREAMBLE family
shipping to players verbatim ("Now let me seat <X> as the player character",
"Continuity check — let me correct that", "Let me set the order of it", "Here's
how round one actually went:", "let me set their advancement through the engine").
PR #972 fixed the leak AT THE SOURCE (DM SKILL.md FICTION-ONLY rule + the
deterministic narration_no_ooc_leak gate in qa/assert_behavioral.py::_NARRATION_LEAK).
This adds the viewer-arm backstop: a new high-confidence, OOC-only _AUTHORING_PREAMBLE
whole-sentence-drop wired into sanitizeNarration's existing _isScaffoldingSentence /
_hasScaffolding pass, so any leak that still reaches the chronicle (old transcripts,
edge cases) is stripped before the player sees it.
Patterns MIRROR _NARRATION_LEAK byte-for-byte (the 5 authoring-preamble arms; the
6th, "inciting incident", is already covered by _CRAFT_JARGON), carrying over its
machine-checked FP-hardening: "through the engine" is engine-sense-anchored (literal
machinery survives), the seat arm is full-word "as the player character" only, and
the round-replay arm is "here's how round <n> … went"-anchored. Additive; legitimate
fiction/dialogue is never stripped.
Tests: 3 new cases in test_sanitize_narration.py (5 verbatim leaks stripped;
inline + multiline surgical strip; legit-fiction FP guards). 13/13 sanitize tests
pass; full viewer suite 749 passed (1 pre-existing env failure: pydantic missing in
the portrait-gen subprocess, unrelated).
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds a ChangesAuthoring Preamble Sentence Stripping
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Poem
✨ Finishing Touches📝 Generate docstrings
Comment |
What
Defense-in-depth viewer arm for the 2026-06-17 craft audit's OOC authoring-preamble leak. Adds a high-confidence, OOC-only whole-sentence-drop (
_AUTHORING_PREAMBLE) tosanitizeNarrationinviewer/openworlds/screen-table.jsx, so the first-person DM "thinking out loud" lines the audit found shipping to players verbatim are stripped before the chronicle renders them:Why this, given #972 already shipped
The primary fix landed in #972: the DM
SKILL.mdFICTION-ONLY rule + the deterministicnarration_no_ooc_leakgate inqa/assert_behavioral.py::_NARRATION_LEAK— that stops the leak at the source (the DM prompt). This PR is the viewer backstop for anything that still reaches the viewer (old transcripts, edge cases): the engine stays the sole writer; this is a read/projection filter only.How
_AUTHORING_PREAMBLEregex wired into the existing_isScaffoldingSentence(whole-sentence drop) +_hasScaffolding(fast path) — the same [playtest][P1-critical] DM scaffolding leaks into player Chronicle — dice tallies + plot-structure jargon ('spine hook'/'cold open') + 'beat complete' stage-directions #347/[a11y][major] Chronicle floods the accessibility tree by beat 2 — Do/Declare unreachable for AT users #752 sentence-level machinery that already strips dice tallies / plot-craft jargon / stage-directions. Fully additive: no existing arm changed or weakened._NARRATION_LEAKbyte-for-byte (the 5 authoring-preamble arms; the 6th —inciting incident— is already covered by the file's_CRAFT_JARGON, so it's not duplicated). A comment cross-references the gate and asks future edits to keep the two layers in sync.through the engineis verb/noun-anchored to the game-ENGINE sense → literal machinery ("Steam screamed through the engine block") survives;as the player characteronly → a bare in-world "PC" / "the player of the lute" survives;here's how round <n> … went-anchored → a bare "round one went" survives.Tests
3 new cases in
viewer/tests/test_sanitize_narration.py(which transpile + run the real shipped.jsxvia the bundled Babel):"";13/13 sanitize tests pass. Full
viewer/tests/suite: 749 passed, 6 skipped, 1 pre-existing env failure unrelated to this change (test_portrait_gensubprocess:ModuleNotFoundError: No module named 'pydantic').Lane: macOS/OpenWorlds (viewer). No engine/content changes.
Summary by CodeRabbit
Bug Fixes
Tests