flan dev is one process, so the unix socket between the compiler and the program was a connect, a write and a read that never left the machine's own memory. A delivery is now a call into the agent's verb table. flan_agent.c's handlers write into a sink that is either an fd or a growing buffer, so serve() and the new flan_agent_request() share one verb table rather than two that would drift -- every answer in there is assembled from several pieces, so the seam had to be the writing. Dev.deliver, Dev.result and Dev.ask were three copies of the same socket dance and are now three lines over one Dev.request. Which path is taken is the linker's answer, not a flag: flan_agent_request is weak in dynload_stubs.c, null in every binary that links no flan_agent.o, and Agent.request is None there. So --two-process, flan reload and every test go on using the socket with nothing to configure. Three things the direct path must not quietly change. The ring's room check is separate from its store, which was safe only while the accept loop was the sole producer, so handle_line runs under a mutex. A delivery is still only published -- the install is one store per function on the game thread at a frame boundary, and doing it on the spot would be a frame running half in the old code and half in the new. And the OCaml runtime system is released across the call, because a delivery is a dlopen. Measured, and the number is the point: the transport was ~50us of a 23ms redefinition, so end to end did not move. Code generation is 19 of the 22 milliseconds. The merge's prize was never latency. The test is a deletion, because a reply cannot say which way it came: test_dev.ml unlinks the agent's socket file once the merged program has bound it, and every evaluation after that still installs.
Description
Languages
OCaml
67.2%
Emacs Lisp
15.2%
C
10.4%
HTML
2.9%
Standard ML
2.8%
Other
1.5%