Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

The validated rstgcalls FFI scaffold lives only in /tmp — the repo contains zero Rust files

Источник: dev/fera-research/docs/oc3/007-rstgcalls-work-lives-in-tmp.md (read-only; сгенерировано scripts/sync-docs.mjs)

The validated rstgcalls FFI scaffold lives only in /tmp — the repo contains zero Rust files

Section titled “The validated rstgcalls FFI scaffold lives only in /tmp — the repo contains zero Rust files”
  • kind: risk
  • area: calls
  • severity: high

~/dev/fera/rstgcalls has no Cargo.toml and no .rs files. git ls-tree -r --name-only HEAD returns exactly three entries: .gitmodules, README.md, tgcalls. find ~/dev/fera/rstgcalls -name '*.rs' | wc -l → 0. cargo check --offline → error: could not find Cargo.toml. README.md:6 and :39 both say “Pre-code.” — accurate, and never updated.

The submodule tgcalls is pinned at d20de8e8 and carries no build system of its own — only tgcalls/Package.swift (SwiftPM). No CMakeLists/meson/Makefile anywhere in it.

The actual validated work is in throwaway spike directories in /tmp:

/tmp/bg-spike bindgen vs real tgcalls headers; src/gen.rs 544 829 B
/tmp/bridge-spike bridge.hpp -> src_gen.rs (13 401 B)
/tmp/cc-spike the `cc` C++ compile path
/tmp/ws-spike 2-crate workspace smoke test
/tmp/stub-spike the closest to real — full workspace, builds & tests green (223 MB)

/tmp/stub-spike/crates/sys/Cargo.toml:1-13 is the validated crate layout:

[package] name="rstgcalls-sys" version.workspace=true edition.workspace=true build="build.rs"
[features] ffi-stub=[] link-tgcalls=[]
[lints.rust] unsafe_code="allow"
[build-dependencies] bindgen="0.72" cc="1.2"

with /tmp/stub-spike/crates/safe/src/lib.rs holding the only real API surface: a CallBackend trait (:10-17), MockBackend (:19-27), TgcallsBackend (:33-51), and CallManager (:57).

Verified by running it:

$ cargo test --offline -> 2 passed (mock only)
$ cargo test --offline --features ffi-stub -> 3 passed

The validated design decisions are real and worth keeping: hand-written C-ABI bridge header (rstgcalls_bridge.h) rather than raw bindgen over tgcalls C++ classes; opaque handle rstgcalls_instance*; int32_t return codes; bindgen with explicit --target plus -isystem paths parsed from clang++ -std=c++20 -E -x c++ -v -; crate-root deny(unsafe_code) + one scoped #[allow(unsafe_code)] on the gated backend module.

The design plan is untracked: ~/dev/fera/rstgcalls/docs/superpowers/plans/2026-10-17-rstgcalls-ffi-scaffold.md (926 lines, 10 tasks, all checkboxes unchecked), and TODO.md — which the plan references 8× — does not exist.

Nothing on the client side consumes it. ~/dev/fera/fera/Cargo.toml workspace members contain no rstgcalls. A recursive grep for rstgcalls|tgcalls|CallBackend|webrtc across the whole fera repo returns one hit: README.md:27, a link in an ecosystem listing.

My own recorded note listed libyuv among the MISSING native libraries. That is wrong. Verified directly:

$ ls -la /usr/lib/libyuv.so
-rwxr-xr-x 1 root root 677840 авг 18 21:15 /usr/lib/libyuv.so
$ ldconfig -p | grep yuv
libyuv.so (libc6,x86-64) => /usr/lib/libyuv.so
$ pkg-config --exists libyuv -> NO

libyuv is present and linkable via -lyuv; it only lacks a .pc file (it is a GStreamer dependency, pulled in without pkg-config metadata).

Corrected native-dependency status on this machine:

Library Status Evidence
libwebrtc MISSING (genuinely) no pkg-config; only libwebrtc-audio-processing-1 / libwebrtc-audio-coding-1 DSP libs and gstreamer-webrtc exist
libtgvoip MISSING absent everywhere
libyuv PRESENT (/usr/lib/libyuv.so, 677 840 B, in ldconfig), no .pc corrected
openssl / libssl / libcrypto 3.6.5 present
opus 1.6.1 present
ffmpeg (libavcodec/format) 63.1.102; libavutil 61.1.102 present
clang / clang++ 23.1.1 binary present, no .pc
cmake 4.4.4 binary present, no .pc
ninja 1.13.2 present

So the full-C++-link blocker list is libwebrtc and libtgvoip only — and libtgvoip is legacy-only per ADR-018’s scope.

The genuinely valuable output of the calls workstream — a proven offline path from real tgcalls headers through bindgen to a green test suite — exists only in /tmp. /tmp is not durable: it is conventionally cleared on reboot and is excluded from every backup. The repo that is supposed to hold it (rstgcalls) is empty and its design plan is untracked.

The risk is not hypothetical drift; it is total loss on a single reboot, with the recovery path being “re-derive the spike from the tgcalls headers” — which is exactly the multi-hour bindgen/clang-discovery work that the plan exists to prevent repeating.

There is also a spec-compliance defect already visible in the spike, which will become a real defect the moment the code is committed:

  • ADR-014 is already violated. ADR.md:566-570 requires one contract suite per trait run against every implementation, and ADR.md:579-581 makes it “required infrastructure before any second implementation of a given trait is accepted”. There are already two CallBackend impls (MockBackend, TgcallsBackend) and no tests/contract.rs / run_backend_contract anywhere. The precondition has been triggered; the suite does not exist. QA.md:29-30 (cross-cutting gate CC5) says the same.
  • ADR-015 is violated. CallManager is a plain struct over Box<dyn CallBackend> with no typestate; join/leave take &mut self, so push_audio is callable before join and only a runtime check catches it (mock_push_before_join_errors). ADR-015 (ADR.md:593-608) requires invalid states to be unconstructible, and specifically names CallManager<Idle|Active> plus a ConnectionState enum — neither exists. The spike even uses started: bool in MockBackend, which is the exact boolean-flag-combination anti-pattern the ADR names.
  • Return-code mapping is a single variant. safe/src/error.rs:4 declares one variant (BackendUnavailable); lib.rs:40 collapses every non-zero code into it. The plan’s -1..-4 table and Unknown(i32) do not exist.

Варианты решения (с tradeoffs)

Section titled “Варианты решения (с tradeoffs)”

Option A — move the spike into the repo now, commit the plan, fix ADR-015/ADR-014 before adding more.

  • Cost: the spike uses unsafe_code="allow" at the sys-crate level and the safe crate currently emits 3 missing_docs warnings — which would fail the Fera workspace’s warnings = "deny" (fera/Cargo.toml). Roughly a day of lint/cleanup.
  • Benefit: preserves the validated work permanently; converts a lost-every-reboot artifact into a real crate.

Option B — leave it in /tmp, re-derive later.

  • Cost: the discovery work (bindgen --target workaround, clang++ -isystem parsing) has to be redone.
  • Benefit: zero cleanup time now.
  • Risk: /tmp loss plus the plan (untracked, 926 lines) is equally exposed.

Option C — commit the spike as-is with #![allow(missing_docs)], and file ADR-014/ADR-015 compliance as follow-ups.

  • Cost: knowingly ships a two-impl trait with no contract suite.
  • Benefit: preserves the work with minimal friction.
  • Risk: ADR.md:579-581 explicitly forbids this, and QA.md:29-30 is a cross-cutting gate that applies to every PR.

A. The validated spike is the single most expensive artifact in the Fera ecosystem so far and it is sitting in a directory the OS is entitled to delete.

  1. Copy /tmp/stub-spike into ~/dev/fera/rstgcalls/crates/{sys,safe}, commit it, and commit the plan doc (docs/superpowers/plans/2026-10-17-rstgcalls-ffi-scaffold.md). Create the TODO.md the plan references, so the blocker record has a committed home. Nothing about the spike needs to be finished to be committed; it needs to be durable.
  2. Fix ADR-015 before adding any feature — it is cheap now (CallManager<S> with Idle/Active is a ~30-line change against a 100-line file) and expensive after a calls surface exists. Add ConnectionState. This is the last cheap moment.
  3. Add the ADR-014 contract suite in the same commit. With two impls already present the gate is live; fn run_contract<S: CallBackend>(fresh: impl FnMut() -> S) mirrors the pattern storage/src/engine.rs:103 already uses, so there is an in-repo precedent to copy rather than invent.
  4. Expand Error to the plan’s -1..-4 table + Unknown(i32) while the header is still 9 lines.
  5. Record the corrected blocker list (libwebrtc + libtgvoip only; libyuv present without .pc) in TODO.md, since the “missing libraries” list has already proven to be the wrong way round once.
  6. Note that a header change is owed regardless: the plan mandates int32_t rstgcalls_instance_stop(…, rstgcalls_final_state* out) while the current stub header returns void and has no receive_signaling at all — and there are no callbacks/events of any kind in the bridge (no extern "C" trampoline, no function-pointer registration). Signaling is a prerequisite for any real call, so it belongs on the critical path, not the backlog.
  • QA.md:172-177 already requires rstgcalls to be “isolated behind its own module boundary; a build without the calls module compiles and runs the rest of the client” — the ffi-stub / link-tgcalls feature split in crates/sys/Cargo.toml:8 is the right shape for that, and it already works.
  • ADR-014 / ADR-015 violations here mirror the same two ADRs at the storage seam; see docs/oc3/004-two-competing-store-seams.md.
  • Calls are deferred past the initial release per RFC.md:27-28 and RFC.md:245-248, so this is not a closed-alpha blocker — the loss risk is what makes it urgent.