Files
libakstdlib/AGENTS.md
Tachikoma 51144038ce
All checks were successful
libakstdlib CI Build / cmake_build (push) Successful in 2m54s
libakstdlib CI Build / sanitizers (push) Successful in 2m50s
libakstdlib CI Build / coverage (push) Successful in 2m44s
libakstdlib CI Build / mutation_test (push) Successful in 12m41s
Point AGENTS.md at the issue tracker for outstanding work
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>
2026-08-02 19:24:45 -04:00

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.