Skip to content

fix(agents): readable sub-agent guide, step-named profile refusals, typing over the bridge - #76

Merged
tee8z merged 4 commits into
mainfrom
fix/agent-image-subagent-guide-readable
Oct 23, 2026
Merged

tee8z merged 4 commits into
mainfrom
fix/agent-image-subagent-guide-readable

Conversation

@tee8z

@tee8z tee8z commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

What

Three fixes from the first relay Sandbox test (Bifrost, staging):

  1. The Sandbox failed to start. The agent user couldn't read the sub-agent guide.
  2. The failure said nothing useful. "Developer profile refused" didn't say which step failed.
  3. Agents never showed as typing. The prod relay refused the Buzz app's typing indicator with restricted: unknown event kind.

This needs a prod relay deploy for fix 3. Fixes 1 and 2 ship in the agent image.

1. Sub-agent guide unreadable

The Sandbox failed at startup:

Developer profile refused; credentials and command output suppressed

Cause:

  • The Sandbox manager enables sub-agents (enable_subagents: true), so every Sandbox gets the sub-agent token.
  • With the token present, buzz-agent-profile run appends /etc/buzz-agent/subagents.md to the Claude Code and Codex instructions.
  • The agent user (UID 10001) can't read that file:
$ stat -c '%A %n' /etc/buzz-agent        # in the published agent image
drw-r--r-- /etc/buzz-agent

COPY --chmod=0644 … /etc/buzz-agent/subagents.md also sets mode 0644 on the directory it creates, and a directory without the execute bit can't be entered. The profile itself was fine: in a pod copied from the failed one, the bootstrap and checkpoint steps succeeded, and only install_subagent_guide failed with PermissionError.

Fix:

  • Create /etc/buzz-agent with install -d -m 0755 before the COPY.
  • test-image.sh checks test -r /etc/buzz-agent/subagents.md as the agent user.

2. Refusal names the failed step

The refusal now says where startup stopped and why, for example:

Developer profile refused while installing the sub-agent guide: PermissionError; credentials and command output suppressed
  • The reason is either one of the profile's own fixed refusal messages (a new Refused subclass of ValueError), or the exception type. Other exceptions can quote a value, so their text is never printed.
  • test-profile.py checks the step in three refused modes (wrong owner, missing secret, symlinked config directory). It still checks that no credential reaches the output.
  • The guide adds this message and the 1Password (403) Forbidden to troubleshooting. It also creates service accounts without an expiry, because leaving out --expires-in means no expiry.

3. Typing indicators over the HTTP bridge

The Buzz app posts its agents' events through the HTTP bridge (POST /api/events), which sent every event to ingest. Ingest has no scope for ephemeral kinds, so it refused kind 20002.

The bridge now sends typing indicators to publish_http_typing, which follows the WebSocket path:

  1. The event pubkey must match the authenticated identity.
  2. The signature is verified.
  3. Write restrictions and the community deletion fence apply.
  4. The event needs a channel h tag, and the signer needs access to that channel.
  5. The event is published to the channel and fanned out to local subscribers. It is not stored.

Every other ephemeral kind is still refused.

Tested

  • Sub-agent guide: I built a minimal image with both forms:

    drw-r--r-- /etc/before    → before NOT readable   (COPY --chmod alone)
    drwxr-xr-x /etc/after     → after readable        (install -d, then COPY)
    
  • Profile: test-profile.py and test-image.sh pass on an image with this profile.py and the guide directory fix.

  • Relay: the new typing_over_the_bridge_is_published_not_stored_and_gated test (Postgres, ignored by default) passes. It checks that:

    • an open channel gives 200 and stores nothing;
    • a private channel the signer is not in gives 400;
    • a missing h tag gives 400;
    • a different signer gives 403.

    Without the bridge change, it fails with restricted: unknown event kind, the same error prod returned.

  • Checks: cargo clippy -p buzz-relay --all-targets -- -D warnings and cargo fmt --check are clean. The api::bridge Postgres suite has the same thread_window and read_state_snapshot failures with and without this change, and which tests fail changes from run to run.

Also

ctutils goes from 0.4.2 to 0.4.5 in the root, desktop and GPUI lockfiles. A new advisory, RUSTSEC-2026-0385, fails the Security check on master and every PR. cargo-deny check advisories passes locally for the root and desktop workspaces.

COPY --chmod=0644 also gave the /etc/buzz-agent directory it created
mode 0644, which the agent user cannot enter. Every Sandbox with the
sub-agent token, which the Sandbox manager always enables, failed at
startup with "Developer profile refused" when it appended the guide to
the harness instructions. Create the directory 0755 first, and check in
test-image.sh that the agent can read the guide.
@coderabbitai

coderabbitai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Warning

Review limit reached

  • Run on-demand review

This review includes 5 billable files and costs up to $1.25.

  • Ask an admin to make reviews automatic

Open in CodeRabbit

Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing.

Or wait 29 minutes for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available. Your 67 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
📥 Commits

Reviewing files that changed from the base of the PR and between 9486595 and e2007ab.

⛔ Files ignored due to path filters: 3
  • Cargo.lock is excluded by !**/*.lock
  • apps/buzz-gpui/Cargo.lock is excluded by !**/*.lock
  • desktop/src-tauri/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing: 5
  • crates/buzz-relay/src/api/bridge.rs
  • crates/buzz-relay/src/handlers/event.rs
  • voltage/images/buzz-agent/profile.py
  • voltage/images/buzz-agent/test-profile.py
  • voltage/remote-agents.md

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
📥 Commits

Reviewing files that changed from the base of the PR and between 5368cad and 9486595.

📒 Files selected for processing: 2
  • voltage/images/buzz-agent/Dockerfile
  • voltage/images/buzz-agent/test-image.sh

Included review availability: This review used your included allowance. 0 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

📝 Walkthrough

The Dockerfile creates /etc/buzz-agent with mode 0755 before copying subagents.md. The image check now verifies that the guide is readable after startup.

Changes: Buzz agent guide permissions

Layer / File(s) Summary
Guide directory setup and readability check
voltage/images/buzz-agent/Dockerfile, voltage/images/buzz-agent/test-image.sh
The Dockerfile creates /etc/buzz-agent with mode 0755 before copying subagents.md. The image check verifies that the guide is readable.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~8 minutes

Change: Bug fix

Possibly related PRs

  • voltagecloud/buzz#44: Adds the Dockerfile and image check that this change updates.
  • voltagecloud/buzz#7: Defines the projected sub-agent credential mount checked before the guide is installed.
  • voltagecloud/buzz#75: Changes the manager launch environment used in the profile-based startup flow.

Suggested reviewers: thebrandonlucas

Merge Risk: ⚪ Minimal · up to 94865

The agent can read the sub-agent guide, and no actionable merge-blocking risk remains.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Title check Warning The title correctly identifies the readable sub-agent guide, which is the main change. However, it also claims changes for step-named profile refusals and typing over the bridge, which are not present… Rename the pull request to focus only on the guide permission fix, for example: "fix(agents): make the sub-agent guide readable".
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 1…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 1 files. (1 skipped: 1 unsupported.)

Full details: Title check

Explanation

The title correctly identifies the readable sub-agent guide, which is the main change. However, it also claims changes for step-named profile refusals and typing over the bridge, which are not present in the changeset.

Resolution

Rename the pull request to focus only on the guide permission fix, for example: "fix(agents): make the sub-agent guide readable".

✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

tee8z added 2 commits October 23, 2026 08:15
The Buzz app posts its agents' events through the HTTP bridge, which sent
every event to ingest and refused ephemeral kinds with "restricted: unknown
event kind". Agents never showed as typing.

The bridge now publishes kind 20002 the way the WebSocket path does: it
checks the signer, write restrictions, the community fence and channel
access, then fans the event out without storing it. Other ephemeral kinds
are still refused.
"Developer profile refused" gave no cause. The message now names the step
that failed, plus either a fixed refusal message or the error type, so a
Sandbox log can be diagnosed without printing any value.

The guide adds this message and the 1Password 403 to troubleshooting, and
creates service accounts without an expiry.
@tee8z tee8z changed the title fix(agents): let the agent read the sub-agent guide fix(agents): readable sub-agent guide, step-named profile refusals, typing over the bridge Oct 23, 2026
ctutils before 0.4.4 generates masks and compares u128 values in
non-constant time. cargo-deny now fails the Security check on 0.4.2.
@tee8z tee8z self-assigned this Oct 23, 2026
@tee8z
tee8z requested a review from thebrandonlucas October 23, 2026 12:26
@tee8z
tee8z merged commit 8568ff5 into main Oct 23, 2026
75 of 78 checks passed
@tee8z
tee8z deleted the fix/agent-image-subagent-guide-readable branch October 23, 2026 12:36
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.

2 participants