From 32be17df794f6582335cc04aba14911295e286fd Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Tue, 14 Apr 2026 15:16:16 -0400 Subject: [PATCH] QA: fix triage follow-ups --- extensions/qa-lab/src/cli.test.ts | 19 +++++++++++++++++++ extensions/qa-lab/src/cli.ts | 15 +++++++++++---- extensions/qa-matrix/src/docker-runtime.ts | 2 +- pnpm-lock.yaml | 9 +++++++++ 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/extensions/qa-lab/src/cli.test.ts b/extensions/qa-lab/src/cli.test.ts index 2e2ef628f1e..8d60b8079fd 100644 --- a/extensions/qa-lab/src/cli.test.ts +++ b/extensions/qa-lab/src/cli.test.ts @@ -42,6 +42,15 @@ function createBlockedQaRunnerContribution(): QaRunnerCliContribution { }; } +function createConflictingQaRunnerContribution(commandName: string): QaRunnerCliContribution { + return { + pluginId: TEST_QA_RUNNER.pluginId, + commandName, + description: TEST_QA_RUNNER.description, + status: "blocked", + }; +} + const { runQaCredentialsAddCommand, runQaCredentialsListCommand, @@ -138,6 +147,16 @@ describe("qa cli registration", () => { ).rejects.toThrow(`Enable or allow plugin "${TEST_QA_RUNNER.pluginId}"`); }); + it("rejects discovered runners that collide with built-in qa subcommands", () => { + listQaRunnerCliContributions + .mockReset() + .mockReturnValue([createConflictingQaRunnerContribution("manual")]); + + expect(() => registerQaLabCli(new Command())).toThrow( + 'QA runner command "manual" conflicts with an existing qa subcommand', + ); + }); + it("routes telegram CLI defaults into the lane runtime", async () => { await program.parseAsync(["node", "openclaw", "qa", "telegram"]); diff --git a/extensions/qa-lab/src/cli.ts b/extensions/qa-lab/src/cli.ts index 0bce3eefe8f..435a08b9486 100644 --- a/extensions/qa-lab/src/cli.ts +++ b/extensions/qa-lab/src/cli.ts @@ -183,6 +183,12 @@ export function isQaLabCliAvailable(): boolean { return hasQaScenarioPack(); } +function assertNoQaSubcommandCollision(qa: Command, commandName: string) { + if (qa.commands.some((command) => command.name() === commandName)) { + throw new Error(`QA runner command "${commandName}" conflicts with an existing qa subcommand`); + } +} + export function registerQaLabCli(program: Command) { const qa = program .command("qa") @@ -284,10 +290,6 @@ export function registerQaLabCli(program: Command) { }, ); - for (const lane of listLiveTransportQaCliRegistrations()) { - lane.register(qa); - } - qa.command("character-eval") .description("Run the character QA scenario across live models and write a judged report") .option("--repo-root ", "Repository root to target when running from a neutral cwd") @@ -579,4 +581,9 @@ export function registerQaLabCli(program: Command) { .action(async (opts: { host?: string; port?: number }) => { await runQaMockOpenAi(opts); }); + + for (const lane of listLiveTransportQaCliRegistrations()) { + assertNoQaSubcommandCollision(qa, lane.commandName); + lane.register(qa); + } } diff --git a/extensions/qa-matrix/src/docker-runtime.ts b/extensions/qa-matrix/src/docker-runtime.ts index f30e083d04c..ada9dff01bc 100644 --- a/extensions/qa-matrix/src/docker-runtime.ts +++ b/extensions/qa-matrix/src/docker-runtime.ts @@ -17,7 +17,7 @@ export async function fetchHealthUrl(url: string): Promise<{ ok: boolean }> { signal: AbortSignal.timeout(2_000), }, policy: { allowPrivateNetwork: true }, - auditContext: "qa-lab-docker-health-check", + auditContext: "qa-matrix-docker-health-check", }); try { return { ok: response.ok }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 433aec270cc..3dceb2db276 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -994,6 +994,15 @@ importers: specifier: workspace:* version: link:../.. + extensions/qa-matrix: + devDependencies: + '@openclaw/plugin-sdk': + specifier: workspace:* + version: link:../../packages/plugin-sdk + openclaw: + specifier: workspace:* + version: link:../.. + extensions/qianfan: devDependencies: '@openclaw/plugin-sdk':