mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
feat(desktop): surface Team Instructions as distinct observer section (#1759)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
This commit is contained in:
co-authored by
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7
parent
84ec63aa24
commit
56d822dc2c
@@ -1634,13 +1634,14 @@ test("buildTranscript same-seq different-timestamp session/new events both produ
|
||||
);
|
||||
});
|
||||
|
||||
test("buildTranscript four-section system prompt card is standalone with all sections; CheckCheck context contains only Buzz/thread context", () => {
|
||||
// Production scenario: harness emits [Base]/[System]/[Agent Memory — core]/[Channel Canvas]
|
||||
test("buildTranscript five-section system prompt card is standalone with all sections; CheckCheck context contains only Buzz/thread context", () => {
|
||||
// Production scenario: team-pack agent harness emits
|
||||
// [Base]/[System (with team delimiter)]/[Agent Memory — core]/[Channel Canvas]
|
||||
// in systemPrompt. The display layer must:
|
||||
// (a) Render it as a standalone single block (acpSource "session/new"),
|
||||
// NOT inside any turn's prompt bundle.
|
||||
// (b) The standalone item must carry all four sections in order:
|
||||
// Base → System → Core Memory → Channel Canvas.
|
||||
// (b) The standalone item must carry all five sections in order:
|
||||
// Base → System → Team Instructions → Core Memory → Channel Canvas.
|
||||
// (c) The prompt segment's context (CheckCheck dialog) must contain only
|
||||
// the session/prompt:context sections (Buzz event + Thread context),
|
||||
// never the system-prompt sections.
|
||||
@@ -1676,6 +1677,10 @@ test("buildTranscript four-section system prompt card is standalone with all sec
|
||||
"[System]",
|
||||
"Custom persona.",
|
||||
"",
|
||||
"---",
|
||||
"# Team Instructions",
|
||||
"Always tag on handoff.",
|
||||
"",
|
||||
"[Agent Memory — core]",
|
||||
"I am Duncan.",
|
||||
"",
|
||||
@@ -1740,14 +1745,14 @@ test("buildTranscript four-section system prompt card is standalone with all sec
|
||||
"exactly one standalone system-prompt single block",
|
||||
);
|
||||
|
||||
// (b) The standalone item carries all four sections in order.
|
||||
// (b) The standalone item carries all five sections in order.
|
||||
const spItem = systemPromptBlocks[0].item;
|
||||
assert.ok(spItem, "system-prompt block must have an item");
|
||||
const titles = (spItem.sections ?? []).map((s) => s.title);
|
||||
assert.deepEqual(
|
||||
titles,
|
||||
["Base", "System", "Core Memory", "Channel Canvas"],
|
||||
"system-prompt standalone card must carry Base → System → Core Memory → Channel Canvas in order",
|
||||
["Base", "System", "Team Instructions", "Core Memory", "Channel Canvas"],
|
||||
"system-prompt standalone card must carry Base → System → Team Instructions → Core Memory → Channel Canvas in order",
|
||||
);
|
||||
|
||||
// (c) The system-prompt item must NOT be inside any turn group.
|
||||
@@ -1781,7 +1786,7 @@ test("buildTranscript four-section system prompt card is standalone with all sec
|
||||
const contextSectionTitles = (promptContextItem.sections ?? []).map(
|
||||
(s) => s.title,
|
||||
);
|
||||
// Must have Buzz event and Thread context sections, NOT Base/System/Core Memory/Channel Canvas.
|
||||
// Must have Buzz event and Thread context sections, NOT Base/System/Team Instructions/Core Memory/Channel Canvas.
|
||||
assert.ok(
|
||||
contextSectionTitles.some((t) => t.toLowerCase().includes("buzz")),
|
||||
"prompt context must contain a Buzz event section",
|
||||
@@ -1791,9 +1796,10 @@ test("buildTranscript four-section system prompt card is standalone with all sec
|
||||
(t) =>
|
||||
t === "Base" ||
|
||||
t === "System" ||
|
||||
t === "Team Instructions" ||
|
||||
t === "Core Memory" ||
|
||||
t === "Channel Canvas",
|
||||
),
|
||||
"prompt context must NOT contain system-prompt sections (Base/System/Core Memory/Channel Canvas)",
|
||||
"prompt context must NOT contain system-prompt sections (Base/System/Team Instructions/Core Memory/Channel Canvas)",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -489,3 +489,195 @@ test("parseSystemPromptSections keeps an embedded canvas-like line literal when
|
||||
{ title: "Channel Canvas", body: "this IS the appended canvas" },
|
||||
]);
|
||||
});
|
||||
|
||||
// ── Team Instructions extraction ───────────────────────────────────────────
|
||||
|
||||
test("parseSystemPromptSections extracts Team Instructions as its own section after System (Base+System+Team)", () => {
|
||||
// compose_prompt() appends "\n\n---\n# Team Instructions\n{instructions}" to the persona body.
|
||||
// The canonical delimiter must split System from Team Instructions.
|
||||
const framed = [
|
||||
"[Base]",
|
||||
"You are a helpful assistant.",
|
||||
"",
|
||||
"[System]",
|
||||
"You are Agent X.",
|
||||
"",
|
||||
"---",
|
||||
"# Team Instructions",
|
||||
"Always respond in markdown.",
|
||||
].join("\n");
|
||||
const sections = parseSystemPromptSections(framed);
|
||||
assert.deepEqual(sections, [
|
||||
{ title: "Base", body: "You are a helpful assistant." },
|
||||
{ title: "System", body: "You are Agent X." },
|
||||
{ title: "Team Instructions", body: "Always respond in markdown." },
|
||||
]);
|
||||
});
|
||||
|
||||
test("parseSystemPromptSections extracts Team Instructions after System-only (no Base)", () => {
|
||||
// When [Base] is absent the [System] header starts the input.
|
||||
const framed = [
|
||||
"[System]",
|
||||
"You are Agent Y.",
|
||||
"",
|
||||
"---",
|
||||
"# Team Instructions",
|
||||
"Keep responses concise.",
|
||||
].join("\n");
|
||||
const sections = parseSystemPromptSections(framed);
|
||||
assert.deepEqual(sections, [
|
||||
{ title: "System", body: "You are Agent Y." },
|
||||
{ title: "Team Instructions", body: "Keep responses concise." },
|
||||
]);
|
||||
});
|
||||
|
||||
test("parseSystemPromptSections extracts Team Instructions with Core Memory and Channel Canvas (full 5-section shape)", () => {
|
||||
// Full production-shaped system prompt: Base → System → Team Instructions → Core Memory → Channel Canvas.
|
||||
// compose_prompt() produces the canonical delimiter; with_core() and with_canvas() append their frames.
|
||||
const framed = [
|
||||
"[Base]",
|
||||
"You are a helpful AI assistant running in Buzz.",
|
||||
"",
|
||||
"[System]",
|
||||
"You are Observer Agent. You coordinate multi-agent workflows.",
|
||||
"",
|
||||
"---",
|
||||
"# Team Instructions",
|
||||
"Always tag on handoff.",
|
||||
"Never expand scope without approval.",
|
||||
"",
|
||||
"[Agent Memory — core]",
|
||||
"I am Observer Agent.",
|
||||
"## Lessons Learned",
|
||||
"Always tag on handoff.",
|
||||
"",
|
||||
"[Channel Canvas]",
|
||||
"Canvas revision (event ID): a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
|
||||
"Last modified: 2026-07-11T10:00:00Z",
|
||||
"Fetch current content with: buzz canvas get --channel 94a444a4-c0a3-5966-ab05-530c6ddc2301",
|
||||
].join("\n");
|
||||
const sections = parseSystemPromptSections(framed);
|
||||
assert.deepEqual(sections, [
|
||||
{ title: "Base", body: "You are a helpful AI assistant running in Buzz." },
|
||||
{
|
||||
title: "System",
|
||||
body: "You are Observer Agent. You coordinate multi-agent workflows.",
|
||||
},
|
||||
{
|
||||
title: "Team Instructions",
|
||||
body: "Always tag on handoff.\nNever expand scope without approval.",
|
||||
},
|
||||
{
|
||||
title: "Core Memory",
|
||||
body: "I am Observer Agent.\n## Lessons Learned\nAlways tag on handoff.",
|
||||
},
|
||||
{
|
||||
title: "Channel Canvas",
|
||||
body: "Canvas revision (event ID): a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2\nLast modified: 2026-07-11T10:00:00Z\nFetch current content with: buzz canvas get --channel 94a444a4-c0a3-5966-ab05-530c6ddc2301",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("parseSystemPromptSections does NOT split on a bare '---' without the '# Team Instructions' heading", () => {
|
||||
// A horizontal rule alone inside a persona body is kept literal.
|
||||
const framed = [
|
||||
"[System]",
|
||||
"Some text.",
|
||||
"",
|
||||
"---",
|
||||
"",
|
||||
"More text after a separator.",
|
||||
].join("\n");
|
||||
const sections = parseSystemPromptSections(framed);
|
||||
assert.deepEqual(sections, [
|
||||
{
|
||||
title: "System",
|
||||
body: "Some text.\n\n---\n\nMore text after a separator.",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("parseSystemPromptSections does NOT split on '# Team Instructions' with only a single preceding newline", () => {
|
||||
// The canonical delimiter requires \n\n---\n before the heading.
|
||||
// A single-newline variant is kept literal inside System.
|
||||
const framed = [
|
||||
"[System]",
|
||||
"Persona preamble.",
|
||||
"---",
|
||||
"# Team Instructions",
|
||||
"These look canonical but lack the double newline before ---.",
|
||||
].join("\n");
|
||||
const sections = parseSystemPromptSections(framed);
|
||||
assert.deepEqual(sections, [
|
||||
{
|
||||
title: "System",
|
||||
body: "Persona preamble.\n---\n# Team Instructions\nThese look canonical but lack the double newline before ---.",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("parseSystemPromptSections does NOT split on '# Team Instructions' heading without the '---' separator", () => {
|
||||
// Only the exact composed form \n\n---\n# Team Instructions\n triggers the split.
|
||||
const framed = [
|
||||
"[System]",
|
||||
"Persona text.",
|
||||
"",
|
||||
"# Team Instructions",
|
||||
"This is just a heading in the persona body.",
|
||||
].join("\n");
|
||||
const sections = parseSystemPromptSections(framed);
|
||||
assert.deepEqual(sections, [
|
||||
{
|
||||
title: "System",
|
||||
body: "Persona text.\n\n# Team Instructions\nThis is just a heading in the persona body.",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("parseSystemPromptSections non-team persona (no delimiter) is unaffected", () => {
|
||||
// A standard Base+System prompt without any team instructions must produce
|
||||
// exactly two sections — no Team Instructions row added.
|
||||
const framed = [
|
||||
"[Base]",
|
||||
"You are a helpful assistant.",
|
||||
"",
|
||||
"[System]",
|
||||
"You are a coding assistant.",
|
||||
].join("\n");
|
||||
const sections = parseSystemPromptSections(framed);
|
||||
assert.deepEqual(sections, [
|
||||
{ title: "Base", body: "You are a helpful assistant." },
|
||||
{ title: "System", body: "You are a coding assistant." },
|
||||
]);
|
||||
});
|
||||
|
||||
test("parseSystemPromptSections splits on the LAST occurrence of the canonical delimiter (embedded lookalike + real appended team suffix)", () => {
|
||||
// A persona body may itself contain the exact delimiter string verbatim
|
||||
// (e.g. an example or a quoted earlier instruction set). compose_prompt()
|
||||
// always APPENDS the real team instructions, so the LAST occurrence is the
|
||||
// authoritative producer boundary. The earlier embedded occurrence must stay
|
||||
// inside the System body.
|
||||
const framed = [
|
||||
"[System]",
|
||||
"Here is an example of team framing:",
|
||||
"",
|
||||
"---",
|
||||
"# Team Instructions",
|
||||
"These are fake — embedded in the persona prose.",
|
||||
"",
|
||||
"---",
|
||||
"# Team Instructions",
|
||||
"These are real — appended by compose_prompt().",
|
||||
].join("\n");
|
||||
const sections = parseSystemPromptSections(framed);
|
||||
assert.deepEqual(sections, [
|
||||
{
|
||||
title: "System",
|
||||
body: "Here is an example of team framing:\n\n---\n# Team Instructions\nThese are fake — embedded in the persona prose.",
|
||||
},
|
||||
{
|
||||
title: "Team Instructions",
|
||||
body: "These are real — appended by compose_prompt().",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -57,15 +57,19 @@ export function parsePromptText(text: string): {
|
||||
|
||||
/**
|
||||
* Split the framed `session/new` `systemPrompt` into its `Base`/`System`/
|
||||
* `Core Memory`/`Channel Canvas` sub-sections deterministically.
|
||||
* `Team Instructions`/`Core Memory`/`Channel Canvas` sub-sections
|
||||
* deterministically.
|
||||
*
|
||||
* The harness composes the value in order:
|
||||
* `[Base]\n{base}\n\n[System]\n{persona}\n\n[Agent Memory — core]\n{core}\n\n[Channel Canvas]\n{canvas}`
|
||||
* with any section omitted when absent. Extraction runs in reverse producer
|
||||
* order so that each `lastIndexOf` search operates on the full input and
|
||||
* each extraction boundary is unambiguous.
|
||||
* with any section omitted when absent. For team-pack agents the persona body
|
||||
* already contains the pack-level instructions appended by `compose_prompt()`
|
||||
* in `buzz-persona/src/resolve.rs`:
|
||||
* `{persona_body}\n\n---\n# Team Instructions\n{pack_instructions}`
|
||||
* Extraction runs in reverse producer order so that each `lastIndexOf` search
|
||||
* operates on the full input and each extraction boundary is unambiguous.
|
||||
*
|
||||
* Three extraction passes before Base/System parsing:
|
||||
* Four extraction passes before Base/System parsing:
|
||||
*
|
||||
* 1. **Canvas** (`[Channel Canvas]`): appended last by `with_canvas()`.
|
||||
* - Start-of-string: canvas-only input.
|
||||
@@ -79,6 +83,15 @@ export function parsePromptText(text: string): {
|
||||
* 3. **Base/System**: remainder after canvas and core extraction.
|
||||
* Split on the first `\n[System]\n` boundary; no embedded `[...]` line
|
||||
* inside a body can start a new section.
|
||||
*
|
||||
* 4. **Team Instructions**: if the `System` body contains the exact canonical
|
||||
* delimiter `\n\n---\n# Team Instructions\n` (produced by `compose_prompt()`),
|
||||
* the body is split at the **last** occurrence of that boundary (same
|
||||
* last-occurrence guard as canvas and core). The text before becomes the
|
||||
* `System` body; the text after becomes a `Team Instructions` section
|
||||
* inserted immediately after `System`. Non-canonical lookalikes (bare `---`
|
||||
* without the heading, a `# Team Instructions` on a different line, or only
|
||||
* a single preceding newline) are kept literal inside `System`.
|
||||
*/
|
||||
export function parseSystemPromptSections(
|
||||
systemPrompt: string,
|
||||
@@ -121,11 +134,36 @@ export function parseSystemPromptSections(
|
||||
}
|
||||
|
||||
// ── 3. Parse Base/System from the remaining prefix ────────────────────────
|
||||
// The canonical team-instructions delimiter produced by compose_prompt() in
|
||||
// buzz-persona/src/resolve.rs:
|
||||
// format!("{persona_prompt}\n\n---\n# Team Instructions\n{instructions}")
|
||||
const TEAM_DELIMITER = "\n\n---\n# Team Instructions\n";
|
||||
|
||||
// splitSystemBody: split a raw [System] body string at the last occurrence
|
||||
// of the canonical team delimiter, returning { systemBody, teamBody | null }.
|
||||
// Using lastIndexOf mirrors the canvas/core last-occurrence guard: a persona
|
||||
// author can embed an exact delimiter-like passage inside the persona body;
|
||||
// only the final occurrence is the producer boundary appended by compose_prompt().
|
||||
function splitSystemBody(raw: string): {
|
||||
systemBody: string;
|
||||
teamBody: string | null;
|
||||
} {
|
||||
const at = raw.lastIndexOf(TEAM_DELIMITER);
|
||||
if (at === -1) return { systemBody: raw.trim(), teamBody: null };
|
||||
return {
|
||||
systemBody: raw.slice(0, at).trim(),
|
||||
teamBody: raw.slice(at + TEAM_DELIMITER.length).trim() || null,
|
||||
};
|
||||
}
|
||||
|
||||
const baseAndSystem = remainder;
|
||||
if (baseAndSystem) {
|
||||
if (baseAndSystem.startsWith("[System]\n")) {
|
||||
const body = baseAndSystem.slice("[System]\n".length).trim();
|
||||
if (body) sections.push({ title: "System", body });
|
||||
const raw = baseAndSystem.slice("[System]\n".length);
|
||||
const { systemBody, teamBody } = splitSystemBody(raw);
|
||||
if (systemBody) sections.push({ title: "System", body: systemBody });
|
||||
if (teamBody)
|
||||
sections.push({ title: "Team Instructions", body: teamBody });
|
||||
} else {
|
||||
const marker = "\n[System]\n";
|
||||
const at = baseAndSystem.indexOf(marker);
|
||||
@@ -134,8 +172,11 @@ export function parseSystemPromptSections(
|
||||
if (baseBody) sections.push({ title: "Base", body: baseBody });
|
||||
|
||||
if (at !== -1) {
|
||||
const systemBody = baseAndSystem.slice(at + marker.length).trim();
|
||||
const raw = baseAndSystem.slice(at + marker.length);
|
||||
const { systemBody, teamBody } = splitSystemBody(raw);
|
||||
if (systemBody) sections.push({ title: "System", body: systemBody });
|
||||
if (teamBody)
|
||||
sections.push({ title: "Team Instructions", body: teamBody });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,7 +418,7 @@ test.describe("observer feed screenshots", () => {
|
||||
method: "session/new",
|
||||
params: {
|
||||
systemPrompt:
|
||||
"[Base]\nYou are a helpful AI assistant running in Buzz.\n\n[System]\nYou are Observer Agent. You coordinate multi-agent workflows in the #agents channel.\n\n[Agent Memory — core]\nI am Observer Agent.\n## Lessons Learned\nAlways tag on handoff.\n\n[Channel Canvas]\nCanvas revision (event ID): a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2\nLast modified: 2026-07-11T10:00:00Z\nFetch current content with: buzz canvas get --channel 94a444a4-c0a3-5966-ab05-530c6ddc2301",
|
||||
"[Base]\nYou are a helpful AI assistant running in Buzz.\n\n[System]\nYou are Observer Agent. You coordinate multi-agent workflows in the #agents channel.\n\n---\n# Team Instructions\nAlways tag on handoff.\n\n[Agent Memory — core]\nI am Observer Agent.\n## Lessons Learned\nAlways tag on handoff.\n\n[Channel Canvas]\nCanvas revision (event ID): a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2\nLast modified: 2026-07-11T10:00:00Z\nFetch current content with: buzz canvas get --channel 94a444a4-c0a3-5966-ab05-530c6ddc2301",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -428,7 +428,7 @@ test.describe("observer feed screenshots", () => {
|
||||
timeout: 5_000,
|
||||
});
|
||||
|
||||
// All four section headings must be present in the card — expand it first.
|
||||
// All five section headings must be present in the card — expand it first.
|
||||
await feedPanel.getByTestId("transcript-metadata-item").evaluate((el) => {
|
||||
if (el.tagName === "DETAILS") (el as HTMLDetailsElement).open = true;
|
||||
for (const details of el.querySelectorAll("details")) {
|
||||
@@ -439,6 +439,9 @@ test.describe("observer feed screenshots", () => {
|
||||
await expect(feedPanel.getByText("System", { exact: true })).toBeVisible({
|
||||
timeout: 5_000,
|
||||
});
|
||||
await expect(feedPanel.getByText("Team Instructions")).toBeVisible({
|
||||
timeout: 5_000,
|
||||
});
|
||||
await expect(feedPanel.getByText("Core Memory")).toBeVisible({
|
||||
timeout: 5_000,
|
||||
});
|
||||
@@ -664,12 +667,12 @@ test.describe("observer feed screenshots", () => {
|
||||
await installMockBridge(page, { managedAgents: MANAGED_AGENTS });
|
||||
const feedPanel = await openObserverFeedPanel(page, OBSERVER_AGENT_PUBKEY);
|
||||
|
||||
// Full realistic pool.rs first-turn wire sequence:
|
||||
// Full realistic pool.rs first-turn wire sequence for a team-pack agent:
|
||||
// turn_started → session/new → session_resolved → session/prompt
|
||||
// Verifies the consolidated presentation: session/new.systemPrompt always
|
||||
// renders as a standalone top-level "System prompt" card (never injected into
|
||||
// the CheckCheck bundle). The CheckCheck dialog contains only per-turn context
|
||||
// (Buzz event / Thread context) — no Base/System/Core Memory/Channel Canvas sections.
|
||||
// (Buzz event / Thread context) — no Base/System/Team Instructions/Core Memory/Channel Canvas sections.
|
||||
await seedObserverEvents(page, OBSERVER_AGENT_PUBKEY, [
|
||||
{
|
||||
seq: 1,
|
||||
@@ -695,7 +698,7 @@ test.describe("observer feed screenshots", () => {
|
||||
method: "session/new",
|
||||
params: {
|
||||
systemPrompt:
|
||||
"[Base]\nYou are a helpful AI assistant running in Buzz.\n\n[System]\nYou are Observer Agent. You coordinate multi-agent workflows in the #agents channel.\n\n[Agent Memory — core]\nI am Observer Agent.\n## Lessons Learned\nAlways tag on handoff.\n\n[Channel Canvas]\nCanvas revision (event ID): a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2\nLast modified: 2026-07-11T10:00:00Z\nFetch current content with: buzz canvas get --channel 94a444a4-c0a3-5966-ab05-530c6ddc2301",
|
||||
"[Base]\nYou are a helpful AI assistant running in Buzz.\n\n[System]\nYou are Observer Agent. You coordinate multi-agent workflows in the #agents channel.\n\n---\n# Team Instructions\nAlways tag on handoff.\n\n[Agent Memory — core]\nI am Observer Agent.\n## Lessons Learned\nAlways tag on handoff.\n\n[Channel Canvas]\nCanvas revision (event ID): a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2\nLast modified: 2026-07-11T10:00:00Z\nFetch current content with: buzz canvas get --channel 94a444a4-c0a3-5966-ab05-530c6ddc2301",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -752,8 +755,8 @@ test.describe("observer feed screenshots", () => {
|
||||
timeout: 5_000,
|
||||
});
|
||||
|
||||
// The standalone card shows "4 sections" collapsed — expand it to reveal
|
||||
// the section headings, then assert all four are present.
|
||||
// The standalone card shows "5 sections" collapsed — expand it to reveal
|
||||
// the section headings, then assert all five are present.
|
||||
await feedPanel.getByTestId("transcript-metadata-item").evaluate((el) => {
|
||||
if (el.tagName === "DETAILS") (el as HTMLDetailsElement).open = true;
|
||||
for (const details of el.querySelectorAll("details")) {
|
||||
@@ -764,6 +767,9 @@ test.describe("observer feed screenshots", () => {
|
||||
await expect(feedPanel.getByText("System", { exact: true })).toBeVisible({
|
||||
timeout: 5_000,
|
||||
});
|
||||
await expect(feedPanel.getByText("Team Instructions")).toBeVisible({
|
||||
timeout: 5_000,
|
||||
});
|
||||
await expect(feedPanel.getByText("Core Memory")).toBeVisible({
|
||||
timeout: 5_000,
|
||||
});
|
||||
@@ -776,7 +782,7 @@ test.describe("observer feed screenshots", () => {
|
||||
await expect(feedPanel.getByText("Prompt context")).toHaveCount(0);
|
||||
|
||||
// Open the CheckCheck dialog: it contains ONLY per-turn context sections
|
||||
// (Buzz event, Thread context). Base/System/Core Memory/Channel Canvas must NOT appear.
|
||||
// (Buzz event, Thread context). Base/System/Team Instructions/Core Memory/Channel Canvas must NOT appear.
|
||||
await feedPanel.getByTestId("transcript-prompt-context-toggle").click();
|
||||
const dialog = page.getByRole("dialog");
|
||||
await expect(dialog).toBeVisible({ timeout: 5_000 });
|
||||
@@ -788,10 +794,16 @@ test.describe("observer feed screenshots", () => {
|
||||
expect(sectionTitles.length).toBe(2);
|
||||
expect(sectionTitles[0]).toContain("Buzz event");
|
||||
expect(sectionTitles[1]).toContain("Thread context");
|
||||
// Collect all article heading text and assert none of the four
|
||||
// Collect all article heading text and assert none of the five
|
||||
// system-prompt section labels appear — including exact "System" which
|
||||
// would be ambiguous via substring search on the full dialog text.
|
||||
const forbidden = ["Base", "System", "Core Memory", "Channel Canvas"];
|
||||
const forbidden = [
|
||||
"Base",
|
||||
"System",
|
||||
"Team Instructions",
|
||||
"Core Memory",
|
||||
"Channel Canvas",
|
||||
];
|
||||
for (const title of sectionTitles) {
|
||||
for (const label of forbidden) {
|
||||
expect(title).not.toContain(label);
|
||||
|
||||
Reference in New Issue
Block a user