/** * Renders REAL captured command output into a legible proof image. * * This is a presentation layer over evidence, never a substitute for it. Every * line rendered here was produced by a command that actually ran against the * live cluster in this same test run — the text is passed through verbatim and * HTML-escaped, never composed or edited. The header states the exact command * so a viewer can re-run it. * * Why render terminal output at all: tidalDB has no operator web UI, so most of * the runbook's evidence is stdout. A screenshot of a terminal is the honest way * to show that to someone, and for this audience — an operator, judged at Kyle * Kingsbury's bar — real command output IS the credibility signal. The * alternative would be inventing a UI that does not exist. */ import { createHash } from 'node:crypto'; import { mkdir, writeFile } from 'node:fs/promises'; import { dirname, join } from 'node:path'; import type { Page, TestInfo } from '@playwright/test'; import { redact } from '../../support/env'; export const CAPTURE_WIDTH = 1600; export const CAPTURE_HEIGHT = 900; /** Where the demo capture run writes its images before review. */ export const CAPTURE_DIR = join('test-results', 'demo-captures'); export type ProofLine = { /** The command exactly as executed. */ command: string; /** Its verbatim output. */ output: string; /** What this line proves, in the viewer's vocabulary. */ verdict?: string; /** Marks a deliberately-negative result (a refusal that should happen). */ negative?: boolean; }; export type ProofPanel = { captureId: string; capabilityId: string; title: string; /** One sentence: what an operator learns from this screen. */ subtitle: string; blocks: ProofLine[]; /** Optional footer, e.g. an explicit caveat. */ footnote?: string; }; export type CaptureRecord = { id: string; capabilityId: string; testId: string; file: string; expected: string; businessPurpose: string; personas: string[]; width: number; height: number; contentHash: string; audienceVerdict: 'pending' | 'perfect' | 'acceptable-with-note' | 'slop'; auditStatus: 'pending' | 'pass' | 'fail'; }; const escapeHtml = (value: string): string => value .replace(/&/g, '&') .replace(//g, '>'); /** * Terminal-styled document. Colours are limited to three roles — command, * output, verdict — so nothing on screen implies a meaning it does not have. A * refusal that is SUPPOSED to happen is marked as expected rather than red, * because a red screen in a verification demo reads as a failure. */ function panelHtml(panel: ProofPanel): string { const blocks = panel.blocks .map((block) => { const verdict = block.verdict ? `
${escapeHtml( block.verdict, )}
` : ''; return `
$ ${escapeHtml(block.command)}
${escapeHtml(block.output.trimEnd())}
${verdict}
`; }) .join('\n'); return `

${escapeHtml(panel.title)}

${escapeHtml(panel.subtitle)}

${blocks}
${panel.footnote ? `` : ''} `; } /** * Render a proof panel and write it as a named capture. * * Secrets are redacted on the way in, so a bearer token can never reach an * image even if a caller passes raw output through. */ export async function captureProofPanel( page: Page, testInfo: TestInfo, panel: ProofPanel, meta: { expected: string; businessPurpose: string; personas: string[] }, ): Promise { const safe: ProofPanel = { ...panel, blocks: panel.blocks.map((block) => ({ ...block, command: redact(block.command), output: redact(block.output), })), }; await page.setViewportSize({ width: CAPTURE_WIDTH, height: CAPTURE_HEIGHT }); await page.setContent(panelHtml(safe), { waitUntil: 'load' }); await page.evaluate(() => document.fonts.ready); const file = join(CAPTURE_DIR, `${panel.captureId}.png`); await mkdir(dirname(file), { recursive: true }); const buffer = await page.screenshot({ path: file }); return { id: panel.captureId, capabilityId: panel.capabilityId, testId: `${testInfo.titlePath.join(' :: ')}`, file: `captures/${panel.captureId}.png`, expected: meta.expected, businessPurpose: meta.businessPurpose, personas: meta.personas, width: CAPTURE_WIDTH, height: CAPTURE_HEIGHT, contentHash: `sha256:${createHash('sha256').update(buffer).digest('hex')}`, audienceVerdict: 'pending', auditStatus: 'pending', }; } /** Record an already-taken screenshot (e.g. a real browser surface). */ export async function recordScreenshot( buffer: Buffer, testInfo: TestInfo, spec: { captureId: string; capabilityId: string; expected: string; businessPurpose: string; personas: string[]; width: number; height: number; }, ): Promise { const file = join(CAPTURE_DIR, `${spec.captureId}.png`); await mkdir(dirname(file), { recursive: true }); await writeFile(file, buffer); return { id: spec.captureId, capabilityId: spec.capabilityId, testId: `${testInfo.titlePath.join(' :: ')}`, file: `captures/${spec.captureId}.png`, expected: spec.expected, businessPurpose: spec.businessPurpose, personas: spec.personas, width: spec.width, height: spec.height, contentHash: `sha256:${createHash('sha256').update(buffer).digest('hex')}`, audienceVerdict: 'pending', auditStatus: 'pending', }; } /** Append capture records to the run manifest for later promotion. */ export async function writeManifestFragment( name: string, records: CaptureRecord[], ): Promise { const file = join(CAPTURE_DIR, `manifest-${name}.json`); await mkdir(dirname(file), { recursive: true }); await writeFile(file, `${JSON.stringify(records, null, 2)}\n`); }