# Rules of engagement — agents working in this repo Read this before touching anything. Then read your lane brief in [docs/agents/](docs/agents/). ## 1. Stay in your lane | Lane | Owns | Never writes | |---|---|---| | PROTO | `contracts/`, `tools/mockd/`, `tests/conformance/` | everything else | | CORE | `core/`, `tools/bench/`, `tools/fuzz/` | `daemon/`, `gui/`, `extension/`, `contracts/` | | DAEMON | `daemon/`, `cli/`, `nmhost/`, `packaging/nativehost/` | `core/`, `gui/`, `extension/`, `contracts/` | | GUI | `gui/` | everything else | | EXT | `extension/` | everything else | | PKG/QA | `packaging/`, `.github/`, `tools/testserver/`, `tests/integration/`, `tests/e2e/`, root build files | any lane's feature code | If your task seems to require editing another lane's files, that is a signal the interface is wrong. **File the request; don't reach across.** ## 2. `contracts/` is sacred - Only PROTO commits there. - Generated code (`core/generated/`, `extension/src/shared/protocol/`) is committed and **must never be hand-edited**. Fix the schema, regenerate. - Need a new field? Open a `contracts/`-only PR: schema + fixtures + regenerated code + `VERSION` bump. Optional field or new method = minor; rename/remove/retype = major + ADR. - Working around a wrong contract locally is the single failure mode most likely to sink this project. Don't. ## 3. Layering ``` core → no JSON, no SQL, no Qt, no RPC. Ever. daemon → depends on core. No Qt. gui / ext → zero download logic. They render state and forward user intent. nmhost → a dumb pipe. Under 300 lines. No logic. ``` A grep for `curl|pwrite|sqlite` in `gui/` must come back empty. Same for download logic in `extension/`. ## 4. Non-negotiable behaviours - **Capture fails open.** Daemon down, slow, or erroring → Firefox downloads normally. Never swallow a user's download. `capture.offer` answers within 750 ms or the extension gives up. - **Resume is validated.** `If-Range` with ETag/Last-Modified; a `200` where `206` was expected means the file changed — ask the user, never silently corrupt. - **Never bind beyond `127.0.0.1`.** The WS transport is token-authenticated, origin-checked, and rate-limited. - **Secrets go to the Secret Service**, never SQLite, never logs. - **Paths are canonicalized** and checked against allowed roots before any write. - **No allocation in the transfer hot path.** ## 5. Definition of done, everywhere Code + tests + docs updated in the same change. A feature with no test does not exist. If you change observable behaviour, update the doc in `docs/` that describes it in the same PR. ## 6. Style - C++23, `-Wall -Wextra -Werror`, clang-format (config at root), clang-tidy clean. - TypeScript strict mode, ESLint, no `any` on protocol boundaries. - Commit messages: `lane: imperative summary` (e.g. `core: add dynamic segment stealing`). - One logical change per commit. Rebase onto `main`; no merge commits. ## 7. When you're unsure Ask in the PR rather than guessing at the interface. A day of clarification is cheaper than an M2 integration rewrite. And record real decisions as an ADR in `docs/adr/` — the next agent to touch this will have none of your context. **ADR numbers:** there is no allocator. Take the next free number in `main`'s `docs/adr/` (gaps from reserved-but-unwritten entries are fine to fill). Lanes draft in parallel, so collisions happen: whoever merges **second** renumbers, updates any cross-references, and keeps going — it is not worth a round trip.