Files
byre/AGENTS.md
T

62 lines
10 KiB
Markdown

# Rules for agents working on byre
These are requirements, not preferences. Changing one needs the owner's agreement first.
## Workflow
- Never deploy unless the owner asks for it in the current turn. A finished change is reported undeployed; the owner deploys it, or asks you to. Never run `mac/deploy.sh` or restart the service on your own initiative, not even to check that a change works.
- Update `docs/` only when a change affects what they hold: rules and process, external facts (where binaries come from, how to regenerate things, vendor quirks), behavior observed live (expected errors, agent refusals), or design intent the code can't show (why a rule exists, cases deliberately ruled out). Never narrate what the code does.
- Never add tests. byre has no test suite; check a change with `go vet ./...`; when `mac/` or `api/openapi.yaml` changed, `mac/bundle.sh` (release only; debug builds are refused); and when `hands/` changed, `swift build --package-path hands --configuration release`.
## Deploying
A deploy is a release that isn't uploaded: the same notarized disk image, installed on the owner's Macs instead of published. When asked to deploy, do exactly this, from the worktree root:
1. Before anything else, write the release notes for everything on main since the last release tag (`git describe --tags --abbrev=0` names it), for a byre user in the "Releasing" shape (`## New` and `## Fixed`, each a bullet with a bold short name and what it does or what now works; no `## Install`). Read `git diff <tag>..HEAD`, not just the commit subjects, and leave out changes no user sees. Once the installs start, nothing may sit between one step and the next, so the notes are done by then.
2. Run `mac/deploy.sh`. It runs `mac/package.sh` (the build, both notarizations and the disk image, as for a release, into `mac/.build/release/<version>/`), builds the iPhone app with `mac/phone.sh` and uploads it to TestFlight (`docs/iphone.md`), and copies the disk image and `mac/install.sh` to `~/byre-deploy/byre.dmg` and `~/byre-deploy/install.sh`. That copy stays there for installing by hand on Macs that aren't connected. It installs nothing.
3. Sort the client Macs `list_clients` shows (leaving out the iPhone) by whether they run a byre server of their own: one does when `launchctl print gui/$(id -u)/run.fc.byre` exits 0 there (`client_bash`). Then, without asking first, update every one that runs no server, all in parallel (one batch of tool calls): upload `~/byre-deploy/byre.dmg` and `~/byre-deploy/install.sh` to the same paths there (`client_upload`), then run `~/byre-deploy/install.sh ~/byre-deploy/byre.dmg` there through `client_bash`. It installs, prints the build number and the commit, and returns; a detached job it leaves behind then quits the app if it is running and reopens it on the new build, so the client disconnects a moment later. Don't wait for these Macs to reconnect: after a protocol change the new app can't reach the old servers, so each one stays broken until the servers are updated. They must be updated before any server restarts, since an old client may be unable to reach a new server to be updated.
4. Immediately after, update every client Mac that runs a server the same way, in parallel. Its server sees its executable replaced and restarts on the new build.
5. Immediately after that, install on the Mac you run on with `mac/install.sh ~/byre-deploy/byre.dmg`. The service runs `/Applications/Byre.app/Contents/Helpers/byre`; within 5 seconds it sees that file replaced and exits, and launchd starts the new server and `byre-hands`. Everything is signed with Developer ID, so the macOS privacy grants (Screen Recording, Accessibility, Automation) survive. `install.sh` relaunches its app if it is running, and leaves it closed if not; the relaunch runs in its own process group, so it survives byre killing the `bash` call when the server exits.
6. Confirm it came up: read the tail of `~/Library/Logs/byre.log`, and `GET /api/tailnet` at the loopback address (`byre check` prints it as `loopback`), whose `state` must be `Running`. Check that `list_clients` shows every updated Mac back on the new build and commit. Report the new build number first (`plutil -extract CFBundleVersion raw /Applications/Byre.app/Contents/Info.plist` prints it; it is the Unix time of the build, and the iPhone build uploaded to TestFlight has its own, a few minutes later), then what you deployed and what you checked, and end with the release notes from step 1.
Deploying means building with `mac/deploy.sh` on the Mac you run on. Never stand in for it by running `mac/package.sh` yourself, not even to build once for several Macs. Every Mac, this one included, gets the disk image `mac/deploy.sh` left in `~/byre-deploy/`.
Check each server (this Mac's and each client Mac's from step 4) with the bearer token in `~/Library/Application Support/byre/loopback-token`: `GET https://byre-<host>.tailcecab.ts.net/api/tailnet` (`state` is `Running`) and `/api/admin/status` (report zombies, protocol errors, rate limits). The loopback address serves `/api/tailnet` but not `/api/admin/status`.
What to expect:
- The notarizations take a minute or two each; `notarytool` prints "In Progress" until "Accepted". A rejection stops the script before anything is installed; `xcrun notarytool log <id> --keychain-profile byre-notary` says why.
- You run inside byre, so this Mac's restart interrupts your turn, and the new server wakes you again at once with a "byre restarted while you were mid-turn" message. Carry on with step 6: confirm the service started after your install (the log shows `executable replaced, exiting` just before the last `listening` line).
- If byre restarts without your install, another agent deployed its own branch, and the running build is theirs.
## Releasing
A release is a deploy that is uploaded, and goes to every Mac through the app's updater. Make one only when the owner asks. It needs a `v<version>` tag on a commit of main, pushed by itself (`git push origin v<version>`, never moving or recreating a tag), and a clean checkout at that tag on the Mac byre runs on, where `mac/release.sh <notes>` checks the tag, runs `mac/package.sh`, uploads the image, its `.sha256` and `appcast.xml` to `dl.byre.run`, and creates the release on g.fc.run with copies of the image and its `.sha256`, and the Markdown file `<notes>` as its notes. `docs/operations.md`, "Releases", has the details.
Before running it, write the notes yourself from the diffs since the previous tag (`git describe --tags --abbrev=0 v<version>^` names it; read `git diff <previous>..v<version>`, not just the commit subjects). Write the file outside the checkout, so the tree stays clean. It is a changelog for people who use byre, in this shape:
- `## New`: each user-visible feature as a bullet, a bold short name and then what it does and anything needed to use it.
- `## Fixed`: each user-visible bug fix as a bullet, a bold short name and then what now works.
- `## Install`: "Download `byre-<version>.dmg` and drag `Byre.app` to Applications; Macs already running <previous version> get this through the app's updater. The disk image's SHA-256 is in `byre-<version>.dmg.sha256`.", then a line `Full changes: https://g.fc.run/ai/byre/compare/<previous>...v<version>`.
Leave out changes no user sees (refactors, docs, tagging and deploy commits), and leave out a section with nothing in it. v0.1.1's notes (`https://g.fc.run/ai/byre/releases/tag/v0.1.1`) are the model.
When the owner tells the lead "deploy byre", or anything that means the same, that is the owner asking for both the deploy and the discard below. The lead:
1. Creates a new task on byre whose whole brief is to deploy following this "Deploying" section.
2. Waits for that task's report that the service came back healthy.
3. Discards the task; the deploy changes no files, so there is nothing to merge. If the task reports a problem instead, the lead tells the owner and does not discard it.
## UI layout
- Exactly two panes, side by side, each exactly half the width, at every window size. Never three or more columns, never a sidebar, never a resizable or collapsible split.
- No navbar. The panes fill the window, below only the identity banner when it shows.
- The left pane has six tabs: Lead (the user's lead agent), Status, Notes (what the lead has noted about the user), Workflows (the saved workflows), Server (the byre service on this Mac) and Settings (the user's and this client's preferences), with the connection dot and the user's name at the end of its tab strip. Nothing else of the user's goes in it. The right pane always shows one task's conversation (its transcript, or a workflow stage task's run), or a project's dashboard when no task is selected, or the list of every project's active tasks while the task bar's list button has it open; the lead sets the task, and the human can switch it (⌘K). The task's other views (its other tabs, and images and diagrams zoomed from its transcript) open only when the human opens them, in a sheet over the left pane: attached to the center line, in the task's color and named by it, leaving a strip of the left pane visible that closes it. Images and diagrams zoomed from the lead chat open the same way over the right pane. A new task view is a new tab that opens in that sheet, never a new column or region.
- Overlays (the task switcher, confirmation dialogs) are allowed; they sit above the panes and don't change the layout.
## UI look
- Two fonts, bundled from `mac/fonts/`: Atkinson Hyperlegible Next for everything, and Atkinson Hyperlegible Mono only for text whose columns matter: code (blocks and inline), diffs, file contents, the terminal, raw tool input and output, and shell commands. Next follows the text size setting and Mono the code size setting.
- High contrast means readable text, not harshness. Body text is at least 7:1 against its background and secondary text at least 4.5:1, on soft backgrounds (off-white, dark slate; never pure black or white). Keep the frame quiet: 1px borders in a subtle tone, no dashed or heavy borders, no inverted blocks for selection, color used sparingly for state and accent. The palette lives in `mac/Sources/Byre/Palette.swift`; change it there.
- Never show agent cost, turn duration, or turn counts anywhere in the UI. They stay in the event log for the API, not on screen.