A well-formed provider stream that ends with finish_reason stop and zero
content blocks previously became a successful empty assistant message: the
turn completed silently, and drivers like goal-session counted the no-op
round. Both adapters now map that degenerate completion to a finish
{kind:'error'} with the new canonical EMPTY_RESPONSE code from dsh-llm, and
dsh-llm-retry adds the code to its default retryable set, so the existing
closed-step recovery path retries it and fails loud once the budget is
exhausted.
Covered by adapter unit tests, an llm-retry default-policy test, and a new
authored keyless ACP snapshot (empty-response-retry) with a deterministic
1 ms zero-jitter retry overlay.
BootHostOptions.llm: 'deepseek' | false — false mounts no adapter, boots
keyless, and leaves the llm capability seam open for the embedder to fill
on RunningHost.ctx (now the third sanctioned ctx use, JSDoc + README
amended); an unfilled seam fails loud with NO_ADAPTER at the first stream.
dsh-llm-replay grows two additive surfaces for the web browser e2e lane:
paceMs (validated per-chunk delay so a real transport shows incremental
delivery; abort during a pace wait cancels promptly) and a ReplayHandle
return — dispose() plus assertConsumed(), the teardown check that every
recorded script bound and drained, converting silent fixture underruns
into diagnostics. Existing callers updated; config catalog regenerated.
Design study for a deterministic, keyless browser e2e lane over the real
assembled web chain (chromium -> SSE/HTTP wire -> apiproxy -> agent loop ->
persistence), replayed through dsh-llm-replay from recorded session-log
fixtures, with aria-tree goldens plus in-process world-state assertions.
Synthesized from an OSS prior-art survey (LibreChat, ai-chatbot, lobe-chat,
OpenHands, cline, aimock...), a repo seam deep-dive, and three adversarial
critiques (doctrine, flakiness, YAGNI). Records the settled shape (no new
package, no suite factory, seed via the real persistence API, whenIdle
barrier stack, no transient-DOM assertions) and the open questions (LLM
seam, Loader-izing dsh web, header pin, golden breadth, settled signal).
The master merge introduced a tui goal-restore test and the guard parent
README that still used the removed context/message event. Point both at
the coalesced plugin-sourced user/message.
- Restrict node-24-coverage to same-repo PRs so fork-originated code can
never reach the self-hosted runner (defense in depth; the repo is
private with forking disabled today).
- Revert the pnpm cache path to the literal /home/runner/... save-side
path: actions/cache hashes the path into the cache version, so the ~
variant could never match the cache saved by the master lane. On
self-hosted the persistent local pnpm store covers warm installs.
- Drop the incorrect 'does not gate merges' claim: node-24-coverage is
needed by all-checks-passed. Pool capacity notes moved into comments.