Page worker ending without submit_page is skipped with no retry; run finalizes interrupted and the documented base never advances
Description
A page worker whose stream ends without calling submit_page is marked skipped, its
snapshot restored, and the run finalizes with status: "interrupted" — without advancing the
documented base (gitHead in openwiki/.last-update.json) and with openwiki/.run.json
deleted. Neither 0.4.3 nor 0.5.0 retries the page. The next --update therefore diffs against
the same old base and re-researches the entire changeset again.
For a tool whose architecture is "resumable page jobs, partial progress banked", the missing retry turns a single unlucky final turn into a full-cost regeneration loop.
Observed trigger
A reasoning model that spends its entire completion budget on thinking and returns an empty final
message. With glm-5.3-flash (via an Anthropic-compatible endpoint), one failing worker made
94 clean LLM calls, then ended its stream with no text and no tool call — the final turn's
AIMessage content blocks are thinking only (diagnosed by pulling the worker's turns from
LangSmith and printing the content-block kinds; healthy turns are
thinking,tool_use / thinking,text,tool_use). The agent loop simply ends; nothing throws.
Measured cost
Over four consecutive daily runs (2026-09-01..04), each ended interrupted on one such worker,
the base stayed frozen at the same commit, and every run re-researched the full 74-file changeset
— 8h, then 13h × 3. The completed pages were banked each time, but the un-advanced base means
the next run re-derives them anyway. The first run to finalize cleanly took 1.7h for what the
frozen-base runs were paying 13h.
A secondary observation, possibly by design but worth a look: a run that finalizes interrupted
deletes .run.json, so "rerunning after an interruption resumes the durable queue" holds only
for hard kills, not for finalized-interrupted runs — there is nothing left to resume, and the
skipped page's work is fully discarded.
Suggested fix
One fresh retry of the page worker before marking it skipped — a new agent instance over the same page job. Transient empty final turns then cost one retry instead of the whole page; the deterministic case still skips as today.
We carry a local patch implementing exactly this (rewrites runPageAgent to construct the
worker agent per attempt and retry once when the stream ends without submit_page, preserving
the skip path when the retry also fails) and would be glad to turn it into a PR against either
line if the approach sounds right.
Source: langchain-ai/openwiki