# ADR-004: Composable reins as middleware — stay deferred - Status: accepted - Date: 2026-07-26 - Deciders: Bernd Worsch ## Context `ADR-002` part 2 raised the idea, left deliberately open: could monitoring/evaluation/optimization become their own composable reins — middleware wrapping or observing a base rein's `dispatch_tool` calls, rather than each being a complete alternative harness backend? At the time there was exactly one candidate capability to generalize from (llm-connect's optional Functional-layer modules: diagnostics, replay, budget tracking), and the rule set then was: don't build the abstraction until a second real candidate shows up. Since then, two more observability additions landed: 1. `rein-aharness`'s per-tool-call audit stream (`HARNESS-WP-0002-T03`) — `AgenticClaudeCodeAdapter` gained an `on_tool_event` callback, parsing `claude`'s `stream-json` output. 2. `glas-harness`'s own gateway hub event (`GLAS-WP-0002-T03`) — `run_task_through_rein` posts a `gateway_run` progress event under its own actor attribution, independent of whatever the rein reports. Both are exactly the *kind* of thing the middleware idea was reaching for: cross-cutting observability layered on top of a rein's tool dispatch. Worth checking, now that two real examples exist, whether they should have been built as `Middleware` wrapping a `Rein`, per the sketch in `docs/harness-contract.md`. ## Decision **They shouldn't have been, and composable-reins-as-middleware stays deferred — sketch recorded, no code.** Both additions turned out to be simpler as direct implementations than a wrapping-middleware abstraction would have been: - Item 1 lives *inside* `rein-aharness`'s own adapter, because the thing being observed (Claude Code's internal tool execution) is only visible from inside that adapter's process boundary in the first place — no external wrapper could intercept it, wrapping-middleware or otherwise. - Item 2 lives in `glas-harness`'s own gateway, because it's reporting on the gateway's *own* actions (sandbox lifecycle, dispatch call), not wrapping a rein's internals — there's nothing here a `Middleware` layer would add over just... doing it in the gateway. Neither is evidence for the original idea (a rein-shaped monitoring/eval/ optimization layer sitting *between* glas-harness and a base rein). The original candidate — llm-connect's optional modules — remains untouched and unintegrated into either rein. Two additions still isn't two candidates for *this specific shape*; it's zero candidates plus two observations that the direct-implementation path keeps winning on its own merits. ## Consequences - `docs/harness-contract.md` keeps the `Middleware` ABC as a documented, unimplemented sketch — reachable if a real third case appears, not built speculatively. - `GLAS-WP-0002-T01` closes on this ADR; no code follows from it. - Future observability needs should default to "implement directly where the data actually is" (as both examples here did) unless a genuine cross-rein reuse case forces the composition question — don't reach for `Middleware` reflexively just because it's now documented.