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:

ProfileFlagsUse Case
release-speed-O3Maximum performance
release-size-O2 -compactSmaller 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, default profile
  • Never fight the formatter

Naming

  • Modules: PascalCase filenames (e.g. runtime_config.ml exposes module Runtime_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
  • open at top is acceptable for narrow, obvious cases (e.g. open Cmdliner)
  • Avoid broad open chains that hide symbol origin

Types and Error Handling

  • Prefer explicit records and algebraic types over ad-hoc tuples
  • Use option / result for 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:

LibraryScope
clawq_runtime_coreCLI, config, agent, session, provider, tools, memory, scheduler, audit, rate limiter, Landlock
clawq_runtime_integrationsHTTP server, Telegram, Discord, Slack, WebSocket client, daemon, MCP server, tunnels

Rules:

  1. Keep optional integrations out of clawq_runtime_core.
  2. New network/server features belong in clawq_runtime_integrations.
  3. Integration-only commands must not be exposed as active behavior in the minimal build.
  4. In command_bridge_min.ml, return clear “disabled in minimal build” messages.
  5. 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

FileRole
MakefileBuild/test orchestration
dune-projectDune project configuration
dune-workspaceBuild profiles (release-speed, release-size)
src/main.mlCLI entry point (Cmdliner)
src/main_min.mlMinimal CLI entry point
src/command_bridge.mlFull command routing
src/command_bridge_min.mlMinimal command routing
src/ui_server.mlEmbedded/dev web UI asset serving and versioning
src/chat_ui_assets.mlGenerated OCaml module containing bundled UI files
scripts/gen_chat_ui_assets.shRegenerate src/chat_ui_assets.ml from ui/dist/
test/test_main.mlTest 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

  1. Read the relevant modules and adjacent tests before making changes.
  2. Implement the smallest viable change.
  3. Run focused tests, then make test.
  4. Run make fmt-check if OCaml files changed.
  5. Run make extract-check if Coq/extraction files changed.
  6. Summarize behavior changes in your commit message.

Adding a New CLI Command

  1. Define the Cmdliner command in src/main.ml — use simple for no-arg commands or with_args for commands that forward positional args.
  2. Wire the handler in src/command_bridge.ml — add a match arm in the handle function.
  3. 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.".
  4. Decide core vs integrations — commands that need network (HTTP, WebSocket) belong in clawq_runtime_integrations. Pure-CLI commands belong in clawq_runtime_core.
  5. Add tests in test/test_command_bridge.ml or a dedicated test file.

Adding a New LLM Provider

  1. Create src/provider_<name>.ml implementing the provider interface. See src/provider_openai.ml for the standard pattern.
  2. Register in src/provider_init.ml — add the provider to the initialization map.
  3. Add pricing to src/cost_tracker.ml — add model entries with input/output token costs.
  4. Add to models catalog in src/models_catalog.ml if the provider should appear in clawq models list.
  5. Canonical format — models use provider:model format (see src/pmodel.ml). The provider name in the canonical format must match the key used in config.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

TargetDescription
make bootstrapCreate opam switch and install all dependencies
make buildBuild the project
make build-minimalBuild minimal binary (core-only)
make build-opt OPT=speed|sizeOptimized build
make build-opt-speedOptimized build with -O3
make build-opt-sizeOptimized build with -O2 -compact
make build-opt-speed-strippedStripped optimized speed build
make build-opt-size-strippedStripped optimized size build
make build-opt-minimalOptimized minimal binary
make testRun quick tests in Docker
make test-allRun all tests (including Slow) in Docker
make test-nocontainerRun quick tests without Docker
make test-run ARGS="..."Run specific test suite or case
make docker-test-imageBuild the Docker test image
make docker-test-cleanRemove cached Docker build volume
make fmtFormat code
make fmt-checkCheck formatting
make extractRegenerate OCaml from Coq
make extract-checkCheck for extraction drift
make uiBuild the web UI and regenerate embedded assets
make ui-devRun the web UI watcher
make ui-checkVerify embedded web UI assets are current
make sync-versionSync version from VERSION file to all non-OCaml files and re-extract
make sync-version-checkVerify version consistency across files
make runPrint CLI help
make cleanClean build artifacts
make docker-buildBuild Docker image
make docker-runRun daemon in Docker
make verify-reportGenerate formal verification report and badge
make releaseBuild release artifacts