openclaw/scripts/code-mode-model-matrix.ts
Peter Steinberger 7cbaa6b36f
refactor(scripts): deslop shared tooling flows (#162824)
Consolidate script lifecycle and helper ownership while preserving CLI, generated-output, cleanup, and guard contracts. Reject inherited object keys in model-matrix option admission with the existing unknown-argument diagnostic.

Validation: 90 full sibling suites; original-fails parser regression; byte-identical native outputs; changed checks; SDK surface and API comparison; both zero-cycle checks; independent isolated review.
2026-10-01 21:30:19 +00:00

1954 lines
68 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
import { execFile } from "node:child_process";
import { createHash } from "node:crypto";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import process from "node:process";
import { pathToFileURL } from "node:url";
import { promisify } from "node:util";
import {
buildScriptEvidenceSummary,
captureQaEvidenceRuntimeIdentity,
captureQaEvidenceSourceIdentity as readSourceIdentity,
createQaEvidenceInvocation,
QA_EVIDENCE_FILENAME,
QA_FRONTIER_PROVIDER_IDS,
validateQaEvidenceSummaryJson,
type QaEvidenceIdentity,
type QaEvidenceStatus,
type QaEvidenceSummaryJson,
} from "../extensions/qa-lab/test-api.js";
import type { CodeModeExecutorId } from "../src/agents/code-mode-executor-types.js";
import type { AgentExecEnvelope } from "../src/commands/agent-exec-result.ts";
import type { OpenClawConfig } from "../src/config/types.openclaw.js";
import { mergeDeep } from "../src/infra/deep-merge.js";
import { requireOptionArgument } from "./lib/arg-utils.mts";
import { summarizeGatewayMatrixOutcomes } from "./lib/code-mode-matrix-comparison.ts";
import {
GATEWAY_MATRIX_TASKS,
type GatewayMatrixTask,
} from "./lib/code-mode-matrix-gateway-fixtures.ts";
import type {
GatewayMatrixEvidence,
GatewayMatrixWorkload,
} from "./lib/code-mode-matrix-gateway.ts";
import {
MATRIX_PERFORMANCE_TASKS,
isMatrixPerformanceTask,
type MatrixPerformanceTask,
} from "./lib/code-mode-matrix-performance-fixtures.ts";
import {
classifyCodeModeMatrixProviderFailure,
matrixModelConfig,
matrixProviderEnv,
matrixProviderAuthSelection,
type MatrixProviderFailureCategory,
} from "./lib/code-mode-matrix-provider.ts";
import type { MatrixUsageAccounting } from "./lib/code-mode-matrix-usage.ts";
import { previewForDevToolLog, redactJsonValueForDevToolLog } from "./lib/dev-tooling-safety.ts";
import { groupBy } from "./lib/group-by.mts";
export { validateQaEvidenceSummaryJson };
const execFileAsync = promisify(execFile);
const SOURCE_PATH = "scripts/code-mode-model-matrix.ts";
const MATRIX_SCHEMA_VERSION = 1;
const DEFAULT_REPETITIONS = 3;
const DEFAULT_TIMEOUT_SECONDS = 180;
const MAX_REPETITIONS = 10;
const MAX_DIAGNOSTIC_CHARS = 8_000;
const STRICT_SOURCE_IDENTITY_OPTIONS = { gitTimeoutMs: 60_000 };
const DEFAULT_ADMISSION = {
concurrency: 2,
maxCells: 36,
maxTokens: 1_000_000,
maxKnownCostUsd: 25,
maxWallSeconds: 3_600,
};
export type CodeModeMatrixMode = "direct" | "auto" | "code";
const MATRIX_TASKS = [
"read",
"dependent-read-write",
"large-result-reduction",
"parallel-independent-reads",
"dependent-chain",
...GATEWAY_MATRIX_TASKS,
...MATRIX_PERFORMANCE_TASKS,
] as const;
export type CodeModeMatrixTask = (typeof MATRIX_TASKS)[number];
export type CodeModeMatrixOptions = {
allowFailures: boolean;
dryRun: boolean;
gatewayExecutor?: CodeModeExecutorId;
keepState: boolean;
models: string[];
modes: CodeModeMatrixMode[];
outputDir?: string;
repetitions: number;
repoRoot: string;
runtimeDir?: string;
baselineResults?: string;
tasks: CodeModeMatrixTask[];
thinking: string;
timeoutSeconds: number;
concurrency?: number;
maxCells?: number;
maxTokens?: number;
maxKnownCostUsd?: number;
maxWallSeconds?: number;
schedulePath?: string;
};
export type MatrixCell = {
id: string;
mode: CodeModeMatrixMode;
model: string;
repetition: number;
task: CodeModeMatrixTask;
};
type MatrixTaskFixture = {
expected: string;
prompt: string;
resultPath?: string;
};
export type MatrixRuntimeEntrypoint = {
args: string[];
cwd: string;
};
type CellFailureCategory =
| MatrixProviderFailureCategory
| "activation"
| "agent_error"
| "answer_mismatch"
| "effect_mismatch"
| "harness_error"
| "interview_mismatch"
| "model_mismatch"
| "timeout"
| "tool_execution";
export type CodeModeMatrixCellResult = {
accounting?: MatrixUsageAccounting;
assistantTurns?: number;
bridgeCalls?: AgentExecEnvelope["bridgeCalls"];
buildSha256: string;
codeModeEngaged: boolean | null;
costUsd?: number;
diagnostics?: string;
elapsedMs: number;
evidenceOccurrenceId?: string;
executor?: CodeModeExecutorId;
error?: AgentExecEnvelope["error"];
expected: string;
failureCategory: CellFailureCategory | null;
final: string;
gitSha: string;
id: string;
mode: CodeModeMatrixMode;
model: string;
observedModel: string | null;
observedProvider: string | null;
oracle: {
answer: boolean | null;
effect: boolean;
engagement: boolean;
identity: boolean;
toolExecution: boolean;
};
passed: boolean;
repetition: number;
sourceDirty: boolean;
sourcePatchSha256: string | null;
status: AgentExecEnvelope["status"];
task: CodeModeMatrixTask;
timestamp: string;
toolSummary?: AgentExecEnvelope["toolSummary"];
usage?: AgentExecEnvelope["usage"];
gateway?: GatewayMatrixEvidence;
workload?: GatewayMatrixWorkload;
};
export type RunCellParams = {
abortSignal?: AbortSignal;
buildSha256: string;
cell: MatrixCell;
executor?: CodeModeExecutorId;
gitSha: string;
keepState: boolean;
outputDir: string;
repoRoot: string;
runtime?: MatrixRuntimeEntrypoint;
sourceDirty: boolean;
sourcePatchSha256: string | null;
thinking: string;
timeoutSeconds: number;
};
type MatrixRunDependencies = {
buildCliArtifacts?: (repoRoot: string) => Promise<void>;
now?: () => Date;
readBuildSha256?: (repoRoot: string) => Promise<string>;
readGitSha?: (repoRoot: string) => Promise<string>;
readSourceIdentity?: (repoRoot: string) => Promise<SourceIdentity>;
runCell?: (params: RunCellParams) => Promise<CodeModeMatrixCellResult>;
};
type SourceIdentity = {
gitSha: string;
sourceDirty: boolean;
sourcePatchSha256: string | null;
};
function usage() {
return `Usage: pnpm qa:code-mode-models --model <provider/model> [options]
Runs repeated Code Mode acceptance cells through the normal embedded agent path.
Options:
--model <provider/model> Model reference; repeat for multiple models
--mode <mode> direct | auto | code; repeat to select modes
--executor <executor> node | quickjs for Gateway tasks only (default: node)
--task <task> ${MATRIX_TASKS.join(" | ")}; repeat to select tasks
(default: read, dependent-read-write)
--repetitions <n> Runs per model/mode/task cell (default: ${DEFAULT_REPETITIONS}, max: ${MAX_REPETITIONS})
--timeout <seconds> Per-run agent deadline (default: ${DEFAULT_TIMEOUT_SECONDS})
--thinking <level> Agent thinking level (default: low)
--concurrency <n> Concurrent root cells per wave, 1–3 (default: 2)
--schedule <path> JSON array of {model,task,repetition,firstMode} paired waves
--max-cells <n> Admit complete waves up to this count (default: 36)
--max-tokens <n> Stop new waves at observed token total (default: 1000000)
--max-known-cost-usd <n> Stop new waves at known cost (default: 25)
--max-wall-seconds <n> Stop new waves after elapsed time (default: 3600)
--output-dir <path> Repo-relative artifact directory
--runtime-dir <path> Use a clean, already-built checkout without rebuilding it
--baseline-results <path> Compare matching cells from an earlier results.jsonl
--keep-state Retain per-cell state and workspace directories
--allow-failures Exit zero after writing evidence even when cells fail
--dry-run Write the manifest without calling models
-h, --help Show this help
Provider credentials are read from the environment and are never written to artifacts.
Token, known-cost, and wall limits stop admission; already admitted waves settle.
Missing usage and prices remain unknown. These limits are not hard spending caps.
`;
}
function parseIntegerOption(raw: string, flag: string, max?: number): number {
if (!/^\d+$/u.test(raw)) {
throw new Error(`${flag} must be a positive integer`);
}
const value = Number(raw);
if (!Number.isSafeInteger(value) || value < 1 || (max !== undefined && value > max)) {
const suffix = max === undefined ? "" : ` from 1 to ${max}`;
throw new Error(`${flag} must be an integer${suffix}`);
}
return value;
}
function collectUnique<T extends string>(values: T[], value: T, flag: string): void {
if (values.includes(value)) {
throw new Error(`Duplicate ${flag} value: ${value}`);
}
values.push(value);
}
function parseMode(raw: string): CodeModeMatrixMode {
if (raw === "direct" || raw === "auto" || raw === "code") {
return raw;
}
throw new Error(`--mode must be one of direct, auto, code; got ${JSON.stringify(raw)}`);
}
function parseTask(raw: string): CodeModeMatrixTask {
const task = MATRIX_TASKS.find((candidate) => candidate === raw);
if (task) {
return task;
}
throw new Error(`--task must be one of ${MATRIX_TASKS.join(", ")}; got ${JSON.stringify(raw)}`);
}
function isGatewayTask(
task: CodeModeMatrixTask,
): task is GatewayMatrixTask | MatrixPerformanceTask {
return isMatrixPerformanceTask(task) || isGatewayContractTask(task);
}
function isGatewayContractTask(task: CodeModeMatrixTask): task is GatewayMatrixTask {
return GATEWAY_MATRIX_TASKS.some((candidate) => candidate === task);
}
export function parseCodeModeMatrixOptions(
argv: readonly string[],
cwd = process.cwd(),
): CodeModeMatrixOptions {
const models: string[] = [];
const modes: CodeModeMatrixMode[] = [];
const tasks: CodeModeMatrixTask[] = [];
const options: CodeModeMatrixOptions = {
allowFailures: false,
...DEFAULT_ADMISSION,
dryRun: false,
gatewayExecutor: "node",
keepState: false,
models,
modes,
outputDir: undefined,
repetitions: DEFAULT_REPETITIONS,
repoRoot: path.resolve(cwd),
tasks,
thinking: "low",
timeoutSeconds: DEFAULT_TIMEOUT_SECONDS,
};
const seen = new Set<string>();
const recordOnce = (flag: string) => {
if (seen.has(flag)) {
throw new Error(`${flag} was provided more than once`);
}
seen.add(flag);
};
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index] ?? "";
if (arg === "--model") {
const value = requireOptionArgument(argv, index, arg).trim();
if (!value.includes("/")) {
throw new Error(
`--model must use a provider/model reference; got ${JSON.stringify(value)}`,
);
}
collectUnique(models, value, arg);
index += 1;
continue;
}
const admissionKeys: Record<string, keyof typeof DEFAULT_ADMISSION> = {
"--concurrency": "concurrency",
"--max-cells": "maxCells",
"--max-tokens": "maxTokens",
"--max-known-cost-usd": "maxKnownCostUsd",
"--max-wall-seconds": "maxWallSeconds",
};
const admissionKey = Object.hasOwn(admissionKeys, arg) ? admissionKeys[arg] : undefined;
if (admissionKey) {
recordOnce(arg);
const raw = requireOptionArgument(argv, index, arg);
const value =
admissionKey === "maxKnownCostUsd"
? Number(raw)
: parseIntegerOption(raw, arg, admissionKey === "concurrency" ? 3 : undefined);
if (!Number.isFinite(value) || value <= 0) {
throw new Error(`${arg} must be a positive number`);
}
options[admissionKey] = value;
index += 1;
continue;
}
switch (arg) {
case "--schedule":
recordOnce(arg);
options.schedulePath = path.resolve(cwd, requireOptionArgument(argv, index, arg));
break;
case "--mode":
collectUnique(modes, parseMode(requireOptionArgument(argv, index, arg)), arg);
break;
case "--executor": {
recordOnce(arg);
const value = requireOptionArgument(argv, index, arg);
if (value !== "node" && value !== "quickjs") {
throw new Error(`--executor must be node or quickjs; got ${JSON.stringify(value)}`);
}
options.gatewayExecutor = value;
break;
}
case "--task":
collectUnique(tasks, parseTask(requireOptionArgument(argv, index, arg)), arg);
break;
case "--repetitions":
recordOnce(arg);
options.repetitions = parseIntegerOption(
requireOptionArgument(argv, index, arg),
arg,
MAX_REPETITIONS,
);
break;
case "--timeout":
recordOnce(arg);
options.timeoutSeconds = parseIntegerOption(requireOptionArgument(argv, index, arg), arg);
break;
case "--thinking":
recordOnce(arg);
options.thinking = requireOptionArgument(argv, index, arg).trim();
break;
case "--output-dir":
recordOnce(arg);
options.outputDir = requireOptionArgument(argv, index, arg);
break;
case "--runtime-dir":
case "--baseline-results": {
recordOnce(arg);
const value = path.resolve(cwd, requireOptionArgument(argv, index, arg));
if (arg === "--runtime-dir") {
options.runtimeDir = value;
} else {
options.baselineResults = value;
}
break;
}
case "--allow-failures":
recordOnce(arg);
options.allowFailures = true;
continue;
case "--keep-state":
recordOnce(arg);
options.keepState = true;
continue;
case "--dry-run":
recordOnce(arg);
options.dryRun = true;
continue;
case "--help":
case "-h":
throw Object.assign(new Error(usage()), { code: "HELP" });
default:
throw new Error(`Unknown argument: ${arg}`);
}
index += 1;
}
if (models.length === 0) {
throw new Error("At least one --model <provider/model> is required");
}
if (
seen.has("--executor") &&
(tasks.length === 0 || tasks.some((task) => !isGatewayTask(task)))
) {
throw new Error("--executor requires only Gateway-backed tasks.");
}
if (tasks.some(isGatewayContractTask)) {
if (modes.length !== 1 || modes[0] !== "code") {
throw new Error("Gateway interview tasks require --mode code.");
}
}
if (
tasks.some(isGatewayTask) &&
models.some(
(model) => !QA_FRONTIER_PROVIDER_IDS.some((provider) => model.startsWith(`${provider}/`)),
)
) {
throw new Error("Gateway tasks require explicit OpenAI, Anthropic, or Google models.");
}
if (
tasks.some(isMatrixPerformanceTask) &&
modes.length > 0 &&
(modes.length !== 2 || !modes.includes("direct") || !modes.includes("code"))
) {
throw new Error("Performance tasks require both explicit direct/code treatment arms.");
}
if (
options.baselineResults &&
(tasks.length === 0 || tasks.some((task) => !isGatewayTask(task)))
) {
throw new Error(
"--baseline-results requires Gateway interview tasks with fixed workload fingerprints.",
);
}
options.modes =
modes.length > 0
? modes
: tasks.some(isMatrixPerformanceTask)
? ["direct", "code"]
: ["direct", "auto", "code"];
options.tasks = tasks.length > 0 ? tasks : ["read", "dependent-read-write"];
return options;
}
function slug(value: string): string {
return value
.toLowerCase()
.replace(/[^a-z0-9]+/gu, "-")
.replace(/^-+|-+$/gu, "")
.slice(0, 80);
}
function defaultOutputDir(now: Date): string {
return path.join(
".artifacts",
"qa-e2e",
"code-mode-model-matrix",
now.toISOString().replaceAll(":", "-"),
);
}
export function resolveCodeModeMatrixOutputDir(
repoRoot: string,
configured: string | undefined,
now = new Date(),
): string {
const raw = configured?.trim() || defaultOutputDir(now);
if (path.isAbsolute(raw)) {
throw new Error("--output-dir must be repo-relative");
}
const resolvedRoot = path.resolve(repoRoot);
const resolved = path.resolve(resolvedRoot, raw);
if (resolved === resolvedRoot || !resolved.startsWith(`${resolvedRoot}${path.sep}`)) {
throw new Error("--output-dir must stay within the repository");
}
return resolved;
}
function pathsOverlap(left: string, right: string, caseInsensitive: boolean): boolean {
const normalize = (value: string) => {
const resolved = path.resolve(value);
return caseInsensitive ? resolved.toLowerCase() : resolved;
};
const resolvedLeft = normalize(left);
const resolvedRight = normalize(right);
return (
resolvedLeft === resolvedRight ||
resolvedLeft.startsWith(`${resolvedRight}${path.sep}`) ||
resolvedRight.startsWith(`${resolvedLeft}${path.sep}`)
);
}
async function filesystemUsesCaseInsensitivePaths(repoRoot: string): Promise<boolean> {
const canonicalRoot = await fs.realpath(repoRoot);
const rootName = path.basename(canonicalRoot);
const letterIndex = rootName.search(/[a-z]/iu);
if (letterIndex < 0) {
return process.platform === "win32";
}
const letter = rootName[letterIndex] ?? "";
const alternateLetter =
letter === letter.toLowerCase() ? letter.toUpperCase() : letter.toLowerCase();
const alternateRoot = path.join(
path.dirname(canonicalRoot),
`${rootName.slice(0, letterIndex)}${alternateLetter}${rootName.slice(letterIndex + 1)}`,
);
return await fs.realpath(alternateRoot).then(
(resolved) => resolved === canonicalRoot,
() => false,
);
}
async function canonicalizeExistingPathPrefix(value: string): Promise<string> {
let current = path.resolve(value);
const missingSegments: string[] = [];
for (;;) {
try {
return path.join(await fs.realpath(current), ...missingSegments.toReversed());
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
throw error;
}
const parent = path.dirname(current);
if (parent === current) {
throw error;
}
missingSegments.push(path.basename(current));
current = parent;
}
}
}
async function runtimeArtifactDirectories(repoRoot: string, outputDir: string): Promise<string[]> {
const packagesRoot = path.join(repoRoot, "packages");
const packageEntries = await fs
.readdir(packagesRoot, { withFileTypes: true })
.catch((error: unknown) => {
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
return [];
}
throw error;
});
const artifacts = [
path.join(repoRoot, "dist"),
...packageEntries
.filter((entry) => entry.isDirectory())
.map((entry) => path.join(packagesRoot, entry.name, "dist")),
];
const outputSegments = path
.relative(path.resolve(repoRoot), path.resolve(outputDir))
.split(path.sep);
const outputPackage = outputSegments[0]?.toLowerCase() === "packages" && outputSegments[1];
if (outputPackage) {
artifacts.push(path.join(packagesRoot, outputPackage, "dist"));
}
return artifacts;
}
async function assertOutputOutsideRuntimeArtifacts(
repoRoot: string,
outputDir: string,
): Promise<void> {
const caseInsensitive = await filesystemUsesCaseInsensitivePaths(repoRoot);
const canonicalOutput = await canonicalizeExistingPathPrefix(outputDir);
for (const artifactDir of await runtimeArtifactDirectories(repoRoot, outputDir)) {
const canonicalArtifact = await canonicalizeExistingPathPrefix(artifactDir);
if (pathsOverlap(canonicalOutput, canonicalArtifact, caseInsensitive)) {
throw new Error(
`--output-dir must not overlap runtime artifacts: ${path.relative(repoRoot, artifactDir)}`,
);
}
}
}
async function assertOutputOutsideGitMetadata(repoRoot: string, outputDir: string): Promise<void> {
const gitDirectories = [path.join(repoRoot, ".git")];
const discovered = await execFileAsync(
"git",
["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"],
{
cwd: repoRoot,
encoding: "utf8",
},
).catch(() => null);
if (discovered) {
gitDirectories.push(
...discovered.stdout
.split(/\r?\n/u)
.map((value) => value.trim())
.filter(Boolean),
);
}
const caseInsensitive = await filesystemUsesCaseInsensitivePaths(repoRoot);
const canonicalOutput = await canonicalizeExistingPathPrefix(outputDir);
for (const gitDirectory of new Set(gitDirectories)) {
const canonicalGitDirectory = await canonicalizeExistingPathPrefix(gitDirectory);
if (pathsOverlap(canonicalOutput, canonicalGitDirectory, caseInsensitive)) {
throw new Error("--output-dir must not overlap Git metadata");
}
}
}
export async function reserveCodeModeMatrixOutputDir(
repoRoot: string,
outputDir: string,
): Promise<void> {
const resolvedRoot = path.resolve(repoRoot);
const resolvedOutput = path.resolve(outputDir);
const relative = path.relative(resolvedRoot, resolvedOutput);
if (
!relative ||
relative === ".." ||
relative.startsWith(`..${path.sep}`) ||
path.isAbsolute(relative)
) {
throw new Error("--output-dir must stay within the repository");
}
let current = resolvedRoot;
for (const segment of relative.split(path.sep)) {
current = path.join(current, segment);
for (;;) {
try {
const stats = await fs.lstat(current);
if (stats.isSymbolicLink()) {
throw new Error(`--output-dir must not traverse symlinks: ${relative}`);
}
if (current === resolvedOutput) {
throw new Error(`--output-dir must not already exist: ${relative}`);
}
if (!stats.isDirectory()) {
throw new Error(
`--output-dir parent must be a directory: ${path.relative(repoRoot, current)}`,
);
}
break;
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
throw error;
}
try {
await fs.mkdir(current);
break;
} catch (mkdirError) {
if ((mkdirError as NodeJS.ErrnoException).code !== "EEXIST") {
throw mkdirError;
}
// A concurrent creator won the race. Inspect its path before proceeding.
}
}
}
}
}
type MatrixScheduleEntry = {
model: string;
task: CodeModeMatrixTask;
repetition: number;
firstMode: "direct" | "code";
};
async function buildCellWaves(options: CodeModeMatrixOptions): Promise<MatrixCell[][]> {
let schedule: MatrixScheduleEntry[];
if (options.schedulePath) {
const value: unknown = JSON.parse(await fs.readFile(options.schedulePath, "utf8"));
if (!Array.isArray(value) || value.length === 0) {
throw new Error("--schedule must contain a nonempty JSON array of paired waves.");
}
if (
options.modes.length !== 2 ||
!options.modes.includes("direct") ||
!options.modes.includes("code")
) {
throw new Error("--schedule requires exactly --mode direct and --mode code.");
}
schedule = value.map((row: unknown): MatrixScheduleEntry => {
if (
!row ||
typeof row !== "object" ||
!("model" in row) ||
!("task" in row) ||
!("repetition" in row) ||
typeof row.model !== "string" ||
!options.models.includes(row.model) ||
typeof row.task !== "string" ||
!options.tasks.some((task) => task === row.task) ||
typeof row.repetition !== "number" ||
!Number.isInteger(row.repetition) ||
row.repetition < 1 ||
row.repetition > options.repetitions ||
!("firstMode" in row) ||
(row.firstMode !== "direct" && row.firstMode !== "code")
) {
throw new Error(
"Each schedule wave must select an admitted model, task, repetition, and direct/code firstMode.",
);
}
return {
model: row.model,
task: parseTask(row.task),
repetition: row.repetition,
firstMode: row.firstMode,
};
});
} else {
schedule = options.models.flatMap((model, modelIndex) =>
options.tasks.flatMap((task, taskIndex) =>
Array.from({ length: options.repetitions }, (_, index) => ({
model,
task,
repetition: index + 1,
firstMode:
(modelIndex + taskIndex + index) % 2 === 0 ? ("direct" as const) : ("code" as const),
})),
),
);
}
const seen = new Set<string>();
return schedule.map(({ model, task, repetition, firstMode }) => {
const key = `${model}\0${task}\0${repetition}`;
if (seen.has(key)) {
throw new Error(`Duplicate scheduled wave: ${model} ${task} ${repetition}`);
}
seen.add(key);
const modes: CodeModeMatrixMode[] =
options.modes.includes("direct") && options.modes.includes("code")
? [
firstMode,
firstMode === "direct" ? ("code" as const) : ("direct" as const),
...options.modes.filter((mode) => mode === "auto"),
]
: options.modes;
return modes.map((mode) => ({
id: `${modelCellPrefix(model)}-${mode}-${task}-${repetition}`,
mode,
model,
repetition,
task,
}));
});
}
export function modelCellPrefix(model: string): string {
const modelHash = createHash("sha256").update(model).digest("hex").slice(0, 10);
return `${slug(model)}-${modelHash}`;
}
function verificationCode(cell: MatrixCell): string {
return `CM-${createHash("sha256").update(cell.id).digest("hex").slice(0, 12).toUpperCase()}`;
}
function taskFixture(cell: MatrixCell): MatrixTaskFixture & { files: Record<string, string> } {
// Every model/mode gets identical inputs for a task repetition.
const code = verificationCode({ ...cell, id: `${cell.task}-${cell.repetition}` });
const facts = { "facts.txt": `project=openclaw\nverification_code=${code}\n` };
if (cell.task === "read") {
return {
expected: code,
files: facts,
prompt:
"Read facts.txt using tools. Reply with only the verification_code value, with no prose or formatting.",
};
}
if (cell.task === "dependent-read-write") {
return {
expected: code,
files: facts,
prompt:
"Read facts.txt using tools. Write only its verification_code value to result.txt, then read result.txt and reply with only that value. Do not guess or skip verification.",
resultPath: "result.txt",
};
}
const finish =
" Write only the answer to result.txt, read it back, and reply with only that answer, with no prose or formatting. Use file tools, not shell commands.";
if (cell.task === "large-result-reduction") {
const rows = Array.from({ length: 512 }, (_, index) => ({
id: index + 1,
region: index % 3 === 0 ? "west" : "east",
units: (index % 7) + 1,
unitPriceCents: 100 + (index % 23),
note: "irrelevant-detail-".repeat(4),
}));
const selected = rows.filter((row) => row.region === "east" && row.units >= 4);
return {
expected: `${code}:${selected.length}:${selected.reduce((total, row) => total + row.units * row.unitPriceCents, 0)}`,
files: {
"rules.json": JSON.stringify({ region: "east", minUnits: 4, verificationCode: code }),
"orders.jsonl": rows.map((row) => JSON.stringify(row)).join("\n") + "\n",
},
prompt:
"Read rules.json and all of orders.jsonl using tools, following read continuations when truncated. Select orders matching rules.region with units >= rules.minUnits. Compute their count and sum of units * unitPriceCents. The answer is verificationCode:count:sum, using rules.verificationCode and integer decimal numbers. Do not emit the raw orders." +
finish,
resultPath: "result.txt",
};
}
if (cell.task === "parallel-independent-reads") {
const names = ["north", "south", "west"];
const values = names.map((name, index) => `${code}-${index + 1}-${name}`);
return {
expected: values.join("|"),
files: Object.fromEntries(
names.map((name, index) => [`${name}.json`, JSON.stringify({ value: values[index] })]),
),
prompt:
"Read north.json, south.json, and west.json using independent tool calls. Run the reads in parallel when the tool surface supports it; in Code Mode use Promise.all. Join their value fields in north, south, west order with | (not completion order)." +
finish,
resultPath: "result.txt",
};
}
const route = `route-${code.slice(3, 9)}.json`;
const payload = `payload-${code.slice(9)}.json`;
return {
expected: code,
files: {
"start.json": JSON.stringify({ next: route }),
[route]: JSON.stringify({ next: payload }),
[payload]: JSON.stringify({ value: code }),
},
prompt:
"Read start.json using tools. Its next field names the next file; read that file and follow its next field to the payload file. The answer is the payload's value. Await each read before choosing the next path; do not list the directory or guess paths." +
finish,
resultPath: "result.txt",
};
}
async function prepareTaskFixture(workspace: string, cell: MatrixCell): Promise<MatrixTaskFixture> {
const { files, ...fixture } = taskFixture(cell);
await fs.mkdir(workspace, { recursive: true });
for (const [name, content] of Object.entries(files)) {
await fs.writeFile(path.join(workspace, name), content, "utf8");
}
if (fixture.resultPath) {
fixture.resultPath = path.join(workspace, fixture.resultPath);
await fs.rm(fixture.resultPath, { force: true });
}
return fixture;
}
async function hashDirectory(root: string): Promise<string> {
const hash = createHash("sha256");
const visit = async (directory: string): Promise<void> => {
const entries = (await fs.readdir(directory, { withFileTypes: true })).toSorted((a, b) =>
a.name < b.name ? -1 : a.name > b.name ? 1 : 0,
);
for (const entry of entries) {
const filePath = path.join(directory, entry.name);
const relativePath = path.relative(root, filePath);
hash.update(`\0${relativePath}\0`);
if (entry.isDirectory()) {
await visit(filePath);
} else if (entry.isSymbolicLink()) {
hash.update(await fs.readlink(filePath));
} else if (entry.isFile()) {
hash.update(await fs.readFile(filePath));
}
}
};
await visit(root);
return hash.digest("hex");
}
async function hashRuntimeArtifacts(repoRoot: string): Promise<string> {
const artifacts = [{ label: "dist", root: path.join(repoRoot, "dist") }];
const packagesRoot = path.join(repoRoot, "packages");
const packageEntries = await fs.readdir(packagesRoot, { withFileTypes: true });
for (const entry of packageEntries.toSorted((a, b) =>
a.name < b.name ? -1 : a.name > b.name ? 1 : 0,
)) {
if (!entry.isDirectory()) {
continue;
}
const packageDist = path.join(packagesRoot, entry.name, "dist");
const stat = await fs.stat(packageDist).catch(() => null);
if (stat?.isDirectory()) {
artifacts.push({ label: `packages/${entry.name}/dist`, root: packageDist });
}
}
const hash = createHash("sha256");
for (const artifact of artifacts) {
hash.update(`\0${artifact.label}\0${await hashDirectory(artifact.root)}`);
}
return hash.digest("hex");
}
async function buildMatrixCliArtifacts(repoRoot: string): Promise<void> {
for (const args of [
["--import", "tsx", "scripts/bundled-plugin-assets.mts", "--phase", "build"],
["--import", "tsx", "scripts/tsdown-build.mts", "--no-clean"],
["scripts/runtime-postbuild.mjs"],
]) {
const { stderr, stdout } = await execFileAsync(process.execPath, args, {
cwd: repoRoot,
encoding: "utf8",
env: {
...process.env,
OPENCLAW_BUILD_ALL_NO_PNPM: "1",
OPENCLAW_RUN_NODE_SKIP_DTS_BUILD: "1",
},
maxBuffer: 8 * 1024 * 1024,
timeout: 10 * 60 * 1_000,
});
const output = `${stdout}\n${stderr}`.trim();
if (output) {
console.log(output);
}
}
}
function expectedEngagement(mode: CodeModeMatrixMode, engaged: boolean | undefined): boolean {
if (mode === "auto") {
return typeof engaged === "boolean";
}
return engaged === (mode === "code");
}
export function classifyCodeModeMatrixCell(params: {
diagnostics: string;
effectPassed: boolean;
envelope: Readonly<AgentExecEnvelope>;
expected: string;
mode: CodeModeMatrixMode;
model: string;
stdoutContractValid?: boolean;
task: CodeModeMatrixTask;
}): {
failureCategory: CellFailureCategory | null;
oracle: CodeModeMatrixCellResult["oracle"];
passed: boolean;
} {
const engagement = expectedEngagement(params.mode, params.envelope.codeModeEngaged);
const answer = params.envelope.final.trim() === params.expected;
const effect = params.effectPassed;
const separator = params.model.indexOf("/");
const requestedProvider = params.model.slice(0, separator);
const requestedModel = params.model.slice(separator + 1);
const identity =
params.envelope.provider === requestedProvider && params.envelope.model === requestedModel;
const outerToolExecution = (params.envelope.toolSummary?.calls ?? 0) > 0;
// Direct and auto evaluate the model-visible outer tool surface. Forced Code
// Mode additionally proves that the exec cell reached a nested catalog tool.
const toolExecution =
outerToolExecution && (params.mode !== "code" || (params.envelope.bridgeCalls?.call ?? 0) > 0);
const oracle = { answer, effect, engagement, identity, toolExecution };
let failureCategory: CellFailureCategory | null = null;
if (params.stdoutContractValid === false) {
failureCategory = "harness_error";
} else if (params.envelope.status === "timeout") {
failureCategory = "timeout";
} else if (!params.envelope.ok) {
failureCategory =
classifyCodeModeMatrixProviderFailure(
`${params.envelope.error?.message ?? ""}\n${params.diagnostics}`,
) ?? "agent_error";
} else if (!identity) {
failureCategory = "model_mismatch";
} else if (!engagement) {
failureCategory = "activation";
} else if (!toolExecution) {
failureCategory = "tool_execution";
} else if (!effect) {
failureCategory = "effect_mismatch";
} else if (!answer) {
failureCategory = "answer_mismatch";
}
return { failureCategory, oracle, passed: failureCategory === null };
}
function parseAgentExecOutput(stdout: string): {
envelope: AgentExecEnvelope;
trailing: string;
} {
const value = stdout.trimStart();
if (!value) {
throw new Error("agent exec produced no JSON envelope");
}
if (value[0] !== "{") {
throw new Error("agent exec stdout did not begin with a JSON envelope");
}
let depth = 0;
let escaped = false;
let inString = false;
for (let index = 0; index < value.length; index += 1) {
const character = value[index];
if (inString) {
if (escaped) {
escaped = false;
} else if (character === "\\") {
escaped = true;
} else if (character === '"') {
inString = false;
}
continue;
}
if (character === '"') {
inString = true;
} else if (character === "{") {
depth += 1;
} else if (character === "}") {
depth -= 1;
if (depth === 0) {
const envelope = JSON.parse(value.slice(0, index + 1)) as AgentExecEnvelope;
return { envelope, trailing: value.slice(index + 1).trim() };
}
}
}
throw new Error("agent exec produced an incomplete JSON envelope");
}
async function pathIsDirectory(value: string): Promise<boolean> {
return await fs
.stat(value)
.then((stats) => stats.isDirectory())
.catch(() => false);
}
async function cloneTreeWithHardlinks(source: string, destination: string): Promise<void> {
await fs.mkdir(destination, { recursive: true });
for (const entry of await fs.readdir(source, { withFileTypes: true })) {
const sourcePath = path.join(source, entry.name);
const destinationPath = path.join(destination, entry.name);
if (entry.isDirectory()) {
await cloneTreeWithHardlinks(sourcePath, destinationPath);
} else if (entry.isSymbolicLink()) {
await fs.symlink(await fs.readlink(sourcePath), destinationPath);
} else {
try {
await fs.link(sourcePath, destinationPath);
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "EXDEV") {
throw error;
}
await fs.copyFile(sourcePath, destinationPath);
}
}
}
}
async function prepareRuntimeEntrypoint(
repoRoot: string,
runtimeRoot: string,
): Promise<MatrixRuntimeEntrypoint> {
const entrypoint = path.join(repoRoot, "dist", "entry.js");
try {
await fs.access(entrypoint);
} catch (error) {
throw new Error("dist/entry.js is missing; run pnpm build before the matrix", { cause: error });
}
const nodeModules = path.join(repoRoot, "node_modules");
const physicalNodeModules = await fs.realpath(nodeModules);
if (physicalNodeModules === path.resolve(nodeModules)) {
return { args: [entrypoint], cwd: repoRoot };
}
const overlayDist = path.join(runtimeRoot, "dist");
const overlayNodeModules = path.join(runtimeRoot, "node_modules");
await cloneTreeWithHardlinks(path.join(repoRoot, "dist"), overlayDist);
await fs.mkdir(overlayNodeModules);
await fs.copyFile(path.join(repoRoot, "package.json"), path.join(runtimeRoot, "package.json"));
const entries = await fs.readdir(physicalNodeModules, { withFileTypes: true });
for (const entry of entries) {
if (entry.name.startsWith(".") || entry.name === "@openclaw") {
continue;
}
await fs.symlink(
path.join(physicalNodeModules, entry.name),
path.join(overlayNodeModules, entry.name),
process.platform === "win32" ? "junction" : "dir",
);
}
const overlayOpenClaw = path.join(overlayNodeModules, "@openclaw");
const physicalOpenClaw = path.join(physicalNodeModules, "@openclaw");
await fs.mkdir(overlayOpenClaw);
for (const entry of await fs.readdir(physicalOpenClaw, { withFileTypes: true })) {
const worktreePackage = path.join(repoRoot, "packages", entry.name);
const target = (await pathIsDirectory(path.join(worktreePackage, "dist")))
? worktreePackage
: path.join(physicalOpenClaw, entry.name);
await fs.symlink(
target,
path.join(overlayOpenClaw, entry.name),
process.platform === "win32" ? "junction" : "dir",
);
}
return {
args: [path.join(runtimeRoot, "dist", "entry.js")],
cwd: runtimeRoot,
};
}
export function buildCodeModeMatrixAgentEnv(
model: string,
runtimeCwd: string,
baseEnv: NodeJS.ProcessEnv = process.env,
): NodeJS.ProcessEnv {
const provider = model.slice(0, model.indexOf("/"));
const config: OpenClawConfig = {
plugins: { allow: [provider], entries: { [provider]: { enabled: true } } },
};
return {
PATH: baseEnv.PATH,
SystemRoot: baseEnv.SystemRoot,
...matrixProviderEnv(model, config, baseEnv),
NODE_DISABLE_COMPILE_CACHE: "1",
OPENCLAW_BUNDLED_PLUGINS_DIR: path.join(runtimeCwd, "dist", "extensions"),
};
}
async function executeAgentExec(params: {
fixture: MatrixTaskFixture;
matrix: RunCellParams;
stateDir: string;
workspace: string;
}): Promise<{
diagnostics: string;
envelope: AgentExecEnvelope;
stdoutContractValid: boolean;
}> {
const runtime = params.matrix.runtime;
if (!runtime) {
throw new Error("matrix runtime entrypoint was not prepared");
}
const provider = params.matrix.cell.model.split("/")[0]!;
const home = path.join(params.stateDir, "home");
const tmp = path.join(params.stateDir, "tmp");
await Promise.all([fs.mkdir(home, { recursive: true }), fs.mkdir(tmp, { recursive: true })]);
const config = mergeDeep(
{
agents: { entries: { main: {} } },
plugins: { allow: [provider], entries: { [provider]: { enabled: true } } },
tools: { codeMode: { executor: "node" } },
},
{ agents: matrixModelConfig(params.matrix.cell.model, params.matrix.thinking) },
);
const configPath = path.join(params.stateDir, "matrix-config.json");
await fs.writeFile(configPath, JSON.stringify(config), { mode: 0o600 });
const args = [
...runtime.args,
"agent",
"exec",
params.fixture.prompt,
"--cwd",
params.workspace,
"--state-dir",
params.stateDir,
"--model",
params.matrix.cell.model,
"--code-mode",
params.matrix.cell.mode,
"--config",
configPath,
"--thinking",
params.matrix.thinking,
"--timeout",
String(params.matrix.timeoutSeconds),
"--json",
];
let output: { stdout: string; stderr: string };
try {
const env: NodeJS.ProcessEnv = {
...buildCodeModeMatrixAgentEnv(params.matrix.cell.model, runtime.cwd),
HOME: home,
USERPROFILE: home,
OPENCLAW_HOME: home,
OPENCLAW_STATE_DIR: params.stateDir,
TMPDIR: tmp,
TEMP: tmp,
TMP: tmp,
};
output = await execFileAsync(process.execPath, args, {
cwd: runtime.cwd,
encoding: "utf8",
env,
maxBuffer: 4 * 1024 * 1024,
timeout: (params.matrix.timeoutSeconds + 30) * 1_000,
signal: params.matrix.abortSignal,
});
} catch (error) {
const commandError = error as Error & {
code?: string;
killed?: boolean;
stderr?: string;
stdout?: string;
};
if (commandError.killed || commandError.code === "ETIMEDOUT") {
return {
diagnostics: previewForDevToolLog(commandError.stderr ?? commandError.message, 2_000),
envelope: {
ok: false,
status: "timeout",
final: "",
payloads: [],
model: null,
provider: null,
sessionId: "",
error: { kind: "timeout", message: "agent exec process deadline elapsed" },
},
stdoutContractValid: true,
};
}
if (commandError.stdout?.trim()) {
output = { stdout: commandError.stdout, stderr: commandError.stderr ?? "" };
} else {
throw error;
}
}
const parsed = parseAgentExecOutput(output.stdout);
return {
diagnostics:
`${output.stderr}\n${parsed.trailing ? `unexpected stdout after JSON: ${parsed.trailing}` : ""}`
.trim()
.slice(-MAX_DIAGNOSTIC_CHARS),
envelope: parsed.envelope,
stdoutContractValid: parsed.trailing.length === 0,
};
}
async function runMatrixCell(params: RunCellParams): Promise<CodeModeMatrixCellResult> {
if (isGatewayTask(params.cell.task)) {
const { runGatewayMatrixCell } = await import("./lib/code-mode-matrix-gateway.ts");
return await runGatewayMatrixCell({
...params,
cell: { ...params.cell, task: params.cell.task },
});
}
const retainedRoot = path.join(params.outputDir, "state", params.cell.id);
if (params.keepState) {
await fs.rm(retainedRoot, { force: true, recursive: true });
}
const root = params.keepState
? retainedRoot
: await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-code-mode-matrix-"));
const stateDir = path.join(root, "state");
const workspace = path.join(root, "workspace");
await fs.mkdir(stateDir, { recursive: true });
const fixture = await prepareTaskFixture(workspace, params.cell);
const startedAt = Date.now();
try {
const command = await executeAgentExec({
fixture,
matrix: params,
stateDir,
workspace,
});
const effectPassed = fixture.resultPath
? (await fs.readFile(fixture.resultPath, "utf8").catch(() => "")).trim() === fixture.expected
: true;
const diagnosticText = command.diagnostics;
const classification = classifyCodeModeMatrixCell({
diagnostics: diagnosticText,
effectPassed,
envelope: command.envelope,
expected: fixture.expected,
mode: params.cell.mode,
model: params.cell.model,
stdoutContractValid: command.stdoutContractValid,
task: params.cell.task,
});
return {
...(command.envelope.assistantTurns !== undefined
? { assistantTurns: command.envelope.assistantTurns }
: {}),
...(command.envelope.bridgeCalls ? { bridgeCalls: command.envelope.bridgeCalls } : {}),
buildSha256: params.buildSha256,
codeModeEngaged: command.envelope.codeModeEngaged ?? null,
...(command.envelope.costUsd !== undefined ? { costUsd: command.envelope.costUsd } : {}),
...(diagnosticText ? { diagnostics: diagnosticText } : {}),
elapsedMs: Date.now() - startedAt,
...(command.envelope.error ? { error: command.envelope.error } : {}),
expected: fixture.expected,
failureCategory: classification.failureCategory,
final: command.envelope.final,
gitSha: params.gitSha,
id: params.cell.id,
mode: params.cell.mode,
model: params.cell.model,
observedModel: command.envelope.model,
observedProvider: command.envelope.provider,
oracle: classification.oracle,
passed: classification.passed,
repetition: params.cell.repetition,
sourceDirty: params.sourceDirty,
sourcePatchSha256: params.sourcePatchSha256,
status: command.envelope.status,
task: params.cell.task,
timestamp: new Date().toISOString(),
...(command.envelope.toolSummary ? { toolSummary: command.envelope.toolSummary } : {}),
...(command.envelope.usage ? { usage: command.envelope.usage } : {}),
};
} finally {
if (!params.keepState) {
await fs.rm(root, { force: true, recursive: true });
}
}
}
function harnessFailureResult(
cell: MatrixCell,
provenance: Pick<RunCellParams, "buildSha256" | "gitSha" | "sourceDirty" | "sourcePatchSha256">,
elapsedMs: number,
error: unknown,
): CodeModeMatrixCellResult {
const message = previewForDevToolLog(
error instanceof Error ? error.message : String(error),
2_000,
);
return {
buildSha256: provenance.buildSha256,
codeModeEngaged: null,
diagnostics: message,
elapsedMs,
error: { kind: "harness_error", message },
expected: isGatewayTask(cell.task) ? "Gateway task result" : taskFixture(cell).expected,
failureCategory: classifyCodeModeMatrixProviderFailure(message) ?? "harness_error",
final: "",
gitSha: provenance.gitSha,
id: cell.id,
mode: cell.mode,
model: cell.model,
observedModel: null,
observedProvider: null,
oracle: {
answer: isMatrixPerformanceTask(cell.task) ? null : false,
effect: false,
engagement: false,
identity: false,
toolExecution: false,
},
passed: false,
repetition: cell.repetition,
sourceDirty: provenance.sourceDirty,
sourcePatchSha256: provenance.sourcePatchSha256,
status: "error",
task: cell.task,
timestamp: new Date().toISOString(),
};
}
function summarizeMetric(values: (number | undefined)[]) {
const samples = values
.filter((value): value is number => value !== undefined)
.toSorted((a, b) => a - b);
return {
samples: samples.length,
total: samples.length > 0 ? samples.reduce((total, value) => total + value, 0) : null,
p50: samples[Math.floor(samples.length / 2)] ?? null,
};
}
function summarizeResults(results: CodeModeMatrixCellResult[]) {
const groups = groupBy(results, (result) => `${result.model}\0${result.mode}\0${result.task}`);
return [...groups.entries()].map(([key, group]) => {
const [model, mode, task] = key.split("\0");
const passed = group.filter((result) => result.passed);
const failures: Record<string, number> = {};
for (const result of group) {
if (!result.passed) {
const category = result.failureCategory ?? "unknown";
failures[category] = (failures[category] ?? 0) + 1;
}
}
const summary = {
codeModeEngaged: group.filter((result) => result.codeModeEngaged === true).length,
failed: group.length - passed.length,
failures,
firstPassPassed: passed.some((result) => result.repetition === 1),
mode,
model,
eventualPassed: passed.length > 0,
p50WallMs: summarizeMetric(group.map((result) => result.elapsedMs)).p50 ?? 0,
metrics: {
assistantTurns: summarizeMetric(group.map((result) => result.assistantTurns)),
outerToolCalls: summarizeMetric(
group.map((result) => result.toolSummary?.calls ?? result.gateway?.outerCalls),
),
bridgeSearchCalls: summarizeMetric(group.map((result) => result.bridgeCalls?.search)),
bridgeDescribeCalls: summarizeMetric(group.map((result) => result.bridgeCalls?.describe)),
bridgeToolCalls: summarizeMetric(group.map((result) => result.bridgeCalls?.call)),
costUsd: summarizeMetric(group.map((result) => result.costUsd)),
gatewayUpstreamCalls: summarizeMetric(group.map((result) => result.gateway?.upstreamCalls)),
gatewayTaskElapsedMs: summarizeMetric(group.map((result) => result.gateway?.taskElapsedMs)),
inputTokens: summarizeMetric(group.map((result) => result.usage?.input)),
outputTokens: summarizeMetric(group.map((result) => result.usage?.output)),
},
passRate: passed.length / group.length,
passed: passed.length,
task,
total: group.length,
};
return group.some((result) => result.workload)
? Object.assign(summary, { gatewayOutcomes: summarizeGatewayMatrixOutcomes(group) })
: summary;
});
}
async function writeJson(filePath: string, value: unknown): Promise<void> {
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(
filePath,
`${JSON.stringify(redactJsonValueForDevToolLog(value), null, 2)}\n`,
"utf8",
);
}
function evidenceStatus(result: CodeModeMatrixCellResult): QaEvidenceStatus {
if (result.passed) {
return "pass";
}
if (result.failureCategory === "provider_auth" || result.failureCategory === "provider_billing") {
return "blocked";
}
return "fail";
}
function observedModelRef(result: CodeModeMatrixCellResult): string {
if (result.observedProvider && result.observedModel) {
return `${result.observedProvider}/${result.observedModel}`;
}
return result.model;
}
function buildCodeModeMatrixEvidence(params: {
generatedAt: string;
repoRoot: string;
results: readonly CodeModeMatrixCellResult[];
}): QaEvidenceSummaryJson {
const evidenceOptions = {
artifactPaths: [
{ kind: "manifest", path: "manifest.json" },
{ kind: "summary", path: "summary.json" },
{ kind: "results", path: "results.jsonl" },
],
evidenceMode: "full" as const,
providerMode: "live-frontier" as const,
repoRoot: params.repoRoot,
runner: "code-mode-model-matrix",
};
const entries = params.results.flatMap((result) => {
const summary = buildScriptEvidenceSummary({
...evidenceOptions,
generatedAt: result.timestamp,
packageSource: { kind: "source-checkout", sha: result.gitSha },
primaryModel: observedModelRef(result),
targets: [
{
id: result.id,
title: `${result.model} ${result.mode} ${result.task} repetition ${result.repetition}`,
sourcePath: SOURCE_PATH,
},
],
results: [
{
id: result.id,
status: evidenceStatus(result),
durationMs: Math.max(1, result.elapsedMs),
failureMessage: result.failureCategory ?? undefined,
},
],
});
const entry = summary.entries[0];
if (!entry) {
return [];
}
if (entry.result.failure && result.failureCategory) {
entry.result.failure.class = result.failureCategory;
}
return [entry];
});
const base = buildScriptEvidenceSummary({
...evidenceOptions,
generatedAt: params.generatedAt,
packageSource: { kind: "source-checkout" },
primaryModel: "unknown/unknown",
targets: [],
results: [],
});
return validateQaEvidenceSummaryJson({
...base,
entries,
});
}
export async function runCodeModeModelMatrix(
options: CodeModeMatrixOptions,
deps: MatrixRunDependencies = {},
): Promise<{ exitCode: number; outputDir: string; summary: unknown }> {
const now = deps.now?.() ?? new Date();
const outputDir = resolveCodeModeMatrixOutputDir(options.repoRoot, options.outputDir, now);
const runtimeRepoRoot = options.runtimeDir ?? options.repoRoot;
const sourceIdentity = deps.readSourceIdentity
? await deps.readSourceIdentity(runtimeRepoRoot)
: deps.readGitSha
? {
gitSha: await deps.readGitSha(runtimeRepoRoot),
sourceDirty: false,
sourcePatchSha256: null,
}
: await readSourceIdentity(runtimeRepoRoot, STRICT_SOURCE_IDENTITY_OPTIONS);
if (options.runtimeDir && sourceIdentity.sourceDirty) {
throw new Error("--runtime-dir must identify a clean committed checkout.");
}
const waves = await buildCellWaves(options);
const cells = waves.flat();
const admission = {
concurrency: options.concurrency ?? DEFAULT_ADMISSION.concurrency,
maxCells: options.maxCells ?? DEFAULT_ADMISSION.maxCells,
maxTokens: options.maxTokens ?? DEFAULT_ADMISSION.maxTokens,
maxKnownCostUsd: options.maxKnownCostUsd ?? DEFAULT_ADMISSION.maxKnownCostUsd,
maxWallSeconds: options.maxWallSeconds ?? DEFAULT_ADMISSION.maxWallSeconds,
};
if (
!Number.isInteger(admission.concurrency) ||
admission.concurrency < 1 ||
admission.concurrency > 3
) {
throw new Error("concurrency must be an integer from 1 to 3");
}
await assertOutputOutsideGitMetadata(options.repoRoot, outputDir);
if (!options.dryRun && !options.runtimeDir) {
await (deps.buildCliArtifacts ?? buildMatrixCliArtifacts)(options.repoRoot);
}
if (!options.dryRun && options.runtimeDir) {
// Current source can be clean after reverting edits that produced the retained artifacts.
for (const stamp of [".buildstamp", ".runtime-postbuildstamp"]) {
const value: unknown = JSON.parse(
await fs.readFile(path.join(runtimeRepoRoot, "dist", stamp), "utf8"),
);
if (
!value ||
typeof value !== "object" ||
!("head" in value) ||
value.head !== sourceIdentity.gitSha ||
!("inputsClean" in value) ||
value.inputsClean !== true
) {
throw new Error(
`Frozen runtime ${stamp} must match its clean committed source and record clean build inputs. Choose a revision with provenance-capable stamp writers and run pnpm build; rebuilding older source without those writers cannot satisfy this check.`,
);
}
}
}
// Build first so its output set is complete, then reserve evidence storage
// before hashing. Dry runs also write evidence, so every run needs isolation.
await assertOutputOutsideRuntimeArtifacts(options.repoRoot, outputDir);
if (options.runtimeDir) {
await assertOutputOutsideRuntimeArtifacts(runtimeRepoRoot, outputDir);
}
await reserveCodeModeMatrixOutputDir(options.repoRoot, outputDir);
const buildSha256 = options.dryRun
? null
: await (deps.readBuildSha256 ?? hashRuntimeArtifacts)(runtimeRepoRoot);
const launch: QaEvidenceIdentity = {
source: {
ref: sourceIdentity.gitSha,
integrity: `git:${sourceIdentity.gitSha}${sourceIdentity.sourcePatchSha256 ? `+sha256:${sourceIdentity.sourcePatchSha256}` : ""}`,
},
runtime: captureQaEvidenceRuntimeIdentity(),
package: null,
protocol: null,
accountRef: null,
proofClass: null,
};
const invocation = createQaEvidenceInvocation({
scenarios: cells.map((cell) => ({ id: cell.id, execution: { kind: "script" } })),
channel: null,
launch,
});
const writeEvidence = async (generatedAt: string) =>
await writeJson(
path.join(outputDir, QA_EVIDENCE_FILENAME),
invocation.snapshot({ generatedAt, evidenceMode: "full" }),
);
const manifest = {
schemaVersion: MATRIX_SCHEMA_VERSION,
generatedAt: now.toISOString(),
source: SOURCE_PATH,
...(options.runtimeDir
? { harness: await readSourceIdentity(options.repoRoot, STRICT_SOURCE_IDENTITY_OPTIONS) }
: {}),
...(options.runtimeDir ? { runtimeDir: runtimeRepoRoot } : {}),
...sourceIdentity,
buildSha256,
models: options.models,
authentication: options.models.map((model) => ({
model,
...matrixProviderAuthSelection(model),
})),
modes: options.modes,
...(options.tasks.some(isGatewayTask)
? { gatewayExecutor: options.gatewayExecutor ?? "node" }
: {}),
tasks: options.tasks,
repetitions: options.repetitions,
timeoutSeconds: options.timeoutSeconds,
thinking: options.thinking,
keepState: options.keepState,
cells: cells.map((cell) => cell.id),
waves: waves.map((wave) => wave.map((cell) => cell.id)),
admission,
admissionContract:
"Whole waves are admitted. Token, known-cost, and wall limits are soft; already admitted cells settle. Missing usage and price evidence are not zero.",
};
await writeJson(path.join(outputDir, "manifest.json"), manifest);
// Persist the complete schedule before any cell starts. An interrupted or dry
// run retains null selections instead of inventing successful observations.
await writeEvidence(now.toISOString());
if (options.dryRun) {
const summary = { status: "dry-run", total: cells.length };
await writeJson(path.join(outputDir, "summary.json"), summary);
return { exitCode: 0, outputDir, summary };
}
const runtimeRoot = deps.runCell
? undefined
: await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-code-mode-runtime-"));
const abortController = new AbortController();
const interrupt = () => abortController.abort(new Error("Code Mode matrix interrupted"));
process.once("SIGINT", interrupt);
process.once("SIGTERM", interrupt);
try {
const runtime = runtimeRoot
? await prepareRuntimeEntrypoint(runtimeRepoRoot, runtimeRoot)
: undefined;
const results: CodeModeMatrixCellResult[] = [];
const resultsPath = path.join(outputDir, "results.jsonl");
await fs.writeFile(resultsPath, "", "utf8");
const executeCell = deps.runCell ?? runMatrixCell;
const executionStartedAt = Date.now();
let admittedCells = 0;
let stopReason: string | null = null;
const stoppedProviders = new Map<string, string>();
const stoppedModels = new Map<string, string>();
const unstarted: Array<{ id: string; reason: string }> = [];
let persistence = Promise.resolve();
const observedBudget = () => ({
tokens: results.reduce(
(total, result) =>
total + (result.accounting?.knownTotalTokens ?? result.usage?.total ?? 0),
0,
),
knownCostUsd: results.reduce(
(total, result) => total + (result.accounting?.knownCostUsd ?? result.costUsd ?? 0),
0,
),
missingTokenCells: results.filter((result) =>
result.accounting ? !result.accounting.complete : result.usage?.total === undefined,
).length,
missingCostCells: results.filter((result) =>
result.accounting ? !result.accounting.costComplete : result.costUsd === undefined,
).length,
wallSeconds: (Date.now() - executionStartedAt) / 1_000,
});
const stoppedReason = (cell: MatrixCell) =>
stoppedModels.get(cell.model) ?? stoppedProviders.get(cell.model.split("/")[0]!);
const runCell = async (cell: MatrixCell) => {
const index = cells.indexOf(cell);
abortController.signal.throwIfAborted();
const workload = isGatewayTask(cell.task)
? (await import("./lib/code-mode-matrix-gateway.ts")).createGatewayMatrixWorkload(
cell.task,
cell.repetition,
options.thinking,
options.timeoutSeconds,
options.gatewayExecutor ?? "node",
)
: undefined;
// Repetitions are independent scheduled cells, not retries whose eventual
// success could hide an earlier failure.
const occurrenceId = invocation.begin(index, null);
let result: CodeModeMatrixCellResult;
const cellStartedAt = Date.now();
try {
result = await executeCell({
abortSignal: abortController.signal,
buildSha256: buildSha256 ?? "dry-run",
cell,
...(workload ? { executor: workload.settings.executor } : {}),
gitSha: sourceIdentity.gitSha,
keepState: options.keepState,
outputDir,
repoRoot: runtimeRepoRoot,
runtime,
sourceDirty: sourceIdentity.sourceDirty,
sourcePatchSha256: sourceIdentity.sourcePatchSha256,
thinking: options.thinking,
timeoutSeconds: options.timeoutSeconds,
});
} catch (error) {
result = harnessFailureResult(
cell,
{
buildSha256: buildSha256 ?? "dry-run",
...sourceIdentity,
},
Date.now() - cellStartedAt,
error,
);
}
if (workload) {
result.workload = workload;
result.executor = workload.settings.executor;
}
if (
result.failureCategory === "provider_auth" ||
result.failureCategory === "provider_billing"
) {
stoppedProviders.set(cell.model.split("/")[0]!, result.failureCategory);
} else if (result.failureCategory === "model_unavailable") {
stoppedModels.set(cell.model, result.failureCategory);
}
const persist = async () => {
result.evidenceOccurrenceId = occurrenceId;
const artifactPath = path.posix.join("observations", `${occurrenceId}.json`);
const artifactBytes = `${JSON.stringify(redactJsonValueForDevToolLog({ result, launch, buildSha256 }), null, 2)}\n`;
await fs.mkdir(path.join(outputDir, "observations"), { recursive: true });
await fs.writeFile(path.join(outputDir, artifactPath), artifactBytes, {
flag: "wx",
mode: 0o600,
});
const artifact = {
kind: "matrix-observation",
path: artifactPath,
source: "code-mode-model-matrix",
sha256: createHash("sha256").update(artifactBytes).digest("hex"),
};
// The envelope observes provider/model responses, not a target runtime's
// package, protocol, account, or proof class. Keep this receipt prepared-only.
invocation.complete(occurrenceId, {
status: evidenceStatus(result),
entries: buildCodeModeMatrixEvidence({
generatedAt: result.timestamp,
repoRoot: options.repoRoot,
results: [result],
}).entries.map((entry) =>
Object.assign({}, entry, {
execution: entry.execution
? {
...entry.execution,
artifacts: [
...entry.execution.artifacts,
{
kind: artifact.kind,
path: artifact.path,
source: artifact.source,
},
],
}
: undefined,
}),
),
receipts: [
{ id: `${occurrenceId}:prepared`, phase: "prepared", identity: launch, artifact },
],
});
invocation.select(index, occurrenceId);
results.push(result);
await fs.appendFile(
resultsPath,
`${JSON.stringify(redactJsonValueForDevToolLog(result))}\n`,
"utf8",
);
await writeEvidence(result.timestamp);
console.log(
`[code-mode-matrix-result] ${JSON.stringify(redactJsonValueForDevToolLog(result))}`,
);
const label = result.passed ? "PASS" : `FAIL ${result.failureCategory ?? "unknown"}`;
console.log(`[code-mode-matrix] ${label} ${result.id} ${result.elapsedMs}ms`);
};
const written = persistence.then(persist);
persistence = written.catch(() => {});
await written;
};
for (const wave of waves) {
abortController.signal.throwIfAborted();
const budget = observedBudget();
const stopped = stoppedReason(wave[0]!);
stopReason ??=
admittedCells + wave.length > admission.maxCells
? "max_cells"
: budget.tokens >= admission.maxTokens
? "max_tokens"
: budget.knownCostUsd >= admission.maxKnownCostUsd
? "max_known_cost_usd"
: budget.wallSeconds >= admission.maxWallSeconds
? "max_wall_seconds"
: null;
if (stopReason || stopped) {
unstarted.push(...wave.map((cell) => ({ id: cell.id, reason: stopReason ?? stopped! })));
continue;
}
admittedCells += wave.length;
let next = 0;
const workers = Array.from(
{ length: Math.min(admission.concurrency, wave.length) },
async () => {
while (next < wave.length) {
const cell = wave[next++]!;
await runCell(cell);
}
},
);
const outcomes = await Promise.allSettled(workers);
const rejected = outcomes.find((outcome) => outcome.status === "rejected");
if (rejected?.status === "rejected") {
throw rejected.reason;
}
if ((await (deps.readBuildSha256 ?? hashRuntimeArtifacts)(runtimeRepoRoot)) !== buildSha256) {
throw new Error(
"Runtime build changed during the benchmark; per-cell evidence is retained but cannot establish a fixed-build comparison.",
);
}
}
abortController.signal.throwIfAborted();
if (options.runtimeDir && !deps.runCell) {
const finalIdentity = await readSourceIdentity(
runtimeRepoRoot,
STRICT_SOURCE_IDENTITY_OPTIONS,
);
if (finalIdentity.sourceDirty || finalIdentity.gitSha !== sourceIdentity.gitSha) {
throw new Error(
"Frozen runtime changed during the benchmark; per-cell evidence is retained but cannot establish a fixed-source comparison.",
);
}
}
const groups = summarizeResults(results);
const failed = results.filter((result) => !result.passed).length;
const firstPassPassed = groups.filter((group) => group.firstPassPassed).length;
const eventualPassed = groups.filter((group) => group.eventualPassed).length;
const summary = {
schemaVersion: MATRIX_SCHEMA_VERSION,
finishedAt: new Date().toISOString(),
...(options.tasks.some(isGatewayTask)
? { gatewayExecutor: options.gatewayExecutor ?? "node" }
: {}),
...sourceIdentity,
buildSha256,
counts: {
total: results.length,
passed: results.length - failed,
failed,
},
groupCounts: {
total: groups.length,
firstPassPassed,
eventualPassed,
},
...(results.some((result) => result.workload)
? { gatewayOutcomes: summarizeGatewayMatrixOutcomes(results) }
: {}),
groups,
admission: {
...admission,
admittedCells,
plannedCells: cells.length,
completedCells: results.length,
stoppedProviders: Object.fromEntries(stoppedProviders),
stoppedModels: Object.fromEntries(stoppedModels),
stopReason,
unstarted,
observed: observedBudget(),
overshoot: {
tokens: Math.max(0, observedBudget().tokens - admission.maxTokens),
knownCostUsd: Math.max(0, observedBudget().knownCostUsd - admission.maxKnownCostUsd),
wallSeconds: Math.max(0, observedBudget().wallSeconds - admission.maxWallSeconds),
contract:
"Admission limits are soft. An admitted wave finishes before another starts, including its queued arm at concurrency 1. Missing usage/prices make observed totals lower bounds, not hard spend limits.",
},
},
};
await writeJson(path.join(outputDir, "summary.json"), summary);
const performanceResults = results.filter((result) => isMatrixPerformanceTask(result.task));
if (performanceResults.length > 0) {
const { compareCodeModeMatrixModes } = await import("./lib/code-mode-matrix-comparison.ts");
await writeJson(
path.join(outputDir, "mode-comparison.json"),
compareCodeModeMatrixModes(performanceResults),
);
}
if (options.baselineResults) {
const { compareCodeModeMatrixResultsFile } =
await import("./lib/code-mode-matrix-comparison.ts");
await writeJson(
path.join(outputDir, "comparison.json"),
await compareCodeModeMatrixResultsFile(options.baselineResults, results),
);
}
await writeEvidence(summary.finishedAt);
return {
exitCode: (failed > 0 || unstarted.length > 0) && !options.allowFailures ? 1 : 0,
outputDir,
summary,
};
} finally {
process.off("SIGINT", interrupt);
process.off("SIGTERM", interrupt);
if (runtimeRoot) {
await fs.rm(runtimeRoot, { force: true, recursive: true });
}
}
}
async function main(): Promise<void> {
try {
const options = parseCodeModeMatrixOptions(process.argv.slice(2));
const result = await runCodeModeModelMatrix(options);
console.log(
`[code-mode-matrix] artifacts ${path.relative(options.repoRoot, result.outputDir)}`,
);
process.exitCode = result.exitCode;
} catch (error) {
if ((error as { code?: unknown }).code === "HELP") {
console.log(error instanceof Error ? error.message : String(error));
return;
}
console.error(error instanceof Error ? error.message : String(error));
process.exitCode = 1;
}
}
const entrypoint = process.argv[1];
if (entrypoint && import.meta.url === pathToFileURL(path.resolve(entrypoint)).href) {
await main();
}