Stage 386: PR #2575

This commit is contained in:
nesquena-hermes 2026-05-19 18:20:06 +00:00
commit 629ebf45f7
3 changed files with 93 additions and 12 deletions

View file

@ -2,6 +2,9 @@
## [Unreleased]
### Documentation
- **PR #TBD** by @Michaelyklam (refs #1925) — Advance the runtime-adapter RFC to the Slice 4 runner/sidecar planning gate after #2560 shipped the queue-staging clarification. The RFC now marks queue routing as staged by default, defines Slice 4a as a docs/test contract before any runner code lands, and pins default-off feature-flagging, restart/reattach success criteria, control parity, profile/workspace payload isolation, and explicit non-goals for legacy-backend removal or server-side queue scheduler work.
## [v0.51.92] — 2026-05-19 — Release BP (stage-385 — 7-PR full sweep batch — RFC Slice 3c clarification + workspace tree icon alignment + project move cache refresh + auto-compression handoff metadata + Grok OAuth provider catalog + anonymous custom endpoint picker fallback + PWA standalone reload + pull-to-refresh)

View file

@ -52,7 +52,7 @@ The immediate goal is not to build a sidecar. The immediate goal is to define th
browser contract, classify current runtime state, and gate the first reversible
journal slice.
## Current Gate State — 2026-05-18
## Current Gate State — 2026-05-19
Slice 1 is now past the first active validation gate:
@ -90,14 +90,17 @@ adapter-seam work:
`HERMES_WEBUI_RUNTIME_ADAPTER=legacy-journal` is enabled, while preserving the
legacy-direct response shape and leaving post-turn goal evaluation in the
existing agent loop.
- #2560 shipped the queue-staging clarification in v0.51.92. The RFC now treats
`queue_message(...)` as a staged protocol method only; `/queue` remains
browser-side queue/drain behavior, and no server-side queue endpoint or queue
scheduler should be added merely for adapter symmetry.
The next gate is still not the runner/sidecar by default. Slice 3c's goal route
is shipped, and `queue_message(...)` remains a staged protocol method. Queue /
continue routing needs an explicit follow-up contract because the legacy `/queue`
path is browser-side queue/drain behavior today; no new server-side queue endpoint
or queue scheduler should be added just for adapter symmetry. If maintainers want
queue/continue to move before Slice 4, that follow-up should specify the exact
legacy entry point, response shape, and ordering/idempotency contract first.
The next gate is the runner/sidecar planning contract, not queue implementation
by default. Queue / continue routing should only move before Slice 4 if a future
maintainer decision identifies an existing server-side legacy entry point and
pins its response shape, ordering, and idempotency contract. Otherwise, keeping
`queue_message(...)` staged is the honest boundary while execution ownership
moves out of the main WebUI request process.
## Goals
@ -670,8 +673,19 @@ Non-goals for Slice 3c:
### Slice 4: Runner process / sidecar boundary
Explicitly deferred until Slice 1 has worked in production for at least one
release cycle and the adapter surface has review approval.
Slice 4 is the first gate that may move active execution ownership out of the
main WebUI request process. It should start as a docs/test contract PR before any
runner code lands. Slice 1's journal/replay layer has shipped and passed active
validation, Slice 2's default-off adapter seam has shipped, and Slice 3's
cancel/approval/clarify/goal control routing has proven the protocol-translator
pattern. Queue remains staged unless maintainers explicitly ask for a separate
pre-runner queue route.
The Slice 4 implementation must not make the adapter a new runtime surrogate.
The runner boundary may own active execution, process supervision, run lifecycle,
and callback state, but those responsibilities must be centralized behind the
adapter/runner contract rather than recreated as scattered globals in the main
WebUI server.
Scope:
@ -683,6 +697,55 @@ Scope:
Revert path: disable runner backend and fall back to journaled legacy backend.
#### Slice 4a: Runner contract gate
Before runner code lands, define a narrow contract that covers:
1. **Backend selection and rollback.** The existing `legacy-direct` and
`legacy-journal` paths remain available. Any new runner backend is
feature-flagged, default-off, and revertible by switching the adapter mode back
to `legacy-journal` without deleting sessions or journal files.
2. **Process ownership.** The runner, not the main WebUI request process, owns
`AIAgent` construction/reuse, active run execution, cancellation flags,
approval/clarify callback wait state, and post-turn continuation evaluation
for runs assigned to that backend.
3. **Durable observation.** The main WebUI server observes through
`RuntimeAdapter.observe_run(...)`, `get_run(...)`, and the journal cursor. A
WebUI restart must not be required for the runner to finish writing ordered
events and terminal state.
4. **Restart/reattach success criterion.** Start a long-running run, restart only
`hermes-webui.service`, reload the session, rediscover the active or terminal
runner-owned run, replay/catch up from cursor without duplicate transcript /
tool / reasoning state, and preserve cancel if the run is still active.
5. **Control parity.** Cancel, approval, clarify, goal status/control, and any
accepted queue/continue behavior route through adapter methods with stable
browser response shapes. Unsupported controls return bounded `ControlResult`
states instead of silently falling back to stale in-process state.
6. **Profile/workspace isolation.** Runner startup receives explicit profile,
workspace, attachments, model/provider, toolset, and source metadata rather
than relying on process-global environment mutation in the WebUI server.
Suggested contract tests before implementation:
- source/RFC tests proving Slice 4 remains feature-flagged and default-off;
- a fake-runner adapter test that simulates WebUI restart by discarding server
process-local state while preserving runner/journal state, then verifies
`get_run` and replay recover the same terminal state;
- a control-parity fixture proving unsupported runner controls return bounded
`ControlResult` values and do not fall back to legacy `STREAMS` /
`CANCEL_FLAGS` state;
- a profile/workspace payload test proving runner requests carry explicit context
fields without mutating global `os.environ` in the main WebUI process.
Non-goals for Slice 4a:
- no removal of the legacy in-process backend;
- no default-on runner mode;
- no public chat-start/status response-shape expansion;
- no new server-side queue endpoint or scheduler just for adapter symmetry;
- no dependency on Hermes Agent shipping `/v1/runs` before WebUI can validate the
local runner boundary.
## First Meaningful Success Criteria
The first meaningful milestones are deliberately split.

View file

@ -309,7 +309,22 @@ def test_rfc_distinguishes_goal_routing_from_queue_route_staging():
rfc = (routes.Path(__file__).parent.parent / "docs" / "rfcs" / "hermes-run-adapter-contract.md").read_text(encoding="utf-8")
assert "#2544 shipped the first Slice 3c implementation" in rfc
assert "#2560 shipped the queue-staging clarification" in rfc
assert "route now uses `RuntimeAdapter.update_goal(...)`" in rfc
assert "`queue_message(...)` remains a staged protocol method" in rfc
assert "`queue_message(...)` as a staged protocol method only" in rfc
assert "no new server-side queue endpoint" in rfc
assert "or queue scheduler should be added just for adapter symmetry" in rfc
assert "no server-side queue endpoint or queue\n scheduler should be added merely for adapter symmetry" in rfc
def test_rfc_defines_slice4_runner_contract_before_runner_code():
routes = importlib.import_module("api.routes")
rfc = (routes.Path(__file__).parent.parent / "docs" / "rfcs" / "hermes-run-adapter-contract.md").read_text(encoding="utf-8")
assert "#### Slice 4a: Runner contract gate" in rfc
assert "docs/test contract PR before any\nrunner code lands" in rfc
assert "feature-flagged, default-off" in rfc
assert "The runner, not the main WebUI request process, owns" in rfc
assert "restart only\n `hermes-webui.service`" in rfc
assert "profile,\n workspace, attachments, model/provider, toolset, and source metadata" in rfc
assert "no removal of the legacy in-process backend" in rfc
assert "no default-on runner mode" in rfc