From 9bbff9d336536b13e5570df80aa27f7d0c9b2d07 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 26 Jul 2026 20:22:55 +0200 Subject: [PATCH] ADR-004: composable reins as middleware stays deferred (GLAS-WP-0002-T01) 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 --- WORK-RECORDS.md | 2 +- ...edential-brokering-and-composable-reins.md | 8 ++- .../ADR-004-composable-reins-stay-deferred.md | 67 +++++++++++++++++++ docs/harness-contract.md | 54 ++++++++++++--- ...servability-and-composability-followups.md | 21 +++++- 5 files changed, 140 insertions(+), 12 deletions(-) create mode 100644 docs/adr/ADR-004-composable-reins-stay-deferred.md diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index ec13e9e..2d9e004 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -22,5 +22,5 @@ | task | GLAS-WP-0001-T06 | done | — | workplans/GLAS-WP-0001-harness-router-foundation.md | | task | GLAS-WP-0002-T01 | todo | — | workplans/GLAS-WP-0002-observability-and-composability-followups.md | | task | GLAS-WP-0002-T02 | todo | — | workplans/GLAS-WP-0002-observability-and-composability-followups.md | -| task | GLAS-WP-0002-T03 | todo | — | workplans/GLAS-WP-0002-observability-and-composability-followups.md | +| task | GLAS-WP-0002-T03 | done | — | workplans/GLAS-WP-0002-observability-and-composability-followups.md | | task | GLAS-WP-0002-T04 | todo | — | workplans/GLAS-WP-0002-observability-and-composability-followups.md | diff --git a/docs/adr/ADR-002-credential-brokering-and-composable-reins.md b/docs/adr/ADR-002-credential-brokering-and-composable-reins.md index 4dbeb41..a231bf3 100644 --- a/docs/adr/ADR-002-credential-brokering-and-composable-reins.md +++ b/docs/adr/ADR-002-credential-brokering-and-composable-reins.md @@ -75,7 +75,13 @@ repeats the same need — decide from evidence, not in advance. - `GLAS-WP-0001-T06` is done. `GLAS-WP-0001-T05` (bootstrap `rein-openweights`) is unblocked. -## Part 2 — open, deferred: composable reins as middleware +## Part 2 — resolved in ADR-004: composable reins stay deferred + +See `docs/adr/ADR-004-composable-reins-stay-deferred.md` — the deferral +below held even after two more observability additions landed; both +turned out simpler as direct implementations, not middleware. + +## Part 2 (original framing, for context) Separately, the idea surfaced that monitoring/evaluation/optimization (capabilities `llm-connect` already stages as optional Functional diff --git a/docs/adr/ADR-004-composable-reins-stay-deferred.md b/docs/adr/ADR-004-composable-reins-stay-deferred.md new file mode 100644 index 0000000..a385c7b --- /dev/null +++ b/docs/adr/ADR-004-composable-reins-stay-deferred.md @@ -0,0 +1,67 @@ +# 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. diff --git a/docs/harness-contract.md b/docs/harness-contract.md index 68bf621..47ef56b 100644 --- a/docs/harness-contract.md +++ b/docs/harness-contract.md @@ -74,13 +74,51 @@ Every `dispatch_tool` call and lifecycle transition carries an actor glas-harness stamps this at dispatch time; reins do not need their own actor model. +## Composable reins as middleware (deferred — sketch only, no code) + +`docs/adr/ADR-002-credential-brokering-and-composable-reins.md` part 2 +raised whether monitoring/evaluation/optimization should become their +own composable reins — middleware that wraps or observes a base rein's +`dispatch_tool` calls, rather than each being a complete alternative +harness backend the way `rein-aharness`/`rein-openweights` are. Resolved +in `docs/adr/ADR-004-composable-reins-stay-deferred.md`: **still +deferred, sketch recorded, no code written.** The two observability +additions since (rein-aharness's per-tool-call audit stream, +`glas-harness`'s own gateway hub event) both turned out to be simpler as +direct implementations — neither needed a separate wrapping rein — so +there still isn't a second real capability wanting this shape, only the +original one (llm-connect's optional Functional-layer modules, +untouched). + +If a real second candidate appears, the shape would be: + +```python +class Middleware(ABC): + """Wraps another Rein's dispatch_tool, does not replace start_session/end_session.""" + + def __init__(self, inner: Rein) -> None: + self.inner = inner + + @abstractmethod + def dispatch_tool(self, session: dict[str, str], tool_call: ToolCall) -> ToolResult: + """Call self.inner.dispatch_tool(...), observe/transform, return the result.""" + + def start_session(self, profile, inputs, sandbox) -> dict[str, str]: + return self.inner.start_session(profile, inputs, sandbox) + + def end_session(self, session) -> dict[str, str]: + return self.inner.end_session(session) +``` + +A chain of `Middleware` wrapping a base `Rein` still satisfies the `Rein` +ABC itself (composition, not a parallel type), so glas-harness's gateway +would not need to change to consume one — this is deliberately *not* a +speculative gateway-side change, just a documented shape to reach for +if/when a second candidate shows up. + ## Open questions this contract does not resolve yet -- Whether `start_session` also carries scheduling/blueprint-sourcing - parameters (kaizen-agentic blueprint id, activity-core task id) as part - of `inputs`, or whether that stays entirely rein-side — tracked in - `rein-aharness/workplans/HARNESS-WP-0002-T04`. -- The exact `SandboxHandle`/`ToolCall`/`ToolResult` field shapes — sketched - here at the level needed to unblock `GLAS-WP-0001-T04` (the - rein-aharness parity proof); refine once that task is underway rather - than speculatively now. +- The exact `SandboxHandle`/`ToolCall`/`ToolResult` field shapes — these + have stabilized in practice (both reins implement them identically) + but are not yet declared frozen/versioned the way sand-boxer's models + are. diff --git a/workplans/GLAS-WP-0002-observability-and-composability-followups.md b/workplans/GLAS-WP-0002-observability-and-composability-followups.md index 492d236..4d0f1fe 100644 --- a/workplans/GLAS-WP-0002-observability-and-composability-followups.md +++ b/workplans/GLAS-WP-0002-observability-and-composability-followups.md @@ -30,9 +30,18 @@ record the decision as an ADR addendum, and only write code if a real middleware candidate (e.g. a monitoring rein) is scoped alongside it in this same task — not before. +**Done (2026-07-26), stays deferred.** `docs/adr/ADR-004-composable-reins-stay-deferred.md` +checked this against the two observability additions that landed since +ADR-002 (rein-aharness's tool-event stream, glas-harness's own gateway +hub event, both `HARNESS-WP-0002-T03`/`GLAS-WP-0002-T03`) — both turned +out simpler as direct implementations, neither needed a wrapping +middleware layer. Still zero real candidates for *this* shape, so no +code written. `docs/harness-contract.md` now carries the `Middleware` +ABC sketch for future reference, explicitly marked don't-build-yet. + ```task id: GLAS-WP-0002-T01 -status: todo +status: done priority: medium state_hub_task_id: "2de6074e-b72b-4eb7-b454-6b849011348c" ``` @@ -51,9 +60,17 @@ vault round trip actually works end to end. Coordinate with `rein-openweights` directly; this task's outcome should be recorded in both repos. +**Blocked (checked 2026-07-26):** `bao token lookup` from this +workstation returns `403 permission denied` — no usable vault session +here. Provisioning a new AppRole (`auth/approle/role/...`) and a KV +secret for `rein-openweights`, mirroring `rein-aharness`'s `binky-mail` +lane, needs vault-admin access this session doesn't have. Needs the +operator to provision the AppRole (or provide an ambient token with +enough privilege) before this can be exercised for real. + ```task id: GLAS-WP-0002-T02 -status: todo +status: wait priority: medium state_hub_task_id: "74b4f35b-9cdd-4228-aeed-e0eec458c0bf" ```