docs/moonbit-rust-boundary-refactor-plan.md
This plan is based on the current implementation in:
src/ai_tasks.rssrc/ai_taskd.rssrc/tasks.rs.ai/tasks/flow/*Goal: keep Flow's Rust core stable while moving high-change task logic to MoonBit with near-zero boundary overhead and no performance regressions.
Current paths:
f ai:... and task shortcut route through tasks::run_with_discovery in src/tasks.rs.ai_tasks::run_task in src/ai_tasks.rs.f tasks run-ai --daemon) is implemented separately in src/ai_taskd.rs.Refactor target:
AiTaskExecutor policy entrypoint in Rust, used by all callsites.tasks run-ai path share identical behavior and telemetry.Current state:
Refactor target:
[ai_tasks] mode = "cached", daemon = true) and keep CLI as override.Current state:
.ai/tasks/flow/* tasks call shell commands directly.Refactor target:
Added:
scripts/bench-ai-runtime.pyscripts/bench-moonbit-rust-ffi.pyflow.toml task: bench-ai-runtimeflow.toml task: bench-ffi-boundary.ai/tasks/flow/noop/*bench/ffi_host_boundary (Rust staticlib + Rust baseline bench)bench/moon_ffi_boundary (MoonBit native bench calling Rust host exports)Benchmark scenarios:
rust_helpmoon_run_noopcached_noopdaemon_cached_noopcached_binary_directRun:
cd ~/code/flow
f bench-ai-runtime --iterations 80 --warmup 10 --json-out /tmp/flow_ai_runtime_bench.json
This is the baseline gate to ensure refactors do not regress p95 latency.
Run boundary-only microbench:
f bench-ffi-boundary --iters 10000000 --json-out /tmp/flow_ffi_boundary.json
Use a narrow C ABI with primitive handles, not JSON strings, for hot paths.
extern "C".Why: this minimizes allocation/serialization churn and gives a predictable ABI.
Candidate host functions:
flow_host_now_ns() -> u64flow_host_log(level: u32, ptr: *const u8, len: u32) -> i32flow_host_spawn(cmd_ptr, cmd_len, argv_ptr, argv_len, out_handle) -> i32flow_host_read_file(path_ptr, path_len, out_handle) -> i32flow_host_git(op: u32, in_handle, out_handle) -> i32flow_host_drop_handle(handle: u32)For MoonBit string interop helpers, the justjavac/ffi package is useful for C-string/wide-string conversions, but should remain at the edge of the ABI where text crossing is required.
AiTaskExecutor in Rust.ai_task_host C ABI layer in Rust.Use these checks before approving runtime changes:
f bench-ai-runtime --iterations 80 --warmup 10 --json-out ...cached_noopdaemon_cached_noop.ai/tasks/*.mbt.