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 <noreply@anthropic.com>
This commit is contained in:
parent
ab66230c0f
commit
9bbff9d336
5 changed files with 140 additions and 12 deletions
|
|
@ -22,5 +22,5 @@
|
||||||
| task | GLAS-WP-0001-T06 | done | — | workplans/GLAS-WP-0001-harness-router-foundation.md |
|
| 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-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-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 |
|
| task | GLAS-WP-0002-T04 | todo | — | workplans/GLAS-WP-0002-observability-and-composability-followups.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
|
- `GLAS-WP-0001-T06` is done. `GLAS-WP-0001-T05` (bootstrap
|
||||||
`rein-openweights`) is unblocked.
|
`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
|
Separately, the idea surfaced that monitoring/evaluation/optimization
|
||||||
(capabilities `llm-connect` already stages as optional Functional
|
(capabilities `llm-connect` already stages as optional Functional
|
||||||
|
|
|
||||||
67
docs/adr/ADR-004-composable-reins-stay-deferred.md
Normal file
67
docs/adr/ADR-004-composable-reins-stay-deferred.md
Normal file
|
|
@ -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.
|
||||||
|
|
@ -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
|
glas-harness stamps this at dispatch time; reins do not need their own
|
||||||
actor model.
|
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
|
## Open questions this contract does not resolve yet
|
||||||
|
|
||||||
- Whether `start_session` also carries scheduling/blueprint-sourcing
|
- The exact `SandboxHandle`/`ToolCall`/`ToolResult` field shapes — these
|
||||||
parameters (kaizen-agentic blueprint id, activity-core task id) as part
|
have stabilized in practice (both reins implement them identically)
|
||||||
of `inputs`, or whether that stays entirely rein-side — tracked in
|
but are not yet declared frozen/versioned the way sand-boxer's models
|
||||||
`rein-aharness/workplans/HARNESS-WP-0002-T04`.
|
are.
|
||||||
- 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.
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
middleware candidate (e.g. a monitoring rein) is scoped alongside it in
|
||||||
this same task — not before.
|
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
|
```task
|
||||||
id: GLAS-WP-0002-T01
|
id: GLAS-WP-0002-T01
|
||||||
status: todo
|
status: done
|
||||||
priority: medium
|
priority: medium
|
||||||
state_hub_task_id: "2de6074e-b72b-4eb7-b454-6b849011348c"
|
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
|
`rein-openweights` directly; this task's outcome should be recorded in
|
||||||
both repos.
|
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
|
```task
|
||||||
id: GLAS-WP-0002-T02
|
id: GLAS-WP-0002-T02
|
||||||
status: todo
|
status: wait
|
||||||
priority: medium
|
priority: medium
|
||||||
state_hub_task_id: "74b4f35b-9cdd-4228-aeed-e0eec458c0bf"
|
state_hub_task_id: "74b4f35b-9cdd-4228-aeed-e0eec458c0bf"
|
||||||
```
|
```
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue