From f97f5d2788de8067262ccd44e0747cbede6946d5 Mon Sep 17 00:00:00 2001 From: canvas-bot Date: Mon, 15 Jun 2026 04:24:51 +0400 Subject: [PATCH] qa(idle-poll): assert zero background reqs in a 55s idle window MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previous audit allowed up to 5 requests in a 30s window — generous, hid real regressions. Tighten: - 5s settle after each scene becomes interactive (covers the burst of mount-time fetches every scene legitimately fires) - 55s observation window after settle, sized to fit between two consecutive 60s topbar badge polls - MAX_REQ_PER_WINDOW = 0; any /api/* or /internal/* request during the window fails the assertion This is the operator-experience contract: when you stop typing, canvas stops fetching. --- qa/audit_idle_poll.mjs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/qa/audit_idle_poll.mjs b/qa/audit_idle_poll.mjs index 31f58fd..ea9b537 100644 --- a/qa/audit_idle_poll.mjs +++ b/qa/audit_idle_poll.mjs @@ -1,8 +1,16 @@ // Idle-poll audit. User explicitly complained "way too many GETs / non-stop". -// For each scene: visit, settle, then watch network for 30s with no user input. -// Pass if total requests in that 30s window is reasonable (<= 5). -// The topbar identity-hydration + chat/queue badge polls at 60s, so a 30s -// window should see at most 1 such fire. +// For each scene: visit, settle for a generous burst window, then watch network +// for 60s with no user input. Strict assertion: zero background /api/* or +// /internal/* requests during that window. +// +// What counts as "background": +// - Anything fired more than SETTLE_MS after the scene becomes interactive +// - Excludes the 60s topbar badge poll fired exactly at minute boundaries — +// the window is sized to fit BETWEEN two such fires +// +// The topbar badge poll runs every 60s. A 55s observation window after a +// 5s settle covers a full minute of operator inactivity without spanning +// the next poll boundary, so the strict assertion "zero" is correct. import { chromium } from "playwright"; const URL = "https://canvas.flow-master.ai/"; @@ -19,8 +27,9 @@ const SCENES = [ { chip: /What is FlowMaster/, label: "explainer" }, ]; -const IDLE_WINDOW_MS = 30_000; -const MAX_REQ_PER_WINDOW = 5; +const SETTLE_MS = 5_000; +const IDLE_WINDOW_MS = 55_000; +const MAX_REQ_PER_WINDOW = 0; const results = []; function record(name, ok, detail = "") { @@ -49,7 +58,7 @@ for (const s of SCENES) { // Settle period — the scene's initial fetch burst should complete here. await p.waitForLoadState("networkidle").catch(() => {}); - await p.waitForTimeout(1500); + await p.waitForTimeout(SETTLE_MS); // Now record every request fired for the next IDLE_WINDOW_MS. const requests = []; @@ -65,9 +74,9 @@ for (const s of SCENES) { const ok = requests.length <= MAX_REQ_PER_WINDOW; const summary = requests.length - ? `${requests.length} reqs · ${requests.slice(0, 3).map((r) => `${r.method} ${r.url.slice(0, 50)}`).join(", ")}${requests.length > 3 ? "…" : ""}` - : "0 reqs · quiet"; - record(`idle_${s.label}_quiet_at_30s`, ok, summary); + ? `${requests.length} background reqs in ${IDLE_WINDOW_MS / 1000}s · ${requests.slice(0, 3).map((r) => `${r.method} ${r.url.slice(0, 50)}`).join(", ")}${requests.length > 3 ? "…" : ""}` + : `0 background reqs in ${IDLE_WINDOW_MS / 1000}s`; + record(`idle_${s.label}_zero_background_traffic`, ok, summary); } await b.close();