Skip to content

fix(viewer): wire Rest & Prepare relay + browsable level-up subclasses (#610 #617 #607) - #873

Merged
100yenadmin merged 1 commit into
mainfrom
fix/rri-5e98e6f-camp-levelup-viewer
Jun 14, 2026
Merged

fix(viewer): wire Rest & Prepare relay + browsable level-up subclasses (#610 #617 #607)#873
100yenadmin merged 1 commit into
mainfrom
fix/rri-5e98e6f-camp-levelup-viewer

Conversation

@100yenadmin

Copy link
Copy Markdown
Member

What

Two MAJOR viewer-side UI-wiring bugs the optimizer persona filed in the RRI-5e98e6f 5-persona sweep (2026-06-14). In both, the engine already works (the 31-PR fix-phase landed long_rest / short_rest / prepare_spells and PR #742 wired preview_level_up subclass blocks) — the viewer just did not display/use it. This is viewer wiring + rendering only; the engine is untouched.

(1) Rest & Prepare is now functional — #610 / #617

RestPrepareModal (screen-character.jsx) had its "Make camp" and "Seal the choices" CTAs hard-disabled as display-only stubs ("…not saved to the engine"), so spell re-preparation + slot recovery were dead. They now relay a composed do move-intent to /move — the same read-only move-sink path camp-sidebar.jsx and LevelUpModal already use (the engine is the sole writer; the viewer never mutates snapshot). The engine resolves it through long_rest/short_rest + prepare_spells (refresh HP + slots, advance the clock, record prepared spells).

(2) Level-up dialog browses ALL subclasses + says why Confirm is disabled — #607

  • The subclass picker now lists every engine-exposed option (option.subclass.options), each with its level-3 feature breakdown — a real browsable comparison ("what each tradition grants"), not just the first / a name-only line.
  • The disabled "Confirm advancement" now explains WHY (hover tooltip + inline note): a subclass needs naming, or no XP is earned yet (the planner lists no legal level to advance into) — never a silently-dead button.

Invariants held

Viewer stays read-only + move-sink (renders via React text nodes / createElement; no dangerouslySetInnerHTML; client sanitizeNarration preserved). Additive — no existing screen/test broken. Wire contracts frozen (/move kind:"do", /build-options, /character-surface). No engine change — round-trip safe.

Tests

New viewer/tests/test_rest_camp_levelup_wiring.py — the JSX behaviour harness (transpiles the real screen-character.jsx through the vendored babel under a node vm, renders the real RestPrepareModal / LevelUpModal, drives the scripted /build-options + /move fetch, finds nodes by testid, invokes onClick, re-renders). 11 new tests (8 verified RED against pre-fix screen-character.jsx).

  • RestPrepareWiringTests (6): Make-camp enabled + POSTs one do /move (long-rest intent naming slot recovery); disabled + reason when read-only / dm-busy; opening writes nothing; the prep step's Seal CTA relays too; no display-only "(preview)" / "not saved" copy remains.
  • LevelUpSubclassBrowseTests (4): all subclass options + their feature breakdowns render; selecting one enables Confirm; the unnamed-subclass disable carries the inline+tooltip why.
  • LevelUpNoXpTooltipTests (1): no-XP Confirm is disabled with an explanatory "no XP earned" tooltip + inline note.

Gates: full viewer suite 516 passed / 1 skipped; bash qa/fast_gate.sh Tier-0 PASS (195 deterministic engine tests, engine untouched).

These are gate-movers from the RRI-5e98e6f optimizer sweep. Do not merge (milestone v1.0.4 staging).

#610 #617 #607)

From the RRI-5e98e6f optimizer sweep — two MAJOR viewer-side wiring gaps where the
engine already works (the 31-PR fix-phase landed long_rest/short_rest/prepare_spells
+ preview_level_up subclass blocks) but the viewer did not use it:

(1) #610/#617 — RestPrepareModal "Make camp" + "Seal the choices" were `disabled`
    display-only stubs ("…not saved to the engine"), so spell re-preparation + slot
    recovery were non-functional. They now relay a composed `do` move-intent to /move
    (the SAME read-only move-sink path camp-sidebar.jsx + LevelUpModal use — the engine
    is sole writer; the viewer never mutates snapshot). Enabled + functional when a
    session is attached (can_act) AND the DM isn't mid-turn (dmBusy); honestly disabled
    + explained (hover title + inline note) otherwise. Synchronous double-submit lock.
    Threads liveSession into ScreenCharacter (additive prop) for the dmBusy gate,
    mirroring ScreenMap → CampSidebar.

(2) #607 — the level-up dialog now BROWSES all engine-exposed subclass options, each
    with its level-3 feature breakdown (a real comparison, not just the first/name-only),
    and the disabled "Confirm advancement" says WHY inline + via tooltip: a subclass
    needs naming, or no XP is earned yet (no legal level to advance into) — never a
    silently-dead button.

Viewer stays read-only + move-sink; engine untouched. Wire contracts frozen
(/move kind:"do", /build-options, /character-surface). Round-trip safe.

Tests (JSX behaviour harness — transpiles the real .jsx through the vendored babel
under node vm, renders the real RestPrepareModal/LevelUpModal, drives the fetch +
clicks): viewer/tests/test_rest_camp_levelup_wiring.py — 11 new (8 RED on pre-fix
screen-character.jsx). Full viewer suite 516 passed / 1 skipped; fast_gate Tier-0 PASS
(195 deterministic engine tests).
@100yenadmin 100yenadmin added this to the v1.0.4 milestone Jun 14, 2026
@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@100yenadmin, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 1 hour, 44 minutes, and 32 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f04f6ecc-0062-4d16-8689-4c781b188ac8

📥 Commits

Reviewing files that changed from the base of the PR and between 01f3651 and 77fcbae.

📒 Files selected for processing (3)
  • viewer/openworlds/app.jsx
  • viewer/openworlds/screen-character.jsx
  • viewer/tests/test_rest_camp_levelup_wiring.py

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 77fcbae56a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +716 to +719
const named = Object.keys(prepared)
.sort((a, b) => Number(a) - Number(b))
.flatMap((lv) => (prepared[lv] || []).map((n) => n))
.filter(Boolean);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize spell slots before relaying prepared spells

When a caster uses this newly wired preparation flow with the current /character-surface shape, prepared never gets populated: hero.spellSlots is projected as an array of {level, max, ...} and hero.spells is grouped as Prepared/Known, while the modal still keys slot capacity by slots[group.level]. That means no spell buttons render/select, so this new relay always falls through to keeps their currently prepared spells and cannot actually prepare the spells the player chose. Please adapt the modal to the same array slot model used by SpellsTab before building this move text.

Useful? React with 👍 / 👎.

@100yenadmin
100yenadmin merged commit 673c4ad into main Jun 14, 2026
16 checks passed
@100yenadmin
100yenadmin deleted the fix/rri-5e98e6f-camp-levelup-viewer branch June 14, 2026 16:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant