Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions harness/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ export const parseCount = (
raw: string | undefined,
fallback: number,
): number | undefined => {
if (raw === undefined) return fallback;
const value = Number(raw);
return Number.isSafeInteger(value) && value >= 1 ? value : undefined;
const value = raw === undefined ? fallback : Number(raw);
return raw === undefined || (Number.isSafeInteger(value) && value >= 1)
? value
: undefined;
};

export const formatLiveLine = (line: string): string => {
Expand Down
54 changes: 26 additions & 28 deletions harness/gate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,35 +147,33 @@ function runOneCheck(
command: readonly string[],
): string | undefined {
const [executable, ...rest] = command;
if (executable === undefined || executable.length === 0) {
return `${name} failed:\nempty command`;
}
const started = Date.now();
process.stderr.write(`gate: starting ${name}: ${command.join(" ")}\n`);
const result = spawnSync(executable, rest, {
cwd: context.repo,
encoding: "utf8",
env: context.environment,
// 0 => undefined => no timeout: the full gate must run long browser/build checks.
timeout: context.timeoutMs > 0 ? context.timeoutMs : undefined,
});
const elapsedMs = Date.now() - started;
process.stderr.write(
`gate: finished ${name} in ${String(elapsedMs)}ms ` +
`status=${String(result.status)} signal=${String(result.signal)} ` +
`error=${result.error === undefined ? "none" : String(result.error)}\n`,
);
if (isToolMissing(result, executable)) {
return undefined;
}
if (
result.status === 0 &&
result.signal === null &&
result.error === undefined
) {
return undefined;
let failure: string | undefined = `${name} failed:\nempty command`;
if (executable !== undefined && executable.length > 0) {
const started = Date.now();
process.stderr.write(`gate: starting ${name}: ${command.join(" ")}\n`);
const result = spawnSync(executable, rest, {
cwd: context.repo,
encoding: "utf8",
env: context.environment,
// 0 => undefined => no timeout: the full gate must run long browser/build checks.
timeout: context.timeoutMs > 0 ? context.timeoutMs : undefined,
});
const elapsedMs = Date.now() - started;
process.stderr.write(
`gate: finished ${name} in ${String(elapsedMs)}ms ` +
`status=${String(result.status)} signal=${String(result.signal)} ` +
`error=${result.error === undefined ? "none" : String(result.error)}\n`,
);
const hasFailed =
result.status !== 0 ||
result.signal !== null ||
result.error !== undefined;
failure =
hasFailed && !isToolMissing(result, executable)
? `${name} failed:\n${describeFailure(result, command)}`
: undefined;
}
return `${name} failed:\n${describeFailure(result, command)}`;
return failure;
}

/**
Expand Down
31 changes: 16 additions & 15 deletions harness/logging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ interface LogFile extends ParsedLog {

const isRec = (value: unknown): value is Rec =>
typeof value === "object" && value !== null && !Array.isArray(value);
const maybe = <T>(value: T | undefined): T | undefined => value;
const pair = <A, B>(first: A, second: B | undefined): [A, B] | undefined =>
second === undefined ? undefined : [first, second];

const compact = (value: string): string =>
value.replaceAll(/\s+/gu, " ").trim();
Expand Down Expand Up @@ -83,7 +86,7 @@ const numberFrom = (rec: Rec, keys: readonly string[]): number | undefined => {

const usageTotal = (source: Rec): number | undefined => {
const total = numberFrom(source, TOTAL_KEYS);
if (total !== undefined) return total;
if (total !== undefined) return maybe(total);
const input = numberFrom(source, INPUT_KEYS);
const output = numberFrom(source, OUTPUT_KEYS);
return input === undefined && output === undefined
Expand All @@ -100,28 +103,29 @@ const toolText = (part: Rec): string => {
};

const textPart = (content: unknown): string | undefined => {
if (!Array.isArray(content)) return undefined;
if (!Array.isArray(content)) return maybe<string>(undefined);
for (const part of content) {
if (!isRec(part)) continue;
if (part.type === "text" && typeof part.text === "string") return part.text;
if (part.type === "text" && typeof part.text === "string")
return maybe(part.text);
if (part.type === "tool_use" && typeof part.name === "string")
return toolText(part);
return maybe(toolText(part));
}
return undefined;
return maybe<string>(undefined);
};

const agentText = (value: Rec): string | undefined => {
const item = isRec(value.item) ? value.item : undefined;
if (item?.type === "agent_message" && typeof item.text === "string")
return item.text;
return maybe(item.text);
const message = isRec(value.message) ? value.message : undefined;
return textPart(message?.content);
};

const summary = (value: Rec): [number, string] | undefined => {
if (typeof value.result === "string") return [4, value.result];
if (typeof value.result === "string") return pair(4, value.result);
const text = agentText(value);
if (text !== undefined) return [3, text];
if (text !== undefined) return pair(3, text);
const usage = isRec(value.usage) ? usageTotal(value.usage) : undefined;
return usage === undefined
? undefined
Expand Down Expand Up @@ -153,19 +157,16 @@ const keyedUsage = (
lineNumber: number,
): readonly [string, number] | undefined => {
const direct = isRec(value.usage) ? usageTotal(value.usage) : undefined;
if (direct !== undefined)
return [
value.type === "result" ? "result" : `direct-${String(lineNumber)}`,
direct,
];
const key =
value.type === "result" ? "result" : `direct-${String(lineNumber)}`;
if (direct !== undefined) return pair(key, direct);
const message = isRec(value.message) ? value.message : undefined;
const fallback = isRec(message?.usage)
? usageTotal(message.usage)
: undefined;
if (fallback === undefined) return undefined;
const id =
typeof message?.id === "string" ? message.id : `line-${String(lineNumber)}`;
return [`fallback-${id}-${String(fallback)}`, fallback];
return pair(`fallback-${id}-${String(fallback)}`, fallback);
};

const addUsage = (
Expand Down
3 changes: 2 additions & 1 deletion harness/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"@playwright/test": "latest",
"@secretlint/secretlint-rule-preset-recommend": "latest",
"@stoplight/spectral-cli": "latest",
"@typescript/native": "npm:typescript@~7.0.2",
"@types/node": "latest",
"@vitest/coverage-v8": "latest",
"ajv": "latest",
Expand Down Expand Up @@ -75,7 +76,7 @@
"stylelint-media-use-custom-media": "latest",
"stylelint-plugin-defensive-css": "latest",
"tsx": "latest",
"typescript": "~5.9.3",
"typescript": "npm:@typescript/typescript6@~6.0.2",
"typescript-eslint": "latest",
"vite": "latest",
"vitest": "latest"
Expand Down
Loading
Loading