import assert from 'node:assert/strict' import { createHash } from 'node:crypto' import fs from 'node:fs/promises' import path from 'node:path' import { createRequire } from 'node:module' import { spawn } from 'node:child_process' import { pathToFileURL } from 'node:url' import react from '@vitejs/plugin-react' import { createServer } from 'vite' const require = createRequire(import.meta.url) const projectRoot = process.cwd() const rendererRoot = path.join(projectRoot, 'src/renderer') const runtimeRoot = path.join(projectRoot, '.tmp/rea-css-runtime/dist') const sourceFlowEvidenceId = 'ev_fc29c47ef7b284267584efe1fcabe48345e37a0b03cf492b686faec7aa704324' const sourceFlowCssPath = path.join(projectRoot, '.tmp/rea-css-recovered/src-CyXHA0IC.css') const electronExecutable = require('electron') const hash = bytes => createHash('sha256').update(bytes).digest('hex') const captureModes = { 'offscreen-disabled-gpu': { id: 'offscreen-disabled-gpu', offscreen: true, disableHardwareAcceleration: true, disableGpuSwitch: true, captureOptions: null, }, 'hidden-hardware': { id: 'hidden-hardware', offscreen: false, disableHardwareAcceleration: false, disableGpuSwitch: false, captureOptions: { stayHidden: true, stayAwake: true }, }, 'hidden-debugger': { id: 'hidden-debugger', offscreen: false, disableHardwareAcceleration: false, disableGpuSwitch: false, debuggerScreenshot: true, backgroundThrottling: false, }, 'visible-debugger': { id: 'visible-debugger', offscreen: false, disableHardwareAcceleration: false, disableGpuSwitch: false, debuggerScreenshot: true, backgroundThrottling: false, showInactiveBeforeCapture: true, closeAfterCapture: true, }, 'visible-swiftshader': { id: 'visible-swiftshader', offscreen: false, disableHardwareAcceleration: true, disableGpuSwitch: false, debuggerScreenshot: true, backgroundThrottling: false, showInactiveBeforeCapture: true, closeAfterCapture: true, graphicsSwitches: ['--use-angle=swiftshader', '--disable-gpu-compositing'], }, } const electronHelperSource = String.raw` const fs = require('node:fs') const path = require('node:path') const zlib = require('node:zlib') const { app, BrowserWindow, nativeImage } = require('electron') const jobPath = process.env.YCODE_MESSAGE_PARITY_JOB const resultPath = process.env.YCODE_MESSAGE_PARITY_RESULT const logPath = process.env.YCODE_MESSAGE_PARITY_LOG const mark = value => { try { fs.appendFileSync(logPath, String(Date.now()) + ' ' + value + '\n') } catch {} } const finish = (value, code = 0) => { mark('finish:' + code) try { fs.writeFileSync(resultPath, JSON.stringify(value)) } catch {} app.exit(code) } const fail = error => finish({ ok: false, error: String(error && (error.stack || error.message) || error) }, 1) const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) mark('main-module-start') async function captureGpuState(job) { const gpuInfoDetail = 'complete' let gpuFeatureStatus = null try { gpuFeatureStatus = app.getGPUFeatureStatus() } catch (error) { gpuFeatureStatus = { error: String(error.message || error) } } let gpuInfo = null try { gpuInfo = await Promise.race([ app.getGPUInfo(gpuInfoDetail).catch(error => ({ error: String(error.message || error) })), delay(4000).then(() => ({ unavailable: 'timeout' })), ]) } catch (error) { gpuInfo = { error: String(error.message || error) } } return { mode: job.captureMode.id, offscreen: job.captureMode.offscreen, hardwareAccelerationDisabledByApp: job.captureMode.disableHardwareAcceleration, disableGpuSwitchRequested: job.captureMode.disableGpuSwitch, disableGpuSwitchPresent: app.commandLine.hasSwitch('disable-gpu'), backgroundThrottlingDisabled: job.captureMode.backgroundThrottling === false, showInactiveBeforeCapture: job.captureMode.showInactiveBeforeCapture === true, windowVisibleAtCapture: false, windowClosedAfterCapture: false, chromiumVersion: process.versions.chrome || null, electronVersion: process.versions.electron || null, gpuFeatureStatus, gpuInfoDetail, gpuInfo, captureMethod: job.captureMode.debuggerScreenshot ? 'webContents.debugger.sendCommand(Page.captureScreenshot, { format: png, fromSurface: true, captureBeyondViewport: false })' : job.captureMode.captureOptions ? 'webContents.capturePage(undefined, { stayHidden: true, stayAwake: true })' : 'webContents.capturePage()', } } function crc32(bytes) { let crc = 0xffffffff for (const byte of bytes) { crc ^= byte for (let bit = 0; bit < 8; bit += 1) crc = (crc >>> 1) ^ (crc & 1 ? 0xedb88320 : 0) } return (crc ^ 0xffffffff) >>> 0 } function pngChunk(type, payload) { const typeBytes = Buffer.from(type, 'ascii') const length = Buffer.alloc(4) length.writeUInt32BE(payload.length) const checksum = Buffer.alloc(4) checksum.writeUInt32BE(crc32(Buffer.concat([typeBytes, payload]))) return Buffer.concat([length, typeBytes, payload, checksum]) } function detectBitmapChannelOrder() { const header = Buffer.alloc(13) header.writeUInt32BE(1, 0) header.writeUInt32BE(1, 4) header[8] = 8 header[9] = 6 const signature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]) const pixel = zlib.deflateSync(Buffer.from([0, 17, 83, 201, 255])) const png = Buffer.concat([signature, pngChunk('IHDR', header), pngChunk('IDAT', pixel), pngChunk('IEND', Buffer.alloc(0))]) const image = nativeImage.createFromBuffer(png) if (image.isEmpty()) throw new Error('nativeImage could not decode the channel-order calibration PNG') const bitmap = image.toBitmap({ scaleFactor: 1 }) if (bitmap.length !== 4 || bitmap[3] !== 255) throw new Error('nativeImage returned invalid channel-order calibration pixels') if (bitmap[0] === 201 && bitmap[1] === 83 && bitmap[2] === 17) return { name: 'BGRA', red: 2, green: 1, blue: 0, alpha: 3 } if (bitmap[0] === 17 && bitmap[1] === 83 && bitmap[2] === 201) return { name: 'RGBA', red: 0, green: 1, blue: 2, alpha: 3 } throw new Error('nativeImage bitmap channel order did not match the calibration PNG: ' + Array.from(bitmap).join(',')) } function analyzePixelRegion(pngBytes, region, channelOrder) { const image = nativeImage.createFromBuffer(pngBytes) if (image.isEmpty()) throw new Error('nativeImage could not decode a pixel-analysis PNG') const size = image.getSize() const { x, y, width, height } = region if (x < 0 || y < 0 || width < 1 || height < 1 || x + width > size.width || y + height > size.height) { throw new Error('Pixel-analysis region falls outside its PNG') } const bitmap = image.toBitmap({ scaleFactor: 1 }) if (bitmap.length !== size.width * size.height * 4) throw new Error('nativeImage returned an unexpected pixel-analysis bitmap size') const colorAt = (offset) => ({ r: bitmap[offset + channelOrder.red], g: bitmap[offset + channelOrder.green], b: bitmap[offset + channelOrder.blue], a: bitmap[offset + channelOrder.alpha], }) const histogram = new Map() const pixels = [] for (let py = y; py < y + height; py += 1) { for (let px = x; px < x + width; px += 1) { const color = colorAt((py * size.width + px) * 4) if (color.a === 0) continue const key = color.r + ',' + color.g + ',' + color.b histogram.set(key, (histogram.get(key) ?? 0) + 1) pixels.push(color) } } const backgroundEntry = [...histogram.entries()].sort((left, right) => right[1] - left[1])[0] const background = backgroundEntry ? backgroundEntry[0].split(',').map(Number) : [0, 0, 0] const luminance = color => 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b const backgroundColor = { r: background[0], g: background[1], b: background[2] } const backgroundLuminance = luminance(backgroundColor) const orderCounts = { 'R>G>B': 0, 'R>B>G': 0, 'G>R>B': 0, 'G>B>R': 0, 'B>R>G': 0, 'B>G>R': 0, tied: 0 } const chromaticEdges = new Map() let inkPixels = 0 let edgePixels = 0 let chromaticEdgePixels = 0 let darkest = null for (const color of pixels) { const lightness = luminance(color) const contrastFromBackground = backgroundLuminance - lightness if (contrastFromBackground > 24) { inkPixels += 1 if (!darkest || lightness < darkest.luminance) darkest = { color: { r: color.r, g: color.g, b: color.b }, luminance: lightness } } if (contrastFromBackground > 8 && contrastFromBackground < 210) { edgePixels += 1 const max = Math.max(color.r, color.g, color.b) const min = Math.min(color.r, color.g, color.b) if (max - min >= 2) { chromaticEdgePixels += 1 const sorted = [['R', color.r], ['G', color.g], ['B', color.b]].sort((left, right) => right[1] - left[1]) const key = sorted[0][1] === sorted[1][1] || sorted[1][1] === sorted[2][1] ? 'tied' : sorted.map(entry => entry[0]).join('>') orderCounts[key] += 1 const rgb = color.r + ',' + color.g + ',' + color.b chromaticEdges.set(rgb, (chromaticEdges.get(rgb) ?? 0) + 1) } } } return { region, dimensions: size, pixelFormat: channelOrder.name, modalBackground: { ...backgroundColor, pixels: backgroundEntry?.[1] ?? 0 }, darkestInkPixel: darkest, inkCandidatePixels: inkPixels, edgeCandidatePixels: edgePixels, chromaticEdgePixels, chromaticEdgeOrderCounts: orderCounts, commonChromaticEdgeRgb: [...chromaticEdges.entries()] .sort((left, right) => right[1] - left[1]) .slice(0, 12) .map(([rgb, count]) => ({ rgb: rgb.split(',').map(Number), count })), } } function analyzeReferenceAndCandidate(job, candidateBytes) { const referenceBytes = fs.readFileSync(job.referencePng) const channelOrder = detectBitmapChannelOrder() const regions = Object.fromEntries(Object.entries(job.pixelRegions).map(([name, region]) => [name, { reference: analyzePixelRegion(referenceBytes, region, channelOrder), candidate: analyzePixelRegion(candidateBytes, region, channelOrder), }])) return { method: 'Electron nativeImage decoded 8-bit bitmap; channel order calibrated against known RGBA PNG', channelOrder: channelOrder.name, regions } } async function waitUntil(predicate, timeoutMs, label) { const deadline = Date.now() + timeoutMs while (Date.now() < deadline) { if (await predicate()) return await delay(100) } throw new Error('Timed out waiting for ' + label) } async function closeAndWait(win) { if (!win || win.isDestroyed()) return true const closed = new Promise(resolve => win.once('closed', () => resolve(true))) win.close() return Promise.race([closed, delay(2500).then(() => win.isDestroyed())]) } function alignTargets(entries) { const result = {} for (const entry of entries) { const host = document.querySelector(entry.hostSelector) const target = host?.querySelector(entry.targetSelector) if (!host || !target) throw new Error('Missing fixture node for ' + entry.name) const rect = target.getBoundingClientRect() const before = { x: rect.x, y: rect.y, width: rect.width, height: rect.height } const hostRect = host.getBoundingClientRect() const beforeHost = { x: hostRect.x, y: hostRect.y, width: hostRect.width, height: hostRect.height } const deltaY = entry.referenceBounds.y - rect.y host.style.top = (parseFloat(host.style.top) + deltaY) + 'px' result[entry.name] = { referenceBounds: entry.referenceBounds, beforeAlignmentBounds: before, outerHostBefore: beforeHost, outerHostTopDelta: deltaY, anchorMethod: 'outer fixture top adjusted from measured target y; component fonts and intrinsic dimensions untouched', } } return result } function measureFixture() { const rectOf = node => { const rect = node.getBoundingClientRect() return { x: rect.x, y: rect.y, width: rect.width, height: rect.height, right: rect.right, bottom: rect.bottom } } const styleOf = node => { const style = getComputedStyle(node) return { display: style.display, position: style.position, fontFamily: style.fontFamily, fontSize: style.fontSize, fontWeight: style.fontWeight, lineHeight: style.lineHeight, letterSpacing: style.letterSpacing, width: style.width, top: style.top, right: style.right, bottom: style.bottom, left: style.left, transform: style.transform, webkitFontSmoothing: style.getPropertyValue('-webkit-font-smoothing'), mozOsxFontSmoothing: style.getPropertyValue('-moz-osx-font-smoothing'), textRendering: style.textRendering, color: style.color, backgroundColor: style.backgroundColor, padding: style.padding, margin: style.margin, borderRadius: style.borderRadius, boxSizing: style.boxSizing, maxWidth: style.maxWidth, overflow: style.overflow, } } const describe = node => ({ text: node.textContent, bounds: rectOf(node), computedStyle: styleOf(node) }) const userHost = document.querySelector('[data-parity-fixture="user"]') const assistantHost = document.querySelector('[data-parity-fixture="assistant"]') const sourceFlowWrapper = document.querySelector('[data-parity-source-flow]') const userBubble = userHost?.querySelector('.cr-self-bubble') const userLeaf = userHost?.querySelector('.yc-user-editable p > .yc-user-leaf > span') const paragraph = assistantHost?.querySelector('.cr-agent__content .cr-markdown > p:first-child') if (!userHost || !assistantHost || !userBubble || !userLeaf || !paragraph) throw new Error('Fixture target nodes are not present after rendering') return { userBubble: { outerHost: describe(userHost), message: describe(userHost.querySelector('.msg--user')), bubble: describe(userBubble), text: describe(userLeaf), }, firstParagraph: { outerHost: describe(assistantHost), message: describe(assistantHost.querySelector('.msg--assistant')), agent: describe(assistantHost.querySelector('.cr-agent')), paragraph: describe(paragraph), }, viewport: { width: window.innerWidth, height: window.innerHeight, devicePixelRatio: window.devicePixelRatio }, userAgent: navigator.userAgent, readyFonts: document.fonts.status, sourceFlowWrapper: sourceFlowWrapper ? describe(sourceFlowWrapper) : null, fixtureHasFixedTargetFontOrHeight: false, } } try { const job = JSON.parse(fs.readFileSync(jobPath, 'utf8')) const dataRoot = path.join(path.dirname(jobPath), 'electron-data') for (const name of ['userData', 'sessionData', 'logs']) { const directory = path.join(dataRoot, name) fs.mkdirSync(directory, { recursive: true }) try { app.setPath(name, directory) } catch {} } if (job.captureMode.disableHardwareAcceleration) app.disableHardwareAcceleration() mark('app-paths-configured') app.whenReady().then(async () => { mark('app-ready') const consoleErrors = [] const failedRequests = [] let win try { win = new BrowserWindow({ show: false, useContentSize: true, width: job.viewport.width, height: job.viewport.height, backgroundColor: '#ffffff', webPreferences: { offscreen: job.captureMode.offscreen, contextIsolation: true, ...(job.captureMode.backgroundThrottling === false ? { backgroundThrottling: false } : {}), }, }) mark('window-created') win.webContents.on('console-message', (_event, level, message) => { if (level === 3) consoleErrors.push(String(message)) }) win.webContents.on('did-fail-load', (_event, code, description, url) => failedRequests.push({ code, description, url })) win.webContents.on('did-finish-load', () => mark('did-finish-load')) win.webContents.on('dom-ready', () => mark('dom-ready')) mark('load-url-start') await Promise.race([ win.loadURL(job.url), delay(30000).then(() => { throw new Error('Timed out loading the Vite fixture page') }), ]) mark('load-url-finished') await waitUntil(() => win.webContents.executeJavaScript( "document.documentElement.dataset.messageParityReady === 'true'", ), 45000, 'React fixture and document fonts') mark('fixture-ready') await win.webContents.executeJavaScript('new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)))') mark('initial-render-frames') const calibration = await win.webContents.executeJavaScript( '(' + alignTargets.toString() + ')(' + JSON.stringify(job.anchors) + ')', ) mark('targets-aligned') await win.webContents.executeJavaScript('new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)))') mark('aligned-render-frames') const measurements = await win.webContents.executeJavaScript('(' + measureFixture.toString() + ')()') mark('fixture-measured') let windowVisibleAtCapture = false if (job.captureMode.showInactiveBeforeCapture) { win.showInactive() mark('window-shown-inactive') await waitUntil(() => win.isVisible(), 5000, 'non-activating fixture window visibility') await delay(100) await win.webContents.executeJavaScript('new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)))') windowVisibleAtCapture = win.isVisible() if (!windowVisibleAtCapture) throw new Error('Fixture window was not visible before debugger screenshot') } const gpu = await captureGpuState(job) gpu.windowVisibleAtCapture = windowVisibleAtCapture gpu.graphicsSwitchesRequested = job.captureMode.graphicsSwitches || [] mark('gpu-state-captured-after-window-presentation') let candidatePng let size if (job.captureMode.debuggerScreenshot) { const debuggerSession = win.webContents.debugger if (debuggerSession.isAttached()) throw new Error('Fixture webContents debugger unexpectedly already attached') debuggerSession.attach('1.3') mark('debugger-attached') try { await debuggerSession.sendCommand('Page.enable') mark('debugger-page-enabled') const captured = await Promise.race([ debuggerSession.sendCommand('Page.captureScreenshot', { format: 'png', fromSurface: true, captureBeyondViewport: false, }), delay(15000).then(() => { throw new Error('Timed out waiting for Page.captureScreenshot on the hidden fixture window') }), ]) mark('debugger-page-screenshot-captured') candidatePng = Buffer.from(captured.data, 'base64') } finally { if (debuggerSession.isAttached()) debuggerSession.detach() } if (job.captureMode.closeAfterCapture && !win.isDestroyed()) { gpu.windowClosedAfterCapture = await closeAndWait(win) if (!gpu.windowClosedAfterCapture) throw new Error('Fixture window did not close after debugger screenshot') mark('window-closed-after-capture') } const decoded = nativeImage.createFromBuffer(candidatePng) if (decoded.isEmpty()) throw new Error('nativeImage could not decode the debugger Page.captureScreenshot result') size = decoded.getSize() mark('debugger-png-decoded') } else { const image = job.captureMode.captureOptions ? await win.webContents.capturePage(undefined, job.captureMode.captureOptions) : await win.webContents.capturePage() size = image.getSize() candidatePng = image.toPNG() } if (size.width !== job.viewport.width || size.height !== job.viewport.height) { throw new Error('Candidate PNG size is ' + size.width + 'x' + size.height + ', expected ' + job.viewport.width + 'x' + job.viewport.height) } fs.writeFileSync(job.outputPng, candidatePng) mark('capture-written') const pixelAnalysis = analyzeReferenceAndCandidate(job, candidatePng) finish({ ok: true, calibration, measurements, gpu, viewport: size, pixelAnalysis, consoleErrors, failedRequests }) } catch (error) { if (job.captureMode.closeAfterCapture && win && !win.isDestroyed()) { await closeAndWait(win) mark('window-closed-after-failed-capture') } finish({ ok: false, error: String(error && (error.stack || error.message) || error), consoleErrors, failedRequests }, 1) } }).catch(fail) } catch (error) { fail(error) } ` function fail(message) { throw new Error(message) } function readJson(file) { return fs.readFile(file, 'utf8').then(JSON.parse) } function findByClass(nodes, className) { const node = nodes.find(entry => (entry.attributes?.class ?? '').split(/\s+/).includes(className)) if (!node) fail(`REA layout is missing .${className}`) return node } function textWithin(nodes, root) { const children = new Map() for (const node of nodes) { const list = children.get(node.parent_index) ?? [] list.push(node) children.set(node.parent_index, list) } const visit = node => node.node_name === '#text' ? node.text ?? '' : (children.get(node.node_index) ?? []).map(visit).join('') return visit(root) } function isDescendant(nodes, node, ancestorIndex) { const byIndex = new Map(nodes.map(entry => [entry.node_index, entry])) let parent = node.parent_index while (parent !== undefined && parent !== null && parent >= 0) { if (parent === ancestorIndex) return true parent = byIndex.get(parent)?.parent_index } return false } function integerRegion(bounds, viewport) { const [rawX, rawY, rawWidth, rawHeight] = bounds const x = Math.floor(rawX) const y = Math.floor(rawY) const right = Math.ceil(rawX + rawWidth) const bottom = Math.ceil(rawY + rawHeight) const region = { x, y, width: right - x, height: bottom - y } if (x < 0 || y < 0 || right > viewport.width || bottom > viewport.height || region.width < 1 || region.height < 1) { fail(`REA target bounds do not fit in the ${viewport.width}x${viewport.height} viewport`) } return region } async function authenticateEvidence(raw) { const moduleUrl = pathToFileURL(path.join(runtimeRoot, 'domain/evidence.js')) let parseEvidence try { ({ parseEvidence } = await import(moduleUrl)) } catch (error) { fail(`Prepared REA runtime is required to authenticate the greeting archive: ${error.message}`) } return parseEvidence(raw) } async function loadReference(directory) { const reportPath = path.join(directory, 'font-platform-report.json') const layoutPath = path.join(directory, 'layout.json') const screenshotPath = path.join(directory, 'workbuddy.png') const [report, layout, screenshotBytes] = await Promise.all([ readJson(reportPath), readJson(layoutPath), fs.readFile(screenshotPath), ]) if (report.source?.kind !== 'rea-workbuddy') fail('Greeting font report must identify rea-workbuddy as its source') if (!/^ev_[a-f0-9]{64}$/.test(report.evidenceId ?? '')) fail('Greeting report has an invalid Evidence ID') if (report.evidenceId !== layout.evidenceId) fail('Greeting layout and font report Evidence IDs differ') if (report.version === 'unobserved') fail('Greeting report does not record an observed WorkBuddy version') if (!/^[a-f0-9]{64}$/i.test(report.evidenceSha256 ?? '')) fail('Greeting report has no valid archive SHA-256') const archivePath = path.resolve(projectRoot, report.archive) const expectedArchive = path.resolve(projectRoot, '.tmp/rea-css-evidence', `${report.evidenceId}.json`) if (process.platform === 'win32' ? archivePath.toLowerCase() !== expectedArchive.toLowerCase() : archivePath !== expectedArchive) fail('Greeting report archive path is not the archive named by its Evidence ID') if (layout.archive && path.resolve(projectRoot, layout.archive).toLowerCase() !== expectedArchive.toLowerCase()) { fail('Greeting layout archive does not match the font report Evidence ID') } const archiveBytes = await fs.readFile(archivePath) const evidenceSha256 = hash(archiveBytes) if (evidenceSha256 !== report.evidenceSha256.toLowerCase()) fail('Greeting archive SHA-256 does not match its font report') const archive = JSON.parse(archiveBytes.toString('utf8')) if (archive.evidence_id !== report.evidenceId) fail('Greeting archive Evidence ID does not match its font report') const evidence = await authenticateEvidence(archive) if (evidence.operation !== 'inspect_electron_page' || evidence.provider?.id !== 'rea-cdp-electron') { fail('Greeting archive is not an authenticated REA Electron page observation') } const target = evidence.normalized_result?.target?.file_path ?? '' if (!/WorkBuddy[\\/]resources[\\/]app\.asar[\\/]renderer[\\/]index\.html$/i.test(target)) { fail('Greeting Evidence target is not the WorkBuddy renderer') } if (evidence.normalized_result?.browser?.user_agent?.match(/WorkBuddy[ /]([\d.]+)/i)?.[1] !== report.version) { fail('Greeting report version does not match the archived WorkBuddy user agent') } const archiveVisuals = evidence.normalized_result?.visuals if (!Array.isArray(archiveVisuals?.font_samples)) fail('Greeting archive has no font sample observation') assert.deepEqual(report.samples, archiveVisuals.font_samples, 'font report samples differ from authenticated REA archive') assert.deepEqual(report.missing ?? [], archiveVisuals.font_missing ?? [], 'font report missing samples differ from authenticated REA archive') if (layout.artifact?.sha256 !== hash(screenshotBytes)) fail('Greeting PNG SHA-256 differs from its layout record') const archivedScreenshot = archiveVisuals.artifact if (!archivedScreenshot?.data_base64 || hash(Buffer.from(archivedScreenshot.data_base64, 'base64')) !== layout.artifact.sha256) { fail('Greeting PNG is not the screenshot stored in authenticated REA Evidence') } if (layout.viewport?.width !== 1336 || layout.viewport?.height !== 901) fail('Greeting layout viewport must be 1336x901') if (!Array.isArray(layout.message_content?.nodes)) fail('Greeting layout has no REA message_content export') if (!isDeepEqual(report.samples, layout.font_samples)) fail('Greeting font report samples differ from layout font samples') const nodes = layout.message_content.nodes const userRoot = findByClass(nodes, 'cr-user-content') const userLeaf = nodes.find(node => node.node_name === '#text' && isDescendant(nodes, node, userRoot.node_index)) if (!userLeaf) fail('REA message_content has no text leaf under .cr-user-content') const assistantRoot = findByClass(nodes, 'cr-agent__content') const firstParagraph = nodes.find(node => node.node_name === 'P' && isDescendant(nodes, node, assistantRoot.node_index)) if (!firstParagraph) fail('REA message_content has no assistant paragraph under .cr-agent__content') const userText = textWithin(nodes, userRoot) const assistantText = textWithin(nodes, firstParagraph) const userSample = report.samples.find(sample => sample.category === 'user-bubble') const assistantSample = report.samples.find(sample => sample.category === 'chinese-body') if (!userSample || !assistantSample) fail('Greeting report must contain user-bubble and chinese-body samples') if (userText !== userSample.text || assistantText !== assistantSample.text) { fail('REA message_content text differs from the authenticated greeting font sample') } const documentNodes = layout.layout?.documents?.flatMap(document => document.nodes) ?? [] const userBubbleNode = findByClass(documentNodes, 'cr-self-bubble') const agentNode = findByClass(documentNodes, 'cr-agent') if (!Array.isArray(userBubbleNode.bounds) || !Array.isArray(agentNode.bounds)) fail('Greeting layout is missing target node bounds') if (!Array.isArray(userSample.bounds) || !Array.isArray(assistantSample.bounds)) fail('Greeting font samples are missing target bounds') return { reportPath, reportSha256: hash(await fs.readFile(reportPath)), report, layout, archivePath, evidenceSha256, screenshotPath, screenshotSha256: hash(screenshotBytes), userSample, assistantSample, userBubbleNode, agentNode, userText, assistantText, } } function isDeepEqual(left, right) { return JSON.stringify(left) === JSON.stringify(right) } async function readSourceFlowBasis(reference) { if (reference.report.evidenceId !== sourceFlowEvidenceId) { fail(`--source-flow-transform requires REA Evidence ${sourceFlowEvidenceId}`) } const documentNodes = reference.layout.layout?.documents?.flatMap(document => document.nodes) ?? [] const flowNode = findByClass(documentNodes, 'cr-document__virtual-flow') const userBubble = findByClass(documentNodes, 'cr-self-bubble') const firstParagraph = documentNodes.find(node => node.node_name === 'P' && isDeepEqual(node.bounds, reference.assistantSample.bounds)) if (!firstParagraph) fail('Source-flow REA layout has no paragraph matching the selected Chinese-body sample') if (flowNode.styles?.transform !== 'matrix(1, 0, 0, 1, 0, 0)') { fail('Source-flow REA ancestor transform is not the observed identity matrix') } if (!isDescendant(documentNodes, userBubble, flowNode.node_index) || !isDescendant(documentNodes, firstParagraph, flowNode.node_index)) { fail('Source-flow REA ancestor is not shared by the user bubble and sampled paragraph') } const cssBytes = await fs.readFile(sourceFlowCssPath) const cssText = cssBytes.toString('utf8') const expectedRule = '.cr-document__virtual-flow{width:100%;position:absolute;top:0;left:0}' if (!cssText.includes(expectedRule)) fail('Recovered REA stylesheet no longer contains the observed virtual-flow positioning rule') return { evidenceId: reference.report.evidenceId, selector: '.cr-document__virtual-flow', computedTransform: flowNode.styles.transform, bounds: flowNode.bounds, sharedBy: ['.cr-self-bubble', 'first sampled chinese-body paragraph'], recoveredCssPath: path.relative(projectRoot, sourceFlowCssPath).replaceAll('\\', '/'), recoveredCssSha256: hash(cssBytes), recoveredCssRule: expectedRule, } } function entrySource(reference, sourceFlowTransform) { const messages = { user: { id: 'rea-greeting-user', role: 'user', content: reference.userText, createdAt: 0, status: 'done', }, assistant: { id: 'rea-greeting-assistant', role: 'assistant', content: reference.assistantText, parts: [{ type: 'text', text: reference.assistantText }], createdAt: 0, status: 'done', }, } const placement = { x: reference.agentNode.bounds[0], y: reference.agentNode.bounds[1], width: reference.assistantSample.bounds[2], } const userPlacement = { x: reference.agentNode.bounds[0], y: reference.userBubbleNode.bounds[1], width: reference.assistantSample.bounds[2], } const userFixture = `