Checked ADR-002 part 2's deferred question against the two observability additions that landed since (rein-aharness's tool-event stream, glas-harness's own gateway hub event) -- both turned out simpler as direct implementations, neither needed a wrapping middleware layer. Still zero real candidates for that shape. docs/harness-contract.md gains the Middleware ABC as a documented, unimplemented sketch for if a real third case ever appears -- no code written now. Also flagged GLAS-WP-0002-T02 (live OpenBao verification) as blocked: `bao token lookup` from this workstation returns 403, no usable vault session to provision a new AppRole with. Needs the operator. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
3.1 KiB
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:
rein-aharness's per-tool-call audit stream (HARNESS-WP-0002-T03) —AgenticClaudeCodeAdaptergained anon_tool_eventcallback, parsingclaude'sstream-jsonoutput.glas-harness's own gateway hub event (GLAS-WP-0002-T03) —run_task_through_reinposts agateway_runprogress 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 aMiddlewarelayer 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.mdkeeps theMiddlewareABC as a documented, unimplemented sketch — reachable if a real third case appears, not built speculatively.GLAS-WP-0002-T01closes 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
Middlewarereflexively just because it's now documented.