Title here
Summary here
Superseded by ADR-0004
2026-04-10
F002 requires a Prolog inference engine inside zpm. Three approaches were evaluated:
.a artifact via a thin Rust crate exposing extern "C" functions; link it into the Zig binary at build time.The latency target is <5 ms per query. The project is written in Zig and has a single existing dependency (mcp.zig). There is no production-ready Prolog engine written in Zig; scryer-prolog is a mature, ISO-compliant Prolog implemented in Rust.
| Option | Pros | Cons |
|---|---|---|
| Rust FFI static library (scryer-prolog) | <1 ms call overhead; ISO-complete; battle-tested | Two toolchains; large binary; Rust/Zig boundary discipline |
| Subprocess invocation (scryer-prolog CLI) | No FFI complexity; language isolation | 50-200 ms spawn latency; process lifecycle overhead |
| Pure Zig Prolog engine | Single toolchain; smallest binary | Multi-month effort; no production WAM exists in Zig |
| Trealla-Prolog (C library) | Simpler C FFI from Zig | Less ISO-complete than scryer-prolog; smaller community |
Integrate scryer-prolog as a Rust static library linked into the Zig binary, via a thin FFI crate at ffi/zpm-prolog-ffi/.
Specifically:
extern "C" functions (prolog_init, prolog_deinit, prolog_query, prolog_assert, prolog_retract, prolog_load_file, prolog_load_string). No complex C structs cross the boundary — all results are serialized to JSON strings and returned as heap-allocated *mut c_char.build.zig invokes cargo build --release and links the resulting .a artifact. Zig’s build system owns the full pipeline.src/prolog/engine.zig wraps the FFI with idiomatic Zig error handling, memory management via GPA, and JSON deserialization into Zig-native types.engine.zig (not split into types.zig + ffi.zig) to honor the project’s flat module structure principle.prolog_free_string.cargo and zig must both be present. CI and Docker images must install Rust. Build time increases by the Rust compile step (~60–90 s cold, cached thereafter).engine.zig and the FFI crate — a significant but bounded effort since the boundary is thin.| Risk | Likelihood | Severity | Mitigation |
|---|---|---|---|
scryer-prolog does not compile to staticlib | Medium | P0 | Verify in spike before full implementation |
| Binary exceeds 20 MB | Medium | P1 | Enable LTO, strip debug symbols; accept if necessary |
| Rust/Zig allocator mismatch causes double-free | Low | P0 | All FFI strings freed via prolog_free_string; never freed from Zig allocator |
| cargo not available in CI | Low | P1 | Add Rust toolchain step to .github/workflows/ci.yaml |
| Alternative | Why rejected |
|---|---|
| Subprocess invocation | 50–200 ms spawn latency per query violates the <5 ms target; process lifecycle management adds complexity |
| Pure Zig Prolog engine | No production-ready WAM in Zig exists; building one is a multi-month effort outside F002 scope |
| Trealla-Prolog (C library) | C FFI from Zig is simpler than Rust FFI, but trealla-prolog is less ISO-complete than scryer-prolog; revisit if scryer staticlib proves intractable |
| Principle | Status | Justification |
|---|---|---|
Use mcp.zig types directly — no wrapper abstractions | N/A | F002 adds no MCP tools; engine module is independent of MCP layer |
| Flat module structure | Compliant | Single src/prolog/engine.zig file; no premature splits |
| Inline tests in every module | Compliant | engine.zig contains inline tests for all engine functionality |
Tool handlers in src/tools/ | N/A | No new tools in F002; deferred to F003 |