Repository navigation
host: six bounds on text another home supplied cut on code points, not UTF-16 units - #1981
Conversation
A connector item name, a role label another home set on a face-room seat, a cross-home group name, a capability description from a peer server, a remote home's own error text and an abbreviated remote reply still used a bare slice(0, max), so a bound that landed between the halves of a surrogate pair produced a lone high surrogate that reaches a UTF-8 encoder as U+FFFD. Each goes through the shared cut() now, with every max unchanged and the error preview's ellipsis and the reply's trailing-period strip still applied to the cut value. Six red-first tests, one per site; the mirror, the patches and the changelog fragment come with it.
|
Verdict: PASS at db76c76 Ran at this tip: Red first, then green: the run above the second is the tip as pushed; the second swaps only the five changed host modules back to their base 4488ae8 blobs and the six new cases go red — Scope, compare What I checked in the code, not just the tests:
CI at this tip: every check-run green ( Verdict: PASS. Ready for the merge queue. |
Fixes #1979
Owner: Dev2
Reviewer: Birfy
Written-by: Dev-2
What changed
Six places bounded text another home or another person supplied with a bare
String.prototype.slice, which cuts on UTF-16 units. A bound that landed between the halves of a surrogate pair stored or showed a lone high surrogate, which a UTF-8 encoder turns into U+FFFD. Each site now goes throughcut()(core/src/shared/text-boundary.js, re-exported bycore/src/host/text.js) and keepscut.text; every max is unchanged, and the punctuation each site applied around the slice still applies to the cut value.core/src/host/connectors.js:203item.name.trim().slice(0, 80)cut(item.name.trim(), 80).textcore/src/host/face-lead.js:19.slice(0, ROLE_MAX)cut(line, ROLE_MAX).textcore/src/host/project-team.js:48text(room.name).slice(0, 200)cut(text(room.name), 200).textcore/src/host/capabilities.js:97...trim().slice(0, 2000)cut(..., 2000).textcore/src/host/remote-chat.js:30…raw.slice(0, 157)cut(raw, 157).textcore/src/host/remote-chat.js:76(body || raw).slice(0, 80)cut(body || raw, 80).textThe remote error still ends in
…, and the reply still takes its trailing-period strip after the cut, so both read the same at a well-formed input.Tests
Six new cases, one per site; no case stands in for another. Each drives the real function (the Composio search path,
cleanRoleLabel, theproject-teamendpoint,capabilitySave, andnotSentLineon both branches) with an input whose max-th unit is the first half of a pair'a'.repeat(max - 1) + '😀' + 'tail', and each asserts well-formedness, the exact code-point prefix, the prefix relation and the unchanged budget.On the base this branch replaces, all six fail at the first assertion with
false !== true; the old value in every case was exactly at its max and ended in the lone high surrogateU+D83D:a Composio app name cut at 80 does not split an emojiaaa\ud83da role label another home set is cut at 80 without splitting an emojiaaa\ud83dproject-team cuts a room name from another home at 200 without splitting an emojiaaa\ud83da capability description is cut at 2000 without splitting an emojiaaa\ud83d#1979: a long remote error keeps a well-formed 157-unit tail before the ellipsisaaa\ud83d#1979: an abbreviated remote reply is well formed at 80 unitsa\ud83d(after theBot Social send_as_member:prefix strip)Verification at the tip
Tip SHA: db76c76 (tree
1648fa49e23a8542e70bf1a9a77af681db99c1f9, base4488ae81ff97a64963e1642d8c5c92fb998dc99e)Verified commit
ec78e746d84730ec3f8ddc64eab14ffa9068b2b2, and the pushed head above is that same tree rebuilt on the base by the fork push helper, so the two commits differ only in parentage and carry identical content (tree1648fa49e23a8542e70bf1a9a77af681db99c1f9).Ran, on a clean tree (
git status --porcelainempty) at the verified commit:verifyexit 0: core 2382 tests / 2382 pass / 0 fail;plugins/social541 (540 pass, 1 skip);plugins/tree127 (126 pass, 1 skip);plugins/timeline135 (134 pass, 1 skip);plugins/workflow336;.github/scripts563; interop 37; live-e2e 24 — 0 failures everywhere.arch-docs: OK (20 changed file(s)). Lint: 0 errors (core 73 warnings, social 36, timeline 9, tree 2, workflow 16; interop 1 pre-existing warning atdev/interop/isolation/probe.mjs:130; live-e2e 0).The six cases above were re-run against the same base tree with only the new tests copied in: all six red there, all six green at this tip.
Not run: the smoke test (
core/dev/bench.sh smoke …). It needs a free bench port and a DSH checkout on this box, and this change only swaps a string bound in five host modules whose paths the six unit tests drive directly;verifyis the CI equivalent and it is green at the tip.Checkpoint
{ "pr": 1981, "claim": "Dev2", "issue": 1979, "summary": "Six bounds on text another home or another person supplied (connector item name, face-room role label, group name, capability description, remote error text, abbreviated remote reply) used a bare slice(0, max) and could keep half of a surrogate pair, which reaches a UTF-8 encoder as U+FFFD. Each now goes through the shared cut() with its max unchanged, and the remote error's ellipsis and the reply's trailing-period strip still apply to the cut value. Six new tests, one per site, fail on the old code and pass at the tip.", "files": [ "core/src/host/connectors.js", "core/src/host/face-lead.js", "core/src/host/project-team.js", "core/src/host/capabilities.js", "core/src/host/remote-chat.js", "core/test/connectors.test.js", "core/test/face-lead.test.js", "core/test/project-team.test.js", "core/test/capabilities.test.js", "core/test/remote-chat.test.js", "docs/subsystems/core-host.md", "changelog.d/1979-external-text-bounds.fixed.md" ], "checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=4488ae81 npm run verify; node scripts/ds-bot-patches.mjs check; canonical", "result": "green", "at": "ec78e746d84730ec3f8ddc64eab14ffa9068b2b2" }, "next": "none — ready for review" }