PLMB-037: Bridge Design as Natural Transformation

Curated transcripts from the PLMB-037 (Supervisor + Router Runtime) design sessions, focused on the observation that the bridge (lib/fabric/bridge.ml) is a natural transformation between two stream representations — not a morphism.

Key Insight

The bridge is a natural transformation between representations; a ZMQ-native handler is not a bridge at all. — Architect agent
R7: the bridge is NOT a morphism, it is a natural transformation between two concrete stream representations. — bridge.ml module comment

The bridge transforms between ZMQ topic-framed sockets (the fabric's internal representation) and port-multiplexed stdin/stdout (the agent binary's protocol). A ZMQ-native agent bypasses the bridge entirely — it is a direct embedding, not a transformation.

Who Noticed

The Architect agent identified the naturality during the PLMB-037 Step 5c review, distinguishing bridge (natural transformation) from ZMQ handler (direct embedding). This informed the module boundary decision: ZMQ handler goes in zmq_agent.ml, not bridge.ml.

Theory Critic's Verdict

None of substance. The natural transformation (bridge) is preserved for stdio agents, and the ZMQ handler is correctly understood as a direct embedding rather than a bridge.
A natural transformation η : F ⇒ G is a family of morphisms satisfying naturality squares. What happens inside each component is unconstrained by naturality — naturality only requires that the squares commute. The bridge's internal Promise is coordination within the codomain representation. It is invisible to the algebra.

The theory critic confirmed: internal coordination (Eio promises, mutexes) within the bridge does not pollute the categorical semantics. The design correctly separates categorical concerns from operational concerns (lifecycle, shutdown ordering, resource management).

What the Agents Were Asked to Review

The orchestrating agent (Research/Consul) wrote detailed review prompts for each subagent. The naturality observation emerged unprompted — it was not in the instructions.

Round 1: Step 5c Plan Review (session e80e21f4)

The original plan proposed eliminating the bridge by having the agent binary speak ZMQ directly, with a transport abstraction (agent_transport.ml) providing stdio and ZMQ backends.

Architect was asked:
Review for architectural soundness, boundary correctness, system vision alignment. Specific questions: Is "close fabric sockets, agent creates fresh ones" the right boundary? Should make_zmq_handler be in bridge.ml or a separate module? Is the two-backend transport abstraction unnecessary complexity? Does tool dispatch through pipe adapters add too many layers?
Theory Critic was asked:
Review for algebraic/categorical coherence. Specific questions: Does one-socket-per-port preserve categorical semantics? Is there an observable semantic gap during socket handover? Is PUSH/PULL for tool dispatch sound vs REQ/REP? Any coherence concern with Eio cooperative scheduling?
Engineer was asked:
Review for correctness, efficiency, production-readiness, bugs. Specific questions: message loss risk in socket handover gap, ZMQ auto-reconnect timing, pipe adapter overhead for tool dispatch, Eio mutex interactions with ZMQ recv blocking.

The Architect responded (unprompted) with the key observation: "The bridge is a natural transformation between representations; a ZMQ-native handler is not a bridge at all" — answering the make_zmq_handler location question by reframing the entire module boundary in categorical terms.

Round 2: Re-review of Revised Plan (session e80e21f4, later)

After incorporating all first-round feedback, the revised plan dropped the transport abstraction, made agents ZMQ-only, put the ZMQ handler in zmq_agent.ml, and added startup handshake + shutdown protocol.

Theory Critic re-review was asked:
Check remaining algebraic/categorical concerns. Does "fabric binds PULL at agent output" compose correctly? Is the startup ready handshake well-defined? Is the shutdown protocol (D6) ordering sound? Is unlimited ZMQ buffering (ZMQ_SNDHWM=0) correct given pipes had finite buffers?
Architect re-review was asked:
Does the revised plan address original concerns? Does fabric-binds-both-sides work with existing socket creation? Is the endpoints parameter the right contract? Should there be a stdio fallback in main.ml?

Bridge Hang Fix Reviews (session d438378e)

The bridge mux fibre hung when ctrl_in had no upstream writer. The fix used an Eio.Promise as a shutdown signal. Three agents reviewed in parallel:

Theory Critic was asked:
Is it categorically sound for the bridge (as natural transformation) to have internal Promise coordination? Does terminating ctrl_in on data EOF violate product semantics? Is the mux-source-counting scheme the right structural choice? "Focus on whether the internal coordination mechanism is principled or ad hoc."
Architect was asked:
Is the bridge accumulating too much internal state? Could the routing layer ensure ctrl_in always has a writer? Could the fabric skip creating sockets for channels with no writer? Is Eio.Fiber.first cancellation of T.recv well-tested? Is there a race where a ctrl_in message is partially written during cancellation?
Engineer was asked:
Does Eio.Fiber.first correctly cancel T.recv? Race condition with run_in_systhread mid-cancellation? Is Eio.Promise.resolve idempotent? Memory/GC concerns? Performance of spawning two fibres per recv call?

The Theory Critic produced the most detailed naturality analysis here: "A natural transformation η : F ⇒ G is a family of morphisms satisfying naturality squares. What happens inside each component is unconstrained by naturality."

EOF Sentinel Design (session d1d325d0)

Theory Critic was asked:
Does the sentinel approach preserve algebraic properties (associativity, copy/discard naturality)? Is there a categorical issue with EOF being in-band vs out-of-band? Does merge all-EOF / merge_any first-EOF remain sound? Edge cases that break compositionality?
Architect was asked:
Is the sentinel the right boundary contract? Does the forwarding-fibre translation layer for subprocesses violate the "no mux/demux" goal? Does EOF propagation produce correct shutdown ordering? What if a morphism dies without sending EOF? Is this the minimum viable implementation?

Transcripts

01 — Architect Review (naturality observation)
Session e80e21f4 page 2. The architect's review of PLMB-037 Step 5c where the bridge-as-natural-transformation observation is made. Covers one-socket-per-port categorical semantics, bridge retention, and module boundary decisions.
02 — Theory Critic Re-review
Session e80e21f4 page 3. The theory critic's formal re-review: "None of substance" on algebraic/categorical concerns. Identifies real bugs in agent crash lifecycle and fabric crash recovery, but confirms the natural transformation is sound.
03 — Bridge Hang Fix + Theory Critic Deep Review
Session d438378e page 1. The bridge hang investigation. Contains the theory critic's most detailed analysis: "A natural transformation η : F ⇒ G is a family of morphisms satisfying naturality squares. What happens inside each component is unconstrained by naturality." Also has architect and engineer reviews converging on the fix.
04 — Copy/Discard Naturality (EOF sentinel design)
Session d1d325d0 page 2. Theory critic review of the EOF sentinel protocol, confirming copy/discard naturality is preserved and composition remains associative under the ZMQ transport change.
05 — Final Design Summary
Session 7b542c62 page 1. The consolidated design with the bridge.ml module comment encoding the insight: "R7: the bridge is NOT a morphism, it is a natural transformation between two concrete stream representations." Shows the implemented code.