From f2c5048741bc8e93efb86d8456efef3faff9cb88 Mon Sep 17 00:00:00 2001 From: Michael Lam Date: Sun, 17 May 2026 09:58:19 -0700 Subject: [PATCH] docs(runtime): define cancel control gate --- CHANGELOG.md | 4 ++ docs/rfcs/hermes-run-adapter-contract.md | 64 ++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5086dcc..d5a549fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Documentation + +- **PR #2469** by @Michaelyklam (refs #1925) — Advance the runtime-adapter RFC after the Slice 2 seam shipped by marking Slice 2 complete and defining the first Slice 3a cancel-control gate. The new gate scopes Stop Generation through `RuntimeAdapter.cancel_run(...)` only, pins behavior-preserving cancellation, journal/status coherence, idempotent duplicate cancel, and explicit non-goals for approval/clarify, queue/goal, runner/sidecar, and public chat-start response changes. + ## [v0.51.84] — 2026-05-17 — Release BH (stage-377 — 1-PR Docker hygiene — agent-image upgrade docs + read-only WebUI source mount + chown prune widening) ### Changed diff --git a/docs/rfcs/hermes-run-adapter-contract.md b/docs/rfcs/hermes-run-adapter-contract.md index a34f70fa..f2310d66 100644 --- a/docs/rfcs/hermes-run-adapter-contract.md +++ b/docs/rfcs/hermes-run-adapter-contract.md @@ -292,9 +292,11 @@ Success criterion: ### Slice 2: Adapter interface over the journaled legacy path -Status as of 2026-05-16: ready for a planning/adapter-seam PR after the active -Slice 1 validation pass and the #2313 selected-session stream cap. Slice 2 should -still be a reversible boundary change, not a sidecar or execution-ownership move. +Status as of 2026-05-17: shipped. PR #2416 defined the adapter-seam contract, +PR #2424 added the default-off `LegacyJournalRuntimeAdapter` seam, and PR #2438 +kept `/api/chat/start` response shape identical between `legacy-direct` and the +flagged `legacy-journal` path. Slice 2 remains a reversible boundary change, not +a sidecar or execution-ownership move. Scope: @@ -405,6 +407,13 @@ execution-survives-WebUI-restart gate remains deferred to Slice 4. ### Slice 3: Control migration +Status as of 2026-05-17: not started. Slice 3 should begin with cancel only. +Cancel is the smallest control-plane migration because it already has one clear +browser affordance, one active-run target, and an existing legacy handler to +delegate to. Approval, clarify, queue/continue, and goal are intentionally held +behind a successful cancel-control slice because they carry more callback and +state-lifetime risk. + Scope: - move cancel first, @@ -416,6 +425,55 @@ Scope: Revert path: per-control feature flags or route-level fallback to legacy control handlers. +#### Slice 3a: Cancel control gate + +The first control migration should route Stop Generation through the +`RuntimeAdapter.cancel_run(...)` seam while preserving the current legacy cancel +semantics. It should not introduce a new cancellation registry, worker-owned +signal table, or sidecar boundary. During this slice, `cancel_run` is still a +protocol translator over the existing cancellation path. + +Acceptance properties: + +1. **Same visible result as legacy Stop Generation.** A cancelled turn still + emits one terminal cancelled/interrupted state and preserves any already + streamed partial assistant content according to the existing cancellation + contract. +2. **Adapter flag is behavior-preserving.** With + `HERMES_WEBUI_RUNTIME_ADAPTER=legacy-journal`, Stop Generation uses + `RuntimeAdapter.cancel_run(...)`; with the default `legacy-direct` path, the + current route remains available as fallback. +3. **No new runtime-surrogate state.** The implementation must not add a second + `CANCEL_FLAGS`-like map, cached `AIAgent` table, long-lived queue, or local + callback registry inside the main WebUI process. +4. **Journal/status coherence.** After cancellation, replay and session reload + classify the turn as cancelled/interrupted rather than stale/unknown, and the + terminal state is visible through the same journal/session diagnostic surface + used by Slice 1. +5. **Idempotent duplicate cancel.** Repeating cancel for the same run should be + safe: one terminal result is recorded, later attempts return a bounded + `ControlResult` such as `not-active` rather than creating extra terminal + events or resurrecting stale stream state. + +Suggested regression coverage: + +- a route/source test proving the flagged cancel path calls the adapter seam and + the default path remains the legacy route; +- an adapter unit test proving `cancel_run` delegates exactly once and returns a + bounded `ControlResult` for unsupported/not-active runs; +- an existing cancellation preservation suite run (for example the partial-output + and cancelled-turn status tests) under the adapter flag or an equivalent + synthetic harness; +- a replay/session-load assertion that a cancelled run's terminal state remains + classifiable from the journal/session surface after reload. + +Non-goals for Slice 3a: + +- no approval or clarify migration; +- no queue/continue or goal migration; +- no runner process, sidecar, or execution-survives-WebUI-restart claim; +- no public `/api/chat/start` response-shape expansion for adapter-only fields. + ### Slice 4: Runner process / sidecar boundary Explicitly deferred until Slice 1 has worked in production for at least one