Build, test & contribute
This chapter shows how to build from a source checkout, make one change, and run the checks for each boundary.
Checked against the source on 5 October 2026
On this page
Start only the components that your change needs
Use a source checkout with its lockfiles and read AGENTS.md for the current repository contract. The root package manager is pnpm 10.34.5, and the root package.json pins vulnerable transitive versions through pnpm overrides and patched dependencies, which is why --frozen-lockfile fails after a dependency change until the lockfile is regenerated. Install a compatible Node runtime, stable Rust for native work, and the target platform’s native compiler tools. iOS work requires macOS and Xcode. No root command starts every component together. Run only the surfaces that your change needs.
pnpm install --frozen-lockfile| Task | Start command or project, and what it proves |
|---|---|
| Website | pnpm dev:website. Documentation and marketing UI in a browser. |
| Desktop frontend | pnpm -C desktop dev. React layout and frontend behavior. Native calls require a Tauri runtime. |
| Native desktop | pnpm -C desktop tauri:dev. The actual Web |
| Claude agent host | pnpm -C desktop/ |
| Server | pnpm dev:server after local setup. Configured HTTP and relay service behavior. |
| i | mobile/ |
| Android | mobile/ |
The Claude agent host is the pnpm workspace package @hbcode/claude-host in desktop/agent-host. It pins the Claude Agent SDK at 0.3.283. A debug build of the desktop starts node from PATH with desktop/agent-host/dist/src/entry.js and takes the claude binary from the SDK platform package in node_modules. Without the installed package the Claude runtime fails with “Install the Claude host dependencies before running desktop”. A release build runs pnpm -C agent-host package-runtime first and reads node, host/entry.mjs, and claude from the claude folder of the app resources. A missing host script or claude binary fails with “The Claude runtime is not packaged”.
Where the desktop code lives
- Shared chat layerdesktop/src-tauri/src/chat
Eleven folders: approvals, contracts, entrypoints, operation_ledger, outbox, presentation, runs, send, session_messages, sessions, and timeline. Its only import from agents is agents::dispatch.
- Dispatchdesktop/src-tauri/src/agents/dispatch
Six modules: approvals, conversations, runs, selection, send, and timeline. Each function reads the agent of the session and calls the matching adapter.
- Adaptersagents/codex · agents/claude
Each adapter runs one agent and maps it to the chat contracts. Account and usage commands in commands and remote_api call an adapter directly, because they belong to one agent.
- Tool catalogdesktop/src-tauri/src/services/agent_tools
Both adapters call it to list the tools and to run a tool call. It runs the explainer video worker on the agent of the session, with the Codex app-server or the Claude agent host, and uses chat::session_messages for messages between sessions.
- Claude agent hostdesktop/agent-host · @hbcode/claude-host
TypeScript with the Claude Agent SDK 0.3.283. It shares no code with the crate. The Claude adapter starts it as a child and talks to it on stdin and stdout.
The module comments state the rule. chat/mod.rs calls the chat layer agent-neutral. agents/dispatch/mod.rs says that the chat layer reaches agents::codex and agents::claude only through this module. The imports follow it: every reference from chat to agents goes to agents::dispatch, and neither adapter imports the other. No script in check:quality-boundaries enforces this rule, so a reviewer must check it.
The first six paths in the table are inside desktop/src-tauri/src.
| Path | What it owns and what it may import |
|---|---|
| chat | Sessions, the outbox, runs, the live overlay, timeline rows and their presentation, approvals, session messages, and the chat entry points. May import: agents:: |
| agents/ | The choice of adapter for a session: send, runs, timeline, approvals, conversations, and model selection. May import: both adapters and chat. |
| agents/ | The app-server child, threads, turns, goals, history, Codex auth profiles, and usage limits. May import: chat, services/ |
| agents/ | The host process, runs, the history projection, permissions, subagents, and Claude accounts. May import: chat, services/ |
| services/ | The tool catalog and the tool handlers for both agents. May import: chat, agents:: |
| remote_ | RPC handlers for the phones. May import: chat and agents:: |
| desktop/ | The Claude agent host in Type |
Build the native companions
- iPhone: open mobile/ios/HBCode.xcodeproj, select the HBCode scheme, resolve the local Core, VibeUI, and VibeUIFormatting packages, and configure the Auth0 values from mobile/ios/README.md. Run on an iOS 18 simulator or a device with your signing team.
- Android: open mobile/android in Android Studio, let Gradle sync, and configure the Auth0 and Firebase values from mobile/android/README.md. The app targets Android 8.0 and later.
- For a live control test on either platform, connect to a configured desktop with the same account and region and read a known file before you try a mutation.
xcodebuild -showdestinations \
-project mobile/ios/HBCode.xcodeproj \
-scheme "HB Code"The HB Code scheme runs VibeUITests through the test plan mobile/ios/HBCode.xctestplan. The plan sets diagnosticCollectionPolicy to Never, so xcodebuild collects no simulator diagnostics at the end of a test run and does not wait for them.
Simulators and emulators cover layout, decoding, and state transitions. They prove nothing about physical push delivery, store purchases, or a signed release’s provider login, so test those on real services and a real device before you call a release ready.
Follow one operation through the code
rg -n "chatOutboxSubmit" rpc-contract.v1.json desktop/src-tauri/src/remote_api server/src mobile/ios
rg -n "submit_outbox_request" desktop/src-tauri/src/chat/outbox
rg -n "save_workspace_chat_outbox_with_operation_identity" desktop/src-tauri/src- Describe the user action, expected result, and a failure case before editing.
- Find the transport DTO and the domain service that owns the operation. Read both, because an adapter alone does not define behavior.
- Change the smallest owning layer. Preserve stable operation, session, and timeline identities.
- If you change a shared RPC, update the manifest, desktop, server, iOS, and Android consumers together. The manifest pins what they share: page sizes 12 and 100, 40 rows for an anchored window, timeouts of 15, 60, 115, 60, and 20 seconds, the initial-timeline retry policy, and the mutations that require an idempotency key.
- Add a focused regression test at the failure boundary. For visual or lifecycle behavior, also inspect the running surface.
- Update the relevant technical chapter and its source references with the code change.
Choose checks that match the change
| Changed surface | Repository commands |
|---|---|
| Desktop Type | pnpm -C desktop typecheck, pnpm -C desktop lint, pnpm -C desktop test |
| Desktop Rust | pnpm desktop:check, then focused Rust tests for the changed service. |
| Claude agent host | pnpm -C desktop/ |
| Server Rust | pnpm server:check, then focused cargo tests and configured service tests as needed. |
| Shared RPC | pnpm check:rpc-contract, plus the affected client and server tests. |
| Codex storage boundary | pnpm check:codex-storage-boundary |
| Website | pnpm -C website typecheck, pnpm -C website lint, pnpm -C website test, pnpm -C website build |
| File, function, and folder size | pnpm check:max-lines, pnpm check:max-function-lines, pnpm check:max-folder-files |
| Unused code on the desktop | pnpm check:unused runs knip, cargo machete, and clippy with the dead-code and unused warnings. It is not part of check:all. |
| Cross-boundary review | pnpm check:quality-boundaries chains nine checks: the three size checks, the Tauri command surface, the RPC contract, the Intelli |
| Everything | pnpm check:all runs check:quality-boundaries, server:check, desktop:check, the desktop lint and format check, and the typecheck of every workspace package. |
A typecheck proves type consistency. It does not prove delivery correctness, and a relay unit test does not prove a native login. State which checks ran, which need external services, and what you inspected in the live product.
Make the change easy to review
Keep one canonical runtime path. Remove obsolete branches instead of adding silent compatibility behavior. Keep components modular, use the dependencies already present, and retain unrelated work in a dirty checkout. The repository caps files at 550 lines and functions at 100 lines. The baseline files .max-lines-baseline and .max-function-lines-baseline record existing debt and may only shrink. AGENTS.md sets a target of five authored source or test files per folder, and check:max-folder-files fails above seven. Split a growing folder by feature or reader task. A numbered, alphabetic, part, misc, or more folder does not satisfy the rule.
A contribution should explain the trigger, the resulting behavior, the affected contract, and the validation evidence, with a screenshot for a visible change and a bounded trace for a delivery defect. Never include private session transcripts, auth files, or machine-specific paths.