diff --git a/.github/workflows/openclaw-release-telegram-qa.yml b/.github/workflows/openclaw-release-telegram-qa.yml index a515fa6c4384..c64426ceb6d6 100644 --- a/.github/workflows/openclaw-release-telegram-qa.yml +++ b/.github/workflows/openclaw-release-telegram-qa.yml @@ -1089,12 +1089,15 @@ jobs: sudo tee "$preload" >/dev/null <<'PRELOAD' import process from "node:process"; + import { isMainThread } from "node:worker_threads"; - if (process.env.OPENCLAW_QA_SUT_PREENTRY_STOP !== "1") { - throw new Error("trusted Telegram SUT preload was not armed"); + if (isMainThread) { + if (process.env.OPENCLAW_QA_SUT_PREENTRY_STOP !== "1") { + throw new Error("trusted Telegram SUT preload was not armed"); + } + delete process.env.OPENCLAW_QA_SUT_PREENTRY_STOP; + process.kill(process.pid, "SIGSTOP"); } - delete process.env.OPENCLAW_QA_SUT_PREENTRY_STOP; - process.kill(process.pid, "SIGSTOP"); PRELOAD sudo chown root:root "$preload" sudo chmod 0555 "$preload" diff --git a/test/scripts/openclaw-release-telegram-qa-workflow.test.ts b/test/scripts/openclaw-release-telegram-qa-workflow.test.ts index 98f4f08321ae..fe2f1d937171 100644 --- a/test/scripts/openclaw-release-telegram-qa-workflow.test.ts +++ b/test/scripts/openclaw-release-telegram-qa-workflow.test.ts @@ -546,6 +546,43 @@ describe("release Telegram QA workflow", () => { expect(source).toContain('if [[ "${1:-}" == "--root-terminate-uid" ]]'); }); + it("arms the boundary preload only in the gateway main thread", () => { + const createSutStep = workflowStep( + workflowJob("run_telegram"), + "Create isolated Telegram SUT identity and launcher", + ); + const preloadSource = createSutStep.run?.match( + /<<'PRELOAD'\n([\s\S]*?)\nPRELOAD/u, + )?.[1]; + expect(preloadSource).toBeTruthy(); + + const workdir = tempDirs.make("openclaw-telegram-preload-"); + const preloadPath = join(workdir, "preload.mjs"); + writeFileSync(preloadPath, preloadSource ?? ""); + const env = { ...process.env }; + delete env.OPENCLAW_QA_SUT_PREENTRY_STOP; + + const mainResult = spawnSync(process.execPath, ["--import", preloadPath, "-e", ""], { + encoding: "utf8", + env, + }); + expect(mainResult.status).not.toBe(0); + expect(mainResult.stderr).toContain("trusted Telegram SUT preload was not armed"); + + const workerResult = spawnSync( + process.execPath, + [ + "-e", + `const { Worker } = require("node:worker_threads");\n` + + `const worker = new Worker('require("node:worker_threads").parentPort.postMessage("ready")', { eval: true, execArgv: ["--import", ${JSON.stringify(preloadPath)}] });\n` + + `worker.once("message", () => process.exit(0));\n` + + `worker.once("error", (error) => { console.error(error); process.exit(1); });`, + ], + { encoding: "utf8", env, killSignal: "SIGKILL", timeout: 5_000 }, + ); + expect(workerResult.status, workerResult.stderr).toBe(0); + }); + it("reports the namespace-entry stage when its supervised child fails", () => { const source = readFileSync(WORKFLOW_PATH, "utf8"); const trapLine = source.match(/^\s+(trap 'exit_status=.*' ERR)$/mu)?.[1];