Skip to content

social: cap face-room names, roles and platform lines on code points - #2439

Closed
HugoXK wants to merge 1 commit into
ThinkFlowLab:delivery/ai-centric-development-servicefrom
HugoXK:delivery2/social-face-text-codepoints
Closed

HugoXK wants to merge 1 commit into
ThinkFlowLab:delivery/ai-centric-development-servicefrom
HugoXK:delivery2/social-face-text-codepoints

Conversation

@HugoXK

@HugoXK HugoXK commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Written-by: Delivery-2
Fixes #2438
Owner: Delivery-2
Tip SHA: 331b9ca — the fork commit for tree ccaa42f155; the same tree was checked locally at 22511136, whose message and base 5e71a07e are identical
Branch: delivery2/social-face-text-codepoints (fork HugoXK/FlyBot, base delivery/ai-centric-development-service @ 5e71a07e)

Reproduce

A remote member chooses its own display name, role and avatar, and the social plugin caps those strings with a bare String#slice. A cap that lands between the halves of a surrogate pair stores the lone high surrogate; the room list, the room header, the transcript and the <untrusted-message from="…"> fence then carry �:

cd plugins/social && node --import ./test/resolve.mjs -e "
import('./src/face-rooms.js').then(m => {
  const name = m.cleanFaceName('x'.repeat(59) + '🚀rest')
  const role = m.cleanFaceRole('y'.repeat(79) + '🚀rest')
  console.log('name', name.length, name.codePointAt(name.length - 1).toString(16), name.isWellFormed())
  console.log('role', role.length, role.codePointAt(role.length - 1).toString(16), role.isWellFormed())
})"

Actual on base: name 60 d83d false / role 80 d83d false — both end in \ud83d. Expected: the cap lands after the whole emoji and the stored label is well formed, and the 60 / 80 / 200 caps count code points, so a name of 60 emoji is not cut in half at 30. The same class was fixed for the host under #1993/#2000 and for the workflow node avatar under #2017.

What changed

plugins/social/src/face-rooms.js (+11/−12) — one local cut(text, max) helper and five call sites:

  • cleanFaceName (60) — the member and room label, also the from of an <untrusted-message> fence;
  • cleanFaceRole (80) — the member's role label;
  • sanitizePlatformText (200) — a platform line kept in the transcript;
  • the registration-failure detail (200) and the unmapped join-error message (200), both shown to the owner.

No cap value, wording, wire shape or endpoint changes.

plugins/social/test/face-text-bounds.test.js (new, 27 lines): three cases — a name cap and a role cap that land inside an emoji, plus the code-point count a name of whole emoji keeps.

changelog.d/2438-social-face-text-code-points.fixed.md (new, 1 line): a person sees this label.

Red without the source change

plugins/social/test/face-text-bounds.test.js on base 5e71a07e with the source untouched — 3 fail / 0 pass:

not ok 1 - a face-room name bound lands on a code point boundary, not inside an emoji
  expected: true
  actual: false
not ok 2 - the name cap counts code points, so whole emoji are never cut in half
  expected: 60
  actual: 30
not ok 3 - a face-room role bound lands on a code point boundary, not inside an emoji
  expected: true
  actual: false
# pass 0 / # fail 3

With the fix at the tip: # pass 3 / # fail 0.

What I ran (at the tree 2251113)

$ cd plugins/social && TZ=UTC node --import ./test/resolve.mjs --test "test/*.test.js"
# tests 544 / # pass 544 / # fail 0        (3 of them are the new file; the suite was 541 before)

$ cd core && TZ=UTC node --import ./test/harness-resolve.mjs --test "test/*.test.js"
# tests 3159 / # pass 3152 / # fail 7

$ ARCH_DOCS_BASE=upstream/delivery/ai-centric-development-service npm --prefix core run lint
Found 105 warnings and 0 errors.          arch-docs: OK (3 changed file(s))

$ node scripts/lint-plugins.mjs
social 36 / timeline 9 / tree 2 / workflow 16 warnings, 0 errors; all four pack-imports ran

$ node core/scripts/build-client.mjs --check                       # exit 0
$ node scripts/ds-bot-patches.mjs check
ok   ds-bot/ = Fei2411/DeepSeek-Bot@2b10c70 (tree 5834c00, v0.1.5)
ok   10 patches apply cleanly in order
ok   core/ equals ds-bot/ + patches/ds-bot/
$ node .github/scripts/changelog.mjs check
changelog: 80 fragment(s) in changelog.d/ are well formed

The 7 core failures are the same environment group on the base tree, unrelated to this diff: test/project-runtime-setup.test.js (no Codex login cache on this box) and test/workbench-client.test.js (six scoped-routing/composer cases). Base full run: # tests 3159 / # pass 3152 / # fail 7 — same names, same count.

Not run

  • npm run verify as one command stops before the suites on this box: core lint's arch-docs step calls git diff origin/main…, and this clone has no origin/main ref (its remote is the fork; the lane's base is the delivery branch). Each gate above was run individually, with ARCH_DOCS_BASE pointing at the lane's base.
  • node core/scripts/build-workbench.mjs --check fails here on ERR_MODULE_NOT_FOUND: Cannot find package 'react' — a dependency of this box, not of this diff.
  • No live Bot Social room: the five sites are pure string functions and the three cases exercise the two exported ones directly; the two error-message sites share the same helper and stay covered by the existing suite.
  • No smoke test: core/index.js and core/src/host/ are untouched.

Docs-not-needed: no ownership, contract or module boundary changed; five text caps in face-rooms.js now cut on code points. This line is also in the branch commit.

Checkpoint

{
  "pr": 2439,
  "claim": "Delivery-2",
  "issue": 2438,
  "summary": "cleanFaceName, cleanFaceRole, sanitizePlatformText and the two join-error details capped peer-supplied face-room text with a bare String#slice, so a cap inside a surrogate pair stored a lone high surrogate that the room list, the transcript and the untrusted-message fence drew as a replacement character. The caps also counted UTF-16 units, so an emoji-heavy name reached half the room of an ASCII one. One local cut() helper now serves the five sites, and three cases prove the name and role caps on code points; no cap value, wording or wire shape changed.",
  "files": ["plugins/social/src/face-rooms.js", "plugins/social/test/face-text-bounds.test.js", "changelog.d/2438-social-face-text-code-points.fixed.md"],
  "checks": { "ran": "cd plugins/social && node --import ./test/resolve.mjs --test \"test/*.test.js\" && ARCH_DOCS_BASE=upstream/delivery/ai-centric-development-service npm --prefix core run lint && node scripts/ds-bot-patches.mjs check && node .github/scripts/changelog.mjs check", "result": "green", "at": "22511136c8465f0e8360071dfaf3c3f799dc8cbf" },
  "next": "none — ready for review"
}

…hinkFlowLab#2438)

Fixes ThinkFlowLab#2438

Written-by: Delivery-2

cleanFaceName, cleanFaceRole, sanitizePlatformText and the two join-error
details clipped with a bare String#slice, so a cap that landed between the
halves of a surrogate pair stored a lone high surrogate: a peer's display
name of 59 characters plus an emoji came back as 60 units ending in \ud83d,
drawn as a replacement character in the member list and carried into the
untrusted-message fence.

The caps also counted UTF-16 units, so an emoji-heavy name reached half the
room of an ASCII one. One local cut() helper now serves the five sites; no
cap value, wording, wire shape or endpoint changes.

Docs-not-needed: no ownership, contract or module boundary changed; five
text caps in face-rooms.js now cut on code points.
@HugoXK

HugoXK commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

Written-by: Delivery-2

@Birfy review request for this exact tip: 331b9cac78 (base delivery/ai-centric-development-service, merge base 5e71a07e; non-draft, maintainer_can_modify: true).

Red-first on a clean base plus the gates are in the PR body; enqueue-guard is red only because no Verifier report names this tip yet.

Please review 331b9cac78 and post Verdict: PASS (or the changes you want). No new tip will be pushed for CI noise: the base head moved to 5ba3c0bb and this branch is not rebased, per instruction.

@HugoXK

HugoXK commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

Closing: our account has no write access to ThinkFlowLab/FlyBot, so it cannot merge into delivery/ai-centric-development-service; the fix is complete and verified at 331b9cac78ca0b4eb53af6aba4720f2d1a9cda6e, and we will re-open it against main or this branch the moment someone with merge rights wants it.

Owner: Delivery-2
Written-by: Delivery-2

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

Labels

risk:T3 Risk tier T3 (needs a human approval), set by the controller from the diff

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant