fix(orchestrator): keep 3D places and prove bubble reading in e2e

page-graph3d.js keeps node places across refreshes; the e2e helpers wait for the shown screen. page-bubbles.js marks each fading bubble with data-read, the word count its reading reached, and the W6 e2e test records bubble changes with a MutationObserver instead of sampling frames: counts never go down, data-read is 13, the bubble fades and leaves, and the reading lasts 4.233 to 6.333 s. A mutation run that stopped or rewound the reading failed both widths.

🎯 Fix: 90 %
💥 Break: 5 %
💸 Cost: 100 %
⏱️ Time: 100 %
🛡️ Security: 100 %
🏆 Score: 86
🚦 Gate: ✅ 90/5, cost and time <= 100, security 100 %

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kevin Veen-BirkenbachandClaude Opus 5.5 committed 2026-11-12 14:10:04 +00:00
1 parent fd8fc08603
commit cd1a48b615
4 files changed
+92 -29

No files matched your search

+11 -2
View File
@@ -58,10 +58,18 @@
view.node.style.setProperty("--tail", `${fixed((middle - left) / scale)}px`);
}
function saidOf(item) {
return item.words || Bubbles.words(item.line);
}
function wordsRead(item) {
return item.started ? Bubbles.shown(saidOf(item).length, item.duration, (performance.now() - item.started) / 1000) : 0;
}
function drawBubble(entry, view) {
const {item} = entry;
const said = item.words || Bubbles.words(item.line);
const count = item.started ? Bubbles.shown(said.length, item.duration, (performance.now() - item.started) / 1000) : 0;
const said = saidOf(item);
const count = wordsRead(item);
const text = said.slice(0, count).join(" ") || "…";
if (view.words.textContent !== text) {
view.words.textContent = text;
@@ -81,6 +89,7 @@
for (const [item, view] of bubbles.shown) {
if (!kept.has(item)) {
bubbles.shown.delete(item);
view.node.dataset.read = String(wordsRead(item));
view.node.classList.add("leaving");
window.setTimeout(() => view.node.remove(), BUBBLE_FADE);
}
+11
View File
@@ -128,6 +128,16 @@
}
}
function keepPlaces(nodes) {
const laid = new Map(g3.graph.graphData().nodes.filter((node) => Number.isFinite(node.x)).map((node) => [node.id, node]));
for (const node of nodes) {
const old = laid.get(node.id);
if (old) {
Object.assign(node, {x: old.x, y: old.y, z: old.z});
}
}
}
function colors3d() {
const css = window.getComputedStyle(document.documentElement);
const value = (name) => css.getPropertyValue(`--${name}`).trim() || "#888";
@@ -394,6 +404,7 @@
}
}
const layout = layoutOf(tab);
keepPlaces(data.nodes);
place(data.nodes, layout);
g3.badges = new Map();
g3.faces = new Set();