

















@@ -58,6 +58,7 @@ function parseArgs(argv) {
5858commandTimeoutMs: 120_000,
5959buildTimeoutMs: 600_000,
6060qaTimeoutMs: 900_000,
61+allowEmpty: false,
6162keepRunRoot: process.env.OPENCLAW_PLUGIN_GATEWAY_GAUNTLET_KEEP_RUN_ROOT === "1",
6263};
6364const envIds = normalizeCsv(process.env.OPENCLAW_PLUGIN_GATEWAY_GAUNTLET_IDS);
@@ -156,6 +157,9 @@ function parseArgs(argv) {
156157case "--keep-run-root":
157158options.keepRunRoot = true;
158159break;
160+case "--allow-empty":
161+options.allowEmpty = true;
162+break;
159163case "--help":
160164printHelp();
161165process.exit(0);
@@ -191,6 +195,7 @@ Options:
191195 --skip-lifecycle Skip plugin install/inspect/disable/enable/doctor/uninstall
192196 --skip-qa Skip QA Lab RPC conversation runs
193197 --skip-slash-help Skip CLI help probes for plugin-declared command aliases
198+ --allow-empty Allow zero-command runs when every active phase is skipped
194199 --keep-run-root Preserve isolated HOME/state/log temp root after success
195200`);
196201}
@@ -611,6 +616,10 @@ export function runMeasuredCommandLive(params) {
611616});
612617}
613618619+export function hasGauntletWorkRows(rows) {
620+return rows.some((row) => row.phase !== "prebuild");
621+}
622+614623function runPluginLifecycle(params) {
615624for (const plugin of params.plugins) {
616625const commands = [
@@ -814,7 +823,18 @@ async function main() {
814823const failures = rows.filter(
815824(row) => row.status !== 0 || row.timedOut || row.diagnosticFailure,
816825);
817-preserveRunRoot = preserveRunRoot || failures.length > 0;
826+const guardFailures =
827+!hasGauntletWorkRows(rows) && !options.allowEmpty
828+ ? [
829+{
830+kind: "empty-run",
831+message:
832+"No lifecycle, slash-help, or QA gauntlet commands ran; remove a skip flag or pass --allow-empty for intentional dry runs.",
833+},
834+]
835+ : [];
836+const hasFailures = failures.length > 0 || guardFailures.length > 0;
837+preserveRunRoot = preserveRunRoot || hasFailures;
818838let cleanupError = null;
819839if (!preserveRunRoot) {
820840try {
@@ -841,6 +861,7 @@ async function main() {
841861qaScenarios: options.qaScenarios,
842862qaPluginChunkSize: options.qaPluginChunkSize,
843863qaBaseline: options.qaBaseline,
864+allowEmpty: options.allowEmpty,
844865keepRunRoot: options.keepRunRoot,
845866skipLifecycle: options.skipLifecycle,
846867skipQa: options.skipQa,
@@ -861,6 +882,7 @@ async function main() {
861882 rows,
862883observations: [...metricObservations, ...qaBaselineObservations, ...gatewayObservations],
863884 failures,
885+ guardFailures,
864886};
865887const summaryPath = path.join(options.outputDir, "plugin-gateway-gauntlet-summary.json");
866888fs.writeFileSync(summaryPath, `${JSON.stringify(summary, null, 2)}\n`, "utf8");
@@ -876,10 +898,13 @@ async function main() {
876898`[plugin-gauntlet] failure phase=${failure.phase} plugin=${failure.pluginId ?? "<none>"} status=${failure.status} timedOut=${failure.timedOut} diagnostic=${failure.diagnosticFailure ?? ""} wallMs=${Math.round(failure.wallMs)} log=${failure.logPath}\n`,
877899);
878900}
901+for (const failure of guardFailures) {
902+process.stdout.write(`[plugin-gauntlet] failure ${failure.kind}: ${failure.message}\n`);
903+}
879904for (const observation of summary.observations.slice(0, 20)) {
880905process.stdout.write(`[plugin-gauntlet] observation ${JSON.stringify(observation)}\n`);
881906}
882-if (failures.length > 0) {
907+if (hasFailures) {
883908process.exitCode = 1;
884909}
885910} catch (error) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。