feat(ffi): FFI boundary with Protobuf wire format (closes #6, #11) #13

Merged
yorunikakeru merged 6 commits from feat/ffi-protobuf-boundary into main 2026-06-17 16:03:45 +02:00
Owner

Summary

Implements issues #6 and #11 as a single pass — FFI C ABI built directly with Protobuf, skipping the intermediate JSON FFI step.

  • proto/planner.proto — source of truth for the FFI contract (SystemState, Plan, PlanStep, PlanError, PlanResult oneof)
  • Planner.Proto — encode/decode between native Haskell types and proto-lens generated types; IR.Action fields embedded as JSON bytes (frogos-ir owns those instances)
  • Planner.FFIhs_planner_init, hs_plan, hs_plan_free foreign exports compiled as libplanner-ffi.so via foreign-library native-shared
  • Breaking: SystemState and Plan lose manual version fields; versioning moves to proto field numbering
  • Breaking: ToJSON/FromJSON instances removed from Planner.Types; JSON golden tests replaced with proto round-trip tests

C ABI (for Rust extern "C")

void hs_planner_init(void);
int  hs_plan(const char *ir_ptr, int ir_len,
             const char *state_ptr, int state_len,
             char **out_ptr, int *out_len);  // 0=ok, 1=decode error
void hs_plan_free(char *ptr);

Input: IR document as JSON bytes (from frogos-ir) + SystemState as proto bytes.
Output: PlanResult as proto bytes (caller copies, then calls hs_plan_free).

Test plan

  • cabal test — 19 tests pass (proto round-trips + all existing Core tests)
  • cabal build planner-ffilibplanner-ffi.so.0 links cleanly
  • nm -D libplanner-ffi.so | grep hs_plan — all three symbols exported with T
  • Integration test against Rust caller (tracked in #7)
## Summary Implements issues #6 and #11 as a single pass — FFI C ABI built directly with Protobuf, skipping the intermediate JSON FFI step. - **`proto/planner.proto`** — source of truth for the FFI contract (`SystemState`, `Plan`, `PlanStep`, `PlanError`, `PlanResult` oneof) - **`Planner.Proto`** — encode/decode between native Haskell types and proto-lens generated types; `IR.Action` fields embedded as JSON bytes (frogos-ir owns those instances) - **`Planner.FFI`** — `hs_planner_init`, `hs_plan`, `hs_plan_free` foreign exports compiled as `libplanner-ffi.so` via `foreign-library native-shared` - **Breaking:** `SystemState` and `Plan` lose manual version fields; versioning moves to proto field numbering - **Breaking:** `ToJSON`/`FromJSON` instances removed from `Planner.Types`; JSON golden tests replaced with proto round-trip tests ## C ABI (for Rust `extern "C"`) ```c void hs_planner_init(void); int hs_plan(const char *ir_ptr, int ir_len, const char *state_ptr, int state_len, char **out_ptr, int *out_len); // 0=ok, 1=decode error void hs_plan_free(char *ptr); ``` Input: IR document as JSON bytes (from frogos-ir) + `SystemState` as proto bytes. Output: `PlanResult` as proto bytes (caller copies, then calls `hs_plan_free`). ## Test plan - [ ] `cabal test` — 19 tests pass (proto round-trips + all existing Core tests) - [ ] `cabal build planner-ffi` — `libplanner-ffi.so.0` links cleanly - [ ] `nm -D libplanner-ffi.so | grep hs_plan` — all three symbols exported with `T` - [ ] Integration test against Rust caller (tracked in #7)
Sign in to join this conversation.
No reviewers
No labels
core
ffi
haskell
rust
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
FrogOS/Planner!13
No description provided.