Skip to content

NEXUS-4029: Offer folder chips in the composer - #2789

Open
gilgtm wants to merge 1 commit into
gil/nexus-4029-composer-tokensfrom
gil/nexus-4029-task-references
Open

gilgtm wants to merge 1 commit into
gil/nexus-4029-composer-tokensfrom
gil/nexus-4029-task-references

Conversation

@gilgtm

@gilgtm gilgtm commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Offers and sends ! folder chips (828571619).

  • Listing: in the desktop app, ! in any composer lists the immediate folders of the community's repositories folder (Local settings, or the default ~/.buzz/REPOS).
    • It opens at a word start once a character follows !.
    • A bare !, ! before a space, a mid-word ! and ![ stay prose.
    • One open query lists once. Reopening it, a changed preference, or Retry after a failed read lists again.
  • Sending: chips now send. A chip is its label as literal text, and the host composer signs the message's chips as one buzz-tokens-v1 tag.
    • Sends, replies, queued follow-ups after a restart, and the first message of a new Solo chat all sign.
    • The public message API cannot add the tag. A detached window refuses to send chips.
  • Native listing: repositories_folder_list serves the core renderer, and plugin_host_repository_folders serves ctx.host.repositoryFolders after checking the plugin's manager id, revision and file grant. The listing:
    • follows a linked root folder but never a linked child;
    • is capped at 4096 entries and 1 MiB;
    • runs on the directory listing semaphore with the read deadline.
  • Agent grant: a local Claude or Codex agent is told about a folder only when the message's author is the agent's owner and the folder is in the owner's listing at that turn. Claude also gets the folder through --add-dir for the rest of the conversation; Codex gets the prompt section only.

Compared with the previous version of this PR

The previous version ("send task references, # workspaces and @ folders") depended on the removed task mode. The # workspace choice is now in its own commits (see the stack), and folders use ! instead of @.

buzz-review-completed

Testing

Local runs on macOS, on the stack rebased onto main 9596cd3db:

  • The pushed heads differ from the tested adbdf424e chain only in tests/browser/solo-rename.spec.mjs. 02f9c0277 makes the new plugin-owned lifecycle test wait for the chat's row before filing it, after the hosted flake check caught it racing the roster load. With that fix, the full solo-rename.spec.mjs passed 180/180 at 02f9c0277 (10 repeats, Chromium and WebKit) and 120/120 at c99d0ce51 (5 repeats).
  • At this PR's head 1fbd49c49: pnpm check (Biome, TypeScript and the design checks) and the icon check passed. Vitest for the files the flake check selects for this PR: 984 passed, 28 skipped.
  • Rust at 1fbd49c49: cargo fmt --check and cargo clippy -p Buzz --all-targets -- -D warnings passed. host_files and repositories_folder tests at the stack tip: 24 and 4 passed. cargo-deny check passed at the stack tip.
  • At the stack tip adbdf424e: full Vitest, 20556 passed and 23 skipped (includes browser-host/channel-kit.test.mjs).
  • Browser journeys at the stack tip, Chromium and WebKit: the full tests/browser/solo-rename.spec.mjs, repeated 5 times, passed 120/120. The 34 other specs that the flake check relates to the stack, run once in each engine: 343 passed, 3 skipped by their own conditions. The full browser suite was not run locally.
  • The Nexus plugin's connected browser harness ran on the actual Buzz web shell and plugin UI, with a simulated relay and generated identities, before the rebase. Full run at this PR's pre-rebase head as the pin (049a9b57e): 53 passed. The simulated host lists folders; the native listing did not run there.

Native testing: Gil ran the native desktop app on this stack at adbdf424e, with the matching Nexus plugin build. Gil confirmed that these native-only paths passed:

Agent testing did not reach these paths: the agent ran no native app, no real relay and no real agent.

Known gaps

  • Gil exercised the folder listing and the --add-dir grant natively, and they passed. Agent testing did not reach them.

Hosted or local failures that this stack does not cause:

  • tests/browser/typing.spec.mjs:290 ("a growing channel/thread composer lifts history with it at the bottom") intermittently fails with React error Add complete reaction toggles to the message menu #185 on main too. Locally it failed 12 of 12 runs at 9596cd3db and 9 of 12 at a later main commit.
  • On macOS, cargo test -p Buzz crashes the library test binary (SIGSEGV), and some host_command deadline tests fail. Both happen the same way at base 9596cd3db, before this stack. Hosted Linux CI is the reference for these.

Test changes

Titles are compared only in the test files each range changes. Tests that moved or were renamed are matched by title.

Removed by this PR's diff (2)
  • src/features/conversation/service.test.tsx: bundled plugin registers its own completion provider
  • src/features/messages/MessageComposer.test.tsx: inserts a bundled provider's chip as one atom and keeps it unsent until chips ship
Added by this PR's diff (21)
  • src/bundled/claude-code/runtime.test.ts: grants a listed folder its owner referenced by chip for the rest of the conversation, never an unlisted one
  • src/bundled/mentions/FolderCompletion.test.tsx: a ! query reopened at the same position, after a preference change, or retried after a failed read lists the folders again; a closed query's late listing never publishes
  • src/bundled/mentions/FolderCompletion.test.tsx: matches ! only at a word start before a query character, never in image syntax or mid-word
  • src/bundled/mentions/FolderCompletion.test.tsx: offers the repositories folder's folders as chips, listed once per query; a browser build never lists it
  • src/features/agent-harness/referenced-folders.test.ts: grants nothing without chips, for malformed chips, or when the listing fails
  • src/features/agent-harness/referenced-folders.test.ts: grants only folders the owner referenced that their repositories folder lists now
  • src/features/agent-harness/referenced-folders.test.ts: quotes a folder path so it cannot close the prompt section
  • src/features/communities/repositories.test.ts: lists the partition's configured folder and refuses a listing that outlived its preference or caller
  • src/features/conversation/service.test.tsx: bundled plugin registers its own completion providers
  • src/features/host/service.test.ts: lists the binding's configured repositories folder as the calling plugin, never a path it names
  • src/features/messages/MessageComposer.test.tsx: inserts a bundled provider's chip as one atom and signs it into the sent message
  • src/features/messages/follow-up-persistence.test.ts: keeps saved messages with invalid or oversized chips unsent and unchanged: %j
  • src/features/messages/follow-up-persistence.test.ts: restores a queued message's chips and signs them on the recovered send
  • src/features/messages/message-tokens.test.ts: gives the chips being signed only to the first send inside it
  • src/features/messages/message-tokens.test.ts: rejects duplicate tags or chips, malformed shapes and oversized payloads
  • src/features/messages/message-tokens.test.ts: round-trips exact chips with extra metadata in one tag
  • src/features/messages/message-tokens.test.ts: sends chips as their labels, literally and in authored order
  • src/features/messages/message-tokens.test.ts: serializes syntax-like labels literally, including under rich formatting
  • src/features/relay/send-to-channel.test.ts: a host reply signs its chips once and retries the exact bytes; the public API cannot add chips
  • src/features/relay/send-to-channel.test.ts: only the host's own send carries its chips, not a send an outbox subscriber makes while it is queued
  • src/features/solo/SoloDraft.test.tsx: starts a New chat from a saved folder chip and signs the chip into its first message
In the previous version of this PR, not carried over (24)
  • src/bundled/channels/ChannelCompletion.test.tsx: in a task draft, # offers the region's workspaces instead of channels; a task chat keeps channel links
  • src/bundled/mentions/team-mentions.test.tsx: a host task composer offers its repositories folder's folders as chips, listed once per query; other composers never list it
  • src/bundled/mentions/team-mentions.test.tsx: an @ query reopened at the same position, after a preference change, or retried after a failed read lists the folders again; a closed query's late listing never publishes
  • src/features/conversation/completion.test.ts: keeps chip and workspace edits only from a provider a bundled plugin registered, for that registration's lifetime
  • src/features/messages/MessageComposer.test.tsx: a bundled # workspace choice files a task draft in one undo step, and nothing else (task draft: %s)
  • src/features/messages/MessageComposer.test.tsx: a plugin Composer seeded with chip data never gets it signed (%s)
  • src/features/messages/MessageComposer.test.tsx: inserts a bundled provider's chip as one atom and signs it only in a task chat (task chat: %s)
  • src/features/messages/MessageComposer.test.tsx: inside a host task draft, only its own composer gets the draft's completion context (task draft: %s)
  • src/features/messages/follow-up-persistence.test.ts: keeps saved messages with invalid or oversized task tokens unsent and unchanged: %j
  • src/features/messages/follow-up-persistence.test.ts: restores a task chat's queued chips and signs them on the recovered send, never in another chat
  • src/features/messages/mention-draft.test.ts: keeps a task draft's workspace, lifts one saved project into it, and marks other data another build saved as unsupported
  • src/features/messages/task-tokens.test.ts: keeps personal workspace filing out of signed task tags
  • src/features/messages/task-tokens.test.ts: keeps repository and folder references in authored order, inline and without a footer
  • src/features/messages/task-tokens.test.ts: keeps unknown versions inert and fallback labels literal
  • src/features/messages/task-tokens.test.ts: rejects duplicate tags, duplicate identities, malformed shapes and oversized UTF-8 payloads
  • src/features/messages/task-tokens.test.ts: round-trips exact identities and extra metadata without interpreting display verification
  • src/features/messages/task-tokens.test.ts: serializes syntax-like token labels literally in place, including under rich formatting
  • src/features/relay/send-to-channel.test.ts: a host task reply signs its chips once and retries the exact bytes; the public API cannot add chips
  • src/features/solo/SoloDraft.test.tsx: gives the task draft's own region binding to its composer's completions
  • src/features/solo/SoloDraft.test.tsx: restores %s chip saved beside a staged chat link across remount; starts the task with it inline and signed only if it is for the primary agent
  • src/features/solo/SoloDraft.test.tsx: resumes the saved task pair before prepare and transfers edits, including a chip-only change; refuses malformed signed chips (%s)
  • src/features/solo/task-mode.test.ts: checks signed chips against the task chat's recipients, refuses them outside a task chat and malformed ones on send and first-event retry, and ignores unknown versions
  • src/features/solo/task-mode.test.ts: keeps malformed task-token and live task-member guards ahead of informational policy
  • src/features/solo/task-mode.test.ts: refuses recognized information carrying %s even if ordinary admission would allow it

@gilgtm
gilgtm added this pull request to stack #2791 November 6, 2026 12:04
@gilgtm
gilgtm force-pushed the gil/nexus-4029-task-references branch 2 times, most recently from 565d801 to 049a9b5 Compare November 10, 2026 03:00
@gilgtm gilgtm changed the title NEXUS-4029: Choose workspaces and send task references NEXUS-4029: Offer folder chips in the composer Nov 10, 2026
@gilgtm
gilgtm removed this pull request from stack #2791 November 10, 2026 03:03
@gilgtm
gilgtm added this pull request to stack #3176 November 10, 2026 03:03
@gilgtm
gilgtm force-pushed the gil/nexus-4029-task-references branch from 049a9b5 to 1fbd49c Compare November 10, 2026 05:31
In the desktop app, `!` in any composer lists the immediate folders of
the community's repositories folder (Local settings, or the default
`~/.buzz/REPOS`) as folder chips. It opens at a word start, as `@` does,
once a character follows `!`. A bare `!`, `!` before a space, a mid-word
`!` and Markdown image syntax `![` stay prose, so "Merci !" and Enter
still sends. `@` still lists only mentions. One open `!` query lists
once across keystrokes; reopening it, a changed preference or Retry
after a failed read lists again.

Chips now send. A chip is its label as literal text in place, and the
host composer signs the message's chips as one `buzz-tokens-v1` tag.
Only the first send or reply inside `signMessageTokens` takes them, so
a send an outbox subscriber makes meanwhile carries none, and the
public message API cannot add the tag. Sends, replies, queued
follow-ups after a restart, and the first message of a new Solo chat
all sign. A detached window refuses to send chips, since its relay
connection would drop them. Malformed or unknown chip data still stays
in storage unchanged and is refused.

Native listing has two entry points over one bounded helper:

- `repositories_folder_list` serves the trusted core renderer.
- `plugin_host_repository_folders` serves `ctx.host.repositoryFolders`.
  It checks the plugin's manager id, revision and file grant before any
  filesystem work.

The listing follows a linked root folder but never a linked child. It
is capped at 4096 entries and 1 MiB, and runs on the directory listing
semaphore with the read deadline.

A local Claude or Codex agent is told about a referenced folder only
when the message's author is the agent's owner and the folder is in the
owner's repositories folder listing at that turn. Anyone in a chat can
sign a chip, so another author's chip, or an unlisted folder, grants
nothing. Claude also gets the folder through `--add-dir` for the rest
of that conversation; Codex gets the prompt section only.

Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Gil Birman <gil@squareup.com>
@gilgtm
gilgtm force-pushed the gil/nexus-4029-task-references branch from 1fbd49c to 8285716 Compare November 10, 2026 06:17
@gilgtm
gilgtm marked this pull request as ready for review November 10, 2026 06:37
@gilgtm
gilgtm requested a review from a team as a code owner November 10, 2026 06:37

This branch has not been deployed

No deployments
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