fix(terminal): bound bold glitch diagnostics

This commit is contained in:
Brennan Benson
2026-08-27 15:13:36 -07:00
parent 93ea1a9138
commit f1ec80b916
7 changed files with 79 additions and 9 deletions
File diff suppressed because one or more lines are too long
@@ -17,11 +17,11 @@ describe('Electron runtime package contract', () => {
'utf8'
)
expect(patch).toContain('diff --git a/src/Types.ts b/src/Types.ts')
expect(patch).toContain('readonly clearModelGeneration: number')
expect(patch).toContain('const generation = this._atlas.clearModelGeneration')
expect(patch).toContain('this.clearModelGeneration++')
expect(patch).toContain('this._atlas._clearModelGeneration||0')
expect(patch.match(/\^\(\?:\[1-8\]\\d\{2\}\|900\)\$/g)).toHaveLength(3)
})
it('keeps root postinstall as the single Electron binary install owner', () => {
+3 -3
View File
@@ -18,7 +18,7 @@ patchedDependencies:
hash: af3b156143ed2ee9903b753145b63dd4a2ee72cadd7ef333ab0f4d5d3ebfc32c
path: config/patches/@xterm__addon-serialize@0.15.0-beta.287.patch
'@xterm/addon-webgl@0.20.0-beta.286':
hash: cad3f24983886675ce45b67bc169545b9a2f3f9e05f3b0ffac0911d974ca6c54
hash: 335427c05eef212851ba4e2fc19623c65af82898c5243c6b7f308c97b1d6a174
path: config/patches/@xterm__addon-webgl@0.20.0-beta.286.patch
'@xterm/xterm@6.1.0-beta.287':
hash: 46796c152f3b73e28238f44499eaf5a867a863809bc7b470b159526a41e354f7
@@ -229,7 +229,7 @@ importers:
version: 0.13.0-beta.287(@xterm/xterm@6.1.0-beta.287(patch_hash=46796c152f3b73e28238f44499eaf5a867a863809bc7b470b159526a41e354f7))
'@xterm/addon-webgl':
specifier: 0.20.0-beta.286
version: 0.20.0-beta.286(patch_hash=cad3f24983886675ce45b67bc169545b9a2f3f9e05f3b0ffac0911d974ca6c54)(@xterm/xterm@6.1.0-beta.287(patch_hash=46796c152f3b73e28238f44499eaf5a867a863809bc7b470b159526a41e354f7))
version: 0.20.0-beta.286(patch_hash=335427c05eef212851ba4e2fc19623c65af82898c5243c6b7f308c97b1d6a174)(@xterm/xterm@6.1.0-beta.287(patch_hash=46796c152f3b73e28238f44499eaf5a867a863809bc7b470b159526a41e354f7))
'@xterm/xterm':
specifier: 6.1.0-beta.287
version: 6.1.0-beta.287(patch_hash=46796c152f3b73e28238f44499eaf5a867a863809bc7b470b159526a41e354f7)
@@ -9638,7 +9638,7 @@ snapshots:
dependencies:
'@xterm/xterm': 6.1.0-beta.287(patch_hash=46796c152f3b73e28238f44499eaf5a867a863809bc7b470b159526a41e354f7)
'@xterm/addon-webgl@0.20.0-beta.286(patch_hash=cad3f24983886675ce45b67bc169545b9a2f3f9e05f3b0ffac0911d974ca6c54)(@xterm/xterm@6.1.0-beta.287(patch_hash=46796c152f3b73e28238f44499eaf5a867a863809bc7b470b159526a41e354f7))':
'@xterm/addon-webgl@0.20.0-beta.286(patch_hash=335427c05eef212851ba4e2fc19623c65af82898c5243c6b7f308c97b1d6a174)(@xterm/xterm@6.1.0-beta.287(patch_hash=46796c152f3b73e28238f44499eaf5a867a863809bc7b470b159526a41e354f7))':
dependencies:
'@xterm/xterm': 6.1.0-beta.287(patch_hash=46796c152f3b73e28238f44499eaf5a867a863809bc7b470b159526a41e354f7)
@@ -15,6 +15,10 @@ import {
import { maybeStartTerminalRenderDesyncSentinel } from './terminal-render-desync-trigger'
const rendererDeliveryBreadcrumbs = createPtyDeliveryBreadcrumbRing()
const ATLAS_FONT_PROBE_MISMATCH = 'atlas-font-probe-mismatch'
const ATLAS_CRASH_MIRROR_INTERVAL_MS = 30_000
let lastAtlasCrashMirrorAt = Number.NEGATIVE_INFINITY
let suppressedAtlasCrashMirrors = 0
export function recordTerminalFreezeBreadcrumb(
kind: string,
@@ -35,9 +39,26 @@ export function recordTerminalFreezeBreadcrumb(
// instrumentation, not absence of the event.
setTerminalWebglDiagnosticRecorder((kind, detail) => {
rendererDeliveryBreadcrumbs.record(kind, detail)
if (kind === ATLAS_FONT_PROBE_MISMATCH) {
const now = Date.now()
if (now - lastAtlasCrashMirrorAt < ATLAS_CRASH_MIRROR_INTERVAL_MS) {
suppressedAtlasCrashMirrors++
return
}
lastAtlasCrashMirrorAt = now
}
// `kind` last: it is the coalescing discriminator, so a detail field of the
// same name must not be able to shadow it.
recordRendererCrashBreadcrumb(TERMINAL_WEBGL_DIAGNOSTIC_BREADCRUMB, { ...detail, kind })
recordRendererCrashBreadcrumb(TERMINAL_WEBGL_DIAGNOSTIC_BREADCRUMB, {
...detail,
...(kind === ATLAS_FONT_PROBE_MISMATCH && suppressedAtlasCrashMirrors > 0
? { rendererSuppressedSinceLast: suppressedAtlasCrashMirrors }
: {}),
kind
})
if (kind === ATLAS_FONT_PROBE_MISMATCH) {
suppressedAtlasCrashMirrors = 0
}
})
// Why: the sentinel is a field-diagnostic that must be armable on production
@@ -53,7 +74,7 @@ type AtlasFontProbeMismatch = { desired?: string; actual?: string }
;(globalThis as { __orcaAtlasFontProbe?: (mismatch: AtlasFontProbeMismatch) => void })[
'__orcaAtlasFontProbe'
] = (mismatch) => {
recordTerminalWebglDiagnostic('atlas-font-probe-mismatch', {
recordTerminalWebglDiagnostic(ATLAS_FONT_PROBE_MISMATCH, {
desired: mismatch?.desired ?? null,
actual: mismatch?.actual ?? null
})
@@ -65,4 +86,6 @@ export function getTerminalFreezeBreadcrumbs(): PtyDeliveryBreadcrumb[] {
export function resetTerminalFreezeBreadcrumbsForTesting(): void {
rendererDeliveryBreadcrumbs.reset()
lastAtlasCrashMirrorAt = Number.NEGATIVE_INFINITY
suppressedAtlasCrashMirrors = 0
}
@@ -186,6 +186,20 @@ describe('terminal-render-desync-sentinel', () => {
expect(getRenderDesyncEvidence()).toHaveLength(4)
})
it('does not spend the capture budget on failed persistence attempts', async () => {
writeTerminalRenderDesyncEvidence.mockRejectedValue(new Error('disk unavailable'))
for (let pane = 1; pane <= 5; pane++) {
sampleWith(divergenceOf(manyCells(0)), false, `m1:p${pane}`)
sampleWith(divergenceOf(manyCells(0)), false, `m1:p${pane}`)
await vi.waitFor(() => expect(writeTerminalRenderDesyncEvidence).toHaveBeenCalledTimes(pane))
await vi.waitFor(() => expect(getRenderDesyncEvidence()).toHaveLength(0))
}
expect(getRenderDesyncEvidence()).toHaveLength(0)
expect(resetAndRefreshAllTerminalWebglAtlases).not.toHaveBeenCalled()
})
it('resets tracking for paused panes instead of sampling them', () => {
const cells = manyCells(0)
sampleWith(divergenceOf(cells))
@@ -233,6 +233,10 @@ async function persistEntry(
if (directory == null) {
// Why: a failed write must leave the bad pixels intact; recovering here
// would destroy the only evidence without producing a durable capture.
const entryIndex = evidence.indexOf(entry)
if (entryIndex !== -1) {
evidence.splice(entryIndex, 1)
}
pendingPaneKeys.delete(entry.paneKey)
return
}
@@ -53,4 +53,33 @@ describe('WebGL diagnostics → freeze breadcrumb ring', () => {
kind: 'webgl-context-restore'
})
})
it('bounds atlas mismatch storm IPC while retaining the local occurrence count', () => {
vi.useFakeTimers()
try {
for (let mismatch = 0; mismatch < 10_000; mismatch++) {
recordTerminalWebglDiagnostic('atlas-font-probe-mismatch', {
desired: '550',
actual: '700 14px Menlo'
})
}
expect(recordRendererCrashBreadcrumb).toHaveBeenCalledTimes(1)
expect(getTerminalFreezeBreadcrumbs()).toEqual([
expect.objectContaining({ kind: 'atlas-font-probe-mismatch', repeats: 10_000 })
])
vi.advanceTimersByTime(30_000)
recordTerminalWebglDiagnostic('atlas-font-probe-mismatch', {
desired: '550',
actual: '700 14px Menlo'
})
expect(recordRendererCrashBreadcrumb).toHaveBeenLastCalledWith(
'terminal_webgl_diagnostic',
expect.objectContaining({ rendererSuppressedSinceLast: 9_999 })
)
} finally {
vi.useRealTimers()
}
})
})