# Repository Guidelines ## Project Structure & Module Organization This is a small C library built with CMake. Core implementation lives in `src/error.c`. `src/lock.h` is private: it selects the threading backend (pthread or none) and is not installed. The public header is generated at build time from `include/akerror.tmpl.h` by `scripts/generrno.sh`, which also generates `src/errno.c` under the build directory and stamps in whether the build is thread safe. CMake package and pkg-config templates are in `cmake/` and `akerror.pc.in`. Tests are one-file C programs in `tests/`; shared test helpers live beside them, such as `tests/err_capture.h` and `tests/err_threads.h`. ## Build, Test, and Development Commands Use an out-of-tree build: ```sh cmake -S . -B build cmake --build build ctest --test-dir build --output-on-failure ``` `cmake -S . -B build` configures the build and generates `akerror.h`. `cmake --build build` compiles `libakerror` and test executables. `ctest` runs the registered unit tests. To emit CI-style results, use: ```sh ctest --test-dir build --output-on-failure --output-junit "$(pwd)/ctest-junit.xml" ``` The suite includes thread tests. The run that proves there is no data race under them is ThreadSanitizer: ```sh scripts/thread_test.sh ``` which configures `build/tsan` with `-DAKERR_SANITIZE=thread`, builds the library *and* the tests with it, and runs CTest with ASLR disabled (TSan aborts with an "unexpected memory mapping" on kernels with `vm.mmap_rnd_bits` above 28). `AKERR_SANITIZE` takes any sanitizer list, so `-DAKERR_SANITIZE=address,undefined` works the same way. CTest gives each test `halt_on_error=1`, so a report fails the test rather than being printed and passed over — running a sanitized test binary by hand does **not** inherit that. Set it yourself (`TSAN_OPTIONS=halt_on_error=1 ./build/tsan/test_err_threads_pool`) or the binary can print a race and still exit 0. Mutation testing is available through: ```sh cmake --build build --target mutation scripts/mutation_test.py --target src/error.c --threshold 65 ``` Code coverage is available through: ```sh cmake --build build --target coverage scripts/coverage.py --threshold 90 --branch-threshold 50 ``` `scripts/coverage.py` configures its own instrumented build tree (default `build/coverage`, `-DAKERR_COVERAGE=ON`), runs the CTest suite there, and reports gcov line/branch coverage per library source. Thresholds gate each file as well as the total. Coverage measures the library's own sources only -- `src/error.c`, `src/lock.h` and the generated `src/errno.c`; the public header's macros expand at their call sites, so mutation testing is what checks those. To build without threads (no locking, no thread-local storage, thread tests not registered), configure with `-DAKERR_THREADS=none`. `auto` is the default and fails the configure rather than falling back. ## Coding Style & Naming Conventions Use C99-compatible C and follow the surrounding style. Functions and types use the `akerr_` prefix; macros and constants use `AKERR_` or all-caps macro names such as `PREPARE_ERROR`. Keep generated-code changes in templates or generator scripts, not in build outputs. Preserve concise comments for invariants, macro constraints, and non-obvious error lifecycle behavior. **Locking.** One recursive lock (`akerr_state_lock`, see `src/lock.h`) covers both the error pool and the status registry. A function named `*_locked` is called with that lock already held; a function without the suffix takes it. Never take it in a body written with the `FAIL_*_RETURN` macros — those return from the middle of the function and would skip the unlock. Split it instead: the locked body does the work, and a thin wrapper takes the lock, calls it, and releases it on the single return path. Do not call consumer code (`akerr_log_method`, `akerr_handler_unhandled_error`) while holding it. ## Testing Guidelines Add tests as `tests/err_.c`. Register each new test in the `AKERR_TESTS` list in `CMakeLists.txt`; tests that intentionally abort must also be listed in `AKERR_WILL_FAIL_TESTS`. Prefer focused executable tests that return zero on success and use existing helpers such as `AKERR_CHECK`. Run the CTest suite before submitting changes, and run mutation testing and coverage when changing core control-flow, reference counting, stack-trace, or handler behavior. Tests that drive the library from several threads go in the `AKERR_THREAD_SAFE` branch of the `AKERR_TESTS` list — an `-DAKERR_THREADS=none` build has no threading to test — and use `tests/err_threads.h`, which runs a body on `AKERR_TEST_THREADS` threads that meet at a barrier first. Count failures per thread with `AKERR_TCHECK` rather than returning early: a thread that abandons its work leaves the others holding pool slots and turns one failure into a cascade. Anything the test shares between its own threads must go through `__atomic` builtins — a race in the test is still a race, and ThreadSanitizer cannot tell you whose it is. The capturing logger in `err_capture.h` is single-threaded (shared buffer, shared length); use `akerr_thread_logger` instead. Run `scripts/thread_test.sh` when changing anything that touches the pool, the registry, initialization, or the lock. ## Commit & Pull Request Guidelines Recent commits use short, imperative, sentence-case subjects, for example `Fix refcount leak and stack-trace buffer overflow`. Keep commits focused and describe the observable behavior changed. Pull requests should include a brief summary, tests run, and any compatibility impact for public macros, generated headers, installation paths, or CMake/pkg-config consumers. ## Agent-Specific Instructions Do not overwrite uncommitted user changes. Avoid editing generated files in `build/`; update `include/akerror.tmpl.h`, `src/error.c`, CMake files, tests, or scripts instead.