Skip to content

host: a group notice or name cut at its bound keeps half a surrogate pair in state.json #1945

Closed

Description

@HugoXK

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.

Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: d1fcaf770d55487d93286a2e455134c8
Branch: dev/2-p299-roster-text-bound
Base: 7f7b42d
At: 2026-10-31T15:33:37Z

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.

Written-by: FlyBot Lead

#1912 gave the host one exported cut(text, max), #1916 swept nine remaining code-unit cuts, and #1933 / #1938 / #1940 cleaned the outbound share envelope and the peer labels. The two storage writers for a group's notice and name were on none of those lists: both still slice on UTF-16 units, so a pair that straddles the bound is written into state.json as a lone surrogate.

What and where (live main 7f7b42d6)

file:line cut
core/src/host/roster.js:265 const cleanNotice = text => String(text ?? '').trim().slice(0, ROOM_NOTICE_MAX) — 2000, trim then slice, no toWellFormed
core/src/host/roster.js:268 unquote(text).replace(/\s+/g, ' ').slice(0, ROOM_NAME_MAX) — 60, same shape

Limits are ROOM_NOTICE_MAX = 2000 / ROOM_NAME_MAX = 60 in core/src/host/constants.js:46-47. roster.js already imports cut (:5) and uses it at :89, :203; only these two writers still cut raw. Call sites: :334 (create-room join) and :405 (updateRoom's patch.notice / patch.name).

#1938's repair runs on the outbound envelope copy only (core/src/shared/room-share.js:233), so the value kept in rooms[].notice / rooms[].name is never made well formed; a guest then receives a body cut one unit short and it never matches the host's.

Reproduce (on the base, three failures, all red for the right reason)

  • updateRoom({ notice: 'a'.repeat(1999) + '😀' + 'tail' }) → stored 2000 units ending 61 d83d, isWellFormed() === false, JSON.stringify → "…aaa\ud83d", UTF-8 tail ef bf bd. The same input through cut(text, 2000).text is 1999 units and well formed.
  • updateRoom({ name: 'r'.repeat(59) + '😀' }) → stored 60 units ending 72 d83d, ill formed; cut(name, 60).text is 59 units and well formed.
  • The same two through the createRoom / update_group path.

Reachable from two everyday surfaces: the group-name field in core/src/client/details-drawer.js:317-322 has no maxLength (saveName → actions.updateRoom → channel.js:371 → roster.js:405), and the notice field in core/src/client/group-settings.js:200 counts code units (maxLength: 2000), as does a Bot's update_room (core/src/tools.js:550).

Expected

  • Both writers store cut(...).text: a stored notice is at most 2000 units and well formed, a stored name at most 60 and well formed, and the stored value is a code-point prefix of the input (one unit shorter when a pair straddles the bound — the pair is dropped, never split).
  • Everything else in the two functions unchanged: the two limits, the trim, the ?? '' fallback, the whitespace collapse, the unquote, and the duplicate-name rejection.
  • A well-formed input is stored byte-identical to today.
  • Tests must cover both writers and both paths (updateRoom and createRoom/update_group), each with a red on base and green on the fix, plus the two positive cases (1999a + 😀 + TAIL → 1999a; 59r + 😀 → 59r).

Out of scope

  • ingestSeatShare's text writers (core/src/host/room-membership.js, around :1990 / :2003 / :2022) — same family, separate card.
  • Reporting the truncation back to the caller (changes gains a "shortened to N" line) — enhancement, separate card.
  • The outbound share envelope — already repaired by #1938.

Dedup

Checked live main 7f7b42d6 (grep -n 'slice(0' core/src/host/roster.js → the two rows above) and the closed issues: #1916 swept nine cuts and neither roster writer is among them; #1933, #1937, #1938, #1940 are the envelope/label side; #1923 is peer-name bounds. Open issues carry no same-topic card.

Scope

Two lines in core/src/host/roster.js, tests, the patches/ds-bot mirrors and one changelog fragment. XS.

Activity

  1. HugoXK commented on Oct 31, 2026

    @HugoXK
    ContributorAuthor

    Heartbeat

    Written-by: FlyBot Lead
    Still-working: d1fcaf770d55487d93286a2e455134c8
    At: 2026-10-31T15:46:06Z

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions