From 05be9ca8dcdcca8971d84ad7fb01f297fb9e2b13 Mon Sep 17 00:00:00 2001 From: shad Date: Sun, 14 Jun 2026 15:14:59 +0400 Subject: [PATCH] fix(oracle-r4-caveats): wizard rewires presentation edges, chat QA proves, Pi leak Three Oracle round-7 caveats: 1. Wizard view replacement now deletes old presentation edges before creating new field-based ones. Previously handleDataSave added new edges additively, so runtime could still hit the generic Notes view. 2. Chat sidebar QA assertion was vacuously true (previewRows >= 0 && timeBadges >= 0). Now: when threads exist, preview count must equal thread count AND at least one relative timestamp must render. 3. Agent composer placeholder rebranded from 'Ask Pi...' to 'Ask the assistant...'. Closes residual Pi/LLM framing leak. --- qa/full_dogfood.mjs | 9 ++++++++- src/scenes/Agent.tsx | 2 +- src/scenes/Wizard.tsx | 24 +++++++++++++++++++++--- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/qa/full_dogfood.mjs b/qa/full_dogfood.mjs index 864aa99..334f3c5 100644 --- a/qa/full_dogfood.mjs +++ b/qa/full_dogfood.mjs @@ -236,9 +236,16 @@ record("llm_unconfigured_falls_back_gracefully", unmatchedReply.length > 0 && !/ // other personas have written since last open. await p.locator(".tab", { hasText: /^\s*Chat\s*$/ }).click(); await p.waitForTimeout(2500); +await p.waitForTimeout(2000); +const threadRowCount = await p.locator(".chat-thread-row").count(); const previewRows = await p.locator(".chat-thread-meta").count(); const timeBadges = await p.locator(".chat-thread-time").count(); -record("chat_sidebar_shows_previews_and_timestamps", previewRows >= 0 && timeBadges >= 0, `previews=${previewRows}, times=${timeBadges}`); +const sidebarHonest = threadRowCount === 0 || (previewRows === threadRowCount && timeBadges >= 1); +record( + "chat_sidebar_shows_previews_and_timestamps", + sidebarHonest, + `threads=${threadRowCount}, previews=${previewRows}, times=${timeBadges}` +); // Summarise const fails = results.filter((r) => !r.ok); diff --git a/src/scenes/Agent.tsx b/src/scenes/Agent.tsx index e72596a..d3ef3af 100644 --- a/src/scenes/Agent.tsx +++ b/src/scenes/Agent.tsx @@ -110,7 +110,7 @@ export default function Agent() { submit(draft); } }} - placeholder="Ask Pi to navigate, start a process, or message a teammate. Enter to send." + placeholder="Ask the assistant to navigate, start a process, or message a teammate. Enter to send." rows={2} disabled={working} /> diff --git a/src/scenes/Wizard.tsx b/src/scenes/Wizard.tsx index 72ab3cb..cc29ca0 100644 --- a/src/scenes/Wizard.tsx +++ b/src/scenes/Wizard.tsx @@ -149,14 +149,32 @@ export default function Wizard() { }); if (draft.fields.length > 0 && draft.nodes.length > 0) { + const token = sessionStorage.getItem("fm.mc.token.v1") || ""; + const oldEdgeKeys: string[] = []; + for (const n of draft.nodes) { + try { + const r = await fetch( + `${(await import("../lib/api")).api.config.baseUrl}/api/ea2/edges/defines?from=flow/${n._key}&limit=50`, + { headers: { Authorization: `Bearer ${token}` } }, + ); + if (!r.ok) continue; + const items = ((await r.json())?.items || []) as any[]; + for (const e of items) { + if (e?.role === "presentation" && e?._key) oldEdgeKeys.push(e._key); + } + } catch { /* ignore */ } + } + const replaceOps: BatchOperation[] = draft.nodes.map(n => wizardApi.ops.createView({ display_name: n.display_name, fields: draft.fields }) ); const res = await wizardApi.applyBatch(draft.flowKey, replaceOps, actor); const newViewKeys = (res?.result?.ops || []).map((o: any) => o.key); - const wireOps: BatchOperation[] = draft.nodes.map((n, i) => - wizardApi.ops.presentationEdge(n._key, newViewKeys[i]) - ); + + const wireOps: BatchOperation[] = [ + ...oldEdgeKeys.map((k): BatchOperation => ({ op: "delete_edge", edge_coll: "defines", key: k })), + ...draft.nodes.map((n, i) => wizardApi.ops.presentationEdge(n._key, newViewKeys[i])), + ]; if (wireOps.length) await wizardApi.applyBatch(draft.flowKey, wireOps, actor); updateDraft({ nodes: draft.nodes.map((n, i) => ({ ...n, viewKey: newViewKeys[i] })) }); }