The agent instructions now say where new work goes -- an issue on the forge, labelled by kind and blast radius and carrying status::grooming until its scope is settled -- and that TODO.md is the record of where the library stands, what is deliberately not wrapped, and which uncovered lines are uncoverable rather than untested. Also: a defect in a dependency is filed against that dependency. Three defects in libakerror that cost this library a workaround each sat in this repository's own notes without anybody upstream being able to see them, which is how a workaround gets written once per consumer. The KNOWN_FAILING_TESTS and pull-request rules now name the issue rather than a TODO entry. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-Authored-By: Andrew Kesterson <andrew@aklabs.net>
157 lines
7.4 KiB
Markdown
157 lines
7.4 KiB
Markdown
# Repository Guidelines
|
|
|
|
## Project Structure & Module Organization
|
|
|
|
`libakstdlib` is a C shared library that wraps libc calls and data structures in
|
|
`libakerror` error contexts.
|
|
|
|
Public API declarations live in `include/akstdlib.h`, which is also where the
|
|
Doxygen documentation lives -- the header is the contract, the sources carry the
|
|
reasoning. The implementation is split by domain:
|
|
|
|
| File | Covers |
|
|
|---|---|
|
|
| `src/stdlib.c` | memory, formatted output, string-to-number, realpath, djb2, and the list/tree traversal entry points |
|
|
| `src/string.c` | the `string.h` surface |
|
|
| `src/stream.c` | `stdio.h` beyond open/read/write/close |
|
|
| `src/collections.c` | list and tree operations, hash map, string buffer, FNV-1a |
|
|
| `src/aksl_internal.h` | shared internals; not installed, not public |
|
|
|
|
Tests are one-file CTest executables under `tests/test_<name>.c`, with shared
|
|
helpers in `tests/aksl_capture.h`. `tests/negative/` holds sources that must
|
|
*fail* to compile, and `tests/consumer/` is a standalone project built against
|
|
the *installed* package rather than the build tree -- see the testing section
|
|
below. CMake package templates are
|
|
in `cmake/` and `akstdlib.pc.in`. The vendored dependency is `deps/libakerror`;
|
|
update it as a submodule rather than editing generated files under `build/`,
|
|
`build-asan/` or `build-coverage/`.
|
|
|
|
## Build, Test, and Development Commands
|
|
|
|
Initialize dependencies before a fresh build:
|
|
|
|
```sh
|
|
git submodule update --init --recursive
|
|
cmake -S . -B build
|
|
cmake --build build
|
|
```
|
|
|
|
Run the normal suite with `ctest --test-dir build --output-on-failure`. Use the
|
|
instrumented build for memory and undefined-behavior checks:
|
|
|
|
```sh
|
|
cmake -S . -B build-asan -DAKSL_SANITIZE=ON
|
|
cmake --build build-asan
|
|
ctest --test-dir build-asan --output-on-failure
|
|
```
|
|
|
|
For coverage, configure a third tree; the report is part of that suite (the
|
|
`coverage_reset` / `coverage_report` CTest entries) and also lands in
|
|
`build-coverage/coverage-summary.txt`:
|
|
|
|
```sh
|
|
cmake -S . -B build-coverage -DAKSL_COVERAGE=ON
|
|
cmake --build build-coverage --target coverage
|
|
```
|
|
|
|
Run `cmake --build build --target mutation` only when you need the slower
|
|
mutation harness, and `cmake --build build --target docs` to regenerate the API
|
|
documentation -- that one fails if any public function, parameter or return value
|
|
is undocumented, so it is a check as well as a generator.
|
|
|
|
`.githooks/pre-push` runs the default build, the sanitizer build and the
|
|
documentation check before a push; enable it with
|
|
`git config core.hooksPath .githooks`. `rebuild.sh` installs to
|
|
`/home/andrew/local` and removes the local build directory, so treat it as a
|
|
local convenience script.
|
|
|
|
## Coding Style & Naming Conventions
|
|
|
|
Use C with 4-space indentation; existing files sometimes use tabs for continued
|
|
statements, so match the surrounding block. Public symbols use the `aksl_`
|
|
prefix, structs use `aksl_<Name>`, and tests use `test_<feature>.c` plus static
|
|
`test_<case>` functions. Preserve the `akerr_ErrorContext AKERR_NOIGNORE *`
|
|
return convention and the `PREPARE_ERROR` / `FAIL_*` / `SUCCEED_RETURN` pattern.
|
|
|
|
Four conventions hold across the whole library, and a new wrapper that breaks one
|
|
of them is wrong even if it compiles and passes:
|
|
|
|
- **A NULL out-param is a caller error**, not "don't care".
|
|
- **Finding nothing is success** -- searching functions write NULL or zero and
|
|
return NULL.
|
|
- **Truncation is a failure.** Take the destination's size, raise
|
|
AKERR_OUTOFBOUNDS, and write nothing rather than a prefix.
|
|
- **Clear `errno` before the wrapped call** and read it back through
|
|
`AKSL_ERRNO_OR`, so no error can carry status 0 -- which every downstream
|
|
`DETECT` reads as success.
|
|
|
|
Every new public function needs a Doxygen block on its **declaration** with
|
|
`@brief`, a `@param` per parameter, a `@throws` per status it can raise, and
|
|
`@return`. The `docs` target fails otherwise.
|
|
|
|
The build is `-Wall -Wextra` and CI adds `-Werror`. `-Wpedantic` is deliberately
|
|
off: libakerror's `FAIL_*` macros trip "ISO C99 requires at least one argument
|
|
for the ..." on their own expansion, not on anything at the call site.
|
|
|
|
## Testing Guidelines
|
|
|
|
Add a new test by creating `tests/test_mything.c` and adding `mything` to the
|
|
right list in `CMakeLists.txt`. `AKSL_TESTS` must exit zero.
|
|
`AKSL_WILL_FAIL_TESTS` are deliberate abort/contract tests.
|
|
`AKSL_KNOWN_FAILING_TESTS` assert defects that have an open issue; when one
|
|
starts unexpectedly passing, move it into `AKSL_TESTS` with the fix and close the
|
|
issue. Both of the
|
|
latter are currently empty -- all six confirmed defects are fixed -- but the
|
|
mechanism stays for the next one.
|
|
|
|
`tests/negative/` holds sources that must **fail** to compile. Each is an
|
|
`EXCLUDE_FROM_ALL` target built with `-Werror` and registered as a `WILL_FAIL`
|
|
CTest entry, so the test passes only when the compile fails. They cover the two
|
|
guarantees the compiler enforces and nothing else does: `AKERR_NOIGNORE` and the
|
|
format attributes. Drop either in a refactor and every ordinary test still
|
|
passes.
|
|
|
|
`tests/consumer/` is configured standalone against `CMAKE_PREFIX_PATH`, not as
|
|
part of this build, because being part of this build is what would let it pass
|
|
without testing anything. It covers the paths only an install has: the version
|
|
file, `find_dependency(akerror)` resolving, and the exported
|
|
`akstdlib::akstdlib` target. CI runs it after `cmake --install`.
|
|
|
|
Coverage is 99.5% of lines and 100% of functions across all four sources; CI
|
|
gates at 90 (line) / 40 (branch), so new code needs tests in the same commit. Run
|
|
`cmake --build build-coverage --target coverage` and check the uncovered-line
|
|
listing before proposing a change. Tests for behaviour an open issue records as
|
|
defective belong in `AKSL_KNOWN_FAILING_TESTS` asserting the *correct* contract —
|
|
do not pin current-but-wrong behaviour in `AKSL_TESTS`, since that turns the
|
|
eventual fix into a test failure.
|
|
|
|
## Commit & Pull Request Guidelines
|
|
|
|
Recent commits use short imperative summaries, for example `Add memory wrapper
|
|
tests` and `Make error-status assertions authoritative`. Keep commits focused
|
|
and include tests with behavior changes. Pull requests should describe the
|
|
changed API or behavior, list the CTest/sanitizer/mutation commands run, and
|
|
link the issue it closes.
|
|
|
|
## Agent-Specific Instructions
|
|
|
|
**Outstanding work goes in the issue tracker, not in a file.** Open an issue at
|
|
<https://source.starfort.tech/andrew/libakstdlib/issues> — `tea issues create
|
|
--repo andrew/libakstdlib` — naming the file and line, the functional
|
|
consequence, and what closing it would touch. Label it by kind and blast radius
|
|
and leave `status::grooming` on it until its scope and approach are settled.
|
|
**Do not add outstanding items to `TODO.md`**: that file is the record of where
|
|
the library stands, what is deliberately not wrapped, and which uncovered lines
|
|
are uncoverable rather than untested. A description of work still to do goes
|
|
stale the moment somebody does it, which is why the two are separated.
|
|
|
|
**A defect in a dependency is filed against that dependency.** `libakerror` has
|
|
a tracker on the same forge, and three defects that cost this library a
|
|
workaround each sat in this repository's own notes for months without anybody
|
|
upstream being able to see them. Comment the workaround at its site with the
|
|
words "filed upstream" and delete it when the fix lands.
|
|
|
|
Do not modify generated build trees, profiling artifacts, or untracked scratch
|
|
files unless explicitly asked. Prefer small, test-backed changes and update
|
|
`README.md` when changing documented workflows.
|