Skip to content

fix(dm,viewer): stop DM story-craft scaffolding leaking into the player Chronicle (Closes #347) - #349

Merged
100yenadmin merged 1 commit into
mainfrom
fix/347-scaffolding-leak
May 30, 2026
Merged

fix(dm,viewer): stop DM story-craft scaffolding leaking into the player Chronicle (Closes #347)#349
100yenadmin merged 1 commit into
mainfrom
fix/347-scaffolding-leak

Conversation

@100yenadmin

@100yenadmin 100yenadmin commented May 30, 2026

Copy link
Copy Markdown
Member

Closes #347.

The bug

The #324 narrative persona (nar1, rated #1 fix) found DM-internal scaffolding bleeding into the player-facing Chronicle between prose beats. Verbatim leak:

"Zevlor held silence after three failed social checks; … connecting directly to the spine hook. Meeting beat of the cold open complete."

Three leak types: (1) dice/check tallies, (2) plot-structure/craft jargon (spine hook/cold open/act/beat/midpoint), (3) stage-direction status summaries (beat complete). Same CLASS as #335 (DM-internal → player) but a NEW pattern family#335's sanitizeNarration strips GM-advisory headers + engine tool names, not scaffolding PROSE. Root: the DM agent writes a "beat-complete state summary" into the player narration.

Fix — two layers (engine stays sole writer; no wire/engine change)

PRIMARY — DM skill (skills/dungeon-master/)

A prominent NON-NEGOTIABLE rule: the player-facing narration is FICTION ONLY. The act/beat structure, dice math, and "what the scene is doing" bookkeeping are the DM's private reasoning; the player only ever sees in-world prose + quoted dialogue.

  • SKILL.md — new bullet in Non-negotiables — every beat (beside "Dice live inside the tools"), breaking out the three leak classes (tallies / craft-jargon / stage-directions) with the verbatim counter-example.
  • AGENT.md — a callout right under The PROCESS heading, where the act/beat vocabulary is introduced: that vocabulary is felt, never labeled, never written into player narration.

DEFENSIVE GUARD — viewer sanitizeNarration (screen-table.jsx)

Extends the #335 line-oriented filter with HIGH-CONFIDENCE-only scaffolding patterns, at two grains:

  1. a dice/check TALLY is excised in place as a sub-clause — so the real prose around it survives;
  2. plot-craft JARGON (spine hook / inciting incident / midpoint reversal) and "beat complete" stage-directions drop the whole sentence (those sentences are scaffolding through-and-through).

False-positive guarded (the critical constraint): bare beat/scene/act/hook/complete never trigger — they only match inside a fixed scaffolding frame. A war-drum's "beat", a tavern "scene", "the second act of the play", "the cold open road", "the ritual is complete", and "failed assaults" all pass through verbatim. The patterns:

  • tally: \b<num>\s+(failed|successful|missed|botched|passed|blown)\s+(\w+\s+){0,2}(checks?|rolls?|saves?|saving throws?)\b (anchored on the check/roll/save noun);
  • jargon: \b(spine[- ]hook|inciting incident|midpoint reversal)\b;
  • stage-direction: a structural unit (cold open/<word> beat/act N/scene N) marked done (complete/wraps/…, optional copula), or meeting|arrival|threshold|setup|payoff|… beat, or connect(s|ing) … to the spine hook, or X of the Y complete.

Before / after (real function, via the bundled in-browser Babel)

LEAK   IN : Zevlor held silence after three failed social checks; the moment connecting directly to the spine hook. Meeting beat of the cold open complete.
LEAK   OUT: Zevlor held silence.

LEGIT  IN : A war-drum's beat rolled across the field; the second act of the play would have to wait.
LEGIT  OUT: A war-drum's beat rolled across the field; the second act of the play would have to wait.   (unchanged)

The leak collapses to clean prose; the legit sentence using "beat" + "second act of the play" is preserved verbatim.

Tests

New viewer/tests/test_sanitize_narration.py transpiles the real .jsx with the bundled babel-standalone-7.29.0 (the exact in-browser path) and runs sanitizeNarration:

(Node-guarded with @unittest.skipIf — mirrors the existing test_combat_event_cards.py Babel-via-Node pattern. This viewer/tests/ lane runs locally; engine/rules/voice CI lanes are unaffected.)

Validation

  • qa/ui_audit_health.sh --quick --axeall PASS, axe 0 violations across all 18 screens (incl. table, which renders the Chronicle).
  • screen-table.jsx + app.jsx transpile clean under bundled Babel; scripts/license_check.py passes.

Scope

DM-skill + viewer only. No engine change, no wire-contract change, no run-artifacts committed. Engine remains the sole writer.

Summary by CodeRabbit

  • Bug Fixes

    • Improved player-facing narration to remove internal game scaffolding and terminology that should not be visible to players.
  • Documentation

    • Updated Dungeon Master agent and skill documentation with clearer guidelines for what should not appear in player-facing content.

Review Change Stack

…er Chronicle (Closes #347)

The #324 narrative persona found DM-internal scaffolding bleeding into the
player-facing Chronicle between prose beats. Verbatim leak: "Zevlor held
silence after three failed social checks; … connecting directly to the spine
hook. Meeting beat of the cold open complete." Three leak types: (1) dice/check
tallies, (2) plot-structure/craft jargon (spine hook/cold open/act/beat/
midpoint), (3) stage-direction status summaries ("beat complete"). Same CLASS
as #335 (DM-internal -> player) but a NEW pattern family: #335's
sanitizeNarration strips GM-advisory headers + engine tool names, not
scaffolding PROSE. Root cause: the DM agent writes a "beat-complete state
summary" into the player narration.

Fix is two-layer (engine stays the sole writer; no wire/engine change):

PRIMARY (skills/dungeon-master) — a prominent NON-NEGOTIABLE rule that the
player-facing narration is FICTION ONLY: never write dice/check tallies, plot-
structure/craft jargon, or stage-direction status summaries. The scaffolding is
the DM's PRIVATE reasoning; the player only ever sees in-world prose + quoted
dialogue.
  - SKILL.md: new bullet in 'Non-negotiables — every beat' (next to 'Dice live
    inside the tools'), with the three leak classes broken out + the verbatim
    counter-example.
  - AGENT.md: a callout right under 'The PROCESS' heading where the act/beat
    vocabulary is introduced — that vocabulary is felt, never labeled, never
    written into player narration.

DEFENSIVE GUARD (viewer/openworlds/screen-table.jsx) — extend the #335
sanitizeNarration with HIGH-CONFIDENCE-only scaffolding patterns, two grains:
  - dice/check TALLY excised in place as a sub-clause ("Zevlor held silence
    after three failed social checks" -> "Zevlor held silence.") so real prose
    survives;
  - plot-craft JARGON (spine hook / inciting incident / midpoint reversal) and
    'beat complete' STAGE-DIRECTIONS drop the whole sentence.
  False-positive-guarded: bare 'beat'/'scene'/'act'/'hook'/'complete' never
  trigger (a war-drum's beat, a tavern scene, 'the second act of the play',
  'the cold open road', 'the ritual is complete', 'failed assaults' all pass
  through verbatim — they only match inside a fixed scaffolding frame).

Test: new viewer/tests/test_sanitize_narration.py transpiles the real .jsx with
the bundled babel-standalone (the in-browser path) and runs sanitizeNarration:
strips the verbatim #347 leak + each scaffolding class, preserves 11 legit-prose
cases, and asserts the #335 advisory/tool guards still apply. qa/ui_audit_health
--quick --axe: all PASS, axe 0 violations across 18 screens.
@coderabbitai

coderabbitai Bot commented May 30, 2026

Copy link
Copy Markdown

Caution

Review failed

Pull request was closed or merged during review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 10ad8e34-0a65-4fa6-bd1f-3f9c6b6c91a3

📥 Commits

Reviewing files that changed from the base of the PR and between d1ada0f and 3936736.

📒 Files selected for processing (4)
  • skills/dungeon-master/AGENT.md
  • skills/dungeon-master/SKILL.md
  • viewer/openworlds/screen-table.jsx
  • viewer/tests/test_sanitize_narration.py

📝 Walkthrough

Walkthrough

This PR implements a three-part fix for issue #347: DM scaffolding (dice tallies, plot-structure jargon like "spine hook" and "cold open", stage-directions like "beat complete") leaks into player-facing Chronicle narration. It adds skill guidance discouraging DM from writing scaffolding into narration, implements defensive sentence-level filtering regexes in sanitizeNarration, and provides comprehensive regression tests covering the new stripping logic and earlier #335 guards.

Changes

DM Scaffolding Leak Prevention

Layer / File(s) Summary
DM skill guidance clarifying fiction-only narration
skills/dungeon-master/AGENT.md, skills/dungeon-master/SKILL.md
AGENT.md and SKILL.md now explicitly warn that act/beat vocabulary (spine hook, cold open, reversal, beat complete) is private craft language that must never be written into player-facing narration; narration must remain in-world fiction only.
Scaffolding stripping implementation in sanitizeNarration
viewer/openworlds/screen-table.jsx
Added regexes to detect dice/check tallies (e.g., "three failed social checks"), plot-structure jargon (spine hook, cold open), and stage-direction patterns (beat complete). New _stripScaffoldingSentences helper excises tally phrases in-place and drops whole sentences matching high-confidence scaffolding patterns. sanitizeNarration now applies this stripping before _isInternalLine filtering.
Comprehensive regression test suite for scaffolding sanitization
viewer/tests/test_sanitize_narration.py
Built test harness that transpiles the shipped screen-table.jsx with bundled Babel and stubs a headless window to execute the real sanitizeNarration export. Tests verify removal of verbatim scaffolding leaks, individual tally/jargon/stage-direction patterns, false-positive avoidance on legitimate prose, and that earlier #335 guards still apply; includes edge cases like scaffolding embedded in multiline beat blocks.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • electricsheephq/WorldOS#335: Extends the same sanitizeNarration guard to handle new scaffolding patterns (dice tallies, plot-structure jargon) beyond the earlier #335 line-oriented GM-advisory and tool-name stripping.

Possibly related PRs

  • electricsheephq/WorldOS#343: Overlaps directly on viewer/openworlds/screen-table.jsx's sanitizeNarration flow and window export exposure.

Poem

🐰 Scaffolding's secrets, now secured and sound,
No tally-marks or plot jargon shall be found,
Pure fiction flows where players' eyes will gaze,
While crafty beats hide in the DM's maze.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the primary fix: stopping DM story-craft scaffolding from leaking into the player Chronicle, with issue reference.
Description check ✅ Passed The description comprehensively documents the bug, two-layer fix, examples, and validation; all required template sections are adequately addressed.
Linked Issues check ✅ Passed The PR fully addresses issue #347 by implementing both the DM-skill policy layer (SKILL.md, AGENT.md) and the defensive viewer filter (sanitizeNarration patterns) to strip all three scaffolding leak types while preserving legitimate prose.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #347: DM-skill guidance updates, sanitizeNarration extension, and comprehensive test coverage; no out-of-scope modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant