Skip to content

Commit 0abfbc1

Browse files
j15zclaude
andcommitted
docs(copilot): name the scan window's blind spot instead of glossing it
Bugbot is right that the window can hide a streaming tail, and the docstring said the bound "reaches the same verdict as the full remainder for any real payload" — true for every payload a tag carries, but it read as unconditional and hid the exception. The exception, now stated and pinned by a test: a JSON body whose top-level value closes BEYOND the window, followed by prose and no closing tag, still reads as a viable prefix, so the remainder waits for the stream to end instead of settling mid-stream. Lossless — the completed parse renders every character — and it needs a payload several times larger than any tag emits. A mention in prose settles at its first character at any length, because prose does not open with a brace; the test pins that half too, since it is the case that actually occurs. Not widening the window: the bound is what took a 58KB borrowed-close reply from 242ms to 35ms per parse, on the main thread, re-run every chunk. Trading a measured freeze for a hypothetical one is the wrong direction. The docstring also now covers the matched-pair path, which the same constant bounds since the previous commit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent d2fd114 commit 0abfbc1

2 files changed

Lines changed: 40 additions & 9 deletions

File tree

apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,24 @@ describe('parseSpecialTags with <question>', () => {
358358
expect(renderedText(segments)).toBe(raw)
359359
})
360360

361+
it('settles a prose mention at any length, but defers a payload that closes past the window', () => {
362+
// The scan window's accepted blind spot, pinned so it stays a decision.
363+
//
364+
// A mention in prose settles at its FIRST character however long the message
365+
// runs — prose does not open with `{`, so viability fails immediately.
366+
const mention = `see <workspace_resource> ${'long prose. '.repeat(600)}`
367+
expect(parseSpecialTags(mention, true).hasPendingTag).toBe(false)
368+
369+
// But a JSON body whose top-level value closes BEYOND the window still reads
370+
// as a viable prefix, so the tail stays hidden until the stream ends. Needs a
371+
// payload several times larger than any tag emits, and it is lossless once
372+
// complete — the cost of bounding a scan that otherwise stalls the main
373+
// thread.
374+
const oversized = `see <workspace_resource>{"type":"file","note":"${'x'.repeat(5000)}"} and then prose.`
375+
expect(parseSpecialTags(oversized, true).hasPendingTag).toBe(true)
376+
expect(renderedText(parseSpecialTags(oversized, false).segments)).toBe(oversized)
377+
})
378+
361379
it('still renders a matched pair whose body IS valid', () => {
362380
const raw =
363381
'see <workspace_resource>{"type":"file","path":"files/a.md","title":"a.md"}</workspace_resource> ok'

apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -489,16 +489,29 @@ const JSON_BODY_TAG_NAMES: ReadonlySet<(typeof SPECIAL_TAG_NAMES)[number]> = new
489489
)
490490

491491
/**
492-
* How much of an unclosed body to inspect per parse.
492+
* How much of a body to inspect per parse, on both the unclosed and matched-pair
493+
* paths.
493494
*
494-
* Both rules in {@link unclosedTagCannotResolve} decide on their FIRST piece of
495-
* evidence — the first foreign marker, or the first character that breaks JSON
496-
* viability — so a bounded window reaches the same verdict as the full remainder
497-
* for any real payload. Unbounded, the check is O(remaining length) and runs
498-
* once per opener inside a parse that re-runs for every streamed chunk, so a
499-
* long reply repeatedly mentioning a tag name in prose costs seconds of
500-
* main-thread time. Evidence past the window only defers the decision to a later
501-
* chunk, which is the same conservative direction the rules already take.
495+
* The rules in {@link unclosedTagCannotResolve} and {@link literalTextReason}
496+
* decide on their FIRST piece of evidence — the first foreign marker, or the
497+
* first character that breaks JSON viability — so a bounded window reaches the
498+
* same verdict as the full remainder for any payload a tag actually carries.
499+
* Unbounded, the check is O(body length) and runs once per opener inside a parse
500+
* that re-runs for every streamed chunk: a long reply repeatedly mentioning a tag
501+
* name cost seconds of main-thread time, and one 58KB reply whose early close was
502+
* misspelled — so a single body stretched most of the message — cost 242ms per
503+
* parse against 35ms bounded.
504+
*
505+
* The window's one blind spot, and why it is accepted: a JSON body whose
506+
* top-level value closes BEYOND the window, followed by prose and no closing tag,
507+
* still reads as a viable prefix, so the remainder stays hidden until the stream
508+
* ends rather than settling mid-stream. It is lossless — the completed parse
509+
* renders every character — and it needs a payload several times larger than any
510+
* tag emits (a `<workspace_resource>` runs ~100 characters, a `<question>` card
511+
* under ~1500). A mention in prose settles at its first character at any length,
512+
* because prose does not open with `{`. Widening or removing the window to close
513+
* that gap would trade a measured, reachable main-thread freeze for a
514+
* hypothetical one.
502515
*/
503516
const MAX_UNCLOSED_BODY_SCAN = 4096
504517

0 commit comments

Comments
 (0)