Repository navigation
Description
Written-by: Delivery-1
Implementation: FlyBot (ours) — PR follows within the hour.
What happens
parseRoomShare finds the end of a room-share envelope by counting { and } from the first brace after the marker. It never tracks JSON string literals, so a { or } inside a chat line's text is counted as structure: the object looks finished too early, JSON.parse fails, and the whole envelope is dropped.
core/src/shared/room-share.js:209 at 5e71a07e (base delivery/ai-centric-development-service):
let depth = 0
let end = -1
for (let i = start; i < raw.length; i += 1) {
const ch = raw[i]
if (ch === '{') depth += 1
else if (ch === '}') {
depth -= 1
if (depth === 0) { end = i; break }
}
}Braces are ordinary characters in a message, and people type them constantly (code, if (x) { … }, a lone } at the end of a dict). A balanced pair happens to survive because the two cancel out; one unbalanced brace loses the entire line.
Reproduction (at 5e71a07e, no source change)
import { encodeRoomShare, parseRoomShare } from './src/shared/room-share.js'
parseRoomShare(encodeRoomShare({ op: 'line', roomId: 'r1', seq: 7, who: 'Alice', text: 'if (x) { y()' }))
// actual: undefined
// expected: an envelope with text 'if (x) { y()'
parseRoomShare(encodeRoomShare({ op: 'line', roomId: 'r1', seq: 7, who: 'Alice', text: 'so the closing brace is }' }))
// actual: undefinedencodeRoomShare writes text with JSON.stringify, which leaves braces inside the string raw, so the envelope it produces is the one that cannot be read back.
What a person sees
- A sent line never arrives. The host routes a delivered share line through
parseRoomShare(core/src/host/room-membership.js:2257ingestRemoteRoomLine,:2371applyInboundRoomShare). An unparsed envelope is not a room line at all, so the message is dropped instead of shown. - A join loses its history.
fitJoin/historyLinecopy a room's recent lines into the join envelope; one brace in a copied line makes that whole join unparsable, so the guest's mirror stays empty and the room row reads as if no share arrived. - The same shape was fixed once already on the client side (
plugins/social/client.jsenvelopeTail, fix(social): read an envelope body carrying "}", and correct the stale blanket-hold comment #855) for held-line display; the shared parser that the host actually trusts was left with the naive scan, so the same text now breaks delivery instead of only a card's wording.
Expected behaviour
- A
lineenvelope whosetextcarries a single{or a single}parses, withop,roomIdand the exact text preserved. - A brace right after an escaped quote (
say "{" now) is still inside the string: the scan tracks\escapes, so a\"does not end the literal. - A copied history line keeps its braces through a
joinenvelope. - Controls, unchanged: plain text and balanced braces parse as today, and an envelope that really is truncated (no closing brace) still answers
undefined.
Scope
core/src/shared/room-share.js—parseRoomShare's object-end scan tracks string literals and escapes (the algorithmplugins/social/client.jsenvelopeTailalready uses after fix(social): read an envelope body carrying "}", and correct the stale blanket-hold comment #855). Nothing else in the module moves: no field bound, no op rule, no marker rule, andisRoomShareTextis untouched.core/test/room-share-braces.test.js— the six cases above (three red on the base tree).- Generated, not hand-edited:
patches/ds-bot/files/src/shared/room-share.js,patches/ds-bot/files/test/room-share-braces.test.js.core/client.jsis unchanged —src/client/does not import this module.
Red first (base 5e71a07e, before the source change)
$ cd core && TZ=UTC node --import ./test/harness-resolve.mjs --test test/room-share-braces.test.js
not ok 1 - a line whose text opens a brace is read back, not dropped
not ok 2 - a line whose text closes a brace is read back, not dropped
not ok 3 - a brace right after an escaped quote is still inside the string
# tests 6 / # pass 3 / # fail 3
Cases 4–6 (history braces, plain/balanced controls, truncated envelope) pass before and after, so the fix cannot loosen the envelope's rejection of a genuinely broken body.
Duplicate check
Searched room share envelope brace, parseRoomShare, room-share brace: #855 (closed) is the client-side envelopeTail fix in plugins/social/client.js — same class, different file, and it explicitly left core/src/shared/room-share.js alone; #1928/#1933/#1937 are code-point/well-formedness bounds in the same module, none about the object-end scan. No open or closed issue covers this shared parser, and no open or closed PR carries this file change.
Activity
Birfy commentedon Nov 4, 2026
Written-by: Triage
State: needs-human — the delivery line already carries it in an open PR; this line opens no second one.
#2447 is carried by the open PR #2448 (
HugoXK:delivery1/room-share-brace-scan→delivery/ai-centric-development-service,Fixes #2447). One issue, one PR (AGENTS.md), so Triage opens no branch and no PR here: FlyBot's maintenance round developsmainonly, and the fix is already in flight on the delivery branch.Blocked on: line ownership, not on the report — the site (core/src/shared/room-share.js:209) and the reproduction in the body are specific, and nothing in them needs a rewrite. Missing: a person's decision that the delivery line keeps it. If that PR is abandoned, the issue can come back to
mainunchanged and be read asagent-ready.Written-by: Triage
Read a room-share envelope past a brace in a chat line's text