From 20ac9c20b88fb87e15bce51cc7a97fbed292213a Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 10 Jul 2026 12:02:58 +0200 Subject: [PATCH] fix(web-probe): preserve YAML timeout without override --- ...web-observe-runner-realtime-source.test.ts | 40 +++++++++++++++++++ ...node-web-observe-runner-realtime-source.ts | 3 +- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 scripts/src/hwlab-node-web-observe-runner-realtime-source.test.ts diff --git a/scripts/src/hwlab-node-web-observe-runner-realtime-source.test.ts b/scripts/src/hwlab-node-web-observe-runner-realtime-source.test.ts new file mode 100644 index 00000000..e929d157 --- /dev/null +++ b/scripts/src/hwlab-node-web-observe-runner-realtime-source.test.ts @@ -0,0 +1,40 @@ +import assert from "node:assert/strict"; +import { test } from "bun:test"; + +import { nodeWebObserveRunnerRealtimeSource } from "./hwlab-node-web-observe-runner-realtime-source"; + +test("realtime fanout keeps the YAML terminal timeout when durationMs is absent", () => { + const source = nodeWebObserveRunnerRealtimeSource(); + const build = new Function( + "boundedInteger", + "sanitize", + `${source}\nreturn realtimeFanoutEffectiveProfile;`, + ) as ( + boundedInteger: (value: unknown, fallback: number, min: number, max: number) => number, + sanitize: (value: T) => T, + ) => (profile: Record, durationMs: unknown) => Record; + const effectiveProfile = build( + (value, fallback, min, max) => Math.min(max, Math.max(min, Number.isFinite(Number(value)) ? Number(value) : fallback)), + (value) => value, + ); + const profile = { + subscriberCount: 2, + debugStreams: ["stdio", "agentrun", "hwlab"], + fromBeginning: false, + barrierTimeoutMs: 45_000, + terminalTimeoutMs: 480_000, + terminalQuietMs: 2_000, + reconnectQuietMs: 3_000, + forbiddenRequestPaths: [], + requiredEventTypes: { agentrun: ["terminal_status"], hwlab: ["terminal"] }, + expectedKafka: { + topics: { stdio: "codex-stdio.raw.v1", agentrun: "agentrun.event.v1", hwlab: "hwlab.event.v1" }, + groups: { directPublish: "direct", transactionalProjector: "projector", liveSse: "live" }, + capabilities: { directPublish: true, liveKafkaSse: true }, + }, + }; + + assert.equal(effectiveProfile(profile, null).terminalTimeoutMs, 480_000); + assert.equal(effectiveProfile(profile, undefined).terminalTimeoutMs, 480_000); + assert.equal(effectiveProfile(profile, 5_000).terminalTimeoutMs, 5_000); +}); diff --git a/scripts/src/hwlab-node-web-observe-runner-realtime-source.ts b/scripts/src/hwlab-node-web-observe-runner-realtime-source.ts index f4942fc4..c9ec7f61 100644 --- a/scripts/src/hwlab-node-web-observe-runner-realtime-source.ts +++ b/scripts/src/hwlab-node-web-observe-runner-realtime-source.ts @@ -324,7 +324,8 @@ function realtimeFanoutEffectiveProfile(profile, durationMs) { if (expectedKafka.capabilities.directPublish !== true || expectedKafka.capabilities.liveKafkaSse !== true) { throw new Error("realtime fanout profile requires YAML-enabled directPublish and liveKafkaSse capabilities"); } - const terminalTimeoutMs = Number.isFinite(Number(durationMs)) + const hasDurationOverride = durationMs !== null && durationMs !== undefined && durationMs !== "" && Number.isFinite(Number(durationMs)); + const terminalTimeoutMs = hasDurationOverride ? Math.min(Number(profile.terminalTimeoutMs), Math.max(1000, Number(durationMs))) : Number(profile.terminalTimeoutMs); return {