Add harness workflow and Material showcase design system

This commit is contained in:
2026-03-23 17:30:30 +08:00
parent c570431dba
commit 5d02bf9df4
46 changed files with 3343 additions and 1068 deletions
+379
View File
@@ -0,0 +1,379 @@
import fs from "node:fs";
import path from "node:path";
import { execFileSync } from "node:child_process";
import { fileURLToPath } from "node:url";
const zeroShaPattern = /^0{40}$/;
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
export const repoRoot = path.resolve(scriptDir, "../..");
export const artifactsDir = path.join(repoRoot, ".artifacts", "harness");
export const pnpmBin = process.platform === "win32" ? "pnpm.cmd" : "pnpm";
export const suiteDefinitions = {
static: {
description: "Fast static validation: lint and workspace typecheck.",
commands: [
{ args: ["lint"], label: "Lint repository" },
{ args: ["typecheck"], label: "Typecheck workspace" }
]
},
component: {
description: "Fast component feedback: lint, typecheck, and unit coverage.",
commands: [
{ args: ["lint"], label: "Lint repository" },
{ args: ["typecheck"], label: "Typecheck workspace" },
{ args: ["test"], label: "Run component tests" }
]
},
docs: {
description: "Build the Storybook review surface.",
commands: [{ args: ["build:docs"], label: "Build Storybook" }]
},
a11y: {
description: "Run Storybook accessibility validation and report violations/incomplete results.",
commands: [{ args: ["test:e2e:a11y"], label: "Run Storybook accessibility checks" }]
},
"docs-smoke": {
description: "Exercise high-value Storybook flows with Playwright.",
commands: [{ args: ["test:e2e:smoke"], label: "Run Storybook smoke tests" }]
},
consumers: {
description: "Validate generated registry metadata and downstream consumers.",
commands: [
{ args: ["registry:check"], label: "Check registry metadata" },
{ args: ["test:registry:consumer"], label: "Run registry consumer smoke test" },
{ args: ["test:package:consumer"], label: "Run package consumer smoke test" }
]
},
pr: {
description: "Baseline pull request gate for source, docs, and consumer surfaces.",
commands: [
{ args: ["lint"], label: "Lint repository" },
{ args: ["typecheck"], label: "Typecheck workspace" },
{ args: ["test"], label: "Run component tests" },
{ args: ["build"], label: "Build packages" },
{ args: ["build:docs"], label: "Build Storybook" },
{ args: ["test:e2e:a11y"], label: "Run Storybook accessibility checks" },
{ args: ["registry:check"], label: "Check registry metadata" },
{ args: ["test:registry:consumer"], label: "Run registry consumer smoke test" },
{ args: ["test:package:consumer"], label: "Run package consumer smoke test" }
]
},
release: {
description: "Full release gate, including browser-driven Storybook coverage.",
commands: [
{ args: ["lint"], label: "Lint repository" },
{ args: ["typecheck"], label: "Typecheck workspace" },
{ args: ["test"], label: "Run component tests" },
{ args: ["build"], label: "Build packages" },
{ args: ["build:docs"], label: "Build Storybook" },
{ args: ["test:e2e:a11y"], label: "Run Storybook accessibility checks" },
{ args: ["registry:check"], label: "Check registry metadata" },
{ args: ["test:registry:consumer"], label: "Run registry consumer smoke test" },
{ args: ["test:package:consumer"], label: "Run package consumer smoke test" },
{ args: ["test:e2e:smoke"], label: "Run Storybook smoke tests" }
]
}
};
const suiteOrder = [
"static",
"component",
"docs",
"a11y",
"docs-smoke",
"consumers",
"pr",
"release"
];
const rootStaticFiles = new Set([
"eslint.config.mjs",
"package.json",
"playwright.config.ts",
"pnpm-lock.yaml",
"pnpm-workspace.yaml",
"tsconfig.base.json",
"tsconfig.json",
"vitest.config.ts"
]);
function normalizeFilePath(filePath) {
return filePath.split(path.sep).join(path.posix.sep);
}
function isExactMatch(filePath, exactPaths) {
return exactPaths.has(filePath);
}
function isWithin(filePath, directory) {
return filePath === directory || filePath.startsWith(`${directory}/`);
}
function isPackageSourceChange(filePath) {
return isWithin(filePath, "packages/ui/src") || isWithin(filePath, "packages/tokens/src");
}
function isPackageContractChange(filePath) {
return (
isPackageSourceChange(filePath) ||
filePath === "packages/ui/package.json" ||
filePath === "packages/tokens/package.json"
);
}
function isDocsSurfaceChange(filePath) {
return isWithin(filePath, "apps/docs") || isWithin(filePath, ".storybook");
}
function isDocsInteractiveChange(filePath) {
return (
isWithin(filePath, "apps/docs/src") ||
isWithin(filePath, ".storybook") ||
filePath === "playwright.config.ts"
);
}
function isConsumerSurfaceChange(filePath) {
return (
isWithin(filePath, "registry") ||
isWithin(filePath, "tests/package-consumer") ||
isWithin(filePath, "tests/registry") ||
filePath === "scripts/build-registry.mjs" ||
filePath === "scripts/registry-install.mjs" ||
isPackageContractChange(filePath)
);
}
function isHarnessCodeChange(filePath) {
return (
isWithin(filePath, "scripts/harness") ||
filePath === "AGENTS.md" ||
filePath === "CONTRIBUTING.md" ||
filePath === "README.md" ||
filePath === "docs/harness-engineering.md" ||
filePath === "docs/orchestration.md" ||
isWithin(filePath, "docs/exec-plans")
);
}
function isStaticSurfaceChange(filePath) {
return (
isPackageContractChange(filePath) ||
isDocsSurfaceChange(filePath) ||
isWithin(filePath, "tests") ||
isWithin(filePath, "scripts") ||
isWithin(filePath, ".github/workflows") ||
isExactMatch(filePath, rootStaticFiles) ||
isHarnessCodeChange(filePath)
);
}
function isLikelyCodeChange(filePath) {
return /\.(?:cjs|css|cts|js|json|jsx|mdx|mjs|mts|scss|ts|tsx|yaml|yml)$/.test(filePath);
}
function runGitCommand(args) {
return execFileSync("git", args, {
cwd: repoRoot,
encoding: "utf8"
})
.split("\n")
.map((line) => line.trim())
.filter(Boolean)
.map((line) => normalizeFilePath(line));
}
function getWorkingTreeChangedFiles() {
const files = new Set([
...runGitCommand(["diff", "--name-only", "--diff-filter=ACMR", "HEAD"]),
...runGitCommand(["diff", "--cached", "--name-only", "--diff-filter=ACMR", "HEAD"]),
...runGitCommand(["ls-files", "--others", "--exclude-standard"])
]);
return [...files].sort();
}
export function getChangedFiles({ changedFiles = [], from = null, to = null } = {}) {
if (changedFiles.length > 0) {
return [...new Set(changedFiles.map((filePath) => normalizeFilePath(filePath)))].sort();
}
if (from || to) {
const toRef = to ?? "HEAD";
if (from && zeroShaPattern.test(from)) {
return [
...new Set([
...runGitCommand(["ls-files"]),
...runGitCommand(["ls-files", "--others", "--exclude-standard"])
])
].sort();
}
if (!from) {
throw new Error("Expected --from when selecting changed files from git refs.");
}
return runGitCommand(["diff", "--name-only", "--diff-filter=ACMR", `${from}...${toRef}`]);
}
return getWorkingTreeChangedFiles();
}
function addReason(reasonMap, suiteName, filePath, reason) {
const currentReasons = reasonMap.get(suiteName) ?? [];
currentReasons.push({ file: filePath, reason });
reasonMap.set(suiteName, currentReasons);
}
export function selectSuitesForChangedFiles(changedFiles) {
const normalizedFiles = [...new Set(changedFiles.map((filePath) => normalizeFilePath(filePath)))].sort();
const selectedSuites = new Set();
const reasons = new Map();
const unmatchedFiles = [];
for (const filePath of normalizedFiles) {
let matched = false;
if (isPackageContractChange(filePath)) {
selectedSuites.add("component");
selectedSuites.add("docs");
selectedSuites.add("a11y");
selectedSuites.add("consumers");
addReason(reasons, "component", filePath, "Package contract or source changed.");
addReason(reasons, "docs", filePath, "Package changes affect the Storybook review surface.");
addReason(reasons, "a11y", filePath, "Package changes can alter Storybook accessibility results.");
addReason(reasons, "consumers", filePath, "Package changes affect downstream consumers.");
matched = true;
if (isPackageSourceChange(filePath)) {
selectedSuites.add("docs-smoke");
addReason(reasons, "docs-smoke", filePath, "Interactive package source changed.");
}
}
if (isDocsSurfaceChange(filePath)) {
selectedSuites.add("static");
selectedSuites.add("docs");
selectedSuites.add("a11y");
addReason(reasons, "static", filePath, "Docs or Storybook source changed.");
addReason(reasons, "docs", filePath, "Docs or Storybook source changed.");
addReason(reasons, "a11y", filePath, "Docs or Storybook source changed.");
matched = true;
if (isDocsInteractiveChange(filePath)) {
selectedSuites.add("docs-smoke");
addReason(reasons, "docs-smoke", filePath, "Storybook stories or smoke harness changed.");
}
}
if (isConsumerSurfaceChange(filePath)) {
selectedSuites.add("static");
selectedSuites.add("consumers");
addReason(reasons, "static", filePath, "Registry or consumer validation surface changed.");
addReason(reasons, "consumers", filePath, "Registry or consumer validation surface changed.");
matched = true;
}
if (isStaticSurfaceChange(filePath)) {
selectedSuites.add("static");
addReason(reasons, "static", filePath, "Static validation surface changed.");
matched = true;
}
if (!matched) {
unmatchedFiles.push(filePath);
}
}
if (selectedSuites.has("component")) {
selectedSuites.delete("static");
reasons.delete("static");
}
if (selectedSuites.size === 0 && unmatchedFiles.some((filePath) => isLikelyCodeChange(filePath))) {
selectedSuites.add("static");
for (const filePath of unmatchedFiles) {
if (isLikelyCodeChange(filePath)) {
addReason(reasons, "static", filePath, "Fallback static validation for unmatched code change.");
}
}
}
return {
changedFiles: normalizedFiles,
reasons: Object.fromEntries(
[...reasons.entries()].map(([suiteName, suiteReasons]) => [suiteName, suiteReasons])
),
suites: suiteOrder.filter((suiteName) => selectedSuites.has(suiteName)),
unmatchedFiles
};
}
export function getCommandsForSuites(suiteNames) {
const seenCommands = new Set();
const commands = [];
for (const suiteName of suiteNames) {
const suite = suiteDefinitions[suiteName];
if (!suite) {
throw new Error(`Unknown suite "${suiteName}".`);
}
for (const command of suite.commands) {
const key = command.args.join("\u0000");
if (seenCommands.has(key)) {
continue;
}
seenCommands.add(key);
commands.push({
...command,
suite: suiteName
});
}
}
return commands;
}
export function ensureKnownSuite(name) {
if (name === "changed") {
return;
}
if (!(name in suiteDefinitions)) {
const knownSuites = [...suiteOrder, "changed"].join(", ");
throw new Error(`Unknown suite "${name}". Expected one of: ${knownSuites}.`);
}
}
export function writeHarnessArtifact(name, payload) {
fs.mkdirSync(artifactsDir, { recursive: true });
const outputPath = path.join(artifactsDir, `${name}.json`);
fs.writeFileSync(outputPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
return outputPath;
}
export function formatSuiteListing() {
return [...suiteOrder, "changed"]
.map((suiteName) => {
if (suiteName === "changed") {
return {
name: suiteName,
description: "Select suites from git diff or working tree changes before validating."
};
}
return {
name: suiteName,
description: suiteDefinitions[suiteName].description
};
});
}
+94
View File
@@ -0,0 +1,94 @@
import fs from "node:fs";
import path from "node:path";
import { execFileSync, spawnSync } from "node:child_process";
import { repoRoot } from "./core.mjs";
const orchBin =
process.env.CADENCE_UI_ORCH_BIN ?? "/Users/xd/.codex/skills/orch/assets/orch";
const defaultDbPath = path.join(repoRoot, ".artifacts", "orch", "coord.db");
const defaultWorkspaceRoot = path.join(repoRoot, ".artifacts", "orch", "worktrees");
function printHelp() {
process.stdout.write(`Cadence UI orchestration wrapper
Usage:
pnpm harness:orch -- <orch command> [flags]
Examples:
pnpm harness:orch -- run init --run cadence_ui_demo --goal "Refine release UX" --summary "Break work into isolated tasks"
pnpm harness:orch -- task add --run cadence_ui_demo --task T1 --title "Stabilize smoke tests" --summary "Fix Storybook smoke drift"
pnpm harness:orch -- dispatch --run cadence_ui_demo --task T1 --to default-worker --body-file docs/exec-plans/task-t1.md
pnpm harness:orch -- status --run cadence_ui_demo
Defaults applied by this wrapper:
--db ${path.relative(repoRoot, defaultDbPath)}
dispatch --repo-path ${repoRoot}
dispatch --workspace-root ${path.relative(repoRoot, defaultWorkspaceRoot)}
dispatch --strict-worktree
dispatch --base-ref <current branch>
`);
}
function hasFlag(args, flag) {
return args.includes(flag);
}
function getCurrentBranch() {
try {
return execFileSync("git", ["branch", "--show-current"], {
cwd: repoRoot,
encoding: "utf8"
}).trim();
} catch {
return "main";
}
}
const rawArgs = process.argv.slice(2).filter((value) => value !== "--");
if (
rawArgs.length === 0 ||
rawArgs[0] === "help" ||
rawArgs[0] === "--help" ||
rawArgs[0] === "-h"
) {
printHelp();
process.exit(0);
}
fs.mkdirSync(path.dirname(defaultDbPath), { recursive: true });
fs.mkdirSync(defaultWorkspaceRoot, { recursive: true });
const command = rawArgs[0];
const orchArgs = [...rawArgs];
if (!hasFlag(orchArgs, "--db")) {
orchArgs.unshift(defaultDbPath);
orchArgs.unshift("--db");
}
if (command === "dispatch") {
if (!hasFlag(orchArgs, "--repo-path")) {
orchArgs.push("--repo-path", repoRoot);
}
if (!hasFlag(orchArgs, "--workspace-root")) {
orchArgs.push("--workspace-root", defaultWorkspaceRoot);
}
if (!hasFlag(orchArgs, "--strict-worktree")) {
orchArgs.push("--strict-worktree");
}
if (!hasFlag(orchArgs, "--base-ref")) {
orchArgs.push("--base-ref", getCurrentBranch());
}
}
const result = spawnSync(orchBin, orchArgs, {
cwd: repoRoot,
stdio: "inherit"
});
process.exit(result.status ?? 1);
+238
View File
@@ -0,0 +1,238 @@
import fs from "node:fs/promises";
import path from "node:path";
import { createRequire } from "node:module";
import { chromium } from "@playwright/test";
import { repoRoot } from "./core.mjs";
const require = createRequire(import.meta.url);
const { startStorybookServer, stopStorybookServer } = require(
path.resolve(repoRoot, "tests/e2e/support/storybook-server.cjs")
);
const baseURL = "http://127.0.0.1:6006";
const axeSourcePath = require.resolve("axe-core/axe.min.js");
const reportDir = path.join(repoRoot, ".artifacts", "a11y");
const reportPath = path.join(reportDir, "storybook-a11y.json");
const stories = [
{
id: "components-button--playground",
label: "Button playground"
},
{
id: "components-combobox--controlled",
label: "Combobox controlled"
},
{
id: "components-data-table--playground",
label: "Data table playground"
},
{
id: "components-datepicker--playground",
label: "Date picker playground",
prepare: async (page) => {
await page.getByRole("combobox", { name: "Launch date" }).click();
await page.getByRole("grid").waitFor({ state: "visible" });
}
},
{
id: "components-dialog--playground",
label: "Dialog playground",
prepare: async (page) => {
await page.getByRole("button", { name: "Open approval dialog" }).click();
await page.getByRole("dialog", { name: "Launch this release?" }).waitFor({ state: "visible" });
}
},
{
id: "components-dropdownmenu--states",
label: "Dropdown menu states",
prepare: async (page) => {
await page.getByRole("button", { name: "Review lane menu" }).click();
await page.getByRole("menu").waitFor({ state: "visible" });
}
},
{
id: "components-form--launch-settings",
label: "Form launch settings",
query: "globals=motion:reduced"
},
{
id: "components-popover--playground",
label: "Popover playground",
prepare: async (page) => {
await page.getByRole("button", { name: "Inspect summary" }).click();
await page.getByText("Release health").waitFor({ state: "visible" });
}
},
{
id: "components-sheet--playground",
label: "Sheet playground",
prepare: async (page) => {
await page.getByRole("button", { name: "Open right sheet" }).click();
await page.getByRole("dialog", { name: "Launch settings" }).waitFor({ state: "visible" });
}
}
];
function buildStoryUrl(story) {
const params = new URLSearchParams({
id: story.id,
viewMode: "story"
});
if (story.query) {
for (const [key, value] of new URLSearchParams(story.query).entries()) {
params.set(key, value);
}
}
return `${baseURL}/iframe.html?${params.toString()}`;
}
async function gotoStory(page, story) {
await page.goto(buildStoryUrl(story));
await page.waitForLoadState("domcontentloaded");
await page.waitForFunction(
(storyId) => {
const title = document.title.toLowerCase();
const root = document.getElementById("storybook-root");
return (
title.includes(String(storyId).toLowerCase()) ||
Boolean(root && root.childElementCount > 0)
);
},
story.id,
{ timeout: 30_000 }
);
}
function summarizeResults(results) {
return results.map((result) => ({
description: result.description,
help: result.help,
helpUrl: result.helpUrl,
id: result.id,
impact: result.impact,
nodeCount: result.nodes.length,
nodes: result.nodes.map((node) => ({
failureSummary: node.failureSummary,
html: node.html,
target: node.target
}))
}));
}
async function runAxe(page) {
return page.evaluate(async () => {
const root = document.getElementById("storybook-root") ?? document.body;
return window.axe.run(root, {
resultTypes: ["violations", "incomplete"]
});
});
}
async function main() {
const axeSource = await fs.readFile(axeSourcePath, "utf8");
await fs.mkdir(reportDir, { recursive: true });
await startStorybookServer();
const browser = await chromium.launch({
headless: true
});
const context = await browser.newContext();
await context.addInitScript({ content: axeSource });
const report = {
checkedAt: new Date().toISOString(),
errors: [],
incompleteCount: 0,
stories: [],
storyCount: stories.length,
violationCount: 0
};
try {
const page = await context.newPage();
for (const story of stories) {
process.stdout.write(`[a11y] checking ${story.id}\n`);
try {
await gotoStory(page, story);
if (story.prepare) {
await story.prepare(page);
}
const result = await runAxe(page);
const violations = summarizeResults(result.violations);
const incomplete = summarizeResults(result.incomplete);
report.violationCount += violations.length;
report.incompleteCount += incomplete.length;
report.stories.push({
id: story.id,
incomplete,
label: story.label,
url: buildStoryUrl(story),
violations
});
process.stdout.write(
`[a11y] ${story.id}: ${violations.length} violations, ${incomplete.length} incomplete\n`
);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
report.errors.push({
id: story.id,
label: story.label,
message
});
report.stories.push({
error: message,
id: story.id,
incomplete: [],
label: story.label,
url: buildStoryUrl(story),
violations: []
});
process.stdout.write(`[a11y] ${story.id}: error: ${message}\n`);
}
}
} finally {
await context.close();
await browser.close();
await stopStorybookServer();
}
await fs.writeFile(reportPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
process.stdout.write(`[a11y] Report written to ${path.relative(repoRoot, reportPath)}\n`);
if (report.incompleteCount > 0) {
process.stdout.write(
`[a11y] ${report.incompleteCount} incomplete results detected. Review the JSON report before merge.\n`
);
}
if (report.errors.length > 0) {
process.stdout.write(
`[a11y] ${report.errors.length} story checks failed before axe completed. Failing validation.\n`
);
process.exit(1);
}
if (report.violationCount > 0) {
process.stdout.write(
`[a11y] ${report.violationCount} violations detected. Failing validation.\n`
);
process.exit(1);
}
}
await main();
+92
View File
@@ -0,0 +1,92 @@
import path from "node:path";
import {
getChangedFiles,
selectSuitesForChangedFiles,
writeHarnessArtifact,
repoRoot
} from "./core.mjs";
function parseArgs(argv) {
const options = {
changedFiles: [],
from: null,
json: false,
to: null
};
for (let index = 0; index < argv.length; index += 1) {
const current = argv[index];
if (current === "--") {
continue;
}
if (current === "--json") {
options.json = true;
continue;
}
if (current === "--changed-file" || current === "--from" || current === "--to") {
const next = argv[index + 1];
if (!next) {
throw new Error(`Expected a value after ${current}.`);
}
if (current === "--changed-file") {
options.changedFiles.push(next);
} else if (current === "--from") {
options.from = next;
} else {
options.to = next;
}
index += 1;
continue;
}
throw new Error(`Unknown argument: ${current}`);
}
return options;
}
const options = parseArgs(process.argv.slice(2));
const changedFiles = getChangedFiles(options);
const selection = selectSuitesForChangedFiles(changedFiles);
const reportPath = writeHarnessArtifact("selection", {
...selection,
selectedAt: new Date().toISOString()
});
if (options.json) {
process.stdout.write(
`${JSON.stringify({ ...selection, reportPath: path.relative(repoRoot, reportPath) }, null, 2)}\n`
);
process.exit(0);
}
process.stdout.write(`Harness selection report written to ${path.relative(repoRoot, reportPath)}\n`);
if (selection.suites.length === 0) {
process.stdout.write("No harness suites selected for the current diff.\n");
} else {
process.stdout.write("Selected harness suites:\n");
for (const suiteName of selection.suites) {
process.stdout.write(`- ${suiteName}\n`);
for (const reason of selection.reasons[suiteName] ?? []) {
process.stdout.write(` - ${reason.file}: ${reason.reason}\n`);
}
}
}
if (selection.unmatchedFiles.length > 0) {
process.stdout.write("Unmatched files:\n");
for (const filePath of selection.unmatchedFiles) {
process.stdout.write(`- ${filePath}\n`);
}
}
+160
View File
@@ -0,0 +1,160 @@
import path from "node:path";
import { spawnSync } from "node:child_process";
import {
ensureKnownSuite,
formatSuiteListing,
getChangedFiles,
getCommandsForSuites,
pnpmBin,
repoRoot,
selectSuitesForChangedFiles,
suiteDefinitions,
writeHarnessArtifact
} from "./core.mjs";
function parseArgs(argv) {
const options = {
changedFiles: [],
dryRun: false,
from: null,
list: false,
to: null,
suite: "component"
};
for (let index = 0; index < argv.length; index += 1) {
const current = argv[index];
if (current === "--dry-run") {
options.dryRun = true;
continue;
}
if (current === "--list") {
options.list = true;
continue;
}
if (
current === "--changed-file" ||
current === "--from" ||
current === "--suite" ||
current === "--to"
) {
const next = argv[index + 1];
if (!next) {
throw new Error(`Expected a value after ${current}.`);
}
if (current === "--changed-file") {
options.changedFiles.push(next);
} else if (current === "--from") {
options.from = next;
} else if (current === "--to") {
options.to = next;
} else {
options.suite = next;
}
index += 1;
continue;
}
if (current === "--") {
continue;
}
throw new Error(`Unknown argument: ${current}`);
}
return options;
}
function runCommand(command) {
const startedAt = new Date().toISOString();
const startTime = Date.now();
process.stdout.write(`\n[harness] ${command.label}\n`);
process.stdout.write(`[harness] pnpm ${command.args.join(" ")}\n\n`);
const result = spawnSync(pnpmBin, command.args, {
cwd: repoRoot,
shell: false,
stdio: "inherit"
});
return {
command: `pnpm ${command.args.join(" ")}`,
durationMs: Date.now() - startTime,
exitCode: result.status ?? 1,
label: command.label,
startedAt,
status: result.status === 0 ? "passed" : "failed"
};
}
const options = parseArgs(process.argv.slice(2));
if (options.list) {
process.stdout.write("Available harness suites:\n");
for (const suite of formatSuiteListing()) {
process.stdout.write(`- ${suite.name}: ${suite.description}\n`);
}
process.exit(0);
}
ensureKnownSuite(options.suite);
const selection =
options.suite === "changed"
? selectSuitesForChangedFiles(getChangedFiles(options))
: null;
const selectedSuites = selection?.suites ?? [options.suite];
const commands = getCommandsForSuites(selectedSuites);
const report = {
commands: [],
description:
options.suite === "changed"
? "Validate suites selected from the current git diff or working tree."
: suiteDefinitions[options.suite].description,
finishedAt: null,
startedAt: new Date().toISOString(),
status: options.dryRun ? "dry-run" : "passed",
suite: options.suite,
selectedSuites
};
if (selection) {
report.selection = selection;
}
for (const command of commands) {
if (options.dryRun) {
report.commands.push({
command: `pnpm ${command.args.join(" ")}`,
label: command.label,
status: "planned",
suite: command.suite
});
continue;
}
const result = runCommand(command);
report.commands.push(result);
if (result.status === "failed") {
report.status = "failed";
break;
}
}
report.finishedAt = new Date().toISOString();
const reportPath = writeHarnessArtifact(options.suite, report);
process.stdout.write(`\n[harness] Report written to ${path.relative(repoRoot, reportPath)}\n`);
if (report.status === "failed") {
process.exit(1);
}