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] })) }); }