One re-exec’d-child eval protocol
Evaluating a Comp in a re-exec’d child is one protocol with two
preludes, not two protocols that must agree. The confined-eval child
(the OS sandbox, grant) and the process-staged pipeline
stage (a helper subprocess, pipelines) both pack a
body plus a WireMobile snapshot, reconstruct a shell, evaluate, and
report a structured outcome with audit nodes. That shared shape is now a
single module — core/src/child_eval.rs, a crate-root sibling of the
transport layer it rides on.
- One wire shape, one runner.
ChildEvalRequestin,ChildEvalResponseout — one frame each way.run_child_eval(request, upstream, kind)is the sole child runner;pack_request/decode_response/break_responseare its inverse and its pre-eval-failure constructor. The oldEvalRequest/StageJob::Ralpair — near-identical structs down to a verbatim-duplicated doc comment, and twopack → reconstruct → run → reportstate machines — collapse into it. ChildKindis the one axis that cannot merge.Sandboxrunseval_compagainst a shell whose mobile already carries the lexical env, and returns the mutated mobile (the contract layer installs it).PipelineStagehydrates a separate captured closure env, buildsShell::child_of, applies the body bycall::invokedata-last, and returns onlylast_status— a stage is a subshell. Folding the two eval shapes would change checker-visible semantics.- Value and mobile serialization are gated before serialization.
wants_value(a byte-mode stage that retains an incidental non-transferable value must not fail the run) andwants_mobile(a pipeline stage must not ship its post-run mobile at all). A non-transferable value becomes a structured boundary error carrying the value type’s own hint when it has one. - The hazard removed is the agreement obligation. Two snapshot
protocols that must stay byte-compatible was the exact bug class the
pipeline parcels had been hunting; with one definition there is nothing
to drift. The value-edge force the pipeline applies before a value
crosses an edge is likewise one shared
force_pipe_value, called by both the parent fold and this runner — the runtime mirror of the pure-pipe-equation. - Live audit streaming was always cosmetic. Both children drain their audit fragment after eval; the parent buffered frames until a final marker. So the unification collapses to one response frame with the nodes embedded, deleting both per-node frame loops. Reintroducing live streaming would reintroduce a frame loop; it does not belong here.
- Surface events take one code path, replayed by choice. The child always buffers them; the sandbox parent replays them through its own sink, the pipeline parent drops them (a stage child has no sink to replay to — preserving prior behaviour).
The OS-entry sequence stays per-consumer and untouched: the sandbox is
entered in early_init before the runner is reached, fd/handle
inheritance arming stays in each transport, and the pipeline keeps its
SIGPIPE/pgid/anchor placement. The runner acquires no new pre-eval
authority a confined child lacks (capabilities).