Recount the consumer calls against this release (#26) #39

Closed
tachikoma wants to merge 2 commits from 26 into main
Collaborator

Closes #26.

akbasic's src/ ported onto this release and counted again. The port is now pushed and open as andrew/akbasic#31; this PR is the measurement it produced.

libakstdlib raw libc bypassed
Baseline — akbasic 4e188b2, 5,679 lines of src/ 10 119 92.2%
Before the port — akbasic 330d731, 20,169 lines 45 285 86.4%
After the port — same tree 313 7 2.2%

What changed here

  • scripts/consumer_calls.py — new. The counting method, as a script rather than a paragraph. The old figure was not reproducible; recovering it cost more than writing it down would have. Reads the wrapped-libc set out of include/akstdlib.h rather than hardcoding it, so a recount after a release measures the surface that release actually shipped.
  • README.md — the harness gets a section next to coverage and mutation testing, including the two warnings --baseline prints: a rate is only comparable between two counts of the same tree, and one consumer's ratio is evidence rather than a plan.
  • TODO.md — the recount, the 7 sites that could not be converted and why, and the call profile by area.

The number moved because #38 was answered

The first pass of this recount reported 301/13, 4.1%, and wrote off 8 of those 13 as blocked: bool predicates and void helpers with no error channel to route a wrapper's failure into. #38 was filed asking this library for a form they could call.

@andrew ruled that invalid (#38 comment 1095): a function that cannot report an error changes its own signature rather than being handed a way to swallow one, and then instructed that the port branch be fixed accordingly (comment 1105).

Seven of the eight did exactly that — they return akerr_ErrorContext * and hand the answer back through an out parameter, one of them as a public header change in akbasic. Four more functions on the same call chains had to move with them. That is the whole of the difference between 4.1% and 2.2%.

The finding is that the wrapper shape was right and the consumer's signatures were wrong, which is the opposite of what the first count assumed. It is recorded that way in TODO.md on purpose.

Two corrections to the record

Rebuilding the method reproduced the TODO §5 table row for row at akbasic 4e188b2, and turned up two errors in the figure above it:

  1. It read 116; its own table sums to 117.
  2. A complete count is 119 — the table had no row for strncmp (1) or memmove (1), both wrapped.

Both fixed. 119 is what the script returns and what everything now compares against.

What is genuinely left

7 sites, and only two reasons:

  • 6 × snprintf that want truncation as an answer rather than an error. #34 is the issue. The sharpest is akbasic_runtime_error, which formats a 12,384-byte error message into a 512-byte line — a report may be shortened, but it may not be replaced by a second, different failure. One of the six reads snprintf's return value to detect the truncation.
  • 1 × strcmp in a bsearch(3) comparator, whose signature libc fixes, so there is no out parameter to report through. Nothing tracks this. #38 attributed it to akbasic #14; that is a mis-citation — #14 is the COLLISION/BUMP pairing threshold.

Verification

  • deps/libakstdlib in akbasic bumped 669b2b32b79aca. Zero source changes needed for the bump itself — the release is drop-in for what akbasic already used.
  • Default build clean at -Wall -Wextra; 112/112 ctest; ASan+UBSan clean, 112/112.
  • The five *_akgl.c files could not be built — SDL3 needs X11/Wayland dev packages this box does not have. They are not unverified, though: all five are gcc -fsyntax-only -Wall -Wextra clean under -DAKBASIC_WITH_AKGL=1. That is syntax and types, not behaviour, so the AKGL configuration still wants a real CI build.
  • This repository's own suite: 20/20.

What the number does not say

92% → 2% reads as a total success and it is not one, so the write-up says three things next to it.

The tree tripled between the first two rows, so only 86.4% → 2.2% is like-for-like. The 45 in the middle row is its own small finding: akbasic had already adopted aksl_f* across runtime_disk.c without anybody counting.

Nothing was blocked by a missing wrapper, in either pass. Every libc call akbasic makes has an aksl_* counterpart. What blocked a conversion was always wrapper shape, and the shapes are filed as #32–#38 rather than listed in TODO.md, per this repo's own rule about where outstanding work lives.

The caveat in #26 held and got sharper. Of the 313 calls, 149 are strings, 72 memory, 43 formatted output, 36 streams and files, 12 conversion, 1 hashing — and 0 into list, tree, hash map and string buffer combined. Five sixths of the evidence is strings, memory and formatting. The largest single body of code in this library has not one consumer call site. The number to distrust is not the 2.2%; it is the 0%, and no number of recounts on this consumer will fix it — a second consumer that allocates would.

Filed by Tachikoma (Claude Code, Opus 5, 1M context)

Closes #26. akbasic's `src/` ported onto this release and counted again. **The port is now pushed and open as [andrew/akbasic#31](https://source.starfort.tech/andrew/akbasic/pulls/31)**; this PR is the measurement it produced. | | libakstdlib | raw libc | bypassed | |---|---|---|---| | Baseline — akbasic `4e188b2`, 5,679 lines of `src/` | 10 | 119 | **92.2%** | | Before the port — akbasic `330d731`, 20,169 lines | 45 | 285 | **86.4%** | | **After the port — same tree** | **313** | **7** | **2.2%** | ## What changed here - **`scripts/consumer_calls.py`** — new. The counting method, as a script rather than a paragraph. The old figure was not reproducible; recovering it cost more than writing it down would have. Reads the wrapped-libc set out of `include/akstdlib.h` rather than hardcoding it, so a recount after a release measures the surface that release actually shipped. - **`README.md`** — the harness gets a section next to coverage and mutation testing, including the two warnings `--baseline` prints: a rate is only comparable between two counts of the same tree, and one consumer's ratio is evidence rather than a plan. - **`TODO.md`** — the recount, the 7 sites that could not be converted and why, and the call profile by area. ## The number moved because #38 was answered The first pass of this recount reported **301/13, 4.1%**, and wrote off 8 of those 13 as blocked: `bool` predicates and `void` helpers with no error channel to route a wrapper's failure into. #38 was filed asking this library for a form they could call. **@andrew ruled that invalid** ([#38 comment 1095](https://source.starfort.tech/andrew/libakstdlib/issues/38#issuecomment-1095)): a function that cannot report an error changes its own signature rather than being handed a way to swallow one, and then instructed that the port branch be fixed accordingly ([comment 1105](https://source.starfort.tech/andrew/libakstdlib/issues/38#issuecomment-1105)). Seven of the eight did exactly that — they return `akerr_ErrorContext *` and hand the answer back through an out parameter, one of them as a public header change in akbasic. Four more functions on the same call chains had to move with them. That is the whole of the difference between 4.1% and 2.2%. **The finding is that the wrapper shape was right and the consumer's signatures were wrong**, which is the opposite of what the first count assumed. It is recorded that way in `TODO.md` on purpose. ## Two corrections to the record Rebuilding the method reproduced the TODO §5 table row for row at akbasic `4e188b2`, and turned up two errors in the figure above it: 1. **It read 116; its own table sums to 117.** 2. **A complete count is 119** — the table had no row for `strncmp` (1) or `memmove` (1), both wrapped. Both fixed. 119 is what the script returns and what everything now compares against. ## What is genuinely left 7 sites, and only two reasons: - **6 × `snprintf` that want truncation as an answer rather than an error.** #34 is the issue. The sharpest is `akbasic_runtime_error`, which formats a 12,384-byte error message into a 512-byte line — a report may be shortened, but it may not be replaced by a second, different failure. One of the six reads `snprintf`'s return value to *detect* the truncation. - **1 × `strcmp` in a `bsearch(3)` comparator**, whose signature libc fixes, so there is no out parameter to report through. **Nothing tracks this.** #38 attributed it to akbasic `#14`; that is a mis-citation — `#14` is the `COLLISION`/`BUMP` pairing threshold. ## Verification - `deps/libakstdlib` in akbasic bumped `669b2b3` → `2b79aca`. **Zero source changes needed for the bump itself** — the release is drop-in for what akbasic already used. - Default build clean at `-Wall -Wextra`; **112/112** ctest; **ASan+UBSan clean**, 112/112. - **The five `*_akgl.c` files could not be built** — SDL3 needs X11/Wayland dev packages this box does not have. They are not unverified, though: all five are `gcc -fsyntax-only -Wall -Wextra` clean under `-DAKBASIC_WITH_AKGL=1`. That is syntax and types, not behaviour, so the AKGL configuration still wants a real CI build. - This repository's own suite: 20/20. ## What the number does not say 92% → 2% reads as a total success and it is not one, so the write-up says three things next to it. **The tree tripled between the first two rows**, so only 86.4% → 2.2% is like-for-like. The 45 in the middle row is its own small finding: akbasic had already adopted `aksl_f*` across `runtime_disk.c` without anybody counting. **Nothing was blocked by a missing wrapper**, in either pass. Every libc call akbasic makes has an `aksl_*` counterpart. What blocked a conversion was always wrapper *shape*, and the shapes are filed as #32–#38 rather than listed in `TODO.md`, per this repo's own rule about where outstanding work lives. **The caveat in #26 held and got sharper.** Of the 313 calls, 149 are strings, 72 memory, 43 formatted output, 36 streams and files, 12 conversion, 1 hashing — and **0 into list, tree, hash map and string buffer combined**. Five sixths of the evidence is strings, memory and formatting. The largest single body of code in this library has not one consumer call site. The number to distrust is not the 2.2%; it is the 0%, and no number of recounts on this consumer will fix it — a second consumer that allocates would. Filed by Tachikoma (Claude Code, Opus 5, 1M context)
andrew was assigned by tachikoma 2026-08-03 12:53:59 -04:00
tachikoma added 1 commit 2026-08-03 12:53:59 -04:00
Recount the consumer calls against this release
All checks were successful
libakstdlib CI Build / cmake_build (push) Successful in 2m52s
libakstdlib CI Build / sanitizers (push) Successful in 2m59s
libakstdlib CI Build / coverage (push) Successful in 2m45s
libakstdlib CI Build / mutation_test (push) Successful in 12m0s
cff2a64575
akbasic's src/ ported onto 0.2.0 calls this library 301 times and raw libc
13 -- 4.1% bypassed, against 86.4% on the same tree before the port and
92.2% at the first count. The port builds clean at -Wall -Wextra, passes
112/112 of akbasic's ctest suite and is ASan+UBSan-clean.

The method was never written down and the figure was not reproducible.
scripts/consumer_calls.py is that method, and reproducing it turned up two
corrections: the old 116 was 117 by its own table's arithmetic, and 119 by
a complete count -- the table had no row for strncmp or memmove.

Nothing was blocked by a missing wrapper. 272 of 285 sites converted; the
13 that did not are blocked by wrapper shape, and are filed as #32-#38.

Say plainly what the number does not cover: akbasic makes 0 calls into
list, tree, hash map and string buffer combined, so the recount is
evidence about the string, memory and format surface and about nothing
else.

Refs #26

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
tachikoma requested review from andrew 2026-08-03 12:54:17 -04:00
tachikoma was assigned by andrew 2026-08-03 15:00:55 -04:00
tachikoma added 1 commit 2026-08-03 15:45:08 -04:00
Recount at 2.2% once the eight bool predicates convert
All checks were successful
libakstdlib CI Build / cmake_build (push) Successful in 3m1s
libakstdlib CI Build / sanitizers (push) Successful in 2m53s
libakstdlib CI Build / coverage (push) Successful in 2m45s
libakstdlib CI Build / mutation_test (push) Successful in 15m16s
e763183cc4
The port landed on andrew/akbasic as libakstdlib-26, and converting the eight
sites the first count wrote off moves the figure from 301/13 to 313/7 --
2.2% bypassed on the same tree, against 86.4% before the port.

Those eight were the whole of #38, which asked this library for a form a bool
predicate could call. That was ruled invalid: a function which cannot report
an error changes its own signature instead. Seven did, and the eighth is a
bsearch(3) comparator whose signature libc fixes, so it has nowhere to put an
out parameter. Record that the wrapper shape was right and the consumer's
signatures were wrong, because the first count assumed the opposite.

What is genuinely left is six snprintf sites that want truncation as an
answer -- #34 -- and the comparator, which nothing tracks. #38 attributed it
to akbasic #14; that is a mis-citation and #14 is an unrelated issue.

Correct the call profile for the new total, and note two findings the port
produced rather than measured: a file descriptor leak in akbasic's DLOAD,
caught by the ATTEMPT/CLEANUP rule the conversion forced somebody to obey,
and a latent collision in its host-type registration.

Refs #26, #38

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
andrew closed this pull request 2026-08-03 16:02:40 -04:00
All checks were successful
libakstdlib CI Build / cmake_build (push) Successful in 3m1s
Required
Details
libakstdlib CI Build / sanitizers (push) Successful in 2m53s
Required
Details
libakstdlib CI Build / coverage (push) Successful in 2m45s
Required
Details
libakstdlib CI Build / mutation_test (push) Successful in 15m16s
Required
Details

Pull request closed

Sign in to join this conversation.