Skip to content

feat(agents): give plugin agent types the Buzz side of a turn - #2364

Merged
joahg merged 2 commits into
block:mainfrom
joahg:joah/agent-harness-kit
Nov 4, 2026
Merged

joahg merged 2 commits into
block:mainfrom
joahg:joah/agent-harness-kit

Conversation

@joahg

@joahg joahg commented Nov 4, 2026 •

Copy link
Copy Markdown
Member

What

ctx.agents.harness gives an agent type that a plugin brings the Buzz side of each turn, built the way the bundled harnesses build it. The harness still owns its process, sessions and saved state.

  • conversation(delivery, scope) admits a mention, plugin wake or attention wake and freezes where it goes: the key (the thread, or the channel; a DM or Solo chat is always one), the root, whether replies go at the top level, and a where label for titles. It returns undefined when the harness must ignore the delivery: any other trigger, a mention that isn't the owner's own or attested by them (workflow mentions never are, per fix(security): close workflow, Canvas, upload, and broker gaps #2360), no channel, or a wake with no conversation to go to.
  • systemPrompt(delivery, { scope, cwd, instructions, tools? }) is Buzz's system prompt: base prompt, session model, instructions, teams, core memory and repositories folder. tools: false adds a section for sessions without Buzz tools, whose final message the harness posts as the reply.
  • turnPrompt(delivery, conversation, { cursor? }) returns { text, cursor, restore }: what the session has not been shown, the event, plugins' notes and the tools' end-of-turn note. The cursor is opaque: the plugin saves it with its session and hands it back next turn (none means a new session). In a channel-scoped conversation it keeps a mark per thread, like claude-code's contextRoot, so a turn in one thread never hides another thread's earlier messages. A resumed turn gets the resume note. restore() puts the notes back for a turn that never started, and asking again takes them again.
  • standalonePrompt(agent, text) gives a watch's or timer's turn its routing context.
  • tools(agent, conversation?, { read? }) answers the Buzz tools over MCP, for ctx.host.serveMcp.
  • reply(agent, conversation, text, { notifyOwner? }) posts as the agent in the thread, or at the top level.

The author package also exports the types a harness needs: AgentHandle, Delivery, StandaloneTurn, TurnResult, McpHandler, HostMcpServer and the kit's types. docs/plugin-architecture.md documents the kit.

Why

An installed plugin can already register an agent type and start its CLI with host.spawn and host.serveMcp. But the Buzz side of a turn only existed inside app internals the author package doesn't expose, so every harness had to be bundled. The first user is an Amp harness that ships as a plugin in squareup/agents buzz-plugins (AGNTOPS-716), replacing the bundled approach in #1539. Its agents can also run their Amp threads in orbs.

Authority

The kit grants nothing ctx.agents and ctx.host don't already give the plugin:

  • It acts through the AgentHandle the plugin was delivered.
  • It reads memory through the owner's relay, as agents.relay already allows.
  • It reads files for uploads only through the read the plugin passes, which comes from the plugin's own host.files grant. Without read, uploading a local file fails.

Bundled harnesses are unchanged.

Validation (at 58ed3ee, on main 063fccd)

  • src/features/agent-harness/kit.test.ts has 17 tests covering:
    • owner gating, including refused workflow mentions and other triggers
    • conversation keys for thread, channel scope, DM and Solo
    • wakes: landing, and dropping one with nowhere to go
    • turn prompts: unseen messages, notes, restore, asking again, resume, per-thread marks in a channel conversation (thread A then thread B), an unreadable cursor
    • the system prompt, with and without tools
    • reply tags in a thread and in Solo
    • the tools handler: tools/list, initialize instructions, a message with no jsonrpc field, and local-file reads going only through the plugin's read
  • src/plugins/author.test.mjs generates the author package and typechecks a consumer that registers an agent type using the kit.
  • pnpm check passes (Biome, tsc, design typecheck and design checks).
  • Full vitest run: 11 failures across 9 files, all timeouts while the machine was loaded. All but one pass when rerun alone. The exception, browser-host/media-preparation.integration.test.mjs (tiled HEIC), fails on main on this machine too.

Deferred: an in-app run of a plugin harness built on the kit. That will be the Amp plugin, which needs this change in a desktop build.

🤖 Generated with Claude Code

An installed plugin can register an agent type and start its harness
with host.spawn and host.serveMcp, but the Buzz side of a turn lived in
app internals the author package doesn't expose: owner gating, the
conversation a mention or wake goes to, the system and turn prompts, the
Buzz tools as an MCP handler, and replies. `agents.harness` gives a
plugin's harness those, built the way the bundled harnesses build them,
and the author package exports the kit and the agent-type types a
harness needs (AgentHandle, Delivery, StandaloneTurn, TurnResult,
McpHandler, HostMcpServer).

The kit grants nothing ctx.agents and ctx.host don't already give the
plugin: it acts through the AgentHandle the plugin was delivered, reads
memory through the owner's relay as agents.relay does, and reads files
for uploads only through a `read` the plugin passes, from its own
host.files grant. Bundled harnesses are unchanged.

AGNTOPS-716

Co-authored-by: Hertz <c6741012416ef5c49a865db79682113385c5fb998e8c890234fb442b59e22748@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
@joahg
joahg marked this pull request as ready for review November 4, 2026 01:10
@joahg
joahg requested a review from a team as a code owner November 4, 2026 01:10

@kazemek21 kazemek21 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kyle reviewed the PR and the Nexus review-tracker notes. Approving with the non-blocking comment below.

Posted by Codex (gpt-6.1-sol) on behalf of kylez.

Comment thread src/features/agent-harness/kit.ts Resolved
In a channel-scoped conversation the kit read only the current thread but
returned a channel-wide seen, so a turn in thread A hid thread B's earlier
messages from B's first turn. turnPrompt now takes and returns an opaque
cursor that keeps a mark for each thread, as claude-code's contextRoot
watermarks do.

AGNTOPS-716

Co-authored-by: Hertz <c6741012416ef5c49a865db79682113385c5fb998e8c890234fb442b59e22748@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
@joahg
joahg merged commit 187aab3 into block:main Nov 4, 2026
41 checks passed
gilgtm added a commit that referenced this pull request Nov 4, 2026
* origin/main: (52 commits)
  feat(agents): give plugin agent types the Buzz side of a turn (#2364)
  feat(plugins): pin buzz://plugin-install links to a path and commit (#2315)
  feat(plugins): tint currentColor SVG page icons like host icons (#2314)
  feat(notifications): add a soft notification sound (#1536)
  feat(agents): let agents propose a profile picture (#2267)
  fix(plugins): default reminders and workflows off for fresh installs (#2291)
  Show all reactors in compact, separate emoji capsules (#2179)
  fix(panels): remember page split widths per workspace (#2264)
  feat(search): browse open channels from a Channels tab in search (#2164)
  perf(relay): page combined replay filters after a reconnect (#2308)
  fix(composer): make native Mac text shortcuts work past link chips (#2390)
  perf(relay): read creator proofs for many channels per filter (#2312)
  fix(channels): let published leave and delete confirm through access loss (#2357)
  fix(media): save native context menu media downloads and hide loopback links on macOS (#2131)
  fix(links): say why a message preview is unavailable, and preview unjoined public channels (#2136)
  test(browser): serve emoji artwork in the reaction hit-area journey (#2138)
  test(browser): click a reply link only after its row settles (#2358)
  fix(agents): restore delegated workflow mentions (#2399)
  feat(identity): always back up keys in managed builds (#2398)
  test(threads): render before checking the reader across activity resizes (#2389)
  ...

Signed-off-by: Gil Birman <gil@squareup.com>
Co-authored-by: Claude <noreply@anthropic.com>

# Conflicts:
#	src/plugins/author.test.mjs
zandt-square added a commit to zandt-square/buzz-app that referenced this pull request Nov 4, 2026
Co-authored-by: Fizz <fbf1920f313ab37ffa4c517e2437e247fe1423378e077eccc4e29f15625b9918@buzz.block.builderlab.xyz>

* origin/main:
  fix(mermaid): cap diagram output size (block#2231)
  feat(agents): give plugin agent types the Buzz side of a turn (block#2364)
  feat(plugins): pin buzz://plugin-install links to a path and commit (block#2315)
  feat(plugins): tint currentColor SVG page icons like host icons (block#2314)
  feat(notifications): add a soft notification sound (block#1536)
  feat(agents): let agents propose a profile picture (block#2267)
  fix(plugins): default reminders and workflows off for fresh installs (block#2291)

Signed-off-by: Zandt <zandt@squareup.com>
loganj added a commit that referenced this pull request Nov 4, 2026
The kit test "refuses a mention from anyone else, and triggers that are
not its" fails on main. Its workflow fixture was a message signed by the
owner with a `buzz:workflow` tag. #2364 wrote it against #2360, whose
`fromOwner` refused any event with that tag. #2399 merged first and
restored the older `fromOwner`, which admits any event the owner signs,
so the fixture became an owner message and the kit admitted it.

The kit itself still calls `fromOwner` without a workflow check, so a
real relay workflow mention is refused. Make the fixture that real
mention: signed by the community's workflow key, owned by the owner and
aimed at this agent, with the workflow key set on the session. The
bundled harnesses admit this event; the kit must not. Wiring the
bundled workflow check into the kit makes the test fail.

Before the refusal, assert that the bundled check admits the fixture,
so the negative case cannot pass for the wrong reason again. Say at the
call that the missing workflow check is deliberate, and fix the workflow
docs, which named only Claude Code and Codex.

Signed-off-by: Logan Johnson <loganj@squareup.com>
Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wpfleger96 added a commit that referenced this pull request Nov 4, 2026
…w-tab

* origin/main: (41 commits)
  [3/5] Add protection to the Claude harness plugin (#1878)
  fix(settings): keep the bottom padding on long sections (#2361)
  fix(mermaid): cap diagram output size (#2231)
  feat(agents): give plugin agent types the Buzz side of a turn (#2364)
  feat(plugins): pin buzz://plugin-install links to a path and commit (#2315)
  feat(plugins): tint currentColor SVG page icons like host icons (#2314)
  feat(notifications): add a soft notification sound (#1536)
  feat(agents): let agents propose a profile picture (#2267)
  fix(plugins): default reminders and workflows off for fresh installs (#2291)
  Show all reactors in compact, separate emoji capsules (#2179)
  fix(panels): remember page split widths per workspace (#2264)
  feat(search): browse open channels from a Channels tab in search (#2164)
  perf(relay): page combined replay filters after a reconnect (#2308)
  fix(composer): make native Mac text shortcuts work past link chips (#2390)
  perf(relay): read creator proofs for many channels per filter (#2312)
  fix(channels): let published leave and delete confirm through access loss (#2357)
  fix(media): save native context menu media downloads and hide loopback links on macOS (#2131)
  fix(links): say why a message preview is unavailable, and preview unjoined public channels (#2136)
  test(browser): serve emoji artwork in the reaction hit-area journey (#2138)
  test(browser): click a reply link only after its row settles (#2358)
  ...

# Conflicts:
#	src/buzz-mcp/tools.ts
loganj added a commit that referenced this pull request Nov 4, 2026
The kit test "refuses a mention from anyone else, and triggers that are
not its" fails on main. Its workflow fixture was a message signed by the
owner with a `buzz:workflow` tag. #2364 wrote it against #2360, whose
`fromOwner` refused any event with that tag. #2399 merged first and
restored the older `fromOwner`, which admits any event the owner signs,
so the fixture became an owner message and the kit admitted it.

The kit itself still calls `fromOwner` without a workflow check, so a
real relay workflow mention is refused. Make the fixture that real
mention: signed by the community's workflow key, owned by the owner and
aimed at this agent, with the workflow key set on the session. The
bundled harnesses admit this event; the kit must not. Wiring the
bundled workflow check into the kit makes the test fail.

Before the refusal, assert that the bundled check admits the fixture,
so the negative case cannot pass for the wrong reason again. Also assert that an owner mention carrying only a
`buzz:workflow` marker is still admitted: the marker neither grants
authority nor revokes the owner's own. Say at the
call that the missing workflow check is deliberate, and fix the workflow
docs, which named only Claude Code and Codex.

Signed-off-by: Logan Johnson <loganj@squareup.com>
Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
johnmatthewtennant added a commit that referenced this pull request Nov 4, 2026
Records main as a parent; 5560670 brought in its changes without one.

Signed-off-by: John Tennant <jtennant@block.xyz>
Co-authored-by: Martha <73923046810b68ae182000e74ef7c444383a907df20f81f2ba1008870a6f45a1@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* origin/main:
  feat(solo): let viewers join read-only spaces (#2301)
  [3/5] Add protection to the Claude harness plugin (#1878)
  fix(settings): keep the bottom padding on long sections (#2361)
  fix(mermaid): cap diagram output size (#2231)
  feat(agents): give plugin agent types the Buzz side of a turn (#2364)
  feat(plugins): pin buzz://plugin-install links to a path and commit (#2315)
  feat(plugins): tint currentColor SVG page icons like host icons (#2314)
  feat(notifications): add a soft notification sound (#1536)
  feat(agents): let agents propose a profile picture (#2267)
  fix(plugins): default reminders and workflows off for fresh installs (#2291)
  Show all reactors in compact, separate emoji capsules (#2179)
  fix(panels): remember page split widths per workspace (#2264)

# Conflicts:
#	src/bundled/claude-code/manifest.json
@bradseiler

Copy link
Copy Markdown
Contributor

🤖 @joahg Please investigate an upstream CI failure in the harness kit introduced by this PR. Main's push run 41872462413 at 1da5031ab and #2245's run 41882409727 both fail JavaScript 1/4 in agent-harness/kit.test.ts:189, harnessKit.conversation > refuses a mention from anyone else…, with expected { …(5) } to be undefined.

Honor traced the fixture's owner-authored workflow event being accepted through fromOwner where the fixture expects refusal; McKeon independently checked the matching CI logs. Please reconcile the intended workflow admission contract with the implementation and regression test in an upstream repair.

The harness-kit files are absent from #2245's published da8fa6ca29ee2ccd8c4997f82891636db62af889 tree. Its hosted run tests a merge with newer main; its branch suite passes, but required CI remains blocked. No rerun or unrelated source change was made. Please link the repair when available so we can include it in the final rebase batch.

loganj added a commit that referenced this pull request Nov 4, 2026
…#2444)

🤖

## Summary
- `main` fails the JavaScript unit tests at
`src/features/agent-harness/kit.test.ts` ("refuses a mention from anyone
else, and triggers that are not its"). This PR makes it pass again
without changing what agents accept.
- The cause is two PRs that merged in an order neither one expected.
#2364 added the harness kit, which plugin agent types use to decide
which messages start a turn. Its test was written against the rule from
#2360, which refused every message that carried a workflow tag. #2399
merged before it and restored the older rule, which accepts any message
the agent's owner signs. The test's "workflow" message was signed by the
owner, so it was accepted.
- The kit's behavior has not changed. It still accepts only messages
from the owner or an agent the owner attests. A real relay workflow
message is signed by the community's workflow key, so the kit still
refuses it. Only the test fixture was out of date.

## Details
- The fixture is now a real relay workflow mention: signed by the
community's workflow key, owned by the agent's owner, and aimed at this
agent. The bundled harnesses (Claude Code, Codex, Pi, Goose) would run
this message.
- Before checking the refusal, the test first checks that the bundled
workflow check accepts the fixture. Without that step the negative test
could pass for the wrong reason again. Giving the kit the bundled
workflow check makes the test fail.
- The test also checks that an owner mention with only a `buzz:workflow`
marker is still accepted: the marker neither grants authority nor
removes the owner's own. This covers the same fix as
#2445, so this PR includes it.
- `docs/workflows.md` listed only Claude Code and Codex as accepting
workflow mentions. It now lists Pi and Goose too, and says that plugin
agent types do not.

## Deliberate divergence
A workflow that mentions an agent of a plugin-provided type is ignored,
while the same workflow wakes a Claude Code or Codex agent. This is
intentional. #2399 restored workflow mentions for the bundled harnesses
only to keep existing behavior working, and it says the
template/substitution issue from #2360 is still open. Plugin agent types
have never accepted workflow mentions, so nothing regresses. Accepting
them would give these agent types a known open issue. If the team wants
plugin agent types to accept workflow mentions after the follow-up
discussion on workflow authorization, that change belongs in its own PR.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: Logan Johnson <loganj@squareup.com>
Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
johnmatthewtennant added a commit that referenced this pull request Nov 4, 2026
Co-authored-by: Martha <73923046810b68ae182000e74ef7c444383a907df20f81f2ba1008870a6f45a1@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* origin/main: (96 commits)
  test(agents): refuse a real relay workflow mention in the harness kit (#2444)
  fix(agents): catch up every event watch on the kinds it watches (#1852)
  Keep local artifact collections current (#1507)
  feat(members): add several people to a chat in one write (#2010)
  feat(solo): let viewers join read-only spaces (#2301)
  [3/5] Add protection to the Claude harness plugin (#1878)
  fix(settings): keep the bottom padding on long sections (#2361)
  fix(mermaid): cap diagram output size (#2231)
  feat(agents): give plugin agent types the Buzz side of a turn (#2364)
  feat(plugins): pin buzz://plugin-install links to a path and commit (#2315)
  feat(plugins): tint currentColor SVG page icons like host icons (#2314)
  feat(notifications): add a soft notification sound (#1536)
  feat(agents): let agents propose a profile picture (#2267)
  fix(plugins): default reminders and workflows off for fresh installs (#2291)
  Show all reactors in compact, separate emoji capsules (#2179)
  fix(panels): remember page split widths per workspace (#2264)
  feat(search): browse open channels from a Channels tab in search (#2164)
  perf(relay): page combined replay filters after a reconnect (#2308)
  fix(composer): make native Mac text shortcuts work past link chips (#2390)
  perf(relay): read creator proofs for many channels per filter (#2312)
  ...

Signed-off-by: John Tennant <jtennant@block.xyz>
ravarora2 added a commit that referenced this pull request Nov 4, 2026
…-mentions

* origin/main: (57 commits)
  Keep local artifact collections current (#1507)
  feat(members): add several people to a chat in one write (#2010)
  feat(solo): let viewers join read-only spaces (#2301)
  [3/5] Add protection to the Claude harness plugin (#1878)
  fix(settings): keep the bottom padding on long sections (#2361)
  fix(mermaid): cap diagram output size (#2231)
  feat(agents): give plugin agent types the Buzz side of a turn (#2364)
  feat(plugins): pin buzz://plugin-install links to a path and commit (#2315)
  feat(plugins): tint currentColor SVG page icons like host icons (#2314)
  feat(notifications): add a soft notification sound (#1536)
  feat(agents): let agents propose a profile picture (#2267)
  fix(plugins): default reminders and workflows off for fresh installs (#2291)
  Show all reactors in compact, separate emoji capsules (#2179)
  fix(panels): remember page split widths per workspace (#2264)
  feat(search): browse open channels from a Channels tab in search (#2164)
  perf(relay): page combined replay filters after a reconnect (#2308)
  fix(composer): make native Mac text shortcuts work past link chips (#2390)
  perf(relay): read creator proofs for many channels per filter (#2312)
  fix(channels): let published leave and delete confirm through access loss (#2357)
  fix(media): save native context menu media downloads and hide loopback links on macOS (#2131)
  ...

Signed-off-by: Ravneet Arora <rarora@squareup.com>
johnmatthewtennant added a commit that referenced this pull request Nov 4, 2026
…est-work-activity

* origin/main:
  test(agents): refuse a real relay workflow mention in the harness kit (#2444)
  fix(agents): catch up every event watch on the kinds it watches (#1852)
  Keep local artifact collections current (#1507)
  feat(members): add several people to a chat in one write (#2010)
  feat(solo): let viewers join read-only spaces (#2301)
  [3/5] Add protection to the Claude harness plugin (#1878)
  fix(settings): keep the bottom padding on long sections (#2361)
  fix(mermaid): cap diagram output size (#2231)
  feat(agents): give plugin agent types the Buzz side of a turn (#2364)
  feat(plugins): pin buzz://plugin-install links to a path and commit (#2315)
  feat(plugins): tint currentColor SVG page icons like host icons (#2314)
  feat(notifications): add a soft notification sound (#1536)
wpfleger96 added a commit that referenced this pull request Nov 4, 2026
…-service

* origin/main: (73 commits)
  fix(bestie): deleting an agent saves no Bestie record where there was none (#2420)
  fix(unread): never treat agents-audience messages as unread (#1552)
  feat(focus): add an optional Focus plugin that quiets everything except Solo chats (#2277)
  [5/5] Add protection to the Goose harness plugin (#839)
  feat(agents): identify plugins in native hook output (#2299)
  [4/5] Add protection to the Codex harness plugin (#1879)
  feat(search): show member counts on channel results (#2333)
  Fix the mute icon flashing when selecting a channel (#2287)
  feat(agents): open a working agent's transcript from its activity (#2276)
  test(agents): refuse a real relay workflow mention in the harness kit (#2444)
  fix(agents): catch up every event watch on the kinds it watches (#1852)
  Keep local artifact collections current (#1507)
  feat(members): add several people to a chat in one write (#2010)
  feat(solo): let viewers join read-only spaces (#2301)
  [3/5] Add protection to the Claude harness plugin (#1878)
  fix(settings): keep the bottom padding on long sections (#2361)
  fix(mermaid): cap diagram output size (#2231)
  feat(agents): give plugin agent types the Buzz side of a turn (#2364)
  feat(plugins): pin buzz://plugin-install links to a path and commit (#2315)
  feat(plugins): tint currentColor SVG page icons like host icons (#2314)
  ...

# Conflicts:
#	src-tauri/src/host_process.rs
#	src-tauri/src/lib.rs
#	src/bundled/claude-code/claude.ts
#	src/bundled/codex/rpc.ts
#	src/bundled/goose/goose.ts
#	src/bundled/goose/runtime.ts
#	src/bundled/goose/sessions.test.ts
#	src/bundled/goose/sessions.ts
#	src/features/agent-harness/acp/process.ts
joahg added a commit that referenced this pull request Nov 5, 2026
## Summary

The bundled GitHub panel couldn't show a pull request's code, so someone
could approve it from its title and description alone. Alex Epifano
raised this as a change-management and compliance problem. This PR adds
the diff to the panel.

- **Summary | Changes.** A segmented control at the top of the pull
request panel switches between the existing summary and the diff.
**Changes** shows the PR's line counts (`+N −M`).
- **Changes** renders the diff inline with the bundled Diffs plugin's
`DiffViewer`. That's a unified view with a header per file showing its
+/− counts. Each file is parsed separately, so a binary or unparseable
file falls back to raw text without taking the rest of the diff with it.
- The diff is read only the first time Changes opens. It stays mounted
after that, so switching back doesn't read it again. A new base or head
reloads it.
- **Submit review stays in the header** in both views, so you can review
while reading. Approve isn't gated on viewing the diff. An earlier
revision of this PR required that, and review feedback found it more
restrictive than other review tools.
- The viewer's own PRs show Changes too.
- **Pinned diff.** The diff is read through a new manifest process: `gh
api --hostname github.com --method GET --header "Accept:
application/vnd.github.diff"` against
`repos/{repo}/compare/{base}...{head}`. Comparing against the head SHA
pins the diff to the commit the panel shows. I checked by hand that this
returns the same bytes as the pull request's `.diff`, for a same-repo PR
(#2364) and a fork PR (#2483).
- **Merged PRs** read `repos/{repo}/pulls/{number}` instead. Once a PR
is merged its head is in the base, so the comparison comes back empty.
For example, cli/cli#14918 has 2 files in its PR diff and 0 in
`trunk...head`, checked live.
- **Limits.** gh is stopped once the diff passes about 1 MB (roughly 20k
lines). Changes then shows the reason and an **Open on GitHub** link to
the Files tab. It does the same when GitHub refuses the diff (for
example its 300-file/20k-line cap) or when gh refuses to print one that
contains a raw ESC byte. gh only prints those with
`--allow-escape-sequences`, and that flag is too new to count on.
- `gh.ts` now has one process runner shared by the GraphQL client and
the new diff reader. The GraphQL behavior is unchanged and its existing
tests still pass.

### Walkthrough

The walkthrough runs the real Buzz web shell against a simulated relay
with made-up people. The `gh` process answers are mocked (pull request
data and a three-file diff). The panel code, the Summary | Changes
control and the diff rendering are all real.


https://github.com/user-attachments/assets/f4c3a875-9f4a-4345-bcf9-7325fc6992b7

Tracked in AGNTOPS-736.

## Scope / non-goals

- No split view, file tree, or per-file collapse. The side panel is
narrow, and this is the smallest complete diff view.
- No inline review comments, and no agent highlights in the diff. Alex
Rosenzweig suggested these as possible follow-ups.

## Validation

- `vitest run src/bundled/github src/bundled/diffs src/features/panels`:
28 files and 412 tests pass.
- New tests:
- Changes shows its line counts, reads the diff only on first open, and
renders each file. Summary comes back, and Submit review stays reachable
from Changes.
  - A diff that's too large shows an alert and a `/files` link.
- A binary file falls back to raw text next to a parsed file, on the
viewer's own PR.
  - A merged PR reads its diff by number.
- The diff reader's `gh` args and endpoint, refused/signed-out errors,
and the size cap that kills gh.
- The Approve test is back to `main`'s version.
- `tsc`, `biome check`, `design:check` and the pre-commit hooks pass.

**Rendered check:** the walkthrough shows it in the Buzz web shell, in
the light theme at a wide width, with `gh` mocked.

**CI:** `navigation-row-menu.spec.mjs` fails on `main` too (run
41914363118). The local `pages.integration.test.mjs` timeout also
happens on a clean `origin/main`, so I pushed with `--no-verify`.

**Deferred:** I haven't run it in the desktop app yet. A human should
still check:
- it against a real PR with `gh` signed in
- Changes in both themes, at narrow and wide panel widths, and with
enlarged text

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Co-authored-by: Hertz <c6741012416ef5c49a865db79682113385c5fb998e8c890234fb442b59e22748@buzz.block.builderlab.xyz>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.

3 participants