feat(graph-lite): make the public surface a C ABI - #9
Conversation
The SQLite comparison is not only about being in-process — SQLite's reach comes from a flat C ABI that every language runtime can call. A C++-only surface caps adoption at C++ projects and does not link reliably even between C++ toolchains, since C++ has no stable ABI across compilers or standard-library versions. graph-lite.h is now the contract: C linkage, opaque handles, integer status codes, caller-owned buffers, additive only. Ruby (FFI), JavaScript (N-API / bun:ffi), Python (ctypes/cffi), Rust, Go and C all bind the same entry points. graph-lite.hpp is header-only C++ ergonomics over it — RAII, real types, an error type that carries the code — and exports nothing of its own. Decided now because it is a one-way door: a C ABI retrofitted onto a C++-shaped design is a second, worse API wrapping the first, and every binding pins whichever shipped. ADR 0004 records the rules and the rejected alternatives. Tested rather than asserted: tests/graph-lite/c_abi_test.c is compiled AS C and linked into the suite, so a header that stops being C-compatible fails our build instead of an embedder's integration. Two things fell out of writing it — graph_lite_status is forced to int width (a binding compiled against a different header WILL pass an unknown code, and converting it was otherwise unspecified), and the C++-only warning flags are now scoped to CXX so the C translation unit does not drown in "valid for C++ but not for C". The vocabulary guard now skips fenced code blocks in markdown only. Bun's FFI genuinely returns a `symbols` object, and a guard that fires on a third-party API name in an example is a guard someone disables. Source files are still scanned in full, fences included — `SymbolNode` in a header is the leak it exists to catch, and a test pins both halves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HLmxQqDABCLu7TCQE3Zzmv
|
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 46 minutes Limit details: You’ve used the included review currently available. Your 70 included PR review attempts over the past 7 days set your current allowance at 1 review per hour. You’re in a promotional period — use the checkbox below to run this review for free:
On-demand reviews are free for the next 29 days. After that, they cost $0.25 per reviewed file. How can I continue?Run this review now using the option above, or comment You can also wait for the limit to reset, then comment An organization admin can change what happens after included review limits in Billing. How do review limits work?CodeRabbit enforces per-developer PR review limits within each organization. For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (20)
Warning
|
What
graph-lite's public contract becomes a C ABI (
graph-lite.h).graph-lite.hppbecomes a header-only C++ ergonomics layer over it.Why
The SQLite comparison is not only about being in-process — SQLite's reach comes from a flat C ABI that every language runtime can call. A C++-only surface caps adoption at C++ projects, and does not link reliably even between C++ toolchains.
With a C ABI, Ruby (FFI), JavaScript (N-API /
bun:ffi), Python (ctypes/cffi), Rust, Go and C all embed the same library through their own FFI — no wrapper to keep in sync, no serialisation, still in-process.Decided now because it is a one-way door: a C ABI retrofitted onto a C++-shaped design is a second, worse API wrapping the first, and every binding pins whichever shipped. Rules and rejected alternatives: ADR 0004.
How
graph-lite.h—extern "C", opaque handles,graph_lite_statuscodes, caller-owned buffers.graph-lite.hpp— header-only, RAII,graph_lite::Errorcarrying the code so a caller can still branch on the reason.tests/graph-lite/c_abi_test.c— compiled as C and linked, so the C-compatibility claim is tested rather than asserted.Two things fell out of writing the C test:
graph_lite_statusis forced to int width. A binding compiled against a different header will pass a code this build has not heard of, and converting an out-of-range value to a narrow enum is unspecified — the compiler said so.CXX, so the C translation unit does not drown in "valid for C++ but not for C".The vocabulary guard now skips fenced code blocks in markdown only — Bun's FFI genuinely returns a
symbolsobject, and a guard that fires on a third-party API name gets disabled. Source files are still scanned in full; a test pins both halves.Checks
bin/checkis greengraph-lite/still depends on nothing in this repo, and names no consumer conceptNeed help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.