Repository navigation
host: a seat line's body keeps a lone surrogate — the seat path is never made well formed - #1951
Conversation
Owner: Dev-2 Reviewer: Birfy Written-by: Dev-2 Branch: dev/2-p301-seat-share-wellformed Base: 48c0df2 The other entry into a room's log (ThinkFlowLab#884, ownSeatOf -> ingestSeatShare) took the envelope's body as it arrived: the same string reached the stored line, the pane and the inbound turn, so half of a surrogate pair was stored, drawn and handed to a model. ThinkFlowLab#1943 repaired the projected-mirror branch only; this is its sibling. One line next to the cleaned name, three consumers use it; already well formed bodies, real emoji included, pass untouched. patches/ds-bot/files/ is the generated copy (node scripts/ds-bot-patches.mjs generate), cmp-equal to core/. Docs-not-needed: `docs/subsystems/core-host.md` documents the room log and the seat path's line fields with their bounds; this change only makes a body that arrives ill formed well formed before it is stored, drawn or handed to a turn — no field, bound, tool or path a page describes is added or changed.
|
Verdict: PASS at 479195a Ran at this tip: Red first (hard requirement): with only Scope: tip Fix reads correctly: one Checks at the tip: 13 check-runs, 12 success; the only failure is Metadata: |
Fixes #1950
Owner: Dev-2
Reviewer: Birfy
Written-by: Dev-2
Claim-Id: 084d2a16e9c94094ba7bc6e241b12afc
Branch: dev/2-p301-seat-share-wellformed
Base: 48c0df2
Docs-not-needed:
docs/subsystems/core-host.mddocuments the room log, the seat path and the fields a line carries with their bounds; this change only makes a body that arrives ill formed well formed before it is stored, drawn or handed to a turn — no field, bound, tool or path a page describes is added or changed.ingestSeatShareincore/src/host/room-membership.jsis the other door into a room's log: a remote seat's line arrives as an envelope, and its body was used exactly asJSON.parsehanded it back. An unpaired surrogate escape in that JSON becomes half a character, so the same ill-formed string was stored in the transcript, drawn in the shared pane and handed to the Bot the line calls.#1943repaired the projected-mirror branch (:2523) of the same function; this is its sibling, left with the identicaldata.text || ''shape at:1990,:2003and:2022.whowas already repaired bycleanPeerName, and the probe confirms it: the name readsW\ufffdhowithisWellFormed() === truewhile the body is still ill formed. So the defect is exactly the threedata.textuses, and the fix is one repaired string next to the cleaned name, used at all three.What changed
Neighbouring the
cleanPeerNamecall, the same expression#1943used in the mirror branch:The three consumers now read
textinstead ofdata.text || '': the stored line (append(room, rt.authoredRoomLine(room, { who, text, … }))), the pane (showShareLine(room, { who, text, … })) and the inbound turn (rt.startInboundTurn({ room, botId, seq: posted.seq, hop, who, text })).Nothing else moves: a non-string
textkeeps today's''fallback, a well-formed body is byte-for-byte unchanged (no re-encoding, no trim, no length change), andwho,authorId,authorKind,hop, the named-call gate and#904's "a guest cannot speak as another Bot" rule are untouched — the line is still accepted, still saved, still fanned out and still calls the Bot it named.patches/ds-bot/files/{src/host/room-membership.js,test/remote-room.test.js}are the generated mirrors (node scripts/ds-bot-patches.mjs generate,cmpequal tocore/), not hand edits.Red first
One case appended to
core/test/remote-room.test.js, run with the base source and the branch otherwise untouched (git show 48c0df28…:core/src/host/room-membership.js > core/src/host/room-membership.js):The Security probe (
.local/p1944-probe.mjs, tagP1944seat) reads the three consumers separately on the base source:handled=true called=["chief-a"],stored.who="W\ufffdho" wf=true,stored.text … wf=false,pane.text="seat \ud83d body" wf=false,turn.text="seat \ud83d body" wf=false.The same test also carries the control the issue asks for: a second line whose body is already well formed (
hi 😀 there) arrives with the emoji intact, same length, no re-encoding — it passes on both sides.Mutation — each of the three uses has its own teeth
Reverting one consumer at a time, fix otherwise in place, probe reading all three (
wf=isWellFormed()):text: data.text || ''wf=falsewf=truewf=truetext: data.text || ''wf=truewf=falsewf=truetext: data.text || ''wf=truewf=truewf=falsehandled=trueandcalled=["chief-a"]in every row: the line is still accepted and still calls the Bot, only the body of the reverted consumer is ill formed. Fixing one consumer alone does not pass.Verification
TZ=UTC ARCH_DOCS_BASE=48c0df28d1085d2414089d8be6192066eca2e42e npm run verify→ exit 0, on a clean tree (git status --porcelainempty) at this change (local commit830940ae…, treeb4b10bd6…; the pushed head is the same tree, rebuilt on48c0df28).arch-docs: OK (5 changed file(s))— the real diff against48c0df28is 5 files# tests 2351 / # pass 2351 / # fail 0; plugins/social 541 (540 pass), plugins/tree 127 (126), plugins/timeline 135 (134), plugins/workflow 336,.github/scripts563, interop 37, live-e2e 24 — all# fail 0,0 not okin the logds-bot-patches checkok (pinFei2411/DeepSeek-Bot@2b10c70, 10 patches apply in order,core/=ds-bot/+ patches);canonicalok;build-client --checkok;changelog.mjs checkok (29 fragments)Not run: the smoke test. This checkout has no
dev/bench.sh(onlydev/interopanddev/live-e2e) and no running FlyBot instance to drive, so there is nothing to smoke; the added case drives the real host harness (applyInboundRoomSharethroughremote-room.test.js) and reads the saved room log. Also not run: the CIpackagejob, typecheck and actionlint.Checkpoint
{
"pr": 1951, "claim": "Dev-2", "issue": 1950,
"summary": "The seat path into a room's log used a guest's body exactly as JSON.parse handed it back, so half of a surrogate pair was stored, drawn and handed to a model. It now computes one toWellFormed string next to the cleaned name and gives it to all three consumers, the shape #1943 used for the mirror branch. A well-formed body is unchanged byte for byte, a non-string text keeps the empty-string fallback, and the seat identity, the authorId split and the hop cap do not move.",
"files": ["core/src/host/room-membership.js", "core/test/remote-room.test.js", "patches/ds-bot/files/src/host/room-membership.js", "patches/ds-bot/files/test/remote-room.test.js", "changelog.d/1950-seat-line-text-wf.fixed.md"],
"checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=48c0df28d1085d2414089d8be6192066eca2e42e 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": "479195a7d95009192f97787da8f7bab490450533" },
"next": "none — ready for review"
}