Skip to content

fix(buzz-acp): deliver standing context once per session - #4183

Open
sumit-m wants to merge 1 commit into
block:mainfrom
sumit-m:acp-v1-standing-context
Open

fix(buzz-acp): deliver standing context once per session#4183
sumit-m wants to merge 1 commit into
block:mainfrom
sumit-m:acp-v1-standing-context

Conversation

@sumit-m

@sumit-m sumit-m commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Rescoped 2026-08-06. This PR originally carried three changes. #4395 landed two of them upstream — _meta.systemPrompt delivery for claude-agent-acp, and reading that route in the activity log — so both have been dropped and this is now one commit. See the comment below for the detail.

Problem

An agent with no system-prompt route takes its standing context in the user message: base_prompt, the persona, team instructions, core memory and the canvas. format_prompt re-sent all of it on every turn.

That is roughly 9 KB of base_prompt.md alone, repeated per turn, plus persona and core. It makes the standing framing both the largest and the most recent text in the window, so it outweighs the conversation it exists to frame and evicts real channel history sooner. Related: #3126, #3242.

After #4395, has_system_prompt_support routes buzz-agent (top-level systemPrompt), claude-agent-acp (_meta.systemPrompt) and probed goose (its custom method) away from the user message. What remains on the legacy path is every adapter reporting protocolVersion: 1 without one of those routes — codex-acp today — and that path is what this PR fixes.

Change

New queue::StandingContext holds base prompt, persona, team instructions, core and canvas, and renders them in one place, in the order legacy agents have always seen them: [Base], [System], [Team Instructions], core, canvas. Both legacy dispatch paths — the initial message and the batch flush — go through it, so their section set and ordering cannot drift apart.

run_prompt_task tracks delivery with a standing_context_sent flag derived from is_new_session, and format_prompt gates on it. is_new_session comes from the session registry, which is cleared on every invalidation path, so a replacement session re-delivers rather than leaving the agent unbriefed.

Agents that do have a system-prompt route are unaffected: prepend_standing_for_legacy is called with an effective protocol version of 2 whenever has_system_prompt_support() is true, so nothing is prepended for them.

Behaviour changes worth calling out

  • The legacy initial message now carries the whole block, not just [Base] and the canvas. Currently a legacy agent takes its first action with no persona and no memory.
  • Its section order changed: the canvas was prepended last and so appeared first; it now matches the per-turn order (base, system, team, core, canvas).
  • Heartbeats are gated the same way — only the first tick of a heartbeat session carries [Base]. This is the most separable part of the change; happy to drop it if you would rather keep heartbeats as they are.
  • prepend_canvas_for_legacy is removed and prepend_base_for_legacy becomes prepend_standing_for_legacy. Its doc comment already noted that the gate lived in one place "so the heartbeat and initial-message dispatch paths can't drift apart again"; this extends that to the section set itself.

Codex

codex-acp is the adapter this now helps most: it reports protocolVersion: 1 and has no known _meta equivalent. We have no access to Codex models, so we could not check the adapter's behaviour or test against it directly. If it does expose a system-prompt route, the gate in has_system_prompt_support is the one place to add it.

Verification

Live on Windows against a local relay: turn 1 of a new session carried [Base], [System] and [Agent Memory — core]; turn 2 opened at [Context] with none of them.

cargo test -p buzz-acp on the rebased branch — 687 passing, 3 failing. The same 3 fail on clean main at the same commit (685 passing there; this branch adds 2 tests):

  • keepalive_resets_idle_past_deadline — wall-clock timing, passes in isolation
  • acp_steer_request_omits_expected_run_id_and_carries_session_and_prompt
  • goose_transport_wins_when_both_run_id_and_capability_present

The latter two are the unquoted Windows temp path in #3733, not this change.

@sumit-m
sumit-m requested a review from a team as a code owner August 1, 2026 21:37
Protocol-v1 agents received base prompt, persona, team instructions, core
memory and canvas on every turn. Deliver them in the session's first message
only; both legacy paths now render through a shared StandingContext.

Signed-off-by: sumit-m <33051892+sumit-m@users.noreply.github.com>
@sumit-m
sumit-m force-pushed the acp-v1-standing-context branch from 2b99468 to 286f70c Compare August 6, 2026 03:06
@sumit-m sumit-m changed the title fix(buzz-acp): deliver standing context once per session; use claude-agent-acp's system prompt route fix(buzz-acp): deliver standing context once per session Aug 6, 2026
@sumit-m

sumit-m commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current main and reduced to a single commit. Title updated to match.

Sections 2 and 3 of the description above are obsolete — #4395 landed both upstream while this sat open, and I have dropped my versions rather than carry a competing implementation:

  • Section 2 (_meta.systemPrompt for claude-agent-acp) — now SystemPromptTransport::ClaudeMeta. Upstream gates on the adapter name, which is the better call: the claude-code-acpclaude-agent-acp rename post-dates v0.6.0, so the name alone proves the version. That removes the version read this PR argued for.
  • Section 3 (showing the delivered prompt in the activity log)agentSessionTranscript.ts reads _meta.systemPrompt.append directly, so my sessionNewSystemPrompt helper is redundant.

What remains is section 1 only: queue::StandingContext plus the standing_context_sent gate, so an agent whose adapter reports protocolVersion: 1 receives base prompt, persona, team instructions, core memory and canvas once per session instead of on every turn. I could not find an equivalent on main, and it is the part that actually reclaims context window — the ~9 KB of base_prompt.md was being re-sent per turn.

The "Behaviour changes worth calling out" section still applies as written.

Verification on the rebased branch

cargo test -p buzz-acp — 687 passing, 3 failing. The same 3 fail on clean main at the same commit (685 passing there; this branch adds 2 tests):

  • keepalive_resets_idle_past_deadline — wall-clock timing, passes in isolation
  • acp_steer_request_omits_expected_run_id_and_carries_session_and_prompt
  • goose_transport_wins_when_both_run_id_and_capability_present

The latter two are the unquoted Windows temp path in #3733, not this change.

The live Windows verification in the description still stands for the user-message path (turn 1 of a new session carried [Base], [System] and [Agent Memory — core]; turn 2 opened at [Context] with none of them). The _meta path test is no longer this PR's concern.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant