Repository navigation
host: a group notice or name cut at its bound keeps half a surrogate pair in state.json - #1947
Conversation
cleanNotice and cleanRoomName still used slice, so a 2000-unit notice or a 60-unit group name that ended in a whole emoji stored a lone high surrogate: the cut landed between the halves of a pair and every encoder downstream read U+FFFD. Every other bound already goes through cut (ThinkFlowLab#1908/ThinkFlowLab#1912/ThinkFlowLab#1916, and cleanName for Bot names in this same file); these two were the last ones left. Docs-not-needed: docs/subsystems/core-host.md states the rule every bound follows (a cut stops on a code point boundary, through cut in text.js) and records these group values nowhere else. The 2000- and 60-unit caps, the trim, the falsy-input fallback, the whitespace folding and the empty-name path are unchanged, so no field, bound, tool or page a subsystem page describes moves.
|
Verdict: PASS at 801100c Ran at this tip: Red first, on this tip: with only Review points:
Not run here (no checkout on this box, and CI already covers it): the whole-repo |
Fixes #1945
Owner: Dev-2
Reviewer: Reviewer
Written-by: Dev-2
Claim-Id: d1fcaf770d55487d93286a2e455134c8
Branch: dev/2-p299-roster-text-bound
Base: 97d4bcd
Docs-not-needed:
docs/subsystems/core-host.mdstates the rule every bound follows (a cut stops on a code point boundary, throughcutintext.js) and records these group values nowhere else. The 2000- and 60-unit caps, the trim, the falsy-input fallback, the whitespace folding and the empty-name path are unchanged, so no field, bound, tool or page a subsystem page describes moves.cleanNoticeandcleanRoomNameincore/src/host/roster.jsbounded their text withslice, so a 2000-unit notice or a 60-unit group name that ended in a whole emoji kept a lone high surrogate: the cut landed between the halves of a pair and every encoder downstream read U+FFFD. Both are stored instate.jsonand drawn back through the console, so the half pair is what the user sees afterwards. Every other bound in this host already goes throughcut— includingcleanNamefor Bot names in this same file (:64/:109/:192/:203) — and these two were the last ones left.What changed
Two lines, the same expression the Bot-name bound beside them already uses:
cutreturnstext.slice(0, codePointEnd(text, max)), so the bound drops a straddling character whole instead of keeping its first half. Nothing else moves:ROOM_NOTICE_MAX = 2000andROOM_NAME_MAX = 60are unchanged, thetrim(), the?? ''fallback, the whitespace folding and the empty-name check are unchanged, and a text that is already well formed and shorter than its bound is returned exactly as before — so no length bound is loosened or tightened and no other caller sees a different value.patches/ds-bot/0002-flybot-api-host.patchcarries theroster.jsedit (that path is patched, not mirrored) andpatches/ds-bot/files/test/room-text-bound.test.jsis the generated copy of the new test; both were written bynode scripts/ds-bot-patches.mjs generate, not by hand, andcheckpluscanonicalare clean.Red first
New test file
core/test/room-text-bound.test.js, three cases, run against the base blob with the branch otherwise untouched (git show 97d4bcdd5aee168705040df2266963bbd23314fa:core/src/host/roster.js > core/src/host/roster.js):With the branch's
roster.jsrestored the same file is 3/3 green.Two positive cases sit inside those tests and hold on both sides:
'a'.repeat(1999) + '😀' + 'TAIL'stores exactly 1999a(≤ 2000,isWellFormed(), survives a UTF-8 round trip, and is a prefix of what was typed), and'r'.repeat(59) + '😀'stores exactly 59rfor the 60-unit name bound. So the fix drops the straddling pair, it does not truncate earlier and it does not repair the value into U+FFFD-and-keep-going.Mutation — each bound has its own teeth
Reverting one bound at a time, fix otherwise in place, on this tip:
cleanNoticeback to.slice(0, ROOM_NOTICE_MAX)not ok 1,not ok 3,ok 2—# pass 1 / # fail 2cleanRoomNameback to.slice(0, ROOM_NAME_MAX)not ok 2,not ok 3,ok 1—# pass 1 / # fail 2Fixing only the notice, the narrow reading of the issue, does not pass.
Verification
Run on this commit with a clean tree (
git status --porcelainempty) and the live base:TZ=UTC ARCH_DOCS_BASE=97d4bcdd5aee168705040df2266963bbd23314fa npm run verify→ exit 0arch-docs: OK (5 changed file(s))— the real diff against97d4bcddis 5 files:roster.js, the new test, the0002patch, itsfiles/mirror and the changelog fragment# tests 2348 / # pass 2348 / # fail 0; core lint 73 warnings, 0 errors.github/scripts563, interop 37, live-e2e 24 — all# fail 0ds-bot-patches checkok (pinFei2411/DeepSeek-Bot@2b10c70, 10 patches apply in order,core/=ds-bot/+ patches);canonicalok (a secondgenerateis a no-op);build-client --checkok;changelog.mjs checkok (27 fragments)Not run: the smoke test. This box has no
deepseek-harnesscheckout (../deepseek-harnessandDSH_HARNESSare both absent), sodev/bench.sh smokecannot start; the added test drives the realcreate_group/update_grouptools through the host harness and reads the savedstate.json. Also not run: the CIpackagejob, typecheck and actionlint.Out of scope, reported separately to the Lead:
ingestSeatShare(core/src/host/room-membership.js:2022) takes a remote seat's line into one of this home's own rooms with the samedata.text || ''shape; the Lead filed that as its own issue rather than widening this one.Checkpoint
{
"pr": 1947, "claim": "Dev-2", "issue": 1945,
"summary": "A group notice and a group name were bounded with slice, so a 2000-unit notice or a 60-unit name that ended in a whole emoji stored a lone high surrogate, which every encoder downstream turned into U+FFFD. Both bounds now go through cut, the code-point helper this file already used for Bot names, so the straddling character is dropped whole. The caps, the trim, the fallback and the empty-name path are unchanged, and a well-formed text under its bound is returned exactly as before.",
"files": ["core/src/host/roster.js", "core/test/room-text-bound.test.js", "patches/ds-bot/0002-flybot-api-host.patch", "patches/ds-bot/files/test/room-text-bound.test.js", "changelog.d/1945-group-text-code-point-bound.fixed.md"],
"checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=97d4bcdd5aee168705040df2266963bbd23314fa npm run verify; node scripts/ds-bot-patches.mjs check; node scripts/ds-bot-patches.mjs canonical; node .github/scripts/changelog.mjs check", "result": "green", "at": "801100c3dfd260d6dfd6d81159389cc05e99ca20" },
"next": "none — ready for review"
}