Created
July 4, 2026 00:27
-
-
Save prateek/1d8f1e31c9f88a9624f5a8ac5e9183a1 to your computer and use it in GitHub Desktop.
Orca terminal tearing reproduction harness
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
| #!/usr/bin/env node | |
| 'use strict'; | |
| /* | |
| * Single-file reproduction for Orca remote-session terminal tearing. | |
| * | |
| * Run: | |
| * node orca-terminal-tearing-harness.js | |
| * | |
| * The script installs the pinned xterm packages into a temp cache if they are | |
| * not already available next to this file. Expected today: 4 pass, 3 fail. | |
| */ | |
| const assert = require('node:assert/strict'); | |
| const childProcess = require('node:child_process'); | |
| const crypto = require('node:crypto'); | |
| const fs = require('node:fs'); | |
| const module_ = require('node:module'); | |
| const os = require('node:os'); | |
| const path = require('node:path'); | |
| const DEPS = { | |
| '@xterm/headless': '6.0.0', | |
| '@xterm/addon-unicode11': '0.9.0', | |
| '@xterm/addon-serialize': '0.14.0', | |
| }; | |
| const WIDENS = [ | |
| ['✅', 0x2705], | |
| ['⚡', 0x26a1], | |
| ]; | |
| const STABLE = [ | |
| ['—', 0x2014], | |
| ['→', 0x2192], | |
| ['█', 0x2588], | |
| ['░', 0x2591], | |
| ]; | |
| const TEARING_LINE = 'build passed all the way to green now ✅✅\r\n'; | |
| const UNICODE_GRID = { cols: 40, rows: 30 }; | |
| const PARAGRAPH = | |
| 'The quick brown fox jumps over the lazy dog and then keeps on running past ' + | |
| 'the fence, across the field, and all the way down to the river where it finally ' + | |
| 'stops to rest for a little while before turning back toward home again at dusk.'; | |
| const WIDTH_INPUT = PARAGRAPH + '\r\n'; | |
| const WIDTH = 80; | |
| function dependencyRequire() { | |
| const localRequire = module_.createRequire(__filename); | |
| try { | |
| loadDeps(localRequire); | |
| return localRequire; | |
| } catch { | |
| return module_.createRequire(path.join(installDeps(), 'package.json')); | |
| } | |
| } | |
| function loadDeps(requireFrom) { | |
| return { | |
| Terminal: requireFrom('@xterm/headless').Terminal, | |
| Unicode11Addon: requireFrom('@xterm/addon-unicode11').Unicode11Addon, | |
| SerializeAddon: requireFrom('@xterm/addon-serialize').SerializeAddon, | |
| }; | |
| } | |
| function installDeps() { | |
| const key = Object.entries(DEPS) | |
| .map(([name, version]) => `${name}@${version}`) | |
| .join('|'); | |
| const hash = crypto.createHash('sha256').update(key).digest('hex').slice(0, 12); | |
| const dir = path.join(os.tmpdir(), `orca-terminal-tearing-harness-${hash}`); | |
| const manifest = path.join(dir, 'package.json'); | |
| fs.mkdirSync(dir, { recursive: true }); | |
| fs.writeFileSync( | |
| manifest, | |
| JSON.stringify({ private: true, dependencies: DEPS }, null, 2) + '\n', | |
| ); | |
| if (depsPresent(dir)) return dir; | |
| console.log(`Installing pinned xterm packages into ${dir}`); | |
| const result = childProcess.spawnSync( | |
| 'npm', | |
| ['install', '--no-audit', '--no-fund', '--ignore-scripts', '--no-package-lock'], | |
| { cwd: dir, stdio: 'inherit' }, | |
| ); | |
| if (result.error || result.status !== 0) { | |
| const detail = result.error ? `: ${result.error.message}` : ''; | |
| throw new Error(`npm install failed${detail}`); | |
| } | |
| return dir; | |
| } | |
| function depsPresent(dir) { | |
| return Object.entries(DEPS).every(([name, version]) => { | |
| const packageJson = path.join(dir, 'node_modules', ...name.split('/'), 'package.json'); | |
| if (!fs.existsSync(packageJson)) return false; | |
| return JSON.parse(fs.readFileSync(packageJson, 'utf8')).version === version; | |
| }); | |
| } | |
| const { Terminal, Unicode11Addon, SerializeAddon } = loadDeps(dependencyRequire()); | |
| function makeTerm({ cols = 80, rows = 30, v11 = false } = {}) { | |
| const term = new Terminal({ cols, rows, allowProposedApi: true }); | |
| if (v11) { | |
| term.loadAddon(new Unicode11Addon()); | |
| term.unicode.activeVersion = '11'; | |
| } | |
| const serializer = new SerializeAddon(); | |
| term.loadAddon(serializer); | |
| return { term, serializer }; | |
| } | |
| function write(term, data) { | |
| return new Promise((resolve) => term.write(data, resolve)); | |
| } | |
| function dumpRows(term) { | |
| const buf = term.buffer.active; | |
| const rows = []; | |
| for (let y = 0; y < buf.length; y++) { | |
| const line = buf.getLine(y); | |
| if (!line) continue; | |
| rows.push(line.translateToString(true).replace(/\s+$/, '')); | |
| } | |
| while (rows.length && rows[rows.length - 1] === '') rows.pop(); | |
| return rows; | |
| } | |
| async function roundTrip({ input, host = {}, view = {} }) { | |
| const h = makeTerm(host); | |
| await write(h.term, input); | |
| const serialized = h.serializer.serialize(); | |
| const hostRows = dumpRows(h.term); | |
| const v = makeTerm(view); | |
| await write(v.term, serialized); | |
| const viewRows = dumpRows(v.term); | |
| return { serialized, hostRows, viewRows }; | |
| } | |
| function makeUnicodeService(v11) { | |
| const term = new Terminal({ allowProposedApi: true }); | |
| if (v11) { | |
| term.loadAddon(new Unicode11Addon()); | |
| term.unicode.activeVersion = '11'; | |
| } | |
| return term._core.unicodeService; | |
| } | |
| const v6Service = makeUnicodeService(false); | |
| const v11Service = makeUnicodeService(true); | |
| const widthV6 = (codepoint) => v6Service.getStringCellWidth(String.fromCodePoint(codepoint)); | |
| const widthV11 = (codepoint) => v11Service.getStringCellWidth(String.fromCodePoint(codepoint)); | |
| const hex = (cp) => 'U+' + cp.toString(16).toUpperCase().padStart(4, '0'); | |
| function scanDivergences({ from = 0x0000, to = 0x1ffff } = {}) { | |
| const diverging = []; | |
| for (let cp = from; cp <= to; cp++) { | |
| if (cp >= 0xd800 && cp <= 0xdfff) continue; | |
| const v6 = widthV6(cp); | |
| const v11 = widthV11(cp); | |
| if (v6 !== v11) diverging.push({ cp, v6, v11 }); | |
| } | |
| return diverging; | |
| } | |
| const tests = []; | |
| function test(name, fn) { | |
| tests.push({ name, fn }); | |
| } | |
| function assertRowsEqual(viewRows, hostRows, message) { | |
| try { | |
| assert.deepEqual(viewRows, hostRows, message); | |
| } catch (error) { | |
| error.message = | |
| `${message}\n\nhost rows:\n${formatRows(hostRows)}\n\nview rows:\n${formatRows(viewRows)}`; | |
| throw error; | |
| } | |
| } | |
| function formatRows(rows) { | |
| return rows.map((row, index) => ` ${String(index).padStart(2, '0')}: ${JSON.stringify(row)}`).join('\n'); | |
| } | |
| test('precondition: Unicode 6 and 11 disagree on cell width (drivers of Mechanism 1)', () => { | |
| const diverging = scanDivergences(); | |
| assert.ok( | |
| diverging.length > 0, | |
| 'expected at least one codepoint whose cell width differs between Unicode 6 and 11', | |
| ); | |
| for (const [glyph, cp] of WIDENS) { | |
| assert.equal(widthV6(cp), 1, `${glyph} ${hex(cp)} should be width 1 at Unicode 6`); | |
| assert.equal(widthV11(cp), 2, `${glyph} ${hex(cp)} should be width 2 at Unicode 11`); | |
| } | |
| for (const [glyph, cp] of STABLE) { | |
| assert.equal(widthV6(cp), widthV11(cp), `${glyph} ${hex(cp)} must not diverge between v6 and v11`); | |
| } | |
| const widened = diverging.filter((d) => d.v6 === 1 && d.v11 === 2); | |
| console.log( | |
| ` diverging codepoints: ${diverging.length} ` + | |
| `(widen 1->2: ${widened.length}, other: ${diverging.length - widened.length})`, | |
| ); | |
| console.log(` first widen-1->2: ${widened.slice(0, 12).map((d) => hex(d.cp)).join(' ')}`); | |
| }); | |
| test('control: serialize is faithful when host and viewer share a Unicode version (v6 -> v6)', async () => { | |
| const { hostRows, viewRows } = await roundTrip({ | |
| input: TEARING_LINE, | |
| host: { ...UNICODE_GRID, v11: false }, | |
| view: { ...UNICODE_GRID, v11: false }, | |
| }); | |
| assertRowsEqual( | |
| viewRows, | |
| hostRows, | |
| 'serialize -> replay must be identity when both sides use the same Unicode version', | |
| ); | |
| }); | |
| test('fix demo: aligning the host serializer to Unicode 11 restores identity (v11 -> v11)', async () => { | |
| const { hostRows, viewRows } = await roundTrip({ | |
| input: TEARING_LINE, | |
| host: { ...UNICODE_GRID, v11: true }, | |
| view: { ...UNICODE_GRID, v11: true }, | |
| }); | |
| assertRowsEqual( | |
| viewRows, | |
| hostRows, | |
| 'loading the unicode11 addon on the host serializer makes serialize -> repaint identity', | |
| ); | |
| }); | |
| test('INVARIANT (Mechanism 1): serialize -> repaint must be Unicode-version-invariant (v6 host -> v11 viewer)', async () => { | |
| const { hostRows, viewRows } = await roundTrip({ | |
| input: TEARING_LINE, | |
| host: { ...UNICODE_GRID, v11: false }, | |
| view: { ...UNICODE_GRID, v11: true }, | |
| }); | |
| assertRowsEqual( | |
| viewRows, | |
| hostRows, | |
| 'serialize@Unicode6 -> repaint@Unicode11 must be identity, but the v11 viewer reserves ' + | |
| '2 cells for each ✅ that the v6 host laid out in 1', | |
| ); | |
| }); | |
| test('control / fix demo: repaint at the serialized grid width is identity (80 -> 80)', async () => { | |
| const { hostRows, viewRows } = await roundTrip({ | |
| input: WIDTH_INPUT, | |
| host: { cols: WIDTH, rows: 30 }, | |
| view: { cols: WIDTH, rows: 30 }, | |
| }); | |
| assert.ok(hostRows.length > 1, 'paragraph must soft-wrap at 80 cols for this test to be meaningful'); | |
| assertRowsEqual( | |
| viewRows, | |
| hostRows, | |
| 'repaint at the same width as the serialized grid must reproduce it exactly', | |
| ); | |
| }); | |
| test('INVARIANT (Mechanism 2): repaint must honor the serialized grid width (80 -> 79)', async () => { | |
| const { hostRows, viewRows } = await roundTrip({ | |
| input: WIDTH_INPUT, | |
| host: { cols: WIDTH, rows: 30 }, | |
| view: { cols: WIDTH - 1, rows: 30 }, | |
| }); | |
| assertRowsEqual( | |
| viewRows, | |
| hostRows, | |
| 'a 1-column-narrower viewport re-wraps soft-wrapped lines; the viewer must repaint at ' + | |
| 'the serialized grid width (80), not its own (79)', | |
| ); | |
| }); | |
| test('INVARIANT (Mechanism 2): repaint must honor the serialized grid width (80 -> 81)', async () => { | |
| const { hostRows, viewRows } = await roundTrip({ | |
| input: WIDTH_INPUT, | |
| host: { cols: WIDTH, rows: 30 }, | |
| view: { cols: WIDTH + 1, rows: 30 }, | |
| }); | |
| assertRowsEqual( | |
| viewRows, | |
| hostRows, | |
| 'a 1-column-wider viewport re-wraps soft-wrapped lines; the viewer must repaint at ' + | |
| 'the serialized grid width (80), not its own (81)', | |
| ); | |
| }); | |
| async function main() { | |
| console.log('Orca terminal tearing harness'); | |
| console.log( | |
| `Pinned deps: ${Object.entries(DEPS) | |
| .map(([name, version]) => `${name}@${version}`) | |
| .join(', ')}`, | |
| ); | |
| console.log('Expected against Orca today: 4 pass, 3 fail\n'); | |
| let passed = 0; | |
| let failed = 0; | |
| for (const { name, fn } of tests) { | |
| try { | |
| await fn(); | |
| passed += 1; | |
| console.log(`ok - ${name}`); | |
| } catch (error) { | |
| failed += 1; | |
| console.log(`not ok - ${name}`); | |
| console.log(indent(error.message)); | |
| } | |
| } | |
| console.log(`\nsummary: ${passed} pass, ${failed} fail`); | |
| if (failed > 0) process.exitCode = 1; | |
| } | |
| function indent(text) { | |
| return String(text) | |
| .split('\n') | |
| .map((line) => ` ${line}`) | |
| .join('\n'); | |
| } | |
| main().catch((error) => { | |
| console.error(error.stack || error.message); | |
| process.exitCode = 1; | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment