Development Workflow#
Ship features, tests, documentation and refactors together, in small verifiable slices.
Recommended Loop#
Define the smallest useful feature increment.
Implement the behavior in
Src/.Add or extend the matching test coverage.
Update Sphinx documentation in the same change.
Refactor the touched area only after the behavior is covered.
Validate with the appropriate local script or preset (Validation Targets).
Documentation Rule#
The Sphinx site is the project handbook, not just API output: setup and local execution, CI workflow behavior, test strategy, and architecture notes for new features. Every non-trivial feature updates it in the same change:
getting_startedwhen user-facing setup or runtime usage changesoverviewwhen architecture or capability boundaries changea new dedicated page when the feature needs operational guidance, examples, or design notes
Test Strategy#
The Suites#
Four C++ suites link AccelerANTgine and import its modules, so they test the
library itself; a fifth, Test/python, tests the Python bindings.
Suite |
Binary |
What it covers |
|---|---|---|
|
|
The C API; the TOML and JSON config parsers, every rejection path
included; the ONNX engine on models the tests generate (Relu, Add, two
outputs, and a Reshape that turns the input into raw YOLO output); YOLO
decoding (thresholds, classes, NMS, scaling, NaN and infinity, rows
shorter than the class count); GStreamer frames from |
|
|
The embedder contract: the C header compiles as C17 and links,
|
|
|
FuzzTest properties over the untrusted inputs: the settings JSON and the inference TOML on arbitrary bytes, unsigned fields that round-trip or are rejected, the stream-name mapping, the COCO table, and the YOLO decoder on arbitrary floats |
|
|
Google Benchmark over the hot paths: both parsers, |
No model or media file is checked in for them:
Test/common/kataglyphis_test_support.h writes the ONNX protobuf itself, and
the GStreamer tests use videotestsrc and appsrc. A test that needs a
GStreamer element the install lacks skips and names it; the WebRTC ones need
webrtcsink (gst-plugins-rs), which the Linux image has and the Windows image
has not.
Top-level CMake selects the suites by build type: commit, compile and fuzz in Debug; perf in
RelWithDebInfo; none in Release unless -DKATAGLYPHIS_RELEASE_TESTS=ON, which builds all four
and which both Windows lanes set (Build-Windows.ps1 -StageTests). FuzzTest needs Clang or
clang-cl. Every lane runs them:
Lane |
Commit + compile |
Fuzz (unit mode) |
Perf |
|---|---|---|---|
Linux x64, arm64 (clang) |
ctest, ASan + UBSan with llvm-cov, then again under TSan |
yes |
|
Linux x64, arm64 (GCC) |
ctest, ASan + UBSan with gcovr |
— (FuzzTest is Clang-only) |
|
Linux riscv64 |
ctest under QEMU, no sanitizers |
yes |
— (QEMU timing) |
Windows x64 |
Debug: ctest in the container (ASan), then on the runner host; Release: staged, on the host |
yes, Debug and Release |
Profile build, container and host; Release, by exit code |
Windows arm64 |
Release, on |
yes |
yes, by exit code |
Run The Suites Locally#
In the family image:
cmake --preset linux-debug-clang && cmake --build build -j
ctest --test-dir build --no-tests=error --output-on-failure
./build/fuzzTestSuite # about a second per property
FUZZTEST_FUZZ_FOR=60s ./build/fuzzTestSuite # longer, still unit mode
Coverage#
scripts/linux/ci-coverage.sh reports llvm-cov on the clang lane (over
lib/libAccelerANTgine.so) and gcovr on the GCC lane, where
linux-debug-GNU now builds with myproject_ENABLE_COVERAGE. The HTML lands
in docs/coverage/.
Python Tests#
The Python bindings test suite (Test/python) runs against both installed
wheels, GIL and free-threaded (see Python Bindings).
test_threading.py calls one engine, detector and config from eight threads at
once.
Where A New Test Goes#
The change |
The test |
|---|---|
Library logic and boundary cases |
small deterministic tests in the debug suites |
Spans CLI to library interactions, optional dependency discovery, packaging/runtime path behavior, or C API to library boundaries |
integration coverage |
Takes inputs that are difficult to enumerate manually: configuration files, CLI argument combinations, parser inputs, externally provided model metadata or request payloads |
|
Claims measurable runtime impact: inference startup time, repeated execution performance, pipeline setup costs, serialization or preprocessing hot paths |
|
Profiling#
ci-profile-bench.sh is the profiling and benchmark lane, and it is what CI
runs:
bash scripts/linux/ci-profile-bench.sh
gperftools: configure links
libprofiler(google-perftools,libgoogle-perftools-dev) when it finds it, and the profiled window writeslogs/profile.prof; a host without it falls back to-pg. The image carries it (2.18.1, checked 2026-10-16), so CI’s window writes the profile on both arches. Reading it takes Go’spprof(plusgraphvizfor its graphs), which the lane itself never calls.On arm64 the script picks gperftools’ generic_fp stack walker. The default,
libgcc, unwinds from inside the profiler’s signal handler and crashed the workload with a segfault in 6 of 140 starts at 4 kHz on a Pi 5 (one CI run on 2026-10-16), whilegeneric_fpcrashed in none of 210 and gave slightly deeper stacks, since clang keeps arm64’s frame records at-O3. x86_64 keeps the default, where clang drops frame pointers. SettingTCMALLOC_STACKTRACE_METHODyourself overrides the choice.callgrind:
valgrind --tool=callgrindis reached throughbash scripts/linux/ci-release.sh --callgrind.perf:
perf recordneeds aperfbinary (Ubuntu 26.04: thelinux-perfpackage;linux-tools-commonno longer ships one) plus permission to open perf events. The CI image hasperf, but CI cannot open perf events, so the lane warns, runs the workload for a short window without a recorder, then runs the benchmarks. The profiled workload, the CLI’s WebRTC producer, streams only while a signalling server answers, so the script starts one on127.0.0.1:18443for the window (--signalling-portmoves it) unless--profile-argsnames a--serverof its own. Why, with the measurements:AGENTS.md§ 4.
Installing the tools is not this project’s subject; the upstream install instructions for gperftools, pprof, valgrind and perf are the ones that stay correct.
Refactoring Cadence#
Refactor periodically, but only tested code. Prefer refactors that:
reduce duplication in frequently touched code paths
clarify runtime or dependency boundaries
simplify test setup
improve naming and control flow without changing behavior
Avoid broad cleanup passes that are not tied to active work unless there is clear payoff and sufficient coverage.
Validation Targets#
Choose the smallest validation path that still proves the change: a debug build for correctness and fuzz targets, a profile build for benchmarks, a release build for packaging validation.
Linux debug correctness:
cmake --preset linux-debug-clangLinux TSan validation:
cmake --preset linux-debug-clang-tsanLinux profile benchmarks:
cmake --preset linux-profile-clangWindows debug/profile/release container builds:
.\scripts\windows\Invoke-ContainerBuild.ps1Windows host execution:
.\scripts\windows\Start-Windows.ps1 -Config Debug|Profile|Release
The GitHub workflows are the reference for how the project builds and validates (Build Matrix Summary); keep local verification on those same paths so CI remains a confirmation step rather than a discovery step.
Static Analysis And Formatting#
One entry point per platform, and CI runs exactly these:
bash scripts/linux/run-static-analysis-format.sh
pwsh -NoProfile -File .\scripts\windows\Build-Windows.ps1
What runs: cmake-format and clang-format everywhere, plus clang-tidy and
scan-buildon the Linux clang lane and clang-tidy in the Windowsclangcl-debugbuild, over the files the project actually compiles (.ixx,.cppmand.mxxincluded).clang++ --analyzeruns only when asked for (--direct-analyze).Rewrite or check: cmake-format rewrites in place, and so does clang-format on Windows. On Linux clang-format checks and does not rewrite: it covers every C/C++ file under
Src/,Test/,Bindings/andcmake/, and fails the lane when one of them deviates from.clang-format.One clang-format release: the Linux gate grades with the clang-format of the hub’s
LLVM_RELEASE(23.1.3), because each release formats differently. The hub’scode_quality_require_pinned_clang_formatlooks in/usr/local/llvm-target/bin, then onPATH, and fails the gate when neither has that release. To fix a file, run theclang-format -ithe gate’s error names; the image’sclang-formatis that release.Every tool is a gate: a tool that is not installed fails the lane rather than being skipped.
Configs:
.clang-formatand.cmake-format.yamlhere are shared configs, andscripts/linux/run-lint-gates.shchecks them against ANTfrastructure’s copies (the rows of.antfrastructure-shared.manifest): edit them upstream, not here..clang-tidy,gcovr.cfgand.pre-commit-config.yamlare this project’s own on purpose; the manifest’s header says why. What each tool does and how to run one of them alone is in ANTfrastructure’s code-quality-tooling.md, and the configs themselves are described in its shared/config/README.md.
The pre-commit hook installs from the same requirements.txt:
uv venv --python 3.14 # the docs theme in requirements.txt needs it
source .venv/bin/activate # .venv/Scripts/activate on pwsh
uv pip install -r requirements.txt
pre-commit install
pre-commit run --all-files # once, optional
Useful Tools#
Benchmarking:
VSCode extensions:
Literature#
Rust:
CMake/C++: