Repository navigation
Conversation
Three independent costs in the DOM fence channel, all measured on a 30-fence transcript: 1. Every sweep re-parsed each fence body (JSON.parse + canonical spec validation + two JSON.stringify) for discovery, ordinal computation and takeover. Now cached by body text (bounded at 500 entries). 2. Every changed node got a full subtree scan even when the subtree held no code block at all. Session switches mark every message row dirty, and most of them are prose or tool cards; a cheap 'has a <pre> or tag-text <dsh-ui>' pre-filter skips them. A later insertion of a <pre> fires its own mutation, so nothing is missed. 3. Takeover budget was 8ms per frame even when dozens of fences were queued after a session switch. When the queue exceeds 4 candidates the budget rises to 40ms so one frame can make progress on several fences; the user is already waiting for the page at that point.
This branch has not been deployed
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.
What
Three independent costs in the DOM fence channel. All three show up on a transcript that renders many fences at once (session switch / history load); the first and third also affect any sweep on a long transcript.
1. Every sweep re-parsed every fence body
isGenericGenuiFencerunsrepairFenceJson+JSON.parse+validateCanonicalGenuiSpec+diagnoseUnknownGenuiFields+JSON.stringify(normalizeGenuiSpec(...))+JSON.stringify(value). The same body is re-parsed several times per sweep (discovery, ordinal computation, takeover), and every sweep re-parses every body again. On a 30-fence transcript that is dozens of full parses per frame.2. Every changed node got a full subtree scan
enqueueMutationTargetcallsfindFenceCandidates(element, { includeScope: true })for every structural change. A session switch marks every message row dirty — most of them prose or tool cards with no code block inside — so the scan is wasted work for the majority of targets.3. Takeover budget stayed at 8ms even with dozens of queued fences
SWEEP_BUDGET_MSis 8ms, sized for streaming growth. After a session switch the queue holds dozens of candidates and each React mount costs tens of milliseconds, so the queue drains at roughly one fence per frame and the rest of the transcript waits.How
isGenericGenuiBody(raw)by body text in aMapbounded at 500 entries (cleared wholesale when full). The body is the full input to the verdict, so the cache is exact, not heuristic.<pre>nor the literal<dsh-ui>text. A later insertion of a<pre>fires its own mutation, so no fence can be missed.pendingCandidates.size > 4(batch takeover). Streaming and single-fence paths keep 8ms.Verification
tsc -p tsconfig.json— cleantsdown— all bundles buildvitest run— 413 pre-existing environment failures (jsdom/localStorage) before and after; 1693 passing on both sides, identical failure set.Scope
Only
src/client/dom-fence.tsx(+43 / −7). Independent of #423 — both touch the same file but different regions; either order merges cleanly. Happy to split this or drop the 40ms budget change if you prefer to keep per-frame work capped.