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:
Will Pfleger
2026-07-11 17:19:30 -04:00
committed by GitHub
co-authored by npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7
parent 84ec63aa24
commit 56d822dc2c
4 changed files with 278 additions and 27 deletions
@@ -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);