diff --git a/AGENTS.md b/AGENTS.md index 5cd0189..d69dbaf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -92,9 +92,9 @@ releases it on the single return path. Do not call consumer code owns the one mapping from an akerr status to an exit code: an exit status is a byte, and every consumer status starts at 256, so passing a status through `exit()` silently truncates it — status 256 exits 0 and reports success. The -only exits that bypass it are the two that have no status to map, both in -`src/error.c`: `ENSURE_ERROR_READY`'s pool-exhaustion abort and the NULL-context -case in `akerr_default_handler_unhandled_error()`. This rule applies to test +only exits that bypass it are the two that have no status to map: +`ENSURE_ERROR_READY`'s pool-exhaustion abort in `include/akerror.tmpl.h`, and the +NULL-context case in `akerr_default_handler_unhandled_error()` in `src/error.c`. This rule applies to test programs too, except where the test's whole point is to observe the raw truncation. diff --git a/src/error.c b/src/error.c index a3ae18e..4182d50 100644 --- a/src/error.c +++ b/src/error.c @@ -129,6 +129,11 @@ akerr_ErrorContext *__akerr_copy_string(char *destination, int capacity, * * Takes no lock: the addresses of the pool slots are fixed for the life of the * process, and nothing here reads a slot's contents. + * + * NULL reads back as valid, because the caller is VALID() asking whether a + * function returned something it has no business returning, and NULL is how a + * function says it succeeded. Anything reading this as "is this a pool slot" + * must check for NULL itself. */ int akerr_valid_error_address(akerr_ErrorContext *ptr) {