Two new primitives in the Python tier dispatch table make
gpu-worker.lsp (pure Scheme) replace mock-worker.py:
(spawn-process-stdio path args) → (stdin-port . stdout-port)
spawns a long-running subprocess via subprocess.Popen with
stdin/stdout piped + line-buffered. Returns a Pair the
caller holds across many request cycles.
(flush-port port)
flushes a write port. No-op when port has no flush method.
read-line also extended to accept file-like ports (subprocess pipes)
not just StringInputPort / sys.stdin.
gpu-worker.lsp fixes:
- run-loop split out as its own tail-recursive function (named-let
inside cond was harder to debug than non-named explicit recursion)
- handle-cuda-shake-fanout unwraps (quote ...) wrapping that
bend.lsp adds when it serializes `'expr` through write-to-string
End-to-end on 3090-ai (lumbda Python tier as both client + worker):
shell A: python3 -u lumbda.py /tmp/launch-worker.lsp
→ gpu-worker: ready cuda-shake-fanout ← ./shake256-fanout
→ gpu-worker listening on port 9091
shell B: python3 lumbda.py smoke-bend.lsp # run 3×
=== smoke-bend ===
1. cost estimator picks local for 3 inputs (cost too small): OK
2. worker available? #t
3. bend! (cuda-shake-fanout '("00" "01" "deadbeef") 32):
("b8d01df855…" "94da6280b2…" "fa094fa86e…")
All three runs identical bytes. All three hashes byte-identical to
hashlib.shake_256 — verified across the full chain:
lumbda Python → bend macro → wire-send (length-prefixed S-exp)
→ gpu-worker.lsp (pure Scheme) → spawn-process-stdio
→ shake256-fanout --daemon (warm CUDA context on 3090)
→ kernel → output portal → wire-send response → bend returns
No Python mock anywhere — except the leaf CUDA binary, which is the
point of the contribution.
Documented in README.md including the python3 -u footnote for
buffering. Once a Scheme-level (flush-port (current-output-port))
is wired into the worker loop, even -u becomes optional.
Per-tier status after this commit:
Python tier ✓ end-to-end working
C tier → still needs spawn-process-stdio + flush-port in
its primitive dispatch (Scheme files unchanged)
asm tier → same, plus raw fork+pipe+execve syscalls for the
spawn primitive