c_runtime: encode singular scalars (ra6 3b)
New entry pb.c_runtime.encode(plan, msg) -> string. Covers int32/64,
uint32/64, sint32/64, bool, fixed32/64, sfixed32/64, float, double,
enum (number or by_name lookup), string, bytes. Repeated, map, and
message-typed fields are silently skipped — those land in 3d/3e.
Buffer is a 4KB stack scratch promoted to a lua_newuserdata on
overflow, so a mid-encode luaL_error doesn't leak: the userdata is
GC'd at the unwind point.
Proto3 zero-suppression mirrors mode=full byte-for-byte: empty strings,
zero ints/fixed/enum skipped, +0.0 double skipped while -0.0 is emitted
via type-pun u64 equality (matches the `1/v == -math.huge` guard on
the Lua side). Proto3-optional fields bypass suppression. Plans with
desc.encode overrides (WKT) are rejected here — bd-rmf scope.
Tests: 16 cases per codegen mode (full + runtime), covering the bd-y1n
acceptance (Person {name='x', age=42, balance=-7,
user_id=0xDEADBEEFCAFEBABEULL, weight_kg=3.14} byte-equal to mode=full)
plus per-kind sweep and heap-grow path at 8KB. Suite: 806/806 with
PB_ENABLE_C=1, 748/748 + 58 skipped without.
Closes bd-y1n
c-accel: descriptor -> C plan compiler (bd-mq7)
First C source for the pb.c_runtime module. Compiles a finalized Lua
descriptor into an opaque pb_plan userdata, the foundation that
bd-ra6's encode/decode entry points will walk.
What the plan carries (per docs/specs/c_accel_strategy.md):
* Per-field records: field_number, wire_type, kind, repeated/packed/
optional flags, pre-encoded tag bytes (varint, up to 5 bytes),
sub_plan_idx (1-based into the sub-plans table), enum_ref
(luaL_ref for enum descriptor), oneof_idx back-pointer.
* Map fields capture map_key_kind, map_value_kind, and the value's
sub_plan_idx when the value is a message.
* Oneofs as a parallel array of {name, member_indices[]}, with
fields' oneof_idx pointing back to their group.
* WKT override detection: when desc.encode/desc.decode are set, the
plan flips has_override=1 and skips field-walk entirely.
* Extension range hooks captured (proto2 scaffolding for bd-3i).
* Field-name cache as a Lua table referenced via luaL_ref, so
encode/decode can do lua_rawgeti instead of re-interning C strings.
Cycle handling: compile_plan stashes the new plan userdata on
desc.c_plan BEFORE recursing into sub-message fields. Person.friends
→ Person resolves to the same userdata; the test asserts identity.
Build entry: 'just build-c' compiles runtime/pb/c/c_runtime.c into
runtime/pb/c_runtime.dylib (or .so on Linux) via the local Makefile.
Module-h location auto-detected the same way bench/c_accel/Makefile
does it. Tarantool's LuaJIT-on-5.1 means we use lua_objlen (not
lua_rawlen) and provide a local abs_idx helper since lua_absindex
isn't available in the 5.1 compat layer.
Smoke test at test/c_runtime_plan_test.lua exercises both codegen
modes (full + runtime). 26 assertions cover:
- module surface + ABI version + KIND/WIRE constants
- Address: 4 fields, names, kinds, wire types, tag bytes, optional
- Person: 14 fields, scalars/enum/message/map/repeated/packed
- Sub-plan resolution + Person.friends self-reference cycle break
- Idempotent compile (second call returns cached plan)
- Result.outcome oneof: 3 members, oneof_idx back-pointers
- WKT Timestamp: has_override=true, field-walk skipped
Full suite: 771/771 with PB_ENABLE_C=1 (745 existing + 26 new),
745+26 skipped without (silent fallback verified).
Justfile fix: 'just build-c' / 'clean-c' used $(MAKE) which Just
doesn't expand — switched to plain 'make'.
Unblocks bd-y1n (encode scalars), bd-mz6 (decode scalars),
bd-awv (64-bit cdata), bd-rmf (WKT passthrough). bd-mq7 closed.
c-accel: arch prereqs — compat contract, C-side strategy, build scaffolding
Three companion specs under docs/specs/ formalize the boundaries
established in docs/c-accel.md, unblocking bd-mq7 (descriptor → C
plan compiler):
* c_accel_compat.md (bd-47e) — pinpoints what must stay byte-equal
between PB_ENABLE_C unset and =1: public surface, generated
module wrappers, 64-bit cdata, WKT shapes, unknown fields,
extensions, errors. Calls out the lazy-view exclusion.
* c_accel_strategy.md (bd-z7x) — pb_plan struct layout, field-name
luaL_ref caching, 4 KB stack-backed pb_buf, cached per-field
stack indices (the 2× win from spike Phase B), sub-buffer over
backpatching, map/oneof/unknown handling.
* c_accel_build_packaging.md (bd-wky) — where the C module lives
(runtime/pb/c/), how it builds, what the rockspec gains, the CI
matrix shape.
Scaffolding that lands now:
* runtime/pb/init.lua — PB_ENABLE_C=1 opt-in pcall hook; the
loaded module (or nil) is exposed as pb.c_runtime for
introspection. Silent fallback when the module is absent.
* Justfile — `build-c` / `clean-c` recipes (stub erroring cleanly
until bd-ra6 lands runtime/pb/c/), new lua_cpath constant,
LUA_CPATH wired through `test` and `test-one`.
* .builds/{pure-lua,c-enabled}.yml — sourcehut CI manifests, one
per activation mode (sourcehut has no matrix; parallel jobs go
in separate files). ubuntu/noble images.
* .sourcehut/conformance.yml — outside .builds/ so it doesn't
auto-submit; trigger manually with `hut builds submit` before
releases.
* .gitignore — runtime/pb/c_runtime.{so,dylib} and runtime/pb/c/*.o.
745/745 tests pass with PB_ENABLE_C unset and PB_ENABLE_C=1
(silent fallback verified).
Closes bd-47e, bd-z7x, bd-wky. Unblocks bd-mq7.
tooling: add Beads workspace + beads agent skill
Checks in the portable parts of the Beads issue tracker that
this project now uses as its durable source of truth (per the
PLAN.md retirement). The Dolt DB internals (.beads/embeddeddolt/,
.beads/backup/, runtime sockets/locks/sync-state) stay
gitignored by .beads/.gitignore; what lands here are:
.beads/.gitignore — Beads-managed ignore rules
.beads/README.md — workspace README from bd init
.beads/config.yaml — workspace config (id, etc.)
.beads/metadata.json — workspace metadata
.beads/issues.jsonl — passive JSONL export of issues
.beads/interactions.jsonl — passive log of state changes
The JSONL files are the human-readable / diffable surface; the
authoritative store remains the Dolt DB synced via refs/dolt/data
on the git remote (see Beads SYNC_CONCEPTS.md).
Also adds .agents/skills/beads/ — the beads agent skill that
tells subagents to use `bd` for task tracking instead of local
TODO files. Mirrors the rule already in CLAUDE.md but in a form
other agents and IDEs can pick up.
Closes the implementation side of bd-2nl (which removed the
.git/info/exclude rule that previously hid .beads/ from Git).