The first six build-order steps from the brief, as one reviewable unit:
- GitbayClient speaking both API surfaces: GET /api/v1/read with per-account ETag revalidation (a repeat read costs a 304), POST /api/v1/cmd for writes, typed envelope decoding, exit-code mapping (3 = empty state, 4 = server's message verbatim, 2 = logged app bug), one 5xx retry, Retry-After backoff capped at 3s. The token only ever travels to the instance host; redirects elsewhere are refused.
- Sign in by pasted token, validated with whoami before storing; Keychain (ThisDeviceOnly); multiple instances; a revoked token becomes a clean sign-out with a sentence, verified against a live 401.
- Repo list/show/tree/cat with Highlightr syntax highlighting and README rendering; repo log with signature verdicts as badges (unsigned = no badge; unknown states decode without sinking the log).
- MRs: list, show, client-side unified-diff parsing (the recorded gap), threads with resolve/reopen/reply, approve, request changes, comment via stdin, merge with all four strategies, close. Refusals surface the server's rule verbatim.
- Issues: comment, close/reopen, labels and assignees via --add/--remove. Builds: list and raw logs.
- Dashboard: client-side fan-out, width 4, archived repos skipped. First real load measured ~130 reads for 66 repos and hit the 120/min bucket at repo 64 — filed as krz/gitbay#41, the aggregate worth adding.
74 tests: recorded envelopes through a stubbed URLProtocol, plus one live test (gated on GITBAY_TEST_TOKEN) proving 200-then-304 against gitbay.org. Screenshot checkpoint done: light and dark on iPhone 17e and 17 Pro Max, content screens on 17 Pro.
Closes #11