#890·openwiki

Page workers skip most planned pages, and a second submit_plan is a run-killing fatal

Author: edettermanCreated Sep 16, 2026Updated Sep 16, 2026

Page workers skip most planned pages, and the planner can kill the run after it wrote a valid plan

Environment: [email protected], OPENWIKI_PROVIDER=openai-compatible against an OpenAI-compatible endpoint (OPENAI_COMPATIBLE_BASE_URL = https://ollama.com/v1), OPENWIKI_MODEL_ID = our own cloud model id, OPENWIKI_OPENAI_COMPATIBLE_STREAMING=true, OPENWIKI_MAX_OUTPUT_TOKENS=16384, OPENWIKI_TELEMETRY_DISABLED=1, openwiki code --update --print, run from GitHub Actions in a private repository (so the run URLs are not public; happy to paste logs into whichever issue you prefer).

1. Page workers exit without submitting, and the run reports success

Every dispatched update run skips a large share of the pages it planned, while the process still exits 0 and the diff is simply smaller:

plan size pages skipped logged line
11 7 It was skipped for this update and will be reconsidered on the next update
13 8 same
18 14 same
16 16 same

Read off dist/agent/repository-runner.js: runPageAgent (line 334) calls streamWorkerTools; a worker that finishes without a submit_page call (line 412) or that throws something which is not a fatalSubmissionFailure (line 411) ends in skipRepositoryPage + emitDeferredPageWarning (line 423), whose text is

<page> was restored after its worker exited without submitting. It was skipped for this update and will be reconsidered on the next update.

submitPageTool converts only invalid_input into a rejection the model can read (dist/agent/repository-runner.js:266, :373), so a worker that ends its turn early — or hits your own turn/tool budget — is dropped instead of retried or surfaced.

The fourth row is a different model id on the same repository and the same workflow, everything else equal; it skipped every planned page, so the share is partly a property of the model.

What would help:

  • a per-page skip reason in the run summary (budget exhausted vs. no submission vs. tool error), and a non-zero exit (or a documented opt-in) when a run skips pages it planned;
  • a bounded in-run retry for a skipped page, the way the planning step is retried elsewhere;
  • documentation of this behaviour: today "skipped for this update" only appears in the log, and a CI consumer has no way to tell a full refresh from a 30% refresh.

2. submitRepositoryPlan throws a fatal on the second call

Three runs on the same day failed before writing a single page:

Repository planning worker exited without submit_plan.

(two runs), and

This OpenWiki run already has a different persisted plan.

The second one is thrown by submitRepositoryPlan at dist/generation/repository-run.js:450 as RepositoryRunError("invalid_state", …). The tool boundary converts only invalid_input into a rejection the model can read, so this exception leaves the worker and the whole run exits 1 — with a valid plan on disk and zero pages generated, ~40 seconds after the heartbeat read phase=generating: 0/13 page jobs done. A model that calls submit_plan twice with a slightly different plan (an easy failure mode for a long planning turn) therefore costs the entire run instead of being told to stop.

Could that path be a tool rejection ("a plan is already installed; continue with it") rather than a fatal? It is the difference between an hour of provider tokens and a retry.

Our mitigation, for reference: we wrap the generator in a script that resets openwiki/.run.json and the generated pages to HEAD and re-runs once, only for those two failures. That is a workaround for behaviour we would rather not have to know about.