# 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 `go install ./cmd/byre` or restart the service on your own initiative, not even to check that a change works. - `docs/operations.md` describes the layout, data, service and APIs; keep it current with every change. - Never add tests. byre has no test suite; check a change with `go vet ./...`, `go generate ./web` (which type-checks the UI) and, when `mac/` changed, `mac/bundle.sh` (release only; debug builds are refused). - The web UI (`web/src/`) and the macOS app (`mac/Sources/Byre/`) are two clients of the same views. A change to what a client shows or does, such as Markdown rendering, goes into both in the same task; never leave one client behind. ## Deploying When asked to deploy, do exactly this, from the worktree root: 1. `go generate ./web`, if anything under `web/src/` or `api/openapi.yaml` changed. 2. `go install ./cmd/byre && launchctl kickstart -k gui/$(id -u)/run.fc.byre`, always as that one command, never `go install` on its own, and always the last action of the turn. 3. 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. 4. Once the service is confirmed healthy, rebuild and relaunch the macOS app: run `mac/bundle.sh`, then `pkill -x Byre` to quit every running byre app, whichever worktree it was built from (it exits 1 when none is running, which is fine), then `open mac/.build/byre.app`. The app reads its server address from its iCloud-synced SwiftData store, shared by every build, so the new build reconnects to prod with no setup. Report that the app was rebuilt and relaunched. What to expect: - `go install` does not restart the service. If byre restarts after your install without your kickstart, another agent deployed its own branch, and the running binary is theirs, not yours. - You run inside byre, so the kickstart kills your own turn partway through the command. 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 before the kickstart, or the kickstart itself, 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 your most recent `go install`: a restart right after your own install is most likely your kickstart. 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. ## Web 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 three tabs: Lead (the user's lead agent), Status and Settings, 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. - Overlays (the task switcher, confirmation dialogs) are allowed; they sit above the panes and don't change the layout. ## Web UI look - One font everywhere: Atkinson Hyperlegible Mono, self-hosted from `web/fonts/`. No font CDNs. - 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 the custom properties at the top of `web/style.css`; 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. ## Web UI code - Frameworkless. TypeScript in `web/src/`, compiled file-by-file to ES modules in `web/dist/` with `tsc`. No bundler, no framework, no runtime dependencies. The exceptions are vendored and lazy-loaded: Mermaid (`web/vendor/mermaid.min.js`, loaded only when a diagram appears) and xterm.js with its fit addon (`web/vendor/xterm.mjs`, `addon-fit.mjs`, `xterm.css`, loaded only when the Terminal tab opens). - API types come from `api/openapi.yaml` via `openapi-typescript` (`web/src/api.gen.ts`); call the API through the typed client in `web/src/api.ts`, never raw `fetch`. - `web/dist/` is committed so `go install` needs no Node. Regenerate it with `go generate ./web` whenever `web/src/` or the spec changes. - Every view mounted into a pane returns a dispose function that removes its state subscriptions. - Never use a service worker, for anything, including Web Push. Anything a page needs from the server comes over its SSE stream or the API.