| Field | Value |
|---|---|
| Date | 2026-09-13 |
| Harness version | 2026-09-13-profiled-context-patch-edit-1 (another session, not logged) → 2026-09-13-reasoning-continuation-3 |
| Models | observed: qwen3:4b-thinking → qwen3:8b (switched during the run). Measured: qwen3:8b on local Ollama. Tests: scripted clients with real capability profiles |
| Evidence grade | B (controlled live measurement of served context) + C (tests) + D (journal) |
| Pre-registered | yes: observation, mechanism hypotheses and baseline recorded before the code change |
| Source record | journal turn 120, run 389; RL-014 |
Observation
After the context default was raised to a per-model profile (8,192 for qwen3) by
a concurrent, unlogged change, the user sent a third continue on the
unfinished 5,532-char dogs draft (RL-014).
| Field | Value |
|---|---|
| Turn / run | 120 / 389, 2026-09-13 00:39:19 UTC |
| Elapsed | 193,324 ms |
| Run model (at start) | qwen3:4b-thinking |
| Turn model (at end) | qwen3:8b |
| Outcome | error: "the model spent its reply budget on reasoning and returned no answer" |
agent_runs.failure |
null |
| Visible reasoning | ≈ 10,000 chars: debating "10 different pages" vs sections, re-deriving the nav ids from the head, then retyping the whole file inside the reasoning, cut off in the 8th section |
/api/ps after the run: qwen3:8b context_length=4096, although the harness
resolved 8,192.
Five defects, found by reading the code paths involved:
- Whole rewrite selected. The fit test compared reply room (≈ 4,024 allowed: 3,000 code reply + 1,024 reasoning reserve; derived from the budget formula, not logged) with the draft (≈ 2,588 needed). It "fitted", so the tail mode of RL-014 was not used — but the reasoning alone was ≈ 2,500–3,000 tokens.
- Keep-warm reloads at 4,096.
ping()used/v1/chat/completions, which cannot carrynum_ctx. - Category lost twice. The code path's
catchcalledfinishwithout a failure; separately the desktop host's secondfinishRun(when website checks were planned) wrotefailure = nullover whatever was recorded. - Mislabelled category. The client raised reasoning exhaustion as
context_overflow, advisingLOCAL_LLM_MAX_REPLY_TOKENS, which the code path does not use. - Attribution. The turn recorded
llm.modelat the end of the run; the model was switched mid-run.
Hypotheses
- H1 — A compatibility-endpoint ping makes Ollama serve the model at its 4,096 default, and a native load with
num_ctxrestores 8,192; alternating between them forces a full reload each time. Refuted if/api/pscontext does not change with the request type. - H2 — For a reasoning model, continuing from the tail (remainder only) instead of rewriting the draft removes the draft-sized output cost that exhausted the budget. Refuted (at grade C) if the prompt still carries the whole draft; at grade B if live continuations still exhaust at a similar rate.
- H3 — A computed outline (ids written, in-page links with no section, classes) supplies the structure the model spent reasoning to recover. Refuted if the outline misses the missing sections on a representative draft.
- H4 — Classifying in the code path and passing the failure through the host's second
finishRungives every such run a non-null category.
Baseline
| Measure | Before |
|---|---|
| Served context after keep-warm ping | 4,096 (live, below) |
| Continuation mode, reasoning model, rewrite nominally fits | whole rewrite |
agent_runs.failure for run 389 |
null |
| Failed runs with no category (journal, all history) | 20/25 (RL-007 snapshot) |
Intervention
LocalLlmClient.ping()— native Ollama:POST /api/generatewith an empty prompt andoptions.num_ctx; other servers unchanged.AgentRunner—tailModewhen continuing an unfinished draft and (the model reasons —thinking: always, oroptionalwith thinking switched on — or a rewrite cannot fit).LlmClient.thinkingRequestedadded (optional).CodeConversation.draftOutline+buildContinuationMessages(…, outline)— bounded to three lines of ≤ 300 chars; "keep the existing structure, ids and class names".failures.ts—reasoning_budget_exhaustedcategory,explainand fix text,classifypattern; client raises it; code-pathcatchpassesclassify(err).agentHost— turn model captured at run start;setModelrefused while a run is active; secondfinishRuncarriesanswer.failure.- Unchanged: whole-file continuation for non-reasoning models with room; patch edits for complete drafts (the other session's change).
Method
- Live, controlled (H1): local Ollama,
qwen3:8b, alternating requests,/api/psread after each: compatibility ping (max_tokens: 1) → native/api/generate(num_ctx: 8192) → compatibility → native. n = 2 per request type. - Harness (H2–H4):
tests/reasoningContinuation.test.tswith scripted clients carrying real capability profiles and 6,000 tokens of room (so only reasoning can select tail mode);tests/ollamaNative.test.tsping body. - Full suite, typecheck, build.
Results
H1 — served context (live)
| Step | Request | context_length |
Wall time |
|---|---|---|---|
| 0 | (state after run 389) | 4,096 | — |
| 1 | compatibility ping | 4,096 | 0.1 s (no reload) |
| 2 | native load, num_ctx 8,192 |
8,192 | 8.0 s |
| 3 | compatibility ping | 4,096 | 9.4 s |
| 4 | native load, num_ctx 8,192 |
8,192 | 11.2 s |
Every switch between request types reloaded the model (8–11 s). With keep-warm pinging every 4 minutes during use, a real request after a ping paid a reload and a ping after a real request undid the profiled window.
H2/H3 — harness
| Case | Prompt | Result |
|---|---|---|
qwen3:4b-thinking (always), room for rewrite |
tail + outline (links with no section yet: border) |
draft joined, complete |
qwen3:8b, thinking on |
tail + outline | draft joined, complete |
qwen3:8b, thinking off, room |
whole draft | unchanged behaviour |
qwen2.5-coder:7b, room |
whole draft | unchanged behaviour |
Outline for the test draft: ids already written: labrador, beagle /
links with no section yet: border / classes in use: breed.
H4 — a thrown reasoning-exhaustion error in the code path is recorded with
category reasoning_budget_exhausted; explain names the model.
Suite — 683 tests, 0 failures (5 new); typecheck clean; build passes.
Threats to validity
- H2's live effect is unmeasured. A reasoning model may still reason at length about a tail; tail mode removes the draft-sized output, not the reasoning.
- Which context run 389 actually received is unknown. The request sent 8,192, but a keep-warm ping could have left the model at 4,096 before or after;
/api/pswas read only afterwards. - Run 389 changed model mid-run; its behaviour cannot be attributed to one model.
- The
agentHostchanges (attribution, switch guard, failure pass-through) are covered by typecheck only, not by a host test. - The concurrent
profiled-context-patch-edit-1change (context default, patch edits) is unlogged; turns under it mix both sessions' changes. - n = 2 for the live measurement; one machine, one Ollama version.
Conclusion
- H1 supported (B).
- H2 supported for prompt shape (C); effect on exhaustion not tested live.
- H3 supported on the test draft (C).
- H4 supported in the runner (C); host pass-through by construction only.
Follow-ups
- Live: restore turn 117's draft;
continueonqwen3:4b-thinking,qwen3:8b(thinking on) andqwen2.5-coder:7b, n = 5 each; record outcome, reasoning tokens (servereval_count), elapsed, seam validity, verifier pass count. - After a week of use, snapshot failure categories: expect no new
unrecordedorunknownfor reasoning exhaustion. - Consider salvaging a fenced block from reasoning when the answer is empty, gated by the same no-progress and verifier checks — only with live evidence it helps.
- Log the concurrent context-profile and patch-edit change as its own entry.
Reproduce
npm test
npm run typecheck
Invoke-RestMethod http://127.0.0.1:11434/api/ps