From b2076329feeacff75349a271f1a2fdd135e05d0d Mon Sep 17 00:00:00 2001 From: Julius Olsson Date: Mon, 11 May 2026 16:53:40 +0200 Subject: [PATCH] =?UTF-8?q?fix(paste):=20lower=20CLAUDE=5FPASTE=5FTHRESHOL?= =?UTF-8?q?D=20to=20100=20=E2=80=94=20accumulator=20engages=20well=20below?= =?UTF-8?q?=20800?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #71 shipped the event-driven submit-after-paste fix but the bug kept firing in practice. Per-paste debug dumps from real usage tell the story: a 215-char single-line message ("i can say that something DID NOt work…") was routed as `route:claude-plain-text` and submitted as text+`\r` in ONE PTY write — the exact code path PR #71's bracketed-paste + placeholder-detector was supposed to bypass. The 800-char gate held the door shut for the most common case. Root cause: 800 confused two different Claude TUI thresholds. * COLLAPSE threshold (~800): the point at which Claude renders `[Pasted text #N]` in the composer instead of inlining the bytes. This is the one upstream's PASTE_THRESHOLD const refers to. * ACCUMULATOR threshold (~100): the point at which Claude's paste state machine starts swallowing trailing `\r` during its ~100 ms debounce window. This fires regardless of whether collapse happens. PR #71 fixed the accumulator race for text that ALSO crossed the collapse threshold (≥800 chars or multi-line). Everything in 100–800 chars without newlines — i.e. most ordinary chat messages — stayed on the broken plain-text path. Lowering the gate to 100 routes those through the bracketed-paste + event-driven submit path, which is where the actual fix lives. Also lowers CLAUDE_PASTE_EVENT_DRIVEN_TIMEOUT_MS from 2000 → 500: in the 100–800 char range the `[Pasted text #N]` placeholder never renders, so every submit hits the timeout fallback. 2000 ms there is a noticeable per-message lag. 500 ms is still well past Claude's 100 ms accumulator window so `\r` is always safe by then. Evidence: per-paste debug dumps in ~/Library/Application Support/cc-shell/paste-debug/ - 9da6e430-...paste.jsonl: 215 chars, isPasteLike=false, plain-text route → bug - b387f634-...paste.jsonl: 145 chars, same shape, same bug - 53f74065-...paste.jsonl: 156 chars, same - 5c2b319c-...paste.jsonl: 177 chars, same Follows up #71. Numbers may need empirical tuning if 100 is too aggressive (i.e., normal-typed short messages get unnecessarily routed through bracketed paste and feel the 500 ms fallback lag), but the per-paste debug journal from #71 makes that easy to spot. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../tile-tree/TileLeaf/claudePaste.ts | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/renderer/src/workspace/tile-tree/TileLeaf/claudePaste.ts b/src/renderer/src/workspace/tile-tree/TileLeaf/claudePaste.ts index def35efb5..71fa53225 100644 --- a/src/renderer/src/workspace/tile-tree/TileLeaf/claudePaste.ts +++ b/src/renderer/src/workspace/tile-tree/TileLeaf/claudePaste.ts @@ -37,18 +37,38 @@ // not a load-bearing primary path. // // Why these numbers: -// - 800 chars matches Claude's current PASTE_THRESHOLD upstream. +// - 100 chars: empirical lower bound on text length where Claude's +// TUI runs its internal paste accumulator. The original constant +// was 800 because we believed it had to match Claude's *collapse* +// threshold (the size at which Claude renders `[Pasted text #N]` +// instead of inlining the bytes). It does not. Claude's paste +// ACCUMULATOR triggers independently, around ~100 chars, regardless +// of whether the inlined-vs-collapse cutoff has been crossed. Real +// per-paste debug dumps showed the bug recurring on 145 / 156 / 177 +// / 215-char single-line text that we were sending as +// `route:claude-plain-text` (raw text + `\r` in one PTY write), +// because Claude's accumulator engaged on the inline bytes and +// swallowed the trailing `\r`. Lowering this to 100 routes those +// cases through the bracketed-paste + event-driven path instead, +// which is the bug fix. // - 125 ms is slightly above Claude's 100 ms paste completion // timeout. Going lower risks reintroducing the race; going much // higher makes submit feel laggy on every long paste. Only // consulted when the event-driven path times out or is disabled. -// - 2000 ms event-driven safety bound is ~10x the harness's -// observed p95 placeholder wait (~108 ms). +// - 500 ms event-driven safety bound (down from 2000 ms): for +// bracketed pastes that DO NOT cross Claude's inline→collapse +// threshold, the `[Pasted text #N]` placeholder never renders, so +// our detector hits the timeout fallback every time. At 2000 ms +// that became a noticeable per-submit lag on any text 100–800 +// chars — exactly the size range this PR moves into the +// bracketed-paste path. 500 ms is well past Claude's 100 ms +// accumulator window (so `\r` is always safe by then) and short +// enough to feel responsive even on the slow-path fallback. -export const CLAUDE_PASTE_THRESHOLD = 800 +export const CLAUDE_PASTE_THRESHOLD = 100 export const CLAUDE_PASTE_SUBMIT_DELAY_MS = 125 export const CLAUDE_IMAGE_PATH_SUBMIT_DELAY_MS = 750 -export const CLAUDE_PASTE_EVENT_DRIVEN_TIMEOUT_MS = 2_000 +export const CLAUDE_PASTE_EVENT_DRIVEN_TIMEOUT_MS = 500 function wait(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms))