refactor(qa): keep synthetic UX evidence out of scenario catalog (#109315)

* refactor(qa): keep UX evidence producer out of catalog

* test(qa): preserve UX producer runner integration
This commit is contained in:
Dallin Romney
2026-07-16 13:51:18 -07:00
committed by GitHub
parent 950ec3c620
commit 8924e27bb6
7 changed files with 135 additions and 167 deletions

View File

@@ -1,4 +1,4 @@
// Produces a QA Lab UX Matrix evidence bundle through the script scenario contract.
// Produces standalone QA Lab UX Matrix fixture artifacts for gallery and evidence tests.
import { execFile } from "node:child_process";
import fs from "node:fs/promises";
import os from "node:os";
@@ -16,16 +16,13 @@ import {
type QaEvidenceSummaryEntry,
type QaEvidenceSummaryJson,
} from "../../extensions/qa-lab/src/evidence-summary.js";
import { readQaScenarioById } from "../../extensions/qa-lab/src/scenario-catalog.js";
import {
ensurePlaywrightChromium,
resolveSystemChromiumExecutablePath,
} from "../ensure-playwright-chromium.mjs";
const execFileAsync = promisify(execFile);
const SCENARIO_ID = "ux-matrix-evidence-dashboard";
const SOURCE_PATH = "scripts/qa/ux-matrix-evidence-producer.ts";
const SUITE_COMMAND = `pnpm openclaw qa suite --scenario ${SCENARIO_ID}`;
type MatrixCell = {
artifacts: Array<{ kind: string; path: string }>;
@@ -227,21 +224,7 @@ function buildExecution(params: {
};
}
function readCanonicalScenarioCoverage(): QaEvidenceSummaryEntry["coverage"] {
// Matrix cells are artifacts of one catalog scenario, not independent scenarios.
// Evidence therefore uses that scenario's mapping instead of cell-local coverage IDs.
const coverage = readQaScenarioById(SCENARIO_ID).coverage;
return [
...(coverage?.primary ?? []).map((id) => ({ id, role: "primary" as const })),
...(coverage?.secondary ?? []).map((id) => ({ id, role: "secondary" as const })),
];
}
function buildEvidenceEntry(
cell: MatrixCell,
repoRoot: string,
coverage: QaEvidenceSummaryEntry["coverage"],
): QaEvidenceSummaryEntry {
function buildEvidenceEntry(cell: MatrixCell, repoRoot: string): QaEvidenceSummaryEntry {
const source = `ux-matrix:${cell.surface}:${cell.stage}`;
return {
test: {
@@ -250,7 +233,8 @@ function buildEvidenceEntry(
title: cell.title,
source: { path: SOURCE_PATH },
},
coverage,
// These entries prove the evidence/gallery infrastructure, not product taxonomy behavior.
coverage: [],
refs: [
{ kind: "code", path: SOURCE_PATH },
{ kind: "docs", path: "docs/concepts/qa-e2e-automation.md" },
@@ -282,13 +266,12 @@ function buildEvidenceSummary(params: {
generatedAt: string;
repoRoot: string;
}): QaEvidenceSummaryJson {
const coverage = readCanonicalScenarioCoverage();
return validateQaEvidenceSummaryJson({
kind: QA_EVIDENCE_SUMMARY_KIND,
schemaVersion: QA_EVIDENCE_SUMMARY_SCHEMA_VERSION,
generatedAt: params.generatedAt,
evidenceMode: "full",
entries: params.cells.map((cell) => buildEvidenceEntry(cell, params.repoRoot, coverage)),
entries: params.cells.map((cell) => buildEvidenceEntry(cell, params.repoRoot)),
});
}
@@ -500,9 +483,9 @@ async function writeProducerArtifactFixtureHtml(params: {
</style>
<main>
<h1>UX Matrix Producer Artifact Fixture</h1>
<p class="meta">Script-produced ${escapeHtml(QA_EVIDENCE_FILENAME)} for ${escapeHtml(
SCENARIO_ID,
)}; this fixture is not the QA Lab Evidence Archive UI.</p>
<p class="meta">Standalone script-produced ${escapeHtml(
QA_EVIDENCE_FILENAME,
)}; this fixture is not the QA Lab Evidence Archive UI or product-behavior proof.</p>
<section class="panel">
<h2>UX Matrix entries</h2>
<ul>${entryRows}</ul>
@@ -612,7 +595,6 @@ async function writeProducerMetadata(params: {
cells: readonly MatrixCell[];
repoRoot: string;
}) {
const coverageIds = readCanonicalScenarioCoverage().map((coverage) => coverage.id);
const counts = params.cells.reduce<Record<string, number>>((acc, cell) => {
acc[cell.status] = (acc[cell.status] ?? 0) + 1;
return acc;
@@ -620,13 +602,12 @@ async function writeProducerMetadata(params: {
await writeJson(path.join(params.artifactBase, "manifest.json"), {
kind: "openclaw.qa.ux-matrix",
run: {
scenarioId: SCENARIO_ID,
status: counts.fail ? "fail" : counts.blocked ? "blocked" : "pass",
},
});
await writeJson(path.join(params.artifactBase, "matrix.json"), {
cells: params.cells.map((cell) => ({
coverageIds,
coverageIds: [],
stage: cell.stage,
status: cell.status,
surface: cell.surface,
@@ -635,7 +616,7 @@ async function writeProducerMetadata(params: {
});
await writeJson(path.join(params.artifactBase, "release-ledger.json"), {
entries: params.cells.map((cell) => ({
coverageIds,
coverageIds: [],
stage: cell.stage,
status: cell.status,
surface: cell.surface,
@@ -644,13 +625,10 @@ async function writeProducerMetadata(params: {
});
await writeText(
path.join(params.artifactBase, "commands.txt"),
[
`${SUITE_COMMAND} --output-dir ${toRepoRelativePath(params.repoRoot, params.artifactBase)}`,
`node --import tsx ${SOURCE_PATH} --artifact-base ${toRepoRelativePath(
params.repoRoot,
params.artifactBase,
)}`,
].join("\n") + "\n",
`node --import tsx ${SOURCE_PATH} --artifact-base ${toRepoRelativePath(
params.repoRoot,
params.artifactBase,
)}\n`,
);
await writeText(
path.join(params.artifactBase, "scorecard.md"),