Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled

Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11),
free to diverge. Tree copied sans upstream .git; upstream remote added for
future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19.
Preserves docs/ARCHITECTURE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
2026-07-05 09:36:54 +00:00
parent 3216769225
commit bedb527145
21108 changed files with 6010766 additions and 0 deletions

View File

@@ -0,0 +1,278 @@
// Check Gateway Watch Regression tests cover check gateway watch regression script behavior.
import { EventEmitter } from "node:events";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { describe, expect, it, vi } from "vitest";
import {
appendBoundedWatchLog,
collectGatewayWatchFindings,
hasGatewayReadyLog,
parseArgs,
runTimedWatch,
readNonNegativeInteger,
shouldRefreshBuildStampForRestoredArtifacts,
stopTimedWatchChild,
updateWatchBuildDetection,
WATCH_LOG_CAPTURE_MAX_CHARS,
writeBuildAndRuntimePostBuildStamps,
} from "../../scripts/check-gateway-watch-regression.mjs";
import {
BUILD_STAMP_FILE,
RUNTIME_POSTBUILD_STAMP_FILE,
} from "../../scripts/lib/local-build-metadata-paths.mjs";
describe("check-gateway-watch-regression", () => {
it("accepts package-manager argument separators before script options", () => {
expect(parseArgs(["--", "--window-ms", "1500", "--skip-build"])).toMatchObject({
skipBuild: true,
windowMs: 1500,
});
});
it("parses timing and growth limits as strict non-negative integers", () => {
expect(readNonNegativeInteger("0", "limit")).toBe(0);
expect(readNonNegativeInteger(" 42 ", "limit")).toBe(42);
expect(
parseArgs([
"--window-ms",
"0",
"--ready-timeout-ms",
"1",
"--ready-settle-ms",
"2",
"--sigkill-grace-ms",
"3",
"--sigkill-exit-grace-ms",
"4",
"--cpu-warn-ms",
"5",
"--cpu-fail-ms",
"6",
"--dist-runtime-file-growth-max",
"7",
"--dist-runtime-byte-growth-max",
"8",
]),
).toMatchObject({
cpuFailMs: 6,
cpuWarnMs: 5,
distRuntimeByteGrowthMax: 8,
distRuntimeFileGrowthMax: 7,
readySettleMs: 2,
readyTimeoutMs: 1,
sigkillExitGraceMs: 4,
sigkillGraceMs: 3,
windowMs: 0,
});
expect(() => readNonNegativeInteger("1.5", "limit")).toThrow(
"limit must be a non-negative integer",
);
expect(() => readNonNegativeInteger("1e3", "limit")).toThrow(
"limit must be a non-negative integer",
);
expect(() => readNonNegativeInteger("-1", "limit")).toThrow(
"limit must be a non-negative integer",
);
expect(() => readNonNegativeInteger("9007199254740992", "limit")).toThrow(
"limit must be a safe integer",
);
expect(() => parseArgs(["--window-ms", "soon"])).toThrow(
"--window-ms must be a non-negative integer",
);
});
it("recognizes current and legacy gateway ready logs", () => {
expect(hasGatewayReadyLog("[gateway] http server listening (0 plugins, 0.8s)")).toBe(true);
expect(hasGatewayReadyLog("[gateway] ready (0 plugins, 0.8s)")).toBe(true);
expect(hasGatewayReadyLog("\u001B[36m[gateway]\u001B[39m \u001B[36mready\u001B[39m")).toBe(
true,
);
expect(hasGatewayReadyLog("[gateway] starting HTTP server...")).toBe(false);
});
it("bounds in-memory watch output capture while keeping the newest logs", () => {
const first = appendBoundedWatchLog("abc", "def", 8);
expect(first).toEqual({ text: "abcdef", truncated: false });
const second = appendBoundedWatchLog(first.text, "ghijkl", 8);
expect(second).toEqual({ text: "efghijkl", truncated: true });
expect(second.text).toHaveLength(8);
expect(WATCH_LOG_CAPTURE_MAX_CHARS).toBeGreaterThan(1024);
});
it("keeps build-regression detection after diagnostic logs truncate", () => {
const detected = updateWatchBuildDetection(
{ buffer: "", triggered: false, reason: null },
"Building TypeScript (dist is stale: source_mtime_newer)\n",
);
const afterNoise = updateWatchBuildDetection(detected, "x".repeat(10_000));
expect(afterNoise.triggered).toBe(true);
expect(afterNoise.reason).toBe("source_mtime_newer");
const coalesced = updateWatchBuildDetection(
{ buffer: "", triggered: false, reason: null },
`Building TypeScript (dist is stale: config_newer)\n${"x".repeat(10_000)}`,
);
expect(coalesced.triggered).toBe(true);
expect(coalesced.reason).toBe("config_newer");
});
it("fails the regression gate when gateway watch never becomes ready", () => {
const findings = collectGatewayWatchFindings({
cpuMs: 0,
distRuntimeByteGrowth: 0,
distRuntimeFileGrowth: 0,
options: {
cpuFailMs: 8000,
cpuWarnMs: 1000,
distRuntimeByteGrowthMax: 2 * 1024 * 1024,
distRuntimeFileGrowthMax: 200,
windowMs: 10_000,
},
watchBuildReason: null,
watchResult: {
idleCpuMs: 0,
readyBeforeWindow: false,
spawnError: null,
timingFileMissing: false,
},
watchTriggeredBuild: false,
});
expect(findings.failures).toContain(
"gateway:watch did not report ready before the idle CPU window",
);
expect(findings.warnings).toEqual([]);
});
it("refreshes restored build stamps only for skip-build config mtime drift", () => {
expect(
shouldRefreshBuildStampForRestoredArtifacts({
skipBuild: true,
buildRequirement: { shouldBuild: true, reason: "config_newer" },
}),
).toBe(true);
expect(
shouldRefreshBuildStampForRestoredArtifacts({
skipBuild: false,
buildRequirement: { shouldBuild: true, reason: "config_newer" },
}),
).toBe(false);
expect(
shouldRefreshBuildStampForRestoredArtifacts({
skipBuild: true,
buildRequirement: { shouldBuild: true, reason: "source_mtime_newer" },
}),
).toBe(false);
});
it("refreshes runtime postbuild stamps after build stamps", () => {
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-gateway-watch-stamps-"));
try {
fs.mkdirSync(path.join(rootDir, ".git"), { recursive: true });
writeBuildAndRuntimePostBuildStamps({ cwd: rootDir });
const buildStampPath = path.join(rootDir, "dist", BUILD_STAMP_FILE);
const runtimeStampPath = path.join(rootDir, "dist", RUNTIME_POSTBUILD_STAMP_FILE);
expect(fs.existsSync(buildStampPath)).toBe(true);
expect(fs.existsSync(runtimeStampPath)).toBe(true);
expect(fs.statSync(runtimeStampPath).mtimeMs).toBeGreaterThanOrEqual(
fs.statSync(buildStampPath).mtimeMs,
);
} finally {
fs.rmSync(rootDir, { recursive: true, force: true });
}
});
it("bounds teardown when the watch process ignores termination signals", async () => {
const child = new EventEmitter() as EventEmitter & {
exitCode: number | null;
signalCode: NodeJS.Signals | null;
stderr: { destroy: ReturnType<typeof vi.fn> };
stdin: { destroy: ReturnType<typeof vi.fn> };
stdout: { destroy: ReturnType<typeof vi.fn> };
unref: ReturnType<typeof vi.fn>;
};
child.exitCode = null;
child.signalCode = null;
child.stderr = { destroy: vi.fn() };
child.stdin = { destroy: vi.fn() };
child.stdout = { destroy: vi.fn() };
child.unref = vi.fn();
const killProcess = vi.fn();
await expect(
stopTimedWatchChild(
child,
1234,
{ sigkillExitGraceMs: 1, sigkillGraceMs: 1 },
{ killProcess },
),
).resolves.toEqual({ code: null, signal: "SIGKILL" });
expect(killProcess).toHaveBeenNthCalledWith(1, 1234, "SIGTERM");
expect(killProcess).toHaveBeenNthCalledWith(2, 1234, "SIGKILL");
expect(child.stdin.destroy).toHaveBeenCalledOnce();
expect(child.stdout.destroy).toHaveBeenCalledOnce();
expect(child.stderr.destroy).toHaveBeenCalledOnce();
expect(child.unref).toHaveBeenCalledOnce();
});
it("removes the isolated watch home after spawn failures", async () => {
const outputDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-gateway-watch-output-"));
const child = new EventEmitter() as EventEmitter & {
stderr: EventEmitter;
stdout: EventEmitter;
};
child.stderr = new EventEmitter();
child.stdout = new EventEmitter();
const sleep = vi.fn(() => new Promise<never>(() => {}));
const stopChild = vi.fn(
() =>
new Promise<never>(() => {
// Spawn failures must win before cleanup waits for a child that never started.
}),
);
const waitForGatewayReady = vi.fn(async () => false);
const spawn = vi.fn(() => {
process.nextTick(() => {
child.emit("error", new Error("spawn failed"));
});
return child;
});
try {
const result = await runTimedWatch(
{
readySettleMs: 0,
readyTimeoutMs: 0,
sigkillGraceMs: 1,
windowMs: 0,
},
outputDir,
{
allocateLoopbackPort: async () => 19042,
spawn,
sleep,
stopTimedWatchChild: stopChild,
waitForGatewayReady,
},
);
const isolatedHomeDir = fs
.readFileSync(path.join(outputDir, "watch.home.txt"), "utf8")
.trim();
expect(result.spawnError).toBe("spawn failed");
expect(fs.existsSync(isolatedHomeDir)).toBe(false);
expect(fs.existsSync(path.join(outputDir, "watch.home.txt"))).toBe(true);
expect(spawn.mock.calls[0]?.[2]?.env?.OPENCLAW_RUNTIME_POSTBUILD_STATIC_ASSETS).toBe("0");
expect(waitForGatewayReady).not.toHaveBeenCalled();
expect(stopChild).not.toHaveBeenCalled();
} finally {
fs.rmSync(outputDir, { recursive: true, force: true });
}
});
});