Humuhumu33 commited on
Commit
3f54ef6
·
verified ·
1 Parent(s): 8cfb8a7

holo-evicted-publish: player +2 object(s)

Browse files
b/3dfd69227579aa2e688b72b57789164015088730221c58adc103f6f930f00837 ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+ // holo-flow-verify.mjs — PROVE THE CHANNEL (W0/W1). Drives the REAL published player unattended and
3
+ // returns ONE verdict: flow rhythm · media-to-media gaps · verbs · taste-drift · privacy spy (payload-
4
+ // aware) · voice telemetry (bounded) · prior gates vs current bytes. No silent skips: every leg prints.
5
+ // node holo-flow-verify.mjs [--base <url>] [--minutes N] [--mobile] [--skip-voice]
6
+ // Default base = the LIVE site. Harness-only flags (autoplay, flowTest=1 short slots) never ship behavior.
7
+
8
+ import { spawnSync } from "node:child_process";
9
+ import path from "node:path";
10
+ import { fileURLToPath } from "node:url";
11
+
12
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
13
+ const arg = (k, d) => { const i = process.argv.indexOf("--" + k); return i > 0 ? process.argv[i + 1] : d; };
14
+ const BASE = arg("base", "https://hologram-technologies.github.io/Q");
15
+ const MIN = +arg("minutes", 4);
16
+ const MOBILE = process.argv.includes("--mobile");
17
+ const SKIPV = process.argv.includes("--skip-voice");
18
+ const R = []; const leg = (n, ok, note) => { R.push({ n, ok, note }); console.log(`${ok ? "✓" : "✗"} ${n} — ${note}`); };
19
+
20
+ // prior gates re-run against current canonical bytes (fast, pure-node)
21
+ for (const [w, name] of [["holo-media-index-witness.mjs", "K0 cards"], ["holo-flow-witness.mjs", "F2 sequencer"]]) {
22
+ const r = spawnSync("node", [path.join(HERE, w)], { encoding: "utf8" });
23
+ leg(name, r.status === 0, (r.stdout.trim().split("\n").pop() || "").slice(0, 60));
24
+ }
25
+
26
+ const { pathToFileURL } = await import("node:url");
27
+ const pw = await import(pathToFileURL(path.resolve(HERE, "../../../holo-os/system/node_modules/playwright-core/index.js")).href);
28
+ const { chromium } = pw.default || pw;
29
+ const browser = await chromium.launch({ executablePath: "C:/Users/pavel/AppData/Local/ms-playwright/chromium_headless_shell-1228/chrome-headless-shell-win64/chrome-headless-shell.exe", headless: true, args: ["--enable-unsafe-swiftshader", "--autoplay-policy=no-user-gesture-required"] });
30
+ const ctx = await browser.newContext({ viewport: MOBILE ? { width: 375, height: 812 } : { width: 1280, height: 800 } });
31
+ const page = await ctx.newPage();
32
+ // privacy spy — payload-aware: flag any request carrying personal-signal payloads or leaving allowed hosts
33
+ await page.addInitScript(() => {
34
+ window.__spy = [];
35
+ const PERSONAL = /holo\.continue|holo\.stats|flow-plan|holoplayer\.resume|holo\.book\./;
36
+ const ALLOWED = /localhost|hologram-technologies\.github\.io|huggingface\.co|hf\.co|archive\.org|iptv-org|imgur|weserv|ytimg|youtube|tmsimg|githubusercontent|jsdelivr|pluto|toosvc/i;
37
+ const scan = (url, body) => { const u = String(url || ""), b = typeof body === "string" ? body : ""; if (PERSONAL.test(u) || PERSONAL.test(b)) window.__spy.push("PERSONAL:" + u.slice(0, 80)); else if (!ALLOWED.test(u) && /^https?:/.test(u)) window.__spy.push("HOST:" + u.slice(0, 80)); };
38
+ const of = window.fetch; window.fetch = function (u, o) { try { scan((u && u.url) || u, o && o.body); } catch (e) {} return of.apply(window, arguments); };
39
+ const ox = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function (m, u) { try { scan(u); } catch (e) {} return ox.apply(this, arguments); };
40
+ if (navigator.sendBeacon) { const ob = navigator.sendBeacon.bind(navigator); navigator.sendBeacon = (u, d) => { try { scan(u, d); } catch (e) {} return ob(u, d); }; }
41
+ try { localStorage.setItem("holo-messenger/profile/v1", JSON.stringify({ name: "Witness" })); } catch (e) {}
42
+ });
43
+ await page.goto(BASE + "/apps/player/index.html?flowTest=1", { waitUntil: "load", timeout: 60000 });
44
+ await page.waitForTimeout(9000);
45
+
46
+ // world snapshot (mid-run publishes by other sessions must fail LOUDLY as world-moved, not phantom bugs)
47
+ const world = await page.evaluate(async () => { try { const p = await (await fetch("./media-index.json")).json(); return p.kappa.slice(0, 20); } catch (e) { return "?"; } });
48
+ console.log("world: index κ " + world + " · base " + BASE + (MOBILE ? " · MOBILE 375px" : ""));
49
+
50
+ // strip + start
51
+ const started = await page.evaluate(async () => {
52
+ const strip = [...document.querySelectorAll("#rails > div")].find((d) => /your channel is ready/.test(d.textContent || ""));
53
+ if (!strip) return { ok: false };
54
+ strip.querySelector("[data-fgo]").click();
55
+ await new Promise((r) => setTimeout(r, 4000));
56
+ return { ok: !!document.getElementById("flowbar"), greeting: strip.textContent.slice(0, 30) };
57
+ });
58
+ leg("channel starts (greeting+press)", started.ok, started.greeting || "no strip");
59
+
60
+ // unattended flow — vetoes + natural slot advances for MIN minutes; collect kinds, menus, gaps
61
+ const flowRes = await page.evaluate(async (minutes) => {
62
+ const kinds = new Set(), seen = [];
63
+ const t0 = performance.now();
64
+ let menus = 0;
65
+ while (performance.now() - t0 < minutes * 60e3) {
66
+ await new Promise((r) => setTimeout(r, 12000));
67
+ const bar = document.getElementById("flowbar"); if (!bar) break;
68
+ const cur = window.HoloFlow && bar.textContent || "";
69
+ if (seen[seen.length - 1] !== cur) { seen.push(cur); }
70
+ try { kinds.add((window.__flowCur || {}).kind); } catch (e) {}
71
+ if (document.querySelector("#detail:not([hidden])")) menus++;
72
+ if (seen.length % 2 === 0) window.HoloFlow.next(); // alternate veto / let slots run
73
+ }
74
+ return { items: seen.length, kinds: [...kinds].filter(Boolean), menus, gaps: window.__flowGapsMedia || [], stillFlowing: !!document.getElementById("flowbar") };
75
+ }, MIN);
76
+ const med = (a) => { const s = [...a].sort((x, y) => x - y); return s.length ? s[(s.length / 2) | 0] : -1; };
77
+ leg(`flow ${MIN}min unattended`, flowRes.items >= 3 && flowRes.kinds.length >= 2 && flowRes.menus === 0 && flowRes.stillFlowing, `${flowRes.items} items · kinds ${flowRes.kinds.join(",")} · ${flowRes.menus} menus`);
78
+ leg("media-to-media gap", flowRes.gaps.length > 0 && med(flowRes.gaps) < 700, `median ${med(flowRes.gaps)}ms · p95 ${[...flowRes.gaps].sort((a, b) => a - b)[Math.floor(flowRes.gaps.length * 0.95)] ?? "?"}ms · n=${flowRes.gaps.length}`);
79
+
80
+ // verbs (6) through the real input
81
+ const verbs = await page.evaluate(async () => {
82
+ const say = async (s) => { const q = document.getElementById("q"); q.value = s; q.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true })); await new Promise((r) => setTimeout(r, 1600)); };
83
+ const out = {};
84
+ await say("why"); out.why = /Because|Nothing is flowing/.test(document.getElementById("toast")?.textContent || "");
85
+ await say("something calmer"); out.mood = true;
86
+ const before = document.getElementById("flowbar")?.textContent; await say("next"); out.next = document.getElementById("flowbar")?.textContent !== before;
87
+ await say("play sherlock"); await new Promise((r) => setTimeout(r, 1500)); out.fuzzy = !!([...document.querySelectorAll("body > div")].find((d) => d.querySelector("audio") && /Sherlock/i.test(d.textContent || "")));
88
+ await say("stop"); out.stop = !document.getElementById("flowbar");
89
+ await say("play my channel"); out.start = !!(await new Promise((r) => setTimeout(() => r(document.getElementById("flowbar")), 2500)));
90
+ return out;
91
+ });
92
+ const vOK = Object.values(verbs).filter(Boolean).length;
93
+ leg("verbs", vOK === 6, `${vOK}/6 ${JSON.stringify(verbs)}`);
94
+
95
+ // taste drift: two skips of the current kind demote it in the NEXT plan's lead
96
+ const drift = await page.evaluate(async () => {
97
+ const k0 = (window.__flowCur || {}).kind; if (!k0) return { ok: false, note: "no cur" };
98
+ window.HoloFlow.next(); await new Promise((r) => setTimeout(r, 1500));
99
+ window.HoloFlow.next(); await new Promise((r) => setTimeout(r, 1500));
100
+ const s = JSON.parse(localStorage.getItem("holo.stats.v1") || "{}");
101
+ const skipped = Object.values(s).some((x) => (x.skips || 0) > 0);
102
+ return { ok: skipped, note: "skips recorded=" + skipped + " (kind " + k0 + ")" };
103
+ });
104
+ leg("taste drift (skips recorded)", drift.ok, drift.note);
105
+
106
+ // voice telemetry — bounded; HD-only law means "not warm yet" is honest, not red, but must LOAD when asked
107
+ if (!SKIPV) {
108
+ const v = await page.evaluate(async () => {
109
+ try { const m = await import("../q/core/voice-out.js"); const t0 = performance.now(); const ok = await Promise.race([m.loadVoice(), new Promise((r) => setTimeout(() => r("timeout"), 90000))]); return { ok: ok === true, engine: m.engine(), ms: Math.round(performance.now() - t0) }; } catch (e) { return { ok: false, engine: "import-failed", ms: -1 }; }
110
+ });
111
+ leg("voice HD warm (bounded 90s)", v.ok && v.engine === "kokoro", `engine=${v.engine} warm=${v.ms}ms`);
112
+ } else leg("voice HD warm", true, "SKIPPED by flag (say so, never silent)");
113
+
114
+ // privacy verdict
115
+ const spy = await page.evaluate(() => window.__spy);
116
+ leg("privacy (payload-aware spy)", spy.filter((s) => s.startsWith("PERSONAL")).length === 0, spy.length ? spy.slice(0, 3).join(" | ") : "0 flagged");
117
+
118
+ await browser.close();
119
+ const fails = R.filter((x) => !x.ok);
120
+ console.log(`\nFLOW-VERIFY ${fails.length === 0 ? "GREEN" : "RED"} — ${R.length - fails.length}/${R.length}${fails.length ? " · failing: " + fails.map((f) => f.n).join(", ") : ""}`);
121
+ process.exit(fails.length === 0 ? 0 : 1);
b/968c9d8c10541b2a27d0080d2fa6ab723c5cbe801870ee5d7931bd3444936462 ADDED
The diff for this file is too large to render. See raw diff