/** * 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.output.trimEnd())}
${verdict}
${escapeHtml(panel.subtitle)}