Skip to content

fix(provider-core): background work notices no longer end in a broken character - #30369

Open
spoukyii wants to merge 2 commits into
pingdotgg:mainfrom
spoukyii:users/spoukyii/notification-label-emoji
Open

spoukyii wants to merge 2 commits into
pingdotgg:mainfrom
spoukyii:users/spoukyii/notification-label-emoji

Conversation

@spoukyii

@spoukyii spoukyii commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Problem

When background work finishes and its label is 80 UTF-16 code units or longer with an emoji straddling the cut, the parent thread's notice quotes the label with only the emoji's high surrogate, so it ends in a replacement character (�) before the …, as in Command "…then: �…" finished. It should end on the last whole character before the emoji, as #29163 and #29563 do for thread titles.

Change

The quoted label comes from reportLabel in packages/provider-core/src/server/notification.ts, which cuts the label's first line at 79 UTF-16 code units and adds …. When the cut would land between a high and a low surrogate, it now steps back one unit. That is the check #29563 added to t3 thread start titles and #29163 added to create_threads titles. The 80-unit limit, the … suffix and every notice's wording are unchanged.

Every notice summary is written into the thread's stored events when it happens: the Claude, Codex, ACP and Muse adapter notices, the delegated-task completion notices and the delegated-task question wake. A notice already written keeps its broken character; this PR fixes the notices written after it.

Where it shows:

  • Web and desktop, the timeline row of a notice with no child thread: a background command, monitor or background task, such as Claude's Bash with run_in_background. The row truncates its text with CSS, so the broken end shows when the window fits the label. A notice that names a child thread (a subagent or a delegated task) renders as a link row with the child thread's own title instead, so it shows the summary only when the child thread cannot be resolved.
  • Mobile, the work log row, which shows the summary.
  • The delegated-task question wake the parent agent reads.

Scope and approval

This is a very small, focused fix for an obvious bug. It is the defect #29563 ("t3 thread start titles no longer end in a broken character") and #29163 ("create_threads titles no longer end in a broken character") fixed in thread titles, still present in the label every background work notice quotes. It applies their check and changes no other behaviour, setting or contract.

Overlap check, by reportLabel, backgroundWorkNotification and by file. No open PR changes notification.ts or its test. #29007 edits DelegatedTaskQuestionReactor.ts, which calls reportLabel, but leaves that call as it is. #29906 (subagentProjection.ts) and #29647 (RestartBackgroundNote.ts) fix the same kind of cut in other files and do not touch this one. #24611 changes the shared truncate, which this function does not call.

Verification

packages/provider-core/src/server/notification.test.ts gets one assertion in its existing names one piece of work and the thread of a subagent case. It reports a command labelled with 78 as followed by 😀, so the emoji sits on the cut.

On main with only the test commit (da7f4b8), from packages/provider-core, vp test run src/server/notification.test.ts:

 FAIL  src/server/notification.test.ts > backgroundWorkNotification > names one piece of work and the thread of a subagent
Expected: Command "<78 a's>…" finished
Received: Command "<78 a's>, the lone high surrogate U+D83D (shown as �), …" finished
      Tests  1 failed | 3 passed (4)

With the fix:

  • vp test run src/server/notification.test.ts in packages/provider-core: 4 tests passed.
  • vp run check:changed from the repo root: format, lint, knip, typecheck (26 packages) and tests (3 files) all pass.

In a running dev build (web, Windows, Claude Haiku as the agent): the agent ran Bash with run_in_background, command sleep 20, and the description "Rename the config loader so it reads the new paths before the old ones, then: 🚀 ship it", whose first 78 UTF-16 code units come before the emoji, then ended its turn. When the command finished, the server wrote the notice. The before run used main's notification.ts, the after run this branch's, each on a fresh server start. The stored summaries ended then: \ud83d…" finished and then: …" finished.

Before (main) After (this branch)
Before: the command notice ends in a replacement character before the ellipsis After: the command notice ends on the colon before the ellipsis

The captures are the preview panel's visible area (952 by 702), not the full window.

What I could not check: the mobile work log on a device, and the delegated-task question wake. Like #29163 and #29563, the cut works per code point, so it can still split a grapheme cluster such as a flag, a skin-tone modifier or a ZWJ sequence; it never leaves a lone surrogate.

Bug: a background work notice ends in a broken character when an emoji sits at the label cut

This was written with Claude Opus 5.5 in Claude Code, driven from T3 Code.

… character

reportLabel cut a subagent, command, monitor or delegated task label at
79 UTF-16 code units before adding an ellipsis. When an emoji straddled
the cut, the label kept only its high surrogate, so the notice the
thread shows when that work finishes or asks a question ended in a
replacement character before the ellipsis. Each notice is stored with
the thread, so the broken character stayed for good.

The cut now steps back one unit when it would land between a high and a
low surrogate, the same check pingdotgg#29163 and pingdotgg#29563 added to thread titles.

Bug: a background work notice ends in a broken character when an emoji sits at the label cut
@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Nov 3, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at f1811c3

Macroscope's review found this PR approvable — Notification labels now avoid cutting a Unicode surrogate pair before the ellipsis. The change is limited to summary formatting and preserves existing length limits and task behavior.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Walkthrough

reportLabel now avoids splitting a UTF-16 surrogate pair when it truncates a long label. A test checks that a completed command summary ends with an ellipsis when the label contains an emoji at the truncation boundary.

Changes: Notification label truncation

Layer / File(s) Summary
Surrogate-safe truncation
packages/provider-core/src/server/notification.ts, packages/provider-core/src/server/notification.test.ts
reportLabel steps back one code unit when the 80-unit boundary would split a surrogate pair. The test checks the resulting summary for a long label containing an emoji.

Estimated code review effort: 2 (Simple) | ~8 minutes

Change: Bug fix · Severity of issue fixed: Low

Possibly related PRs

Suggested labels: size:S

Suggested reviewers: juliusmarminge, macroscopeapp

Merge Risk: Minimal · up to f1811

No actionable merge-blocking risk remains in the reviewed change.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main fix: preventing broken characters at the end of background work notices.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The description follows the repository template closely. It has Problem, Change, Scope and approval, and Verification sections, and the Verification section gives the test commands, results, and befor…
Full details: Description check

Explanation

The description follows the repository template closely. It has Problem, Change, Scope and approval, and Verification sections, and the Verification section gives the test commands, results, and before/after evidence. It states the fix is a small, focused bug fix and cites the related issues #29563 and #29163 as the precedent. It also lists what was not checked: the mobile work log on a device and the delegated-task question wake. One gap is that the Scope and approval section does not link a triaged bug issue or a maintainer-approved discussion. It argues the change qualifies as a very small, focused fix of an obvious bug without a prior issue, which the template allows. The template also asks for the model and harness at the end. The description says it was written with Claude Opus 5.5 in Claude Code, driven from T3 Code, so that requirement is met. The Scope section is long, but it is relevant. The description is substantive and specific, so this check passes.

Finishing Touches
Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.
Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between d1ec71e and f1811c3.

Files selected for processing: 2
  • packages/provider-core/src/server/notification.test.ts
  • packages/provider-core/src/server/notification.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 0 remain after this review.

@maria-rcks

Copy link
Copy Markdown
Collaborator

Note

Written by gpt-6.1-sol on behalf of Maria

Verified in a real dev environment

verified unchanged head f1811c3b34e1666c8b10a8e3c9bb15f667a795ef against github/main at 7c22e24e3fa1a3bb3a95f27aea000067f198fe7f on linux. installed dependencies with vp i in both checkouts and ran one isolated dev server at a time. used repository playwright and /usr/bin/google-chrome to add a real git project through the web app, select codex gpt-6.1-sol, and send real provider turns. no mocks, replay provider, fixtures or demo data.

the agent ran the same bounded background command in both builds, with the emoji crossing the 79-code-unit cut. it ended its turn before the command completed. main persisted a lone high surrogate and rendered �…; this head persisted and rendered the clean ellipsis. the full command, including the intact emoji, remained available when opening the notice detail.

before, main (cropped to the expanded notice):

main: broken character before the ellipsis

after, pr head, matching command and crop:

pr: clean ellipsis and intact command detail

nearby behavior: opening and closing the notice detail works. short labels retain their emoji. a second background command failed with exit 7 and produced a failed notice; another completed with exit 0 and produced a finished notice. both woke the real parent agent, which acknowledged the exit codes.

read-only sqlite inspection of the server-persisted notices:

main summary: "Command \"/bin/bash -lc 'sleep 120 # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ud83d…\" finished (exit 0)"
pr summary:   "Command \"/bin/bash -lc 'sleep 120 # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa…\" finished (exit 0)"
neighbor 1: outcome=failed, exit 7, emoji retained
neighbor 2: outcome=completed, exit 0, emoji retained

blacksmith validation passed: vp test run src/server/notification.test.ts (4 tests), then vp run check:changed d1ec71e6ff8b46bacfbe103af72034eba3654e63 (format, lint, both knip passes, typecheck of 26 affected packages, 5 tests across 2 files). fetched the head history in the worker because its initial shallow snapshot had no merge base. no code changes or pushes were needed.

Not verified

native ios/android work logs and desktop shells were not exercised on this linux browser pass. other provider adapters, delegated-task question wakes, subagent notices, remote/relay and tunnel connections were not exercised. this verifies new command notices; previously persisted notices retain their original text. provider transport returned transient 429s, then recovered; each tested background command completed and delivered its notice.

This branch has not been deployed

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

Labels

size:S 10-29 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants