parse only large transcript chunks off-isolate; deflake stream tests
test / unit + widget + golden + a11y (push) Failing after 27s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 24s

The hang fix offloaded every transcript parse to Isolate.run, including
the small per-poll appends. Spawning a one-shot isolate each tick is pure
overhead and, under concurrent test load, the spawn+round-trip latency
blew the streaming tests' fixed-delay window — transcript_reader_test
flaked intermittently in the full suite.

Only chunks >= 64KB now go off-isolate (the initial-tail case that
actually janks a frame); small appends parse inline. The streaming tests
poll until the expected items arrive instead of waiting a fixed delay, so
they're robust regardless of parse latency or scheduler load.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-22 23:47:25 +02:00
co-authored by Claude Opus 4.7
parent 8fd251ac48
commit 0abc14ed1a
2 changed files with 31 additions and 11 deletions
+11 -3
View File
@@ -152,6 +152,11 @@ String _shortId(String uuid) => uuid.length >= 8 ? uuid.substring(0, 8) : uuid;
/// stream incrementally.
const _defaultInitialTailBytes = 256 * 1024;
/// Chunks at least this large are parsed in a background isolate; smaller
/// ones parse inline. Streaming appends are small, so this keeps the
/// off-thread parse to the initial-tail case that actually janks a frame.
const _isolateParseThreshold = 64 * 1024;
/// Known major transcript versions.
const _knownMajorVersions = {1, 2};
@@ -321,9 +326,12 @@ class TranscriptReader {
}
if (controller.isClosed) return;
// Parse off the UI isolate — the initial chunk can be sizeable and
// JSON-decoding it on the main thread would jank the frame.
final parsed = await Isolate.run(() => parseTranscriptChunk(chunk));
// Parse off the UI isolate only when the chunk is big enough to jank a
// frame — the initial tail read (up to [_initialTailBytes]) is the case
// that froze the app. Streaming appends are small (a message at a time);
// parsing those inline avoids spawning a one-shot isolate every poll
// tick, which is pure overhead and adds latency under load.
final parsed = chunk.length >= _isolateParseThreshold ? await Isolate.run(() => parseTranscriptChunk(chunk)) : parseTranscriptChunk(chunk);
if (controller.isClosed) return;
for (final w in parsed.warnings) {
_onWarn(w);