jcp-gateway: a task's exchanges and an interim result's retry are decided as continuations of the call #336
Merged
dcharlot
merged 2 commits from 2026-10-19 11:04:50 -04:00
fix/jcp-gateway-gates-tasks-and-mrtr into main 2026-10-19
jcp-gateway: the receipt-decoding test helper keeps its doc comment
All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 5s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 40s
standards workspaces build + test / mesh-rs (mesh/mesh-rs, -p mesh-engine-gguf -p mesh-openai --features mesh-engine-gguf/gguf,mesh-openai/server, mesh-rs, linux-arm64) (pull_request) Successful in 1m2s
standards workspaces build + test / jouleclaw-rs (-nodebug, jouleclaw/jouleclaw-rs, -p jouleclaw-energy --features rapl,nvml, jouleclaw-rs, linux-arm64, -C target-feature=+fp16) (pull_request) Successful in 3m6s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 3m17s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 6m58s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 2m32s
standards workspaces build + test / jcp-rs (jcp-conformance, jcp/jcp-rs, -p jcp-energy --features nvml, jcp-rs, linux) (pull_request) Successful in 1m41s
standards workspaces build + test / joule-ui-rs (joule-ui/joule-ui-rs, joule-ui-rs, linux) (pull_request) Successful in 1m13s
standards workspaces build + test / joulehook-rs (joulehook/joulehook-rs, joulehook-rs, linux) (pull_request) Successful in 1m20s
standards workspaces build + test / sandbox-rs (sandbox-conformance, sandbox/sandbox-rs, sandbox-rs, linux) (pull_request) Successful in 2m26s
The task tests were inserted between jcr1_payload's doc comment and the function, so the comment documented the first constant after it and clippy (-D warnings, macOS meter job) refused the empty line after it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
jcp-gateway: a task's exchanges and an interim result's retry are decided as continuations of the call
Some checks failed
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 54s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 7s
standards workspaces build + test / mesh-rs (mesh/mesh-rs, -p mesh-engine-gguf -p mesh-openai --features mesh-engine-gguf/gguf,mesh-openai/server, mesh-rs, linux-arm64) (pull_request) Successful in 1m24s
standards workspaces build + test / jouleclaw-rs (-nodebug, jouleclaw/jouleclaw-rs, -p jouleclaw-energy --features rapl,nvml, jouleclaw-rs, linux-arm64, -C target-feature=+fp16) (pull_request) Successful in 3m15s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 3m30s
standards workspaces build + test / jcp-rs (jcp-conformance, jcp/jcp-rs, -p jcp-energy --features nvml, jcp-rs, linux) (pull_request) Successful in 1m38s
standards workspaces build + test / joule-ui-rs (joule-ui/joule-ui-rs, joule-ui-rs, linux) (pull_request) Successful in 1m11s
standards workspaces build + test / joulehook-rs (joulehook/joulehook-rs, joulehook-rs, linux) (pull_request) Successful in 1m10s
standards workspaces build + test / macOS energy backends (pull_request) Failing after 5m58s
standards workspaces build + test / sandbox-rs (sandbox-conformance, sandbox/sandbox-rs, sandbox-rs, linux) (pull_request) Successful in 2m28s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 4m8s
MCP 2026-07-28 lets a gated call answer before its work is done, and both ways it does so went around the gate. A task handle (resultType "task"): the client fetches the outcome with tasks/get, answers the server with tasks/update, stops it with tasks/cancel. All three passed through as discovery. The outcome, which is the tool's actual output, reached the model unmetered and unsealed and was never admitted to the flow store, so a value read through a task could be sent on by a later call with nothing to fold it against. And any task id at all was relayed, whoever's it was. An interim result (resultType "input_required"): the client repeats the call with its answers in inputResponses. Only `arguments` were folded, so an answer carrying a value from an untrusted read reached the server past the flow ceiling. Now: - A task handle is bound to the grant of the call it came back from. Each tasks/* exchange naming it is decided against that grant at the moment it is made (revoked, expired or spent refuses it), metered, debited, and sealed over a payload that commits to the receipt it continues. Its reply is admitted as untrusted data under the join of everything the client has sent the task. The request sent upstream is rebuilt from the fields decided over, so nothing else the client adds reaches the server. - The tasks/get that brings the outcome closes the task: one zero-cost result frame (tool-result, data) sealed over the content address of a closing record that names the opening receipt, every exchange's receipt, the joules metered across them and the outcome's digest. The record is Partial: the server works between exchanges, in no span the gateway reads. - A tasks/* exchange naming a task this gateway did not open, or has closed, is refused, sealed, and never reaches the server. - A retry's answers (inputResponses, requestState) are part of its sealed payload and are flow-checked with the arguments. The payload commits to the receipt of the interim result it answers. - Handles the server minted (a task id, a request key, a requestState) are left out of the flow check only when they are an exact echo of what the gateway saw the server mint. A 36-byte UUID would otherwise taint every poll under a ceiling, since the scan window is 16 bytes. A handle the client made up is checked. - server/discover carries the advertisement, as initialize does. The advertisement lists the task methods and `continuations: 1`. - Open tasks and pending interim results are bounded (1024 each). SPEC §6.1 states the rule in protocol-neutral terms. §10 gains a row for it. Known limits, stated in the code: - Open tasks are kept in memory. After a restart, polls of earlier tasks are refused (fail closed). - Work the server does between exchanges is not metered. - Server-pushed notifications/tasks are outside the request/response relay. Tests: 12 new, against scripted upstreams. All 19 mutations of the new logic are killed. Co-Authored-By: Claude Opus 5.5 <[email protected]>