393 lines
15 KiB
Markdown
393 lines
15 KiB
Markdown
# Goose Buzz automation
|
|
|
|
These tools connect issues in `aaif-goose/goose` to the public Goose Buzz
|
|
community at `buzz.gdk.so`.
|
|
|
|
The setup uses two separate identities:
|
|
|
|
- **Github Manager** is a service identity. The scripts use its Nostr key to
|
|
create and manage issue channels, add members, post issue summaries, and sync
|
|
channel topics.
|
|
- **A managed bot**, currently **Doose**, is an agent identity run by Buzz
|
|
Desktop. It can review an issue when Github Manager explicitly mentions it.
|
|
|
|
Github Manager owns the channels it creates. The checked-in core team is also
|
|
added so people can find and manage them in Buzz Desktop.
|
|
|
|
## Requirements
|
|
|
|
- Node.js
|
|
- [GitHub CLI](https://cli.github.com/) authenticated with access to the
|
|
repository and its project board; the recipe also needs permission to assign
|
|
issues
|
|
- Buzz Desktop on macOS, or a `buzz` CLI on `PATH`
|
|
- Goose CLI with a configured model provider
|
|
- A running public Buzz community
|
|
|
|
The scripts use `https://buzz.gdk.so` by default. Set `BUZZ_RELAY_URL` to target
|
|
another community. Set `BUZZ_BIN` or `GH_BIN` to override CLI discovery.
|
|
|
|
The community and issue channels are public. Confirm that a new Nostr identity
|
|
can join the community before relying on these scripts; relay configuration
|
|
names have changed between Buzz releases, so use the controls provided by the
|
|
deployed version rather than copying old allowlist environment variables.
|
|
|
|
## Set up a new installation
|
|
|
|
### 1. Create Github Manager
|
|
|
|
Run:
|
|
|
|
```sh
|
|
./buzz/create_github_manager
|
|
```
|
|
|
|
The script:
|
|
|
|
1. Generates a dedicated Nostr key pair.
|
|
2. Stores the keys outside the repository.
|
|
3. Uploads `assets/GithubManager.png`.
|
|
4. Publishes a Buzz profile named `Github Manager`.
|
|
|
|
The identity is stored in:
|
|
|
|
```text
|
|
$GOOSE_BUZZ_HOME/github-manager
|
|
```
|
|
|
|
`GOOSE_BUZZ_HOME` defaults to `$XDG_CONFIG_HOME/goose/buzz`, or
|
|
`~/.config/goose/buzz` when `XDG_CONFIG_HOME` is not set. The identity directory
|
|
is mode `0700`; its files are mode `0600`.
|
|
|
|
The important files are:
|
|
|
|
```text
|
|
private-key.nsec Secret key used by the scripts
|
|
public-key.npub Shareable Nostr public key
|
|
public-key.hex Shareable public key used by the Buzz CLI
|
|
profile-created Time at which the profile was published
|
|
```
|
|
|
|
The command is deliberately idempotent. If all three key files and the profile
|
|
marker exist, it fixes their permissions and changes nothing. If the keys exist
|
|
without the marker, it publishes the profile again without replacing the
|
|
identity. If only part of the key pair exists, it stops rather than silently
|
|
replacing the identity.
|
|
|
|
Back up the entire `github-manager` directory in a secure password or secrets
|
|
manager. The private key cannot be recovered from Buzz. Anyone with it can act
|
|
as Github Manager.
|
|
|
|
### 2. Create and run a bot
|
|
|
|
Create at least one managed agent in Buzz Desktop. The bot must have its own
|
|
Nostr identity; do not reuse the Github Manager key. Start the agent and make
|
|
sure it shows as online.
|
|
|
|
The channel script adds the bot to new channels, but membership alone does not
|
|
let Github Manager instruct it. Buzz agents default to accepting instructions
|
|
only from their human owner.
|
|
|
|
### 3. Allow Github Manager to instruct the bot
|
|
|
|
In Buzz Desktop:
|
|
|
|
1. Open **Agents**.
|
|
2. Select the bot, such as **Doose**.
|
|
3. Choose **Edit agent**.
|
|
4. Expand **Advanced**.
|
|
5. Change **Who can send instructions** to **Selected people**.
|
|
6. Search for **Github Manager** and choose **Add**.
|
|
7. Save the agent and restart it if Buzz does not restart it automatically.
|
|
|
|
This permission is security-sensitive. A selected identity can instruct the
|
|
agent to use the computer, files, accounts, and connected tools available to
|
|
that agent. Keep the list narrow. Do not choose **Anyone** just because the
|
|
community itself is public.
|
|
|
|
To verify the permission, send a message as Github Manager with both readable
|
|
mention text and the bot's public key:
|
|
|
|
```sh
|
|
export BUZZ_PRIVATE_KEY="$(cat ~/.config/goose/buzz/github-manager/private-key.nsec)"
|
|
export BUZZ_RELAY_URL=https://buzz.gdk.so
|
|
BUZZ_CLI="${BUZZ_BIN:-/Applications/Buzz.app/Contents/MacOS/buzz}"
|
|
|
|
"$BUZZ_CLI" messages send \
|
|
--channel <channel-uuid> \
|
|
--content "@Doose reply with OK" \
|
|
--mention <doose-public-key-hex>
|
|
```
|
|
|
|
A literal `@Doose` without the `--mention` public key may be rendered as text
|
|
without waking the remote agent. The successful command returns the bot key in
|
|
`mention_pubkeys`. The bot may take a minute or two to respond.
|
|
|
|
### 4. Configure the core team
|
|
|
|
`core-team.json` defines the people added to every new issue channel. Each
|
|
person has a display name, GitHub handle, stable Buzz public key, and an
|
|
`interest` list of topics they know about, based on their typical public issue
|
|
and pull-request work. `capacity` sets that person's target share of new
|
|
assignments; most people are `1` and Filip is `0.5`. A person can also have a
|
|
`bots` mapping from bot display name to bot public key. Adding that person adds
|
|
those identities with the Buzz `bot` role.
|
|
|
|
The current roster is:
|
|
|
|
- Douwe Osinga as an owner
|
|
- Doose as their bot
|
|
- Alex Hancock as a member
|
|
- filip as a member
|
|
- jasper as a member
|
|
- Mic as a member
|
|
- lifei as a member
|
|
- Lifei goose agent as their bot
|
|
- Jack Amadeo as a member
|
|
|
|
Edit the checked-in file when the permanent team changes. Set
|
|
`BUZZ_CORE_TEAM_FILE` to use a different roster file for another installation.
|
|
Use `--no-core-team` for a one-off channel that should not receive the standard
|
|
people. If a person from the file is then supplied explicitly with `--owner` or
|
|
`--person`, their associated bots are still included.
|
|
|
|
## Tools
|
|
|
|
### `create_github_manager`
|
|
|
|
Creates the Github Manager key pair and profile as described above. It has no
|
|
arguments and never rotates an existing identity.
|
|
|
|
```sh
|
|
./buzz/create_github_manager
|
|
```
|
|
|
|
### `create_issue_channel`
|
|
|
|
Reads a GitHub issue, creates a permanent open stream, sets its initial topic to
|
|
`⚪ GitHub phase: Inbox`, adds owners, people, and bots, and posts the supplied
|
|
summary with a link to the issue. It does not modify the GitHub issue.
|
|
|
|
```sh
|
|
./buzz/create_issue_channel 12345 \
|
|
--summary "Why the issue matters and what needs to be decided." \
|
|
--person "Issue Reporter"
|
|
```
|
|
|
|
The owners and members in `core-team.json` and their associated bots are added
|
|
by default. Repeat `--owner`, `--person`, or `--bot` to add more identities.
|
|
Each command-line value can be an exact Buzz display name or a 64-character
|
|
hexadecimal public key. People receive the `member` role and bots receive the
|
|
`bot` role.
|
|
|
|
Use `--repo owner/repo` for a repository other than `aaif-goose/goose`. A full
|
|
GitHub issue URL also works. For a multiline summary, use `--summary-file path`
|
|
or pipe the text with `--summary-file -`.
|
|
|
|
The command prints the issue, channel, message, and participant details as JSON.
|
|
Channel names use `#<issue-number> <issue-title>` in the Buzz UI. The script
|
|
refuses to create a duplicate when an active or archived channel already
|
|
matches the issue number. For an existing channel, explicitly supplied owners,
|
|
people, and bots are added without posting the summary a second time. An
|
|
archived matching channel is unarchived before its roster is updated. If setup
|
|
of a new channel fails after creation, the incomplete channel is deleted so the
|
|
next run can retry it.
|
|
|
|
Adding a bot does not trigger it. After the channel is created, Github Manager
|
|
must send a new message with an explicit bot mention:
|
|
|
|
```sh
|
|
"${BUZZ_BIN:-/Applications/Buzz.app/Contents/MacOS/buzz}" messages send \
|
|
--channel <channel-uuid> \
|
|
--content "@Doose review this issue and post your assessment in this channel." \
|
|
--mention <doose-public-key-hex>
|
|
```
|
|
|
|
### `list_issue_work`
|
|
|
|
Lists issues in the project's Inbox that need an owner or channel, and open
|
|
issues linked from the Buzz `issues to add` channel. An assigned Inbox issue
|
|
without a channel is included so a failed channel creation can be retried. A
|
|
queue entry can contain a Goose issue or pull request URL, or just `#<issue
|
|
number>`. Queue entries with an existing issue channel are treated as processed.
|
|
Pull request links resolve to their issue when GitHub reports exactly one
|
|
closing issue. The JSON also includes the core team, GitHub handles, Buzz public
|
|
keys, interests, and assignment capacity so a Goose recipe can select an owner.
|
|
`recent_assignment_load` counts core-team assignees across the 100 most recently
|
|
created issues, including closed issues. Phase and issue age do not affect the
|
|
count. It does not change GitHub or Buzz.
|
|
|
|
```sh
|
|
./buzz/list_issue_work
|
|
```
|
|
|
|
The project defaults to `aaif-goose` project 1. Use `--repo`,
|
|
`--project-owner`, `--project-number`, and `--queue-channel` for another
|
|
installation. The command fails instead of returning a partial list when the
|
|
project, channel, or message limits are reached. Queue links that cannot be
|
|
resolved are reported separately instead of guessed. A new installation must
|
|
create one Buzz channel with the configured queue name before running the
|
|
recipe.
|
|
|
|
Only the 20 most recent issue or pull-request links in the queue are considered.
|
|
Conversation without GitHub links does not consume that limit. Use
|
|
`--queue-count` to change it. Older links are reported with
|
|
`outside-recent-window`; messages without a valid timestamp are reported with
|
|
`invalid-created-at`.
|
|
|
|
Queue authors are only returned as `queue_requesters` when their public key is
|
|
present in `core-team.json`. Other authors are reported as
|
|
`ignored_queue_requesters` and cannot influence channel membership.
|
|
|
|
### `syncissues`
|
|
|
|
Fetches all open GitHub issues and all Buzz channels, matches issue channels by
|
|
the GitHub URL in their description, and synchronizes the project phase and
|
|
assignees to the channel topic. Active and archived channels use the same
|
|
matching rules. Legacy number-only channels are checked against GitHub so pull
|
|
request channels are reported and skipped instead of being treated as closed
|
|
issues. When more than one name points at an issue, an explicit GitHub issue URL
|
|
in the channel description wins over a legacy or bare numeric name:
|
|
|
|
| GitHub phase | Buzz topic marker |
|
|
| --- | --- |
|
|
| Inbox | ⚪ |
|
|
| Needs info | 🟡 |
|
|
| Accepted / design | 🟣 |
|
|
| Ready | 🟢 |
|
|
| Verification | 🔵 |
|
|
| Done | ✅ |
|
|
|
|
Topics use this format:
|
|
|
|
```text
|
|
🟢 Ready -- assigned to: @github-handle
|
|
```
|
|
|
|
Multiple assignees are comma-separated. Issues without an assignee say
|
|
`assigned to: unassigned`.
|
|
|
|
Issue channels without a corresponding open GitHub issue get the topic
|
|
`✅ GitHub issue: Closed` and are archived. If an issue reopens, its channel is
|
|
unarchived even when it has no project status; its project phase is restored
|
|
when available. Other Buzz channels are ignored.
|
|
|
|
The script also checks for new replies from people outside the repository.
|
|
GitHub authors associated as `OWNER`, `MEMBER`, or `COLLABORATOR` are considered
|
|
internal; other human users are considered outsiders. For each new outsider
|
|
reply on an open issue with a matching channel, Github Manager mentions the
|
|
issue's core-team assignee and posts the author name and a link to the GitHub
|
|
comment. The comment body is deliberately not copied into Buzz, so comment text
|
|
cannot inject mentions or bot instructions.
|
|
|
|
The `Snoozed until` date comes from the GitHub project. Once that date arrives,
|
|
Github Manager posts `@owner, the snooze is expired.` to the matching channel.
|
|
Each issue is notified once per snooze date. Changing the date permits a new
|
|
notification when the new date arrives.
|
|
|
|
The first non-dry run initializes a repository-specific cursor without posting
|
|
historical replies. Later successful runs advance it. The cursor is stored next
|
|
to the Github Manager key as
|
|
`issue-comment-sync-<owner>-<repository>.json`, with mode `0600`. A dry run reads
|
|
the cursor and reports `would-notify` actions without posting messages or
|
|
advancing it.
|
|
|
|
Run the local Buzz checks with `just test-buzz`. The same checks run in GitHub
|
|
Actions when the Buzz automation changes.
|
|
|
|
Snooze notifications have a separate
|
|
`issue-snooze-sync-<owner>-<repository>.json` state file in the same directory.
|
|
|
|
Always inspect a dry run first:
|
|
|
|
```sh
|
|
./buzz/syncissues --dry-run
|
|
./buzz/syncissues
|
|
```
|
|
|
|
The script checks that neither the GitHub issue list nor the Buzz channel list
|
|
was truncated before changing channels. Raise `--limit` if it stops at the Buzz
|
|
limit. Use `--repo`, `--project`, `--project-owner`, or `--project-number` to
|
|
target another repository or project.
|
|
|
|
### `github_issue_manager.yaml`
|
|
|
|
This Goose recipe manages the full Inbox loop:
|
|
|
|
1. List unassigned Inbox issues and unresolved work from `issues to add`.
|
|
2. Read each issue and rank the three strongest matches based on `interest`.
|
|
3. Choose the least loaded of those three using assignments on the 100 newest
|
|
issues, adjusting only for the person's configured capacity.
|
|
4. Assign the issue to that person's GitHub handle.
|
|
5. Create a focused Buzz channel, or promote the owner when the channel already
|
|
exists. Start with Douwe and the issue owner, then add relevant people until
|
|
the channel has at least three distinct humans. If Douwe owns the issue, two
|
|
others are required. A fourth person may be added when useful.
|
|
6. Run `syncissues`.
|
|
|
|
Issue bodies and comments are treated as untrusted data. The recipe is allowed
|
|
to assign issues but is instructed not to edit bodies, post GitHub comments,
|
|
change project fields, labels, or issue state.
|
|
|
|
Run it once with:
|
|
|
|
```sh
|
|
goose run \
|
|
--recipe "$PWD/buzz/github_issue_manager.yaml" \
|
|
--params "automation_dir=$PWD/buzz" \
|
|
--no-session
|
|
```
|
|
|
|
Preview assignments without changing GitHub or Buzz:
|
|
|
|
```sh
|
|
goose run \
|
|
--recipe "$PWD/buzz/github_issue_manager.yaml" \
|
|
--params "automation_dir=$PWD/buzz" \
|
|
--params "dry_run=true" \
|
|
--no-session
|
|
```
|
|
|
|
### `run_hourly`
|
|
|
|
Runs the recipe, waits an hour after it finishes, and repeats. Override the wait
|
|
with `BUZZ_MANAGER_INTERVAL_SECONDS`.
|
|
|
|
```sh
|
|
./buzz/run_hourly
|
|
```
|
|
|
|
The dedicated machine must stay awake and have working Goose, `gh`, and Buzz
|
|
CLI configuration. This runner does not require Goose's scheduler.
|
|
|
|
## Move or recover the setup
|
|
|
|
To move Github Manager to another machine, copy its complete identity directory
|
|
over a trusted encrypted connection. This moves the manager key and the sync
|
|
cursors without copying a human or bot identity:
|
|
|
|
```text
|
|
~/.config/goose/buzz/github-manager
|
|
```
|
|
|
|
On the destination, preserve the directory as `0700` and its files as `0600`,
|
|
then run `create_github_manager` to confirm that it finds the identity. Also
|
|
install and authenticate `gh`, install and configure Goose, and install Buzz
|
|
Desktop or configure `BUZZ_BIN`.
|
|
|
|
Do not copy Buzz Desktop's application-data directory. The hourly workflow only
|
|
needs Github Manager's key. Doose can keep running on the original machine, or
|
|
a separate bot identity can be created on the dedicated machine later.
|
|
|
|
Running `create_github_manager` with an empty identity directory creates a new
|
|
identity, not a replacement copy of the old one. If the old private key is lost:
|
|
|
|
1. Generate a new Github Manager identity.
|
|
2. Add the new identity as an owner of channels that must remain manageable.
|
|
3. Replace the old Github Manager entry in every bot's **Selected people** list.
|
|
4. Remove the old identity where possible.
|
|
5. Store a secure backup of the new key.
|
|
|
|
Existing channel events remain signed by the old identity; they cannot be
|
|
re-signed by the new one.
|