Expand description
§6 plugin contract & dispatch — subprocess-uniform.
A plugin is a single binary, invoked identically whether it is one of the
shipped capabilities or a third party: there is no in-process path and no
privileged plugin. balls spawns <bin> <op> <phase> with the §7 payload on
stdin and the §6 env set, and reads NOTHING back — a plugin contributes by
editing the change worktree, never by printing values (§7, no return
channel). The plugin stays DUMB about diagnostics too: it writes raw stderr
and is told nothing about where it lands (no BALLS_LOG_DIR — a new env is a
§0 smell); balls pipes the child’s stderr and ENVELOPES each line as a record
into the unified op log (src=<name>, lvl=info). A non-zero exit aborts the
op — core emits an error record naming the locus first — and the
crate::lifecycle engine then rolls the prior plugins back in reverse.
Subprocess is the production Plugins seam. It is built once per op
with the op-constant OpContext (the §7 wire data the verb layer authored),
the op’s Log sink (it logs each invoke and envelopes plugin stderr), and
the recursion depth balls is running at. The engine hands it the per-phase
post-seal Sealed facts.
Recursion guard (§6, bl-7110). A plugin may shell back to bl; every
nested call bumps BALLS_PLUGIN_DEPTH. Crossing DEPTH_CAP ABORTS the op —
fail, not silent: Subprocess::run returns an error naming the op/phase that
overran, so the crate::lifecycle engine rolls the prior plugins back in
reverse (§8/§14) and the runaway SURFACES. There is no hatch to re-enable
plugins on a nested call — that would let a runaway defeat its own backstop.
rollback cannot spawn at the cap either, so it no-ops (best-effort, §14).
Structs§
- Protocol
- A plugin’s self-description from
<bin> protocol: the protocol version(s) it speaks and the ops it handles. balls never persists it — it is read at install time to validate a binding, and is diagnostics otherwise (§6). - Subprocess
- The production
Pluginsseam: spawns each plugin as a subprocess with the §7 wire on stdin (§6). Borrows the op’sLogfor the run’s lifetime — it shares the one per-clone sink with core’s own lifecycle records.
Constants§
- DEPTH_
CAP - The built-in recursion cap (§6, bl-7110): reaching this depth ABORTS the op.