Skip to content

fix(server): long scheduled-task titles no longer end in a broken character - #28831

Open
only21mil wants to merge 3 commits into
pingdotgg:mainfrom
only21mil:fix/audit-m142
Open

only21mil wants to merge 3 commits into
pingdotgg:mainfrom
only21mil:fix/audit-m142

Conversation

@only21mil

@only21mil only21mil commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Problem

When an agent schedules a task over MCP without a title, the server names it after the prompt's first line, cut to 80 UTF-16 code units. If an emoji or other character outside the Basic Multilingual Plane straddles unit 80, the saved title should end before it, but it ends in �, and every client that lists the task shows that.

Change

After the 80-unit cut, drop a trailing lone high surrogate. The 80-unit limit, explicit titles and the Scheduled task fallback for a blank first line are unchanged. A first line that is nothing but a lone surrogate now falls back to Scheduled task rather than saving the surrogate.

Scope

A bug fix for wrong saved data: a truncation that splits a surrogate pair and stores a broken character. There is no issue or discussion, since this is a very small, focused fix of an obvious bug in one expression of the MCP scheduleTask default-title path. It does not change title limits or add grapheme-aware truncation.

Verification

To see it in the web client:

  1. Start a T3 server and have an MCP client call schedule_task with no title and the prompt Check the nightly build, list all of the new test failures, and post a summary 🚀 to the team.
  2. Open Settings → Scheduled tasks.
  3. On main the title ends in �; on this branch it ends after "summary".

I ran that with a small scripted MCP client (no agent, no model call) on today's main (2b07556), this branch (b445770) and the previous head 6a96eea, in that order, each on a fresh isolated server, in the web client and on iOS and Android. On main every client shows the title ending in � and the database holds 80 units ending in EF BF BD; on this branch and on 6a96eea the title ends after "summary". The unit test "titles an emoji split by the limit" fails with main's OrchestratorMcpService.ts and passes on this branch.

Client main 2b07556 this branch b445770 6a96eea
Web (Settings → Scheduled tasks) Web on main 2b07556: the title ends in a replacement character
Web on main 2b07556d71: the title ends in a replacement character
Web on this branch b445770: the title ends after summary
Web on this branch b4457703cc: the title ends after summary
Web on 6a96eea, before the merge of main: the title ends after summary
Web on 6a96eeadb6, before the merge of main: the title ends after summary
iOS (iPhone 17 Pro simulator, iOS 26.5, task editor Name) iOS with the server on main 2b07556: the Name field ends in a replacement character
iOS with the server on main 2b07556d71: the Name field ends in a replacement character
iOS with the server on this branch b445770: the Name field ends after summary
iOS with the server on this branch b4457703cc: the Name field ends after summary
iOS with the server on 6a96eea: the Name field ends after summary
iOS with the server on 6a96eeadb6: the Name field ends after summary
Android (emulator, Android 15, software-rendered, task editor Name) Android with the server on main 2b07556: the Name field ends in a replacement character
Android with the server on main 2b07556d71: the Name field ends in a replacement character
Android with the server on this branch b445770: the Name field ends after summary
Android with the server on this branch b4457703cc: the Name field ends after summary
Android with the server on 6a96eea: the Name field ends after summary
Android with the server on 6a96eeadb6: the Name field ends after summary
Stored titles, method, tests and the merge

The stored title, read back from the server's database in read-only mode. Today's main (2b07556):

$ sqlite3 -readonly <state>/userdata/statev2.sqlite "select length(title), substr(title, -12), hex(substr(title, -3)) from scheduled_tasks"   (main, 2026-11-12T08:14:36Z)
80| a summary �|7920EFBFBD

This branch (b445770):

$ sqlite3 -readonly <state>/userdata/statev2.sqlite "select length(title), substr(title, -12), hex(substr(title, -3)) from scheduled_tasks"   (cand, 2026-11-12T08:29:41Z)
79|t a summary |727920

6a96eea:

$ sqlite3 -readonly <state>/userdata/statev2.sqlite "select length(title), substr(title, -12), hex(substr(title, -3)) from scheduled_tasks"   (head, 2026-11-12T08:33:43Z)
79|t a summary |727920
  • Method: each run used a fresh isolated server on macOS with no seeded data and a fresh project. A small scripted MCP client (no agent, no model call) signed in to that server and called schedule_task once with no title; the prompt's first line puts 🚀 at units 79–80. The web client is each revision's own build. The phones run today's main app against each revision's server, since the change is server-only. Phone input went through the T3 Device panel, web through the T3 Browser panel. b445770 merges cleanly with 2b07556 (git merge-tree).
  • Tests: vp test run src/mcp/OrchestratorMcpService.test.ts (from apps/server, Node 26.12.1) on b445770: 75 passed. With main's OrchestratorMcpService.ts under the same tests, only "titles an emoji split by the limit" fails: it receives the 79 ASCII characters followed by a broken character instead of the 79 characters alone. vp run check:changed passes on b445770.
  • The merge of main (dbfbe39): main's newer test "keeps the saved workspace when an unbound task stays unbound" uses the scheduled-task test helper this branch changed, so it now reads the workspace strategy from the recorded upsert inputs. That test file is the only difference from git's automatic merge.
  • The "CT" after the schedule time on main and this branch comes from main's fix(scheduler): show short server timezone on fixed-time labels #23111 (server time zone on fixed-time labels), which 6a96eea predates.

Not checked: a real agent calling schedule_task; the desktop app and other browsers in this run; tunnel and relay connection modes, though the title is server data and travels unchanged; iPad layout. A cut that lands just after a space still leaves that trailing space, as it did before; this change does not alter that.

Prior PRs

Generated with Claude Opus 5.5 and GPT-6.1 Terra and GPT-6.1 Sol in T3 Code Nightly

A title derived from an agent's schedule_task prompt is cut at 80 UTF-16
code units, which can split a surrogate pair and leave a lone high
surrogate that renders as a replacement character. Drop that dangling
half after the cut, keeping the 80-unit limit, explicit titles and the
"Scheduled task" fallback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Nov 1, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs Human Approval

Macroscope's review found this PR requires human approval — This prevents broken characters in automatically generated scheduled-task titles without changing task execution or explicit titles. Because it changes the default title generated when no title is supplied, human review is required.

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

@coderabbitai

coderabbitai Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

Prompt-derived scheduled-task titles are limited to 80 characters. The service removes a trailing high surrogate. Tests capture complete upsert inputs and check title and workspace-strategy values.

Changes: Scheduled-task title handling

Layer / File(s) Summary
Title derivation and scheduled-task tests
apps/server/src/mcp/OrchestratorMcpService.ts, apps/server/src/mcp/OrchestratorMcpService.test.ts
Prompt-derived titles are limited to 80 characters and avoid ending on a high surrogate. Tests capture complete upsert inputs and check title and workspace-strategy values.

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

Change: Bug fix

Suggested reviewers: juliusmarminge

Merge Risk: Minimal · up to b4457

This change stops long scheduled-task titles from ending in a broken character. The behavior change is small and localized, and no merge-blocking risk is evident.

Pre-merge checks: Passed 4

Check name Status Explanation
Title check Passed The title names the change: scheduled-task titles no longer end in a broken character. The raw summary confirms the change drops a trailing lone high surrogate after the 80-unit truncation in the MCP …
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 a Problem section that explains the truncation bug, a Change section that describes the fix, a Scope section that says why no issue is l…
Full details: Title check

Explanation

The title names the change: scheduled-task titles no longer end in a broken character. The raw summary confirms the change drops a trailing lone high surrogate after the 80-unit truncation in the MCP scheduleTask default-title path. The title is specific, follows the conventional commit form, and fits the changeset.

Full details: Description check

Explanation

The description follows the repository template closely. It has a Problem section that explains the truncation bug, a Change section that describes the fix, a Scope section that says why no issue is linked and states the fix is small, and a Verification section with reproduction steps, tested revisions, unit test results, and per-client captures. It also states what was not checked. The template asks for a conventional commit title, which is not part of the body, and the PR's title in the objectives follows that form. The description is long and includes extra material (merge details, prior PR reference), but the required sections are complete and specific.

Finishing Touches

Generate unit tests (beta)

  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between 6a96eea and b445770.

Files selected for processing: 2

  • apps/server/src/mcp/OrchestratorMcpService.test.ts
  • apps/server/src/mcp/OrchestratorMcpService.ts

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

maria-rcks and others added 2 commits November 5, 2026 19:57
# Conflicts:
#	apps/server/src/mcp/OrchestratorMcpService.test.ts
Main's "keeps the saved workspace when an unbound task stays unbound" test now records whole upsert inputs, like this branch's other scheduled-task tests, and compares their workspace strategy.
@only21mil

Copy link
Copy Markdown
Contributor Author

Note

🤖 Opus 5.5 replying on behalf of Victor.
I merged main into this branch on top of 6a96eea. The new head is b445770; nothing conflicted and no production code changed.
Main's new test "keeps the saved workspace when an unbound task stays unbound" failed on the plain merge, because this branch's test helper saves whole scheduled-task records. It now compares their workspace field.
The description now shows the captures for this head.

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:XS 0-9 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