You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The fetch-streaming design (spec §4) names three byte sources. Two shipped in #790. The third has no mechanism at either end.
source
condition
status
inline
config.queue === false
works — response.body.getReader()
queued, in-process worker
handle.onStream is a function
works — job stream channel
queued, out-of-process worker
otherwise
no mechanism
Why the third one has nothing
The job stream channel is in-memory. publishStreamChunk / subscribeToStream are implemented only by InMemoryQueueStorage, which is same-process by definition — the other files matching those names are the interfaces, the telemetry passthrough, the wrapper, and tests. So on SQLite or Postgres, handle.onStream is undefined and FetchUrlTask settles the job and yields whatever the output carries. For response_type: "stream" that is { metadata } with no bytes at all.
An out-of-process worker therefore cannot return a large body without materializing it into the job output — which is precisely what this work exists to avoid.
Why it hasn't bitten
Nothing runs a genuinely out-of-process worker today. sec — the only real consumer — calls client.attach(server), so its client holds a server reference, handle.onStream is defined, and it takes the in-process path.
What was designed and not built
Worker side. The worker was to sink bytes to a shared backing and return a CacheRef on the port. Nothing calls saveOutputStreamPort or makeCacheRef outside task-graph's own cache layer and its tests.
Client side.JobQueueClientOptions.outputStreamResolver exists (JobQueueClient.ts:102, stored at :117/:178, read at :883) and is injected nowhere.
Consumer half.Stream-based response handling for FetchUrlTask #790 built the code that reads such a ref back and re-emits it as deltas, with loud failures for a missing cache or an unresolvable entry. It is correct against the contract it states and genuinely tested — but only against refs the tests construct by hand, and it is unreachable in production. It is being removed in Stream-based response handling for FetchUrlTask #790 rather than maintained as a speculative half whose design choices (e.g. preferring the deterministic cache slot over private) cannot be validated without a real worker. The loud-failure posture was right and worth keeping if this is rebuilt.
What building it would need
A shared backing both processes can reach. This is the hard part, and it is a scoping decision before it is a plumbing one: RunPrivateCacheRepo rejects foreign-run refs by design, so a cross-process ref has to live in the deterministic tier or carry an explicit shared scope. Getting this wrong reopens the cross-run leak that tier exists to prevent.
A worker-side sink in the job execution path that writes the port's bytes and returns the ref.
Injecting outputStreamResolver at the composition root.
Failure modes decided up front: no shared cache configured, entry evicted, entry written by a run this cache does not serve. Each wants a distinct message — they have opposite operator actions.
Backpressure. The in-process path paces the producer through awaited dispatch. A ref-based path has no natural gate: the worker writes to the backing at full speed regardless of whether anything is reading. That may be fine — the backing is the buffer — but it should be a decision, not an accident.
Alternative worth weighing first
Implementing a durable stream channel (publishStreamChunk / subscribeToStream on the SQLite and Postgres queue storages) would make the existing in-process path work across processes unchanged, and reuse the backpressure and ordering that already exist. That is plausibly less new surface than the ref path, at the cost of putting body chunks through the queue storage. Worth comparing before building the ref design, rather than treating the ref approach as settled because it was written down first.
When this matters
The first deployment that runs workers in a separate process or on a separate machine against a durable queue. Until then the in-process path covers every real caller.
The fetch-streaming design (spec §4) names three byte sources. Two shipped in #790. The third has no mechanism at either end.
config.queue === falseresponse.body.getReader()handle.onStreamis a functionWhy the third one has nothing
The job stream channel is in-memory.
publishStreamChunk/subscribeToStreamare implemented only byInMemoryQueueStorage, which is same-process by definition — the other files matching those names are the interfaces, the telemetry passthrough, the wrapper, and tests. So on SQLite or Postgres,handle.onStreamis undefined andFetchUrlTasksettles the job and yields whatever the output carries. Forresponse_type: "stream"that is{ metadata }with no bytes at all.An out-of-process worker therefore cannot return a large body without materializing it into the job output — which is precisely what this work exists to avoid.
Why it hasn't bitten
Nothing runs a genuinely out-of-process worker today.
sec— the only real consumer — callsclient.attach(server), so its client holds a server reference,handle.onStreamis defined, and it takes the in-process path.What was designed and not built
CacheRefon the port. Nothing callssaveOutputStreamPortormakeCacheRefoutsidetask-graph's own cache layer and its tests.JobQueueClientOptions.outputStreamResolverexists (JobQueueClient.ts:102, stored at:117/:178, read at:883) and is injected nowhere.deterministiccache slot overprivate) cannot be validated without a real worker. The loud-failure posture was right and worth keeping if this is rebuilt.What building it would need
RunPrivateCacheReporejects foreign-run refs by design, so a cross-process ref has to live in the deterministic tier or carry an explicit shared scope. Getting this wrong reopens the cross-run leak that tier exists to prevent.outputStreamResolverat the composition root.Alternative worth weighing first
Implementing a durable stream channel (
publishStreamChunk/subscribeToStreamon the SQLite and Postgres queue storages) would make the existing in-process path work across processes unchanged, and reuse the backpressure and ordering that already exist. That is plausibly less new surface than the ref path, at the cost of putting body chunks through the queue storage. Worth comparing before building the ref design, rather than treating the ref approach as settled because it was written down first.When this matters
The first deployment that runs workers in a separate process or on a separate machine against a durable queue. Until then the in-process path covers every real caller.