fix(server): unwrap JSON envelope leaking into generated titles - #7413
fix(server): unwrap JSON envelope leaking into generated titles#7413sideeffffect wants to merge 6 commits into
Conversation
Some provider models ignore the structured-output contract and emit the
whole JSON envelope as the field value, so a generated thread title (or PR
title) comes back as the literal string `{"title": "the actual name"}`
instead of `the actual name`. The shared sanitizers only stripped outer
quotes, so that JSON leaked straight into the sidebar label.
Add `unwrapJsonEnvelopeTitle`, which detects a JSON object whose `title` is
a string and unwraps it (recursing for double-wrapping, tolerating
surrounding prose or code fences), and apply it in both
`sanitizeThreadTitle` and `sanitizePrTitle`. Plain titles that merely
contain braces are left untouched.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — Bug fix for unwrapping JSON envelopes in generated titles. An unresolved Medium severity finding identifies a bug in the Not approved because:
Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more. |
…ny key Models don't always label the wrapped value `title` — it can come back as `name`, `summary`, etc. When the emitted JSON object has exactly one string value, take it regardless of the key name. Only fall back to preferring a `title` key when several string values make the choice ambiguous. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Dismissing prior approval to re-evaluate 8567d51
Clarify that the sole-string-value rule ignores non-string fields: an
object like `{"title": "Fix bug", "confidence": 0.9}` is unambiguous and
unwraps to its one string, regardless of key name.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review feedback (Macroscope, Cursor Bugbot): the previous implementation
used `extractJsonObject`, which pulls the first `{...}` out of surrounding
prose, so a legitimate title like `Document {"foo":"bar"} syntax` collapsed
to `bar`. Require the whole title — after stripping an optional code fence —
to be a single JSON object before unwrapping; otherwise leave it untouched.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 0adf7d8. Configure here.
Review feedback (Cursor Bugbot): models often wrap titles in quotes, so a
quote-decorated envelope like `"{"title":"X"}"` failed the brace check and
then quote-stripping left the raw JSON in the sidebar — the very leak this
change fixes. `sanitizePrTitle` had the same miss and does no quote pass at
all. Strip surrounding quotes/backticks (and a code fence) inside the unwrap
before the brace check, so both sanitizers recognise decorated envelopes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the brace-matching/quote-stripping heuristic with a simpler, more
robust rule: decode the value as JSON and recurse.
- decodes to a JSON string -> recurse on the decoded string (handles quoted
titles and JSON-string-encoded / doubly-encoded envelopes for free);
- decodes to an object with one string value -> recurse on it (any key);
- decodes to an object with several strings -> recurse on a `title` key if
present, else give up;
- anything else (not JSON, number, array, ambiguous object) -> unchanged.
Prose is not valid JSON, so titles like `Document {"foo":"bar"} syntax` are
left intact without any special embedded-object guard. Note: a title wrapped
in a Markdown code fence is no longer unwrapped, since a fence is not JSON.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Problem
Sometimes T3 Code names a thread with the raw JSON envelope — e.g. the sidebar shows
{"title": "the actual name"}instead of justthe actual name. Some provider models ignore the structured-output contract and emit the whole JSON object as the value of thetitlefield. The shared title sanitizers only stripped surrounding quotes, so that JSON string leaked straight through into the label.Fix
Add
unwrapJsonEnvelopeTitleinTextGenerationUtils, which detects a JSON object whosetitleis a string and unwraps the inner value:{"title": "{\"title\": \"…\"}"}),extractJsonObjecthelper,{/}untouched.It's applied in both
sanitizeThreadTitleandsanitizePrTitle, so every provider (Claude, Codex, Cursor, Grok, OpenCode) benefits from the single shared sink. Added focused unit tests covering the wrapped, double-wrapped, code-fenced, and plain-braces cases.Testing
vp test run apps/server/src/textGeneration/TextGenerationPrompts.test.ts— 25 passedtsgo --noEmitandvp linton the changed files — cleanDone with Claude Opus 4.8 via Claude Code.
Note
Low Risk
Localized display normalization in text-generation utils with conservative parse rules and extensive tests; no auth, persistence, or API contract changes.
Overview
Fixes thread and PR labels sometimes showing raw structured-output junk (e.g.
{"title": "…"}) when models put the whole JSON object in the title field.Adds
unwrapJsonEnvelopeTitleinTextGenerationUtils, which recursivelyJSON.parses the trimmed value: unwraps JSON strings, objects with a single string field (any key), or multi-string objects when atitlekey exists; stops at depth 8; leaves invalid JSON, ambiguous multi-string objects withouttitle, and prose with embedded{…}unchanged.sanitizeThreadTitleandsanitizePrTitlenow run through this helper before their existing line/quote/truncation logic. Unit tests cover wrapped, double-wrapped, JSON-string-encoded, ambiguous, and prose cases for both sanitizers.Reviewed by Cursor Bugbot for commit 33c550f. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Fix JSON envelope leaking into generated titles in
sanitizeThreadTitleandsanitizePrTitleunwrapJsonEnvelopeTitlein TextGenerationUtils.ts to strip JSON object/string envelopes from raw titles before sanitization, including nested and double-encoded forms.titlefield; ambiguous objects with notitlefield are left unchanged to avoid false positives.sanitizeThreadTitleandsanitizePrTitlenow pass input throughunwrapJsonEnvelopeTitlebefore applying existing normalization logic.{"title":"My PR"}) will now yield the inner string value.Macroscope summarized 33c550f.