fix(workbench): bound producer / completed-envelope render history end to end - #715
Merged
ScriptedAlchemy merged 9 commits intoSep 6, 2026
Merged
Conversation
🦋 Changeset detectedLatest commit: 83e90af The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
cursor Bot
pushed a commit
that referenced
this pull request
Sep 6, 2026
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
ScriptedAlchemy
marked this pull request as ready for review
September 6, 2026 21:33
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
ScriptedAlchemy
added a commit
that referenced
this pull request
Sep 6, 2026
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
ScriptedAlchemy
force-pushed
the
cursor/bound-invocation-render-history-02ec
branch
from
September 6, 2026 22:55
46b4383 to
678f00e
Compare
One retention policy (route-invocation-render-history.ts: 256 events, 2 MiB, newest and newest document-bearing event pinned) now governs the live replay buffer, the completed and cancelled envelopes, history reads, the final stream message, and the Workbench live window. The child no longer returns the event stream over IPC and the production producer keeps only the complete document. Envelopes carry a retention account when events were evicted. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
…replay The production worker host dropped the route's progress messages the dispatcher was ready to turn into progress render events; forward them as the generated CLI session does. The invocation SSE queue now derives its limits from the render-history window so a backpressured socket can still replay a whole retained record. The dev-server test drives a compiled tool through 400 progress ticks of 16 KiB and checks every reader sees the same bounded window. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
…, honest bounds Failed envelopes now carry the same retained window and latest document as cancelled ones, so replay and envelope agree after eviction. The child awaits each render IPC send so the channel paces a fast producer. The browser schema rejects a retention account that does not match the events beside it. Bound prose states the pinned-event allowance. Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
…ion, pinned-newest module test, history ring, HTTP full-window replay
…dow, state the bound precisely
ScriptedAlchemy
force-pushed
the
cursor/bound-invocation-render-history-02ec
branch
from
September 6, 2026 23:09
678f00e to
83e90af
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #681. Survivor of the duplicate pair #699 / #715 (#699 closed; its unique tests are ported here).
What changed
One retention policy now governs every retained copy of a route invocation's render stream (
packages/agent-bundle/src/dev/routes/route-invocation-render-history.ts):shell/replace/complete) are pinned, so a reader always folds to a coherent latest document after eviction; those two alone may carry a window past 2 MiB (≤ 2 MiB + 2 KiB for runtime-produced streams, whose events are capped at 1 MiB + 1 KiB).events,document),GET /api/routes/invocations/<id>, the terminalfinalstream message, and the Workbench live window (reduceInvocationState), which imports the same pureretainRenderEventthroughcontracts/invocations.ts. The reducer stays pure: the window is an immutable value and each push returns a new one.RouteInvocationChildResultno longer carriesevents; the child publishes each event as arenderIPC message and awaits the send, so the channel paces a fast producer.renderCompiledkeeps only thecompletedocument. Nothing can rehydrate what the live buffer evicted.retention: { producedEvents, evictedEvents, evictedBytes, retainedBytes }; the stream still sendstruncatedonce.document,result,outcome, andcorrelationIdare never truncated (the runtime'smaxDocumentBytesbounds the document before it reaches the service). Cancelled and failed runs keep the retained window and the latest document it reached. The Raw tab shows the account; the browser schema rejects an account that does not match the events beside it.streamFromWorkerdropped the worker'sprogressmessages although the dispatcher was ready to turn them intoprogressrender events; they are now forwarded the way the generated CLI session does. Without this no compiled route could produce more than a handful of render events, so the acceptance criterion could not be met.Tests
route-invocation-service.test.ts: byte-bound eviction with 300 KiBreplacesnapshots + pinned latest document; final envelope /read()/ stream replay parity over 1,001 events; cancel after the shell was evicted (601 events); child failure after eviction; a real forked child (unit-render) rendering 400 progress reports plus five 150 KiB Suspense chunks, asserting boundedevents,retainedBytes ≤ 2 MiB, and identical replay.route-invocation-dev-server.test.ts: a compiled tool reporting 400 × 16 KiB progress ticks (6.4 MiB) through the foreground server; POST envelope, GET read, stream replay (truncatedfirst, same events, samefinal), and list summaries all agree.invocation-model.test.ts,invocation-client.test.ts,route-workspace.test.ts: pinned document across 400 progress events, byte-bound live window, strict decoding ofretention.route-invocation-dev-server.test.ts— a compiled CLI route (cli:flood, 300 Suspense boundaries settling one per macrotask) through the foreground server: byte-boundreplacesnapshots, GET/replay parity, mid-run reconnect after the shell was evicted, and cancel after eviction with the pinned latest document; newroute-invocation-retention.test.ts— pinned newest/document entries survive oversized events, history-ring eviction drops the replay with the record, and a full count-bounded replay overRouteInvocationRoutesthrough a real HTTP socket.runtime-backend.test.ts: a 301-event runtime run document folds to the same window withretention.Not covered here
cli:floodtest covers it through the compiled CLI surface, which streams.unit-renderstill collects the full stream inside the disposable child (renderRouteEventsin the public test harness) before forwarding; it is bounded by the runtime's ownmaxEvents/maxEventBytesand never reaches the service or IPC as a whole. A streaming harness primitive would be a publicagent-bundle/testAPI change.audiobook-curator.acceptance.e2e.test.tswas not extended: the public example has no long-stream route andexamples/*are user-facing products.Coordination with #684 and #718
renderCompiledinroute-invocation-production.ts(removes the candidate loop; this PR drops theeventsarray). Whichever merges second keeps fix(workbench): bind production route executables from the artifact manifest, fail closed (#680) #684's single-worker structure, tracksdocumentfrom thecompleteevent, andawaitspublishRender.streamFromWorker.executeand the dev-server test; resolution posted on fix: stream generated MCP/event Flight bytes before render completion #718: keep itscancelhook, this PR'sAgentRenderDispatchsignature andprogressentry field, both test additions.Gate
Run on Node 22.23.2 in
/fast/projects/agent-bundle-wt/i715at the final head, rebased ontoorigin/main@ a057fdf (#718), with the two predicted #718 conflicts resolved as posted on #718 (kept #718'scancelhook and message set plus this PR'sAgentRenderDispatchsignature andprogressentry; kept #718'sinvocationMessagesstream reader and rewrote the portedcli:floodtest on it):*
route-invocation-service.test.ts :: resolves a.jsimport of a.tsxsibling…— a pre-existing real-compile test, untouched here — hit its 30 s timeout with "no expect assertions completed" while the machine's load average was 60–95 (concurrent gates in other worktrees); the file passes 32/32 on every standalone rerun (three times this session). #718's gate log records the same load timeout onmain.Previous full runs of the same gate before the #718 rebase (at fa2a871 and 678f00e on top of #720): unit 4417/0, integration 148/0 and 149/0, docs ok.
Deslop: Claude Fable 5.1, 4 edits (flattened
cancelledInvocationto an expression body, removed a redundantlatestDocumenthelper in favour of the sharedretainedHistory, dropped a stale "Type-only" header comment, replaced the Workbench's duplicatedocumentForwith the sharedretainedLatestDocumentand fixed the Raw-tab note that called a cancelled run's document "final").Self-review
Reviewer:
gpt-5.6-sol-high(generalPurpose subagent), two passes against the diff vsorigin/main.process.sendfor render events was fire-and-forget, so a fast producer could pile up in the child's IPC queueforwardRenderEventreturns the send promise;renderCompiledandrenderUnitRouteawait each eventunit-renderstill collects the whole stream in the child viarenderRouteEventsfailedInvocationuses the record's window; new testevents; fixture was inconsistentretentionMatchesEventsrefine, fixture corrected, mismatch case addedSecond pass: no findings.
Reviewer (post-dedupe):
gpt-5.6-sol-max(change-risk-reviewer), two passes against the diff vsorigin/mainafter porting #699's tests.unit-renderstill collects the whole stream in the child (renderRouteEvents)agent-bundle/testAPI change (recorded above)errorcan drop a report still awaiting the dispatcher's queueinvocationForRun) builtRouteInvocations from whole run documents without the windowretainRenderEvent, usesretainedLatestDocument(replacing the duplicatedocumentFor), attachesretention; test added#streampaces replay one frame per socket drain; live frames arriving during replay wait behind it under their own byte/record accounting (ported from #699)retainedBytesSecond pass: no defects in the pump or the runtime-backend fold; #2 re-raised and dismissed as above, #6 wording completed.