Skip to content

Polish Chronicle rows, Forge art, and table readiness - #548

Merged
100yenadmin merged 5 commits into
mainfrom
codex/worldos-chronicle-row-polish
Jun 2, 2026
Merged

Polish Chronicle rows, Forge art, and table readiness#548
100yenadmin merged 5 commits into
mainfrom
codex/worldos-chronicle-row-polish

Conversation

@100yenadmin

@100yenadmin 100yenadmin commented Jun 1, 2026

Copy link
Copy Markdown
Member

Summary

  • Dedup /chat player replay rows against matching optimistic player echoes in the OpenWorlds Chronicle.
  • Remove the repeated inline Chronicle label from each DM narration row; the section title and role=log label still name the region.
  • Keep this as a viewer projection change only: engine remains the sole campaign-state writer and /move remains the intent lane.

Product Evidence

  • Pre-fix browser scout on current main showed playable wiring but duplicated/glued Chronicle rows: /Volumes/LEXAR/Codex/worldos-product-slices/browser-scout-5012d02-20260602T064900/.
  • Post-fix in-app browser proof on the patched worktree: /Volumes/LEXAR/Codex/worldos-product-slices/chronicle-polish-5012d02-20260602T065420/.
  • Post-fix proof: ready_for_play=true, can_act=true, six enabled actions, private art present, zero console errors, inlineChronicleLabels=0, and one clean player echo plus one DM reply after Continue.

Test Plan

  • python3 -m pytest viewer/tests/test_live_narration_stream.py viewer/tests/test_openworlds_static.py -q
  • git diff --check

Summary by CodeRabbit

  • New Features

    • Camp deep-linking expanded (many screen aliases) with dedicated camp/rest mode and “Camp” title display
    • Player lines now render as distinct action vs dialog beats and suppress empty beats
  • Bug Fixes

    • Deduped player replay vs optimistic echoes; quick-action aliases normalized
    • Merchant purchases blocked until live market readiness; Resting toast shown on success
  • Changes

    • Default merchant changed to Old Troutman; journal list filters by selected tab
  • Tests

    • Added/updated tests covering replay dedupe, quick-action aliases, camp/rest flows, merchant gating, and item-icon aliasing

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

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: 9b53d8a0-f858-4299-ac76-8c59729beb05

📥 Commits

Reviewing files that changed from the base of the PR and between e481055 and 1911076.

📒 Files selected for processing (8)
  • viewer/openworlds/app.jsx
  • viewer/openworlds/camp-sidebar.jsx
  • viewer/openworlds/chrome.jsx
  • viewer/openworlds/screen-forge.jsx
  • viewer/openworlds/screen-table.jsx
  • viewer/tests/test_item_icons.py
  • viewer/tests/test_live_narration_stream.py
  • viewer/tests/test_openworlds_static.py
💤 Files with no reviewable changes (1)
  • viewer/openworlds/camp-sidebar.jsx
✅ Files skipped from review due to trivial changes (1)
  • viewer/tests/test_item_icons.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • viewer/openworlds/app.jsx
  • viewer/tests/test_live_narration_stream.py

📝 Walkthrough

Walkthrough

Parses player routing tags into replayable beats, deduplicates replayed player rows against optimistic echoes, adds app-status play gating and a blocks-play banner, expands camp deep-linking/campMode, updates merchant surface and journal filtering, adjusts item-art alias resolution, and updates related tests and narration markup.

Changes

Player Routing, Chronicle Dedup, and UI Updates

Layer / File(s) Summary
Player beat routing and replay parsing
viewer/openworlds/app.jsx
window.playerReplayBeat(text) parses leading routing tags into { kind, who, text, route } or null; /chat replay ingest for role === "player" now uses this parser and suppresses empty results.
Chronicle replay aliasing, dedup, and narration markup
viewer/openworlds/screen-table.jsx
Adds quick-action alias/label normalization and replay-key helpers, de-duplicates replayed chat beats against optimistic echoes when assembling the chronicle tail, and adds data-worldos-testid="chronicle-narration" while removing the inline "Chronicle" eyebrow span.
App-status fetch and play gating
viewer/openworlds/screen-table.jsx
Introduces appStatus state, fetches /app-status, computes appStatusBlocksPlay and a human-readable block reason, blocks UI actions (palette, dice, move/Declare), shows a blocks-play banner, and uses the block reason for toasts and move gating.
Hash deep-link routing and campMode handling
viewer/openworlds/app.jsx
Expands hash aliases to additional screens, sets/clears campMode for camp/rest links and on navigation, and shows "Camp" in TitleBar when map screen is in campMode.
Journal tab filtering and visible quests
viewer/openworlds/screen-journal.jsx
Adds journalQuestInTab(), memoizes visibleQuests, updates active-quest synchronization to follow visibleQuests, and uses a tab-specific empty state for selection/listing.
Merchant default and shared market stock
viewer/openworlds/screen-merchant.jsx
Changes initial merchantId to "old-troutman", introduces GATE_MARKET_STOCK, and refactors MERCHANTS to reference the shared stock constant.
Merchant surface status and /move relay
viewer/openworlds/screen-merchant.jsx
Adds surfaceStatus and a /character-surface fetch with abort handling, derives merchantWaresName, prevents submits while surfaceLoading, and refines the /move relay to throw on non-OK, clear cart on success, and show toasts; button labels/tooltips reflect loading state.
CampSidebar rest flow
viewer/openworlds/camp-sidebar.jsx
Removes optional onBeginRest() invocation on success and shows a "Resting" toast after a successful /move POST.
Item art aliases and forge scope
viewer/openworlds/chrome.jsx, viewer/openworlds/screen-forge.jsx
Adds alias entries (including empty-string aliases); uses hasOwnProperty to preserve empty-string aliases; fItemScope delegates to window.itemArtScope(name) before falling back to slug-based scope.
Item icon and forge tests
viewer/tests/test_item_icons.py
Extends alias assertions and adds a test ensuring screen-forge.jsx uses the shared window.itemArtScope helper.
Live narration stream tests
viewer/tests/test_live_narration_stream.py
Updates existing chronicle expectation to render replayed player action as a clean action row and adds tests for replay dedup vs optimistic echoes, quick-action alias dedup, clean action rendering after reload, and routing preservation for [do] vs [say].
OpenWorlds static JSX route tests
viewer/tests/test_openworlds_static.py
Adds/updates static assertions for camp deep-link/campMode, merchant default vendor and live-lane gating, journal tab/active-quest selection, table app-status blocking, narration markup (chronicle-narration), and Camp rest toast/no onBeginRest.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I parse the tags and tidy echoed speech,
Replay the deeds and keep duplicates out of reach.
Camp links that wander, merchants that wait,
Journals that match the tab's chosen state,
A rabbit nods — the chronicle's complete!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.75% 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 concisely summarizes the three main focus areas (Chronicle rows, Forge art, table readiness) and accurately reflects the changeset's primary improvements to viewer projection and presentation.
Description check ✅ Passed The description covers the main change (Chronicle dedup), product evidence with file paths, and a clear test plan, though it could reference the additional scope changes (camp routing, merchant defaults, etc.) for completeness.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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.

@100yenadmin
100yenadmin force-pushed the codex/worldos-chronicle-row-polish branch 5 times, most recently from 445b7ca to b73ec38 Compare June 2, 2026 00:46
@100yenadmin
100yenadmin force-pushed the codex/worldos-chronicle-row-polish branch from b73ec38 to e481055 Compare June 2, 2026 00:50

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🧹 Nitpick comments (3)
viewer/tests/test_openworlds_static.py (2)

699-700: ⚡ Quick win

Make the inline-Chronicle regression check whitespace-agnostic.

Line 700 matches one exact newline/indent layout, so the test can miss the regression if <span>Chronicle</span> comes back in the narration row with different formatting. Prefer a regex scoped to data-worldos-testid="chronicle-narration" instead of a fixed multiline literal.

Suggested assertion hardening
         self.assertIn("sanitizeNarration(entry.text)", source)
         self.assertIn('data-worldos-testid="chronicle-narration"', source)
-        self.assertNotIn('>Chronicle</span>\n          {text}', source)
+        self.assertNotRegex(
+            source,
+            r'data-worldos-testid="chronicle-narration"[\s\S]*?>Chronicle</span>',
+        )
🤖 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 `@viewer/tests/test_openworlds_static.py` around lines 699 - 700, The current
assertion uses a literal multiline string ('>Chronicle</span>\n         
{text}') which is whitespace-sensitive; update the test around
data-worldos-testid="chronicle-narration" to use a regex match instead (e.g.,
use re.search with re.DOTALL or collapse whitespace) to assert that a
<span>Chronicle</span> is not present in the narration row; modify the
assertNotIn line to perform a regex search on the source variable scoped to the
data-worldos-testid="chronicle-narration" fragment (and add an import for re if
missing).

189-202: ⚡ Quick win

Assert the merchant error-feedback branch too.

Line 201 only proves the non-OK fetch throws. If the .catch(...toast(...)) path regresses, this test still passes while the user loses the “Move not sent” feedback promised by viewer/openworlds/screen-merchant.jsx:329-365. Add an assertion for the catch/toast strings here so the static contract covers the full failure UX.

Suggested assertion additions
         self.assertIn("if (surfaceLoading) return;", source)
         self.assertIn("disabled={cart.length === 0 || surfaceLoading", source)
         self.assertIn("Checking the counter", source)
         self.assertIn("if (!response.ok) throw new Error", source)
+        self.assertIn(".catch((e) => toast({ kind: \"danger\"", source)
+        self.assertIn('title: "Move not sent"', source)
🤖 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 `@viewer/tests/test_openworlds_static.py` around lines 189 - 202, The test
test_merchant_waits_for_live_action_lane_before_purchase currently only asserts
the non-OK fetch throws; add assertions to cover the error-feedback branch by
checking the rendered source contains the fetch .catch handler and the toast
call plus the UX string used for failures (e.g., assertIn('.catch', source),
assertIn('toast(', source) and assertIn('Move not sent', source)) so the static
test ensures the catch/toast "Move not sent" feedback from the merchant screen
is present.
viewer/tests/test_live_narration_stream.py (1)

740-756: ⚡ Quick win

Add a regression case for [say] continue vs [do] continue.

This test validates routed speech/action, but not the quick-label overlap case. Adding [say] continue as dialog would guard against accidental speech→action coercion.

🤖 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 `@viewer/tests/test_live_narration_stream.py` around lines 740 - 756, Update
the test_routed_player_replay_preserves_action_vs_speech_after_reload test to
include a regression case for the quick-label overlap by adding a player item
with text '[say] continue' (distinct timestamp) to the h.enqueue payload so the
run exercises both '[do] continue' and '[say] continue'; then assert that
h.chronicle() contains two separate entries (one action row for the '[do]'
continue and one dialog row for the '[say]' continue) preserving routing
tags—use the existing _run call, h.enqueue invocation, and the returned
out["chronicle"] comparison to add this extra input and expected chronicle
entry.
🤖 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 `@viewer/openworlds/app.jsx`:
- Around line 801-814: fromHash/onHash currently only call setCampMode when
route.campMode is a boolean, so moving away from `#camp/`#rest leaves the old
campMode set; change both the initial handling and the onHash handler to always
call setCampMode(route.campMode) (i.e. remove the typeof boolean guard) so that
undefined from fromHash clears the previous campMode state; update references in
the initial block and the onHash function that use fromHash, setCampMode, and
setScreen accordingly.

In `@viewer/openworlds/screen-table.jsx`:
- Around line 283-290: The projectPlayerReplay projection is converting "You"
dialog entries into actions for quick-label words and needs to skip that
conversion when the replay explicitly routed the text as speech; update
projectPlayerReplay to detect explicit speech-routing indicators (e.g.
entry.route === 'say', entry.mode === 'say', or entry.routing?.type === 'say' —
check whichever routing field your model uses) and return the original entry if
any such indicator is present before applying
playerEchoKeys/QUICK_ACTION_REPLAY_LABELS; apply the same guarding logic to the
duplicate projection at the other location mentioned (around the second
occurrence at line ~315).

---

Nitpick comments:
In `@viewer/tests/test_live_narration_stream.py`:
- Around line 740-756: Update the
test_routed_player_replay_preserves_action_vs_speech_after_reload test to
include a regression case for the quick-label overlap by adding a player item
with text '[say] continue' (distinct timestamp) to the h.enqueue payload so the
run exercises both '[do] continue' and '[say] continue'; then assert that
h.chronicle() contains two separate entries (one action row for the '[do]'
continue and one dialog row for the '[say]' continue) preserving routing
tags—use the existing _run call, h.enqueue invocation, and the returned
out["chronicle"] comparison to add this extra input and expected chronicle
entry.

In `@viewer/tests/test_openworlds_static.py`:
- Around line 699-700: The current assertion uses a literal multiline string
('>Chronicle</span>\n          {text}') which is whitespace-sensitive; update
the test around data-worldos-testid="chronicle-narration" to use a regex match
instead (e.g., use re.search with re.DOTALL or collapse whitespace) to assert
that a <span>Chronicle</span> is not present in the narration row; modify the
assertNotIn line to perform a regex search on the source variable scoped to the
data-worldos-testid="chronicle-narration" fragment (and add an import for re if
missing).
- Around line 189-202: The test
test_merchant_waits_for_live_action_lane_before_purchase currently only asserts
the non-OK fetch throws; add assertions to cover the error-feedback branch by
checking the rendered source contains the fetch .catch handler and the toast
call plus the UX string used for failures (e.g., assertIn('.catch', source),
assertIn('toast(', source) and assertIn('Move not sent', source)) so the static
test ensures the catch/toast "Move not sent" feedback from the merchant screen
is present.
🪄 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: 175c75d0-81c1-44d9-b0f2-dc52240ece49

📥 Commits

Reviewing files that changed from the base of the PR and between 5012d02 and e481055.

📒 Files selected for processing (6)
  • viewer/openworlds/app.jsx
  • viewer/openworlds/screen-journal.jsx
  • viewer/openworlds/screen-merchant.jsx
  • viewer/openworlds/screen-table.jsx
  • viewer/tests/test_live_narration_stream.py
  • viewer/tests/test_openworlds_static.py

Comment thread viewer/openworlds/app.jsx
Comment thread viewer/openworlds/screen-table.jsx
@100yenadmin 100yenadmin changed the title Polish Chronicle move rows Polish Chronicle rows, Forge art, and table readiness Jun 2, 2026
@100yenadmin
100yenadmin merged commit a1e860b into main Jun 2, 2026
3 of 13 checks passed
@100yenadmin
100yenadmin deleted the codex/worldos-chronicle-row-polish branch June 2, 2026 02:02
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