This guide covers everything needed to build, test, and contribute to clawq --- from bootstrapping the toolchain to running individual test cases.
Prerequisites
- opam --- the OCaml package manager
- libsqlite3-dev (or your distro’s equivalent, e.g.
sqlite3-devel) - Coq 8.19 --- only required if modifying the Coq theories; the extracted OCaml is tracked in git so the project builds without Coq installed
Bootstrap
Create an opam switch with all dependencies:
make bootstrap
This creates the clawq-5.1 switch (OCaml 5.1), installs all opam
dependencies, and leaves you ready to build.
If you already have the switch but need to sync dependencies:
opam install . --deps-only --with-test
Building
Standard Build
make build
This runs dune build under the clawq-5.1 opam switch. The Makefile sets
SHELL to route through opam exec --switch=clawq-5.1 --, so you do not
need to activate the switch manually.
You can also build directly:
opam exec --switch=clawq-5.1 -- dune build
Minimal Build
The minimal binary (clawq-min) includes only clawq_runtime_core --- no
network channels, no gateway, no daemon:
make build-minimal
Optimized Builds
Two build profiles are defined in dune-workspace:
| Profile | Flags | Use Case |
|---|---|---|
release-speed | -O3 | Maximum performance |
release-size | -O2 -compact | Smaller binary |
# Choose mode
make build-opt OPT=speed
make build-opt OPT=size
# Or use explicit targets
make build-opt-speed
make build-opt-size
# Stripped variants
make build-opt-speed-stripped
make build-opt-size-stripped
Optimization targets print a final line: <path/to/exe> <size_kb> KB.
Testing
Run All Tests
make test
Or directly:
opam exec --switch=clawq-5.1 -- dune runtest
List All Test Suites and Cases
opam exec --switch=clawq-5.1 -- dune exec test/test_main.exe -- list
Run a Subset by Suite Name
The test binary accepts a test subcommand with a regex that matches suite
names:
# All suites matching "command_bridge"
opam exec --switch=clawq-5.1 -- dune exec test/test_main.exe -- test command_bridge
# All suites matching "scheduler"
opam exec --switch=clawq-5.1 -- dune exec test/test_main.exe -- test scheduler
Run Specific Test Cases by Index
After finding case numbers with list, run individual cases:
# Single case
opam exec --switch=clawq-5.1 -- dune exec test/test_main.exe -- test command_bridge 20
# Multiple cases and ranges
opam exec --switch=clawq-5.1 -- dune exec test/test_main.exe -- test scheduler 0,3,8-10
When to Run Tests
- After any non-trivial OCaml edit:
make test - After formatting-sensitive edits:
make fmt-check - After extraction-related edits:
make extract-check - After runtime/library reshaping: full tests + at least one optimized build
Formatting
Clawq uses ocamlformat 0.28.1 with the default profile.
# Format all code
make fmt
# Check formatting without modifying files
make fmt-check
# CI-equivalent gate
dune fmt && git diff --exit-code
Never hand-format to fight the formatter’s output. Keep lines and layout formatter-friendly.
Extraction Workflow
The Coq-to-OCaml extraction pipeline regenerates src/extracted/clawq_core.ml
and clawq_core.mli from the Coq theories:
# Regenerate extracted code from Coq
make extract
# Check for extraction drift (CI gate)
make extract-check
The extraction is driven by scripts/extract.sh and the directives in
coq/theories/Clawq/Extract.v. Type mappings use ExtrOcamlBasic,
ExtrOcamlNativeString, and ExtrOcamlNatInt to produce idiomatic OCaml.
The extracted files are tracked in git so the project builds without Coq
installed. The extract-check target verifies that the tracked files match
what extraction would produce, catching accidental drift.
Code Style
Formatting
- ocamlformat 0.28.1,
defaultprofile - Never fight the formatter
Naming
- Modules:
PascalCasefilenames (e.g.runtime_config.mlexposes moduleRuntime_config) - Values and functions:
snake_case - Command handlers:
cmd_<name>pattern - Test names: concise behavior phrases
Imports
- Prefer minimal
open; use fully qualified modules where reasonable openat top is acceptable for narrow, obvious cases (e.g.open Cmdliner)- Avoid broad
openchains that hide symbol origin
Types and Error Handling
- Prefer explicit records and algebraic types over ad-hoc tuples
- Use
option/resultfor expected failure paths - Reserve exceptions for I/O and external boundaries
- Return user-facing error strings in command bridge paths
- Keep error messages actionable and specific
Comments
- Add comments only for non-obvious invariants or protocol details
- Do not add explanatory noise for straightforward code
Runtime Split Rules
The runtime is split into two libraries to keep the core small:
| Library | Scope |
|---|---|
clawq_runtime_core | CLI, config, agent, session, provider, tools, memory, scheduler, audit, rate limiter, Landlock |
clawq_runtime_integrations | HTTP server, Telegram, Discord, Slack, WebSocket client, daemon, MCP server, tunnels |
Rules:
- Keep optional integrations out of
clawq_runtime_core. - New network/server features belong in
clawq_runtime_integrations. - Integration-only commands must not be exposed as active behavior in the minimal build.
- In
command_bridge_min.ml, return clear “disabled in minimal build” messages. - Evaluate new dependencies for core vs integration placement before linking.
Project Structure
coq/theories/Clawq/ Coq source theories
src/extracted/ Auto-generated OCaml from Coq
src/ Runtime OCaml code
test/ Alcotest test suites
scripts/ Build and extraction scripts
ui/ Bun/TypeScript source for the embedded web chat UI
docs/ Documentation site (Astro)
Key Files
| File | Role |
|---|---|
Makefile | Build/test orchestration |
dune-project | Dune project configuration |
dune-workspace | Build profiles (release-speed, release-size) |
src/main.ml | CLI entry point (Cmdliner) |
src/main_min.ml | Minimal CLI entry point |
src/command_bridge.ml | Full command routing |
src/command_bridge_min.ml | Minimal command routing |
src/ui_server.ml | Embedded/dev web UI asset serving and versioning |
src/chat_ui_assets.ml | Generated OCaml module containing bundled UI files |
scripts/gen_chat_ui_assets.sh | Regenerate src/chat_ui_assets.ml from ui/dist/ |
test/test_main.ml | Test entry point |
Web UI Development
The streaming browser chat lives in ui/ and is bundled into the daemon.
# Build the UI bundle and embed it into src/chat_ui_assets.ml
make ui
# Watch UI source files during development
make ui-dev
# Verify checked-in embedded assets match ui/dist
make ui-check
Normal daemon startup extracts the embedded bundle into ~/.clawq/ui/ when the bundle version changes. For live UI iteration, create ~/.clawq/ui/DEV; with that marker present, clawq serves files from disk without overwriting them and recomputes the UI version from the local files.
Contributing Workflow
- Read the relevant modules and adjacent tests before making changes.
- Implement the smallest viable change.
- Run focused tests, then
make test. - Run
make fmt-checkif OCaml files changed. - Run
make extract-checkif Coq/extraction files changed. - Summarize behavior changes in your commit message.
Adding a New CLI Command
- Define the Cmdliner command in
src/main.ml— usesimplefor no-arg commands orwith_argsfor commands that forward positional args. - Wire the handler in
src/command_bridge.ml— add a match arm in thehandlefunction. - Add a minimal-build stub in
src/command_bridge_min.ml— for integration-only commands, return"<Command> is not available in the minimal build. Use the full clawq binary for this feature.". - Decide core vs integrations — commands that need network (HTTP, WebSocket) belong in
clawq_runtime_integrations. Pure-CLI commands belong inclawq_runtime_core. - Add tests in
test/test_command_bridge.mlor a dedicated test file.
Adding a New LLM Provider
- Create
src/provider_<name>.mlimplementing the provider interface. Seesrc/provider_openai.mlfor the standard pattern. - Register in
src/provider_init.ml— add the provider to the initialization map. - Add pricing to
src/cost_tracker.ml— add model entries with input/output token costs. - Add to models catalog in
src/models_catalog.mlif the provider should appear inclawq models list. - Canonical format — models use
provider:modelformat (seesrc/pmodel.ml). The provider name in the canonical format must match the key used inconfig.json.
Running Individual Tests
The make test-run target provides a convenient shorthand for running specific test suites or cases:
# List all test suites and cases
make test-run ARGS="list"
# Run a specific test suite by regex
make test-run ARGS="test command_bridge"
# Run a specific test case by index
make test-run ARGS="test command_bridge 20"
# Run multiple indices or ranges
make test-run ARGS="test scheduler 0,3,8-10"
Docker Test Isolation
make test and make test-all automatically run tests inside Docker for a clean, reproducible environment. The image is built from Dockerfile.test.
# Build the test image (done automatically by make test)
make docker-test-image
# Run quick tests in container
make test
# Run all tests (including Slow-tagged) in container
make test-all
# Run tests outside Docker (faster iteration, skips isolation)
make test-nocontainer
# Clean the build volume used by Docker test runs
make docker-test-clean
Maintenance Notes
llms-full.txt Updates
When adding, removing, or renaming CLI commands, config fields, tools, gateway endpoints, or channels, update docs/public/llms-full.txt to reflect the changes. This file is the complete self-knowledge reference that clawq uses to understand itself.
Binary Size CI
Optimized builds enforce size thresholds. After adding dependencies or significant code, run make binary-size-report to check impact. Update thresholds in ci/binary-size-thresholds.tsv if a size increase is justified.
WASM Build Target
An experimental WASM target exists via wasm_of_ocaml-compiler. Build with make build-wasm. This is not actively maintained but is preserved for future exploration.
Docker
Build and run clawq in a container:
# Build image
make docker-build
# Run daemon (foreground)
make docker-run
# Health check
curl http://127.0.0.1:13451/health
To persist config and state across restarts:
docker run -it --rm -p 13451:13451 \
-v "$HOME/.clawq:/root/.clawq" \
-e CLAWQ_MASTER_KEY="your-passphrase" \
clawq:latest agent
Make Targets Reference
| Target | Description |
|---|---|
make bootstrap | Create opam switch and install all dependencies |
make build | Build the project |
make build-minimal | Build minimal binary (core-only) |
make build-opt OPT=speed|size | Optimized build |
make build-opt-speed | Optimized build with -O3 |
make build-opt-size | Optimized build with -O2 -compact |
make build-opt-speed-stripped | Stripped optimized speed build |
make build-opt-size-stripped | Stripped optimized size build |
make build-opt-minimal | Optimized minimal binary |
make test | Run quick tests in Docker |
make test-all | Run all tests (including Slow) in Docker |
make test-nocontainer | Run quick tests without Docker |
make test-run ARGS="..." | Run specific test suite or case |
make docker-test-image | Build the Docker test image |
make docker-test-clean | Remove cached Docker build volume |
make fmt | Format code |
make fmt-check | Check formatting |
make extract | Regenerate OCaml from Coq |
make extract-check | Check for extraction drift |
make ui | Build the web UI and regenerate embedded assets |
make ui-dev | Run the web UI watcher |
make ui-check | Verify embedded web UI assets are current |
make sync-version | Sync version from VERSION file to all non-OCaml files and re-extract |
make sync-version-check | Verify version consistency across files |
make run | Print CLI help |
make clean | Clean build artifacts |
make docker-build | Build Docker image |
make docker-run | Run daemon in Docker |
make verify-report | Generate formal verification report and badge |
make release | Build release artifacts |