# 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. 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//`), 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. 2. 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`). Update the app on each one that runs no server, one Mac at a time, through the client tools and without asking first: 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, then quits the app if it is running and reopens it on the new build, which ends the `client_bash` call as the client disconnects while the script carries on. Wait until `list_clients` shows that Mac back on the new build and commit, and report them. Every one of these must be done before any server restarts, since a protocol change can leave an old client unable to reach a new server to be updated. 3. Only then update each client Mac that runs a server, the same way. Its server sees its executable replaced and restarts on the new build. 4. Install on the Mac you run on with `mac/install.sh ~/byre-deploy/byre.dmg`, always as the last action of the turn. 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. 5. When you resume, 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`. Report the new build number first (`plutil -extract CFBundleVersion raw /Applications/Byre.app/Contents/Info.plist` prints it; the iPhone build uploaded to TestFlight has the same one), then what you deployed and what you checked. 6. End the report with release notes for everything on main since the last release tag (`git describe --tags --abbrev=0` names it), written 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 ..HEAD`, not just the commit subjects, and leave out changes no user sees. 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 3) with the bearer token in `~/Library/Application Support/byre/loopback-token`: `GET https://byre-.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 --keychain-profile byre-notary` says why. - You run inside byre, so the restart kills your own turn just after the install. That is expected, not a reason to skip it. You resume with a "byre restarted while you were mid-turn" message; do the checks then, and confirm the service started after your install (the log shows `executable replaced, exiting` just before it). - The restart can cut off the record of the last tool call, so you may resume with no memory of having deployed. Before concluding that another agent restarted byre, compare the service's start time (the last `listening` line in the log) with when you ran `mac/install.sh`: a restart within seconds of it is most likely your install. 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` tag on a commit of main, pushed by itself (`git push origin v`, never moving or recreating a tag), and a clean checkout at that tag on the Mac byre runs on, where `mac/release.sh ` 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 `` 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^` names it; read `git diff ..v`, 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-.dmg` and drag `Byre.app` to Applications; Macs already running get this through the app's updater. The disk image's SHA-256 is in `byre-.dmg.sha256`.", then a line `Full changes: https://g.fc.run/ai/byre/compare/...v`. 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; 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, colour 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.