commit f0c61a10bc43afecc58d18f97b016ba0c0c78ddb
parent 4cfd0ed0e6bf4249279f7da7fca97ef03dc39e29
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Sat, 26 Sep 2026 14:43:13 -0400
mcp: fetch_clip keeps the job id when the editor stops answering mid-poll (review S1)
A network error while polling used to answer "Could not reach the editor"
with no job id, so the natural retry was the same call again, which for
full: true queues a second whole-recording download (the saved-video
pointer is checked only when a request arrives). The poll-phase outcome
carries the jobId, and the text says to call again with job: "<id>" and
not to repeat the original request. Test: POST 202, poll throws, then the
named resume polls once and posts nothing.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
2 files changed, 47 insertions(+), 2 deletions(-)
diff --git a/mcp/src/fetchClip.test.ts b/mcp/src/fetchClip.test.ts
@@ -568,3 +568,34 @@ test("a resumed whole-recording job renders as one (no from/to in its answer)",
assert.match(r.text, /^Fetched the whole recording \(job j2, 0s waited\)\./);
assert.ok(!/window:/.test(r.text));
});
+
+test("the editor dropping out WHILE POLLING hands back the job, and says not to repeat the request", async () => {
+ // Full mode is where it matters: a repeated full: true would queue a second
+ // whole-recording download, because the saved-video pointer is only checked
+ // when a request arrives.
+ const { deps, calls } = editor(
+ seq(
+ { status: 202, body: { cached: false, jobId: "j3", file: null, from: 0, to: 0 } },
+ new Error("socket hang up"),
+ ),
+ );
+ const outcome = await fetchClip(fullRequest(), deps);
+ assert.deepEqual(calls.map((c) => c.method), ["POST", "GET"]);
+ const r = renderFetchClip(outcome, CTX);
+ assert.deepEqual(r, {
+ text:
+ "Could not reach the editor at http://editor.test while polling job j3: socket hang " +
+ 'up. Is it running? Call fetch_clip again with job: "j3" — do not repeat the original ' +
+ "request, the fetch may still be running.",
+ isError: true,
+ });
+ // …and the call it names is a resume: one poll, no second POST.
+ const again = editor(
+ seq({ status: 200, body: { status: "done", jobId: "j3", file: "/corpus/saved-videos/x.mkv", bytes: 9 } }),
+ );
+ const v = validateFetchClipArgs({ job: "j3" });
+ assert.ok(v.ok);
+ const resumed = renderFetchClip(await fetchClip(v.request, again.deps));
+ assert.deepEqual(again.calls.map((c) => c.method), ["GET"]);
+ assert.match(resumed.text, /^Fetched the whole recording \(job j3, 0s waited\)\./);
+});
diff --git a/mcp/src/fetchClip.ts b/mcp/src/fetchClip.ts
@@ -280,7 +280,11 @@ export type FetchClipOutcome =
full?: boolean;
}
| { kind: "failed"; jobId: string; status: string; log: string }
- | { kind: "unreachable"; url: string; message: string };
+ // `jobId` when the editor stopped answering WHILE POLLING: the job was
+ // queued and may still be running, so the answer must hand back the id to
+ // resume with. Repeating the original request instead would queue a second
+ // fetch — for full: true, a second whole-recording download.
+ | { kind: "unreachable"; url: string; message: string; jobId?: string };
type Json = Record<string, unknown>;
@@ -345,7 +349,7 @@ export async function fetchClip(
{ method: "GET", headers },
);
if (res instanceof Error) {
- return { kind: "unreachable", url: editor!.url, message: res.message };
+ return { kind: "unreachable", url: editor!.url, message: res.message, jobId };
}
const body = await readJson(res);
if (res.status < 200 || res.status >= 300) {
@@ -613,6 +617,16 @@ export function renderFetchClip(
isError: true,
};
case "unreachable":
+ if (outcome.jobId) {
+ return {
+ text:
+ `Could not reach the editor at ${outcome.url} while polling job ` +
+ `${outcome.jobId}: ${outcome.message}. Is it running? Call ` +
+ `fetch_clip again with job: "${outcome.jobId}" — do not repeat the ` +
+ `original request, the fetch may still be running.`,
+ isError: true,
+ };
+ }
return {
text: `Could not reach the editor at ${outcome.url}: ${outcome.message}. Is it running?`,
isError: true,