fix(dm+engine): the 2 opus golden-spine RED bugs (OOC seating-leak + CompanionQuestArc.summary) - #1077
Conversation
…CompanionQuestArc.summary)
Both surfaced on the gs-ember-opus golden-spine (embergloom-pact, 24 beats, OPUS DM)
that RED'd on narration_no_ooc_leak + no_rejected_tool_calls.
1a — narration_no_ooc_leak: the DM narrated its own seating decision into player prose
("Now I'll seat Bryn as the player character — a hedge-healer's daughter fits a Bard…")
on the opening beat. The FICTION-ONLY rule (SKILL.md:83) already bans the phrasing, but the
leak keeps shipping on the very first beat where seating happens. Add a sharp wrong→correct
'SEAT THE PC SILENTLY' reminder anchored to the cold-open pointer (no engine change).
1b — no_rejected_tool_calls: set_companion_quest_arc(arc={"summary": ...}) was REJECTED
because CompanionQuestArc (extra='forbid') had no summary field (extra_forbidden ⇒ FATAL ⇒
lenses RED-capped). Add summary: str = '' additively (mirrors the #1048 per-stage description
fix) + 2 regression tests (accepted+persisted; old snapshots round-trip). 77/77 pass.
Refs the standardized-closeout opus run; un-RED-caps authored-companion-arc golden-spines.
📝 WalkthroughWalkthroughAdds a ChangesCompanionQuestArc summary field
DM Beat Cycle Silent Seating Guidance
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~5 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@servers/engine/tests/test_companion_arc.py`:
- Around line 516-522: The test function
test_companion_quest_arc_summary_defaults_empty_and_old_snapshots_round_trip
claims to verify round-trip behavior but only checks the immediate response from
set_companion_quest_arc without verifying persistence. To fix this, after the
existing assertion on the set_companion_quest_arc response, add a call to
get_companion_quest_arcs to retrieve the persisted companion quest arc data and
assert that the summary field in the retrieved data is still an empty string,
ensuring the data actually round-trips correctly through storage and retrieval.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: a5c60680-cb45-4b6a-8367-536f0a2248eb
📒 Files selected for processing (3)
servers/engine/models.pyservers/engine/tests/test_companion_arc.pyskills/dungeon-master/SKILL.md
| def test_companion_quest_arc_summary_defaults_empty_and_old_snapshots_round_trip(camp): | ||
| # Additive invariant: an arc authored WITHOUT a summary keeps today's behavior (empty string), | ||
| # so pre-fix snapshots deserialize unchanged. | ||
| cid, comp = camp | ||
| out = server.set_companion_quest_arc(cid, comp, {"title": "No Summary Arc", "status": "locked"}) | ||
| assert out["companion_quest_arc"]["summary"] == "" | ||
|
|
There was a problem hiding this comment.
Test name claims round-trip but only verifies write response.
This test currently checks the immediate set_companion_quest_arc payload, not persisted read-back. Add a get_companion_quest_arcs assertion so the “round trip” claim is actually covered.
Suggested patch
def test_companion_quest_arc_summary_defaults_empty_and_old_snapshots_round_trip(camp):
@@
cid, comp = camp
out = server.set_companion_quest_arc(cid, comp, {"title": "No Summary Arc", "status": "locked"})
assert out["companion_quest_arc"]["summary"] == ""
+ got = server.get_companion_quest_arcs(cid, companion_id=comp)["companion_quest_arcs"][0]
+ assert got["summary"] == ""📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def test_companion_quest_arc_summary_defaults_empty_and_old_snapshots_round_trip(camp): | |
| # Additive invariant: an arc authored WITHOUT a summary keeps today's behavior (empty string), | |
| # so pre-fix snapshots deserialize unchanged. | |
| cid, comp = camp | |
| out = server.set_companion_quest_arc(cid, comp, {"title": "No Summary Arc", "status": "locked"}) | |
| assert out["companion_quest_arc"]["summary"] == "" | |
| def test_companion_quest_arc_summary_defaults_empty_and_old_snapshots_round_trip(camp): | |
| # Additive invariant: an arc authored WITHOUT a summary keeps today's behavior (empty string), | |
| # so pre-fix snapshots deserialize unchanged. | |
| cid, comp = camp | |
| out = server.set_companion_quest_arc(cid, comp, {"title": "No Summary Arc", "status": "locked"}) | |
| assert out["companion_quest_arc"]["summary"] == "" | |
| got = server.get_companion_quest_arcs(cid, companion_id=comp)["companion_quest_arcs"][0] | |
| assert got["summary"] == "" |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@servers/engine/tests/test_companion_arc.py` around lines 516 - 522, The test
function
test_companion_quest_arc_summary_defaults_empty_and_old_snapshots_round_trip
claims to verify round-trip behavior but only checks the immediate response from
set_companion_quest_arc without verifying persistence. To fix this, after the
existing assertion on the set_companion_quest_arc response, add a call to
get_companion_quest_arcs to retrieve the persisted companion quest arc data and
assert that the summary field in the retrieved data is still an empty string,
ensuring the data actually round-trips correctly through storage and retrieval.
What
Fixes the two behavioral-gate REDs the OPUS golden-spine (
gs-ember-opus, embergloom-pact, 24 beats) surfaced — the decisive run that proved opus drives the campaign (act 2, 3 locations, quest resolved) and scored story 4.9 (the "DM under-drives" scare was a Sonnet-measurement artifact, not a regression).1a ·
narration_no_ooc_leak— OOC seating-preamble leak (DM prompt)The DM narrated its own casting decision into player-facing prose on the opening beat:
The FICTION-ONLY rule (
SKILL.md:83) already bans this exact family, but it keeps shipping on the very first beat where PC-seating happens. Fix: a sharp wrong→correct "SEAT THE PC SILENTLY" reminder anchored right at the cold-open pointer. No engine change.1b ·
no_rejected_tool_calls—CompanionQuestArc.summaryrejected (engine model)set_companion_quest_arc(arc={"summary": ...})was REJECTED —CompanionQuestArcisextra="forbid"and had nosummaryfield (extra_forbidden→no_rejected_tool_callsFATAL → all lenses RED-capped). Addsummary: str = ""additively (mirrors the just-merged #1048 per-stagedescriptionfix).Why it matters
Both are the cap-rate failure class — a single rejected tool call / OOC leak RED-caps the whole run's lenses, which is what kept the honest opus story score (4.9 raw) from showing as a clean GREEN run.
Tests
+2regression tests intest_companion_arc.py(summary accepted+persisted; arc without summary defaults""so old snapshots round-trip).uv run --directory servers/engine python -m pytest tests/test_companion_arc.py→ 77 passed.Invariants
Additive-by-default (empty == today; old snapshots round-trip); engine stays sole-writer; no gate weakened. 1a is a prompt-only change.
Verification (post-merge)
A short opus golden-spine →
narration_no_ooc_leakGREEN (seating leak gone). Reported in the standardized closeout.Summary by CodeRabbit
New Features
Documentation
Tests