fix(ui): manually created tasks render as "Unnamed task" in the sidebar - #407
Merged
Merged
Conversation
…prompt box Since #377 the list-tasks response omits `params`, and the sidebar label resolver reads `task_metadata.display_name` then `task.name`. The prompt-box writer was never updated to match: it still set only `params.description`, which the sidebar can no longer see, so every manually created task rendered as "Unnamed task". Write the label into `task_metadata.display_name` at creation (prompt text, whitespace-collapsed, truncated to 80 chars) and plumb `task_metadata` through useCreateTask, which previously dropped it. `task.name` is left untouched: it is a globally-unique get-or-create idempotency key, and writing prompts into it would silently merge unrelated tasks. Adds a writer/reader contract test so the label source and the label writer can't drift apart silently again. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
jromualdez-scale
marked this pull request as ready for review
August 18, 2026 18:08
rpatel-scale
approved these changes
Aug 18, 2026
…ogate pairs A UTF-16 unit-based slice(0, 80) can cut an astral character (e.g. emoji) in half at the boundary, producing a lone surrogate. Postgres rejects lone surrogates in JSONB, so the whole task/create request would fail. Truncate by code points instead, and cover the boundary case in tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
declan-scale
approved these changes
Aug 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Every task created by typing into the prompt box renders as "Unnamed task" in the sidebar. The message and reply are fine — only the label is wrong. Scheduled tasks are unaffected.
#377 made the list-tasks response a lean
TaskSummarythat omitsparams(correctly —paramsis the arbitrary caller-supplied create-time payload and can carry sensitive material, so it must not be exposed in bulk). The sidebar's label resolver was repointed accordingly, fromparams.descriptiontotask_metadata.display_name→task.name. But the writer was never updated to match: the prompt box still created tasks with onlyparams.description, which the sidebar can no longer see, so the label fell all the way through to the default.Fix
Frontend-only — the backend already accepts and persists
task_metadataontask/createand returns it in both the create and list responses:prompt-input.tsxnow writes the label the resolver reads first:task_metadata: { display_name: deriveTaskDisplayName(prompt) }.deriveTaskDisplayName(intask-utils.ts, next to the reader) trims, collapses whitespace/newlines, and truncates to 80 chars.useCreateTasknow plumbstask_metadatathrough to the RPC body; previously anything besidesagentName/paramswas silently dropped.The label renders immediately on create (the optimistic cache insert uses the create response, which carries
task_metadata) and correctly after reload (the list response includestask_metadata).Deliberately not used:
task.name. It looks like the obvious fix given the resolver's fallback, butnameis a globally-unique get-or-create idempotency key:task/createwith an existing name returns that task (and can overwrite its params). Writing prompts into it would silently merge unrelated conversations — a data-correctness bug worse than the cosmetic one being fixed.Trade-off, stated explicitly: this intentionally publishes a normalized 80-char prefix of the prompt into
task_metadata, which the broadly-scoped list response includes. That is strictly less exposure than pre-#377 behavior (fullparams, including the full prompt, in every list item), and it is the same mechanism the scheduler already uses to title tasks — butdisplay_nameshould be treated as a public-ish display field, and nothing sensitive should ever be auto-derived into it beyond this.Tests
task-utils.test.ts: unit coverage forderiveTaskDisplayName, plus a writer/reader contract test — a task shaped like the prompt-box writer's output must not resolve to "Unnamed task". The absence of exactly this test is how the writer and reader drifted apart unnoticed.use-create-task.test.tsx(new): asserts the mutation forwardstask_metadatain thetask/createrequest body, and sendsnullwhen omitted.Not addressed here
paramsfrom the list-tasks response #377 with nulltask_metadatakeep their default label (no backfill; cosmetic).name(an idempotency key) as its only human-visible identifier, which now surfaces in the sidebar as<uuid>-task; fixing that to also setdisplay_nameis a follow-up in that repo.Verification
npm run typecheck,npm run lint,npm test— all green (82 tests, 7 new).🤖 Generated with Claude Code
Greptile Summary
The PR aligns manually created task labels with the sidebar’s metadata-based reader.
Confidence Score: 5/5
The PR appears safe to merge.
No blocking failure remains.
Important Files Changed
Sequence Diagram
Reviews (2): Last reviewed commit: "fix(ui): truncate display_name by code p..." | Re-trigger Greptile
Context used: