Repository navigation
Description
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.
Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: cdeaf6e53bc74b81866f828c49889cdf
Branch: dev/1-workflow-avatar-initial
Base: b207364
At: 2026-11-01T09:44:28Z
Dedup (2026-11-01, live main b207364f plus every open and closed issue/PR): nothing covers this — #1459 (closed) was about broken flowchart edges and hiding the composer, #464 (closed) was one look per Bot in face-to-face rooms, and neither touches the node avatar's initial. The sibling text-bound work (#2002, #2005, #2006, #2007) is about cutting a string to a length; this is taking a first character, so it is not covered by any of them either.
What happens
core/src/client/workflows.js:302 draws each node of the Workflow graph with a one-character avatar:
h('span', { className: 'bt-wf-av', style: { background: COLORS[node.name] ?? '#4d6bfe' } }, (node.name ?? '?').slice(0, 1)),slice counts UTF-16 units, so a name that starts with an emoji or any other astral character is cut through the surrogate pair. '🚀 Deployer'.slice(0, 1) is "\ud83d" — not well formed, and every encoder draws that as the replacement character — so the avatar shows � where the name's first character is 🚀. Array.from('🚀 Deployer')[0] is '🚀' and well formed.
The empty-name fallback is fine today (?); only a name whose first character is outside the BMP is affected.
What it should do
- Take the first code point, not the first UTF-16 unit:
Array.from(String(node.name ?? '').trim())[0] ?? '?'. - Do not use
cut(node.name, 1): that helper keeps the longest prefix that stays inside a budget, so an emoji first character is dropped whole and it returns an empty string — the avatar would fall back to?for a name that has a perfectly good first character. - Keep the case as it is: this issue is about the broken character, not about typography.
core/src/client/mark.js:96(personInitial) already takes the first code point but upper-cases it; adopting it is acceptable only if the PR body says in so many words that this card's initials become upper case (a visible change), otherwise use the plainArray.fromform. - A name that is empty or whitespace still shows
?.
Reproduce
core/test/workflows-page.test.js already renders the graph, so the case goes there: a node named '🚀 Deployer' renders a .bt-wf-av whose text is '🚀' and is well formed; a node with no name still renders '?'. On the page, a workflow whose first node is named with a leading emoji shows � in its coloured avatar circle today.
Scope
core/src/client/workflows.js:302— the one expression (a local helper is fine if it reads better).core/test/workflows-page.test.js— the two cases above.core/client.js— rebuilt withnode core/scripts/build-client.mjs(the same bundlenpm run verifychecks with--check).patches/ds-bot/files/src/client/workflows.js— the full-file mirror that exists at this ref; regenerate it, do not hand-edit.- a
changelog.d/fragment, one paragraph, in the shape the recent fixes use.
Not in scope: the colour picked from the name (COLORS), the fallback for a missing name, and any other place that takes a first character.
Activity