Repository navigation
Description
Problem
The browser world presenter refuses to render an entire screen when any cell contains an emoji with a skin-tone modifier (or a subdivision-flag tag sequence) — glyphs the Rust side of the same surface explicitly validates and emits as ordinary 2-column cells. One such glyph anywhere on the screen makes renderWorldSurface return false for the whole surface, so the world view silently stays on its previous/legacy content.
Evidence (verified this run, main 37418e56)
Rust side — pkgs/id/src/world_presentation.rs:219 and :350 validate a cell by UnicodeWidthStr::width(symbol) == columns using unicode-width 0.2.2 (pinned in pkgs/id/Cargo.lock). A probe crate compiled and run on the laptop (unicode-width = "=0.2.2", unicode-segmentation) measures:
"👋🏽"(U+1F44B U+1F3FD) — graphemes=1, width=2"🧑🏽"(U+1F9D1 U+1F3FD) — graphemes=1, width=2"🏋🏽"(U+1F3CB U+1F3FD) — graphemes=1, width=2- England flag
U+1F3F4 U+E0067 U+E0062 U+E0065 U+E006E U+E0067 U+E007F— graphemes=1, width=2
So the server legitimately produces columns: 2 cells carrying these symbols, with a columns: 0 continuation cell.
TS side — pkgs/id/web/src/world-surface.ts:149-156 (graphemeWidth) has no modifier/tag-sequence case: outside the ZWJ (:150-152) and U+FE0F (:153) shortcuts it sums per-code-point widths. The skin-tone modifiers U+1F3FB–U+1F3FF fall inside the wide range [0x1f3f8, 0x1f43e] at :95, so a modified emoji measures 2 + 2 = 4; tag characters U+E0067… each measure 1, so the flag measures 8. At :234-235 the presenter checks graphemeWidth(cell.symbol) !== cell.columns and returns false.
Probe against the verbatim world-surface.ts from main (sha256 e92f95f99844db8553c8a60f2368305a8b4d654d4b03dfc4132c5b26ff62bd91, run with Bun on the laptop, minimal DOM stub):
- controls render:
x/1 → true,😀/2 → true,👋/2 → true, ZWJ family/2 → true, keycap1️⃣/2 → true 👋🏽/2 → false,🧑🏽/2 → false,🏋🏽/2 → false, England tag flag/2 → false
Why it matters
Skin-tone emoji are everyday user content in a world/chat surface. The Rust producer and the TS presenter disagree about the same contract (the TS file's own header at :163-166 claims its width measurement "matches Rust's unicode-width 0.2 semantics"), and the failure mode is total: one cell rejects the whole frame, with no fallback rendering of the remaining cells. This is the same presenter/Rust width-parity class as closed #761, which special-cased ZWJ and U+FE0F sequences but missed emoji modifiers and tag sequences.
Acceptance criteria
- A 2-column cell carrying an emoji + skin-tone modifier grapheme (e.g.
👋🏽,🧑🏽,🏋🏽) plus its continuation cell renders (renderWorldSurfacereturnstrueand produces the span), matching the Rust validator. - A 2-column cell carrying a subdivision-flag tag sequence (e.g. the England flag above) renders the same way.
- Existing width behaviour is unchanged: plain wide glyphs, ZWJ sequences, U+FE0F sequences, keycaps, flags (regional-indicator pairs), combining marks, and the refusal cases in
world-surface.test.ts(mismatched widths, multi-grapheme cells, control characters) all keep their current verdicts. - New regression tests in
pkgs/id/web/src/world-surface.test.tscover skin-tone-modified emoji and a tag-sequence flag at columns=2 (accepted) — and, where the Rust width would differ, at columns=1 (refused). - The fix is in the TS presenter's grapheme-width measurement; the Rust validator and the wire format do not change.
Activity
developing-today commentedon Oct 27, 2026
CODE-518
dezren39 commentedon Oct 27, 2026
🤖 Agent note · state: filed
dezren39 commentedon Oct 27, 2026
🤖 Agent note · state: filed (evidence addendum, same run)
graphemeWidthdivergence, two more verified classes from a ~60-grapheme Rust↔TS parity sweep (same laptop probes; logsts-out2.log+rust-sweep.login the goal'shidden_files/probe-2026-10-27-laneB-world/):WIDE_RANGEStable (world-surface.ts:53-133) — acolumns: 2cell is refused (rendered=false) and acolumns: 1cell is wrongly accepted (rendered=true), the reverse error of the modifier case. (〰 U+3030 and ㊗ U+3297 controls render correctly at 2.)columns: 2— the modifier rule in the fix should make the cluster total 2 regardless of the base's own width, not just handle wide bases./^\p{M}$/ucombining test at:134covers spacing marks too); refused atcolumns: 1. Lower confidence in its real-world reachability — a triager may scope it out.29 remaining items
dezren39 commentedon Oct 28, 2026
🤖 Agent note · picked up
fix(id/web): measure emoji-modifier and tag sequences as their base —…
dezren39 commentedon Oct 28, 2026
🤖 Agent note · done
dezren39 commentedon Oct 28, 2026
🤖 Agent note · done
gh pr checks 1542(verified twice) shows run 40611675179 actually had biome-check FAIL and treefmt-check FAIL — one biome formatting diff in the PR's new test code (world-surface.test.tssequenceGlyphsarray) — plus several jobs still pending. All functional checks that completed did pass, and the merge itself is real (5a0173c, on main). Fix-forward filed as issue id(web): biome-check + treefmt-check red on main — world-surface.test.ts array formatting (from PR #1542) #1561 with PR opened from branch fix/1561-biome-world-surface (biome-prescribed one-line change, biome check clean + world-surface tests 6/6 on the laptop).