- 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.
- 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`.
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`, always as the last action of the turn. It runs `mac/package.sh` (the build, both notarizations and the disk image, as for a release, into `mac/.build/release/<version>/`), installs the image's app as `/Applications/Byre.app` with `mac/install.sh`, and restarts the service with `launchctl kickstart -k`. The service runs `/Applications/Byre.app/Contents/Helpers/byre`, so the kickstart starts the new server and `byre-hands`. Everything is signed with Developer ID, so the macOS privacy grants (Screen Recording, Accessibility, Automation) survive.
2. When you resume, confirm it came up: read the tail of `~/Library/Logs/byre.log`, and `GET /api/admin/status` through the `tailscale serve` URL (`tailscale serve status` shows it). Report what you deployed and what you checked.
3. Once the service is confirmed healthy, update each client Mac whose app is running, through the client tools and without asking first: upload the disk image (`mac/.build/release/<version>/byre-<version>.dmg`) and `mac/install.sh`, quit the app, run `install.sh <dmg>`, and `open /Applications/Byre.app`. The app reads its server address from its iCloud-synced SwiftData store, so it reconnects to prod with no setup. Report each Mac's app commit (`install.sh` prints it).
- 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 the kickstart kills your own turn partway through the script. 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 kickstart.
- 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 (`started_at` in `/api/admin/status`, or the last `listening` line in the log) with when you ran `mac/deploy.sh`: a restart a few minutes after it, once the notarizations are done, is most likely your kickstart. If byre restarts without your kickstart, another agent deployed its own branch, and the running build is theirs.
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 iso, where `mac/release.sh <notes>` checks the tag, runs `mac/package.sh`, and publishes the image, its `.sha256` and `appcast.xml` as the release on g.fc.run, with 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.
- 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.
- The left pane has four tabs: Lead (the user's lead agent), Status, 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 goes in it. The right pane always shows one task, through tabs of that task's views; the lead sets the task, which tabs come first and which one is selected, and the human can switch the task (⌘K) and tabs too. A new task view is a new tab in the right pane, never a new column or region.
- One font everywhere: Atkinson Hyperlegible Mono, bundled from `mac/fonts/`.
- 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.