Field notes
Asked a Question, Got a Status Report
My agent harness answered a person's question with a worker status. VanillaCore Harness core #142 now puts questions ahead of background work and steers them into a busy turn, without aborting work that may already have side effects.
An agent that runs background work all day has to decide, many times an hour, whose turn comes next: the worker that just finished, the scheduled job that is due, or the person who typed a question into the chat. On Wednesday morning my harness made that decision badly. A person asked it a question in a chat at a time when results from a background worker kept coming in, and what came back was a worker status instead of an answer.
The fix landed in the VanillaCore Agent Harness about 90 minutes later, in core pull request #142, and the interesting part is what it refuses to do. A question now goes ahead of queued background work and gets steered into a busy background turn, but nothing is aborted to make room for it. I think that is the right trade for an agent that calls tools with real side effects, and it comes with a cost I want to be honest about: a person still waits for the next safe point, not for an instant reply.
What went wrong
The pull request documents the morning in its own notes. The harness transcript from that conversation shows a worker result entering the model, then a result-status answer, then the operator complaining about an authorization error and about getting worker status replies, and the assistant acknowledging that the question had received a worker status instead of an answer. More wait checks and another worker result followed within the next eight minutes. The author is careful about what that transcript proves: it shows the symptom and the background traffic, not when the message actually arrived or how long it sat in a queue, and the cause comes from the source code and the tests.
The cause was two rules in the harness loop. A message that arrived while a worker-result turn was running could neither be steered into that turn nor interrupt it, because results were excluded from both paths. It went into the queue, and the queue was strictly first in, first out, so a question that arrived after three results waited behind all three. A second gap sat at the end of a turn: the harness still counted a turn as prompting after the model had already produced its reply, so a question arriving in that window could be attached to a reply that was computed before the question existed, and then marked as answered.
Steer, do not abort
The obvious fix is to stop the background turn, answer the question and run the background work again afterwards. The pull request rejects that explicitly, and the reason is the best argument in it: a worker-result turn that was interrupted may already have called tools, and those calls may already have changed something outside the harness. Running the same prompt a second time is not safe when nothing guarantees that its effects happen only once.
So the harness now does two things instead. Queued chat and API inputs move ahead of results, heartbeats, scheduled jobs, loops and goals, and they keep their own order among themselves, also after a restart restores the queue. When a background turn is already running, the question is saved durably first and then handed to the running turn as a steering message, which the runtime picks up at its next safe boundary instead of being cut off in the middle of a step. That happens in all three busy modes, the default queue mode included, and also in the one called interrupt, where a background turn now gets a steering request instead of an abort. Child workers keep running, and the answer goes back to the place the question came from, not to the old result's status message. A question that arrives after the model call has returned gets its own turn, and if a runtime rejects the steering message, the question falls back to the front of the queue.
One kind of input stays out of this. A run started through the harness API never gets merged with another prompt and is never steered into, because each API run owns its prompt, its settings, its progress and its final delivery, and sharing one generated answer across two runs would report work as finished that never ran. API requests still get the same priority over background work, but they wait for the current turn to end, even in steer or interrupt mode.
What I checked
I copied the five test files from the #142 merge into the code of the previous release, v0.1.92, and ran them there: 18 of 34 tests pass, 11 fail and 5 are cancelled by the test runner, because the first of them is still waiting for a steering call that the old code never makes. The failures are the behavior described above, for example a question that should be answered first and instead runs after the background prompt. At the #142 merge, which is release v0.1.93, the same 34 tests pass, and at the end of the window, release v0.1.99, those files hold 35 tests and all of them pass. These are fake runtimes driven by the test suite, not a live chat. The pull request reports 794 of 794 tests passing in its full suite; that is the author's result, and I only reran these five files.
Where it stops
The author's own limits are specific, and I agree with all of them. This is cooperative preemption and not a latency promise: a long-running tool, a slow model provider, a review or maintenance step or a blocked delivery can still hold an answer until the next safe boundary. A runtime that rejects steering falls back to the priority queue. Heavy, continuous chat traffic can starve background work. The tests prove the coordination, not that a model reads the steered question and actually answers it well, and a model can keep calling tools after reading it. Several questions steered into one turn share that turn's single answer. Nothing adds an exactly-once guarantee for a tool effect that was still unsettled when the process crashed. No live Matrix round trip was performed. Two known gaps stay open: a short steering window during runtime finalization, and steering across different chat addresses.
What merged and what shipped
This post covers 28 October 2026, 08:00 to 20:00, Berlin time (CET), which is 07:00 to 19:00 UTC. #142 merged at 10:45 and was first contained in release v0.1.93, published at 10:49. Core merged eight pull requests and published eight releases, v0.1.92 to v0.1.99, in that window. Two of them touch the same conversation flow: #144 keeps the harness's own wait checks from using up the budget for productive follow-up work, and #145 lets a successful wait check that renews the same condition and next step, and marks its entire reply as routine, show up as a status update instead of a chat message, where the chat adapter supports that. Operator answers, results, errors and blockers stay in the chat. The others fix which parent task a worker result gets its authority from (#141) and keep that authority across parent checkpoints (#148), add dispatcher model routing rules (#147), an opt-in private credential file for the local API (#146) and bounded context-maintenance notices (#149). The plugins repository merged two changes; deployment-base merged nothing.
A release tag is not a deployment. Nothing here says which version any installation runs, and the #142 description itself states that the source merge does not activate a running instance.
Where I land
An agent that works in the background still answers to the person in the chat, and #142 puts that person first without pretending that half-finished tool calls can be undone. I would rather wait for a safe boundary than have a worker's side effects run twice because a question arrived at the wrong moment. What I want next is evidence from a live conversation that the steered question actually gets the answer, because the tests can only prove that it gets there.
Sources used
- VanillaCore Harness core pull request #142, "fix: prioritize operator input while preserving API run obligations", merged 28 October 2026: https://git.vanillacore.net/harness/core/pulls/142
- docs/operator-responsiveness.md at the #142 merge commit eee4da0: https://git.vanillacore.net/harness/core/src/commit/eee4da08eeb3b2f3e671e1c7b11656faedfc65ed/docs/operator-responsiveness.md
- src/harness.ts at eee4da0: https://git.vanillacore.net/harness/core/src/commit/eee4da08eeb3b2f3e671e1c7b11656faedfc65ed/src/harness.ts
- Release v0.1.93: https://git.vanillacore.net/harness/core/releases/tag/v0.1.93
- Release v0.1.99: https://git.vanillacore.net/harness/core/releases/tag/v0.1.99
- Core pull requests #144 and #145: https://git.vanillacore.net/harness/core/pulls/144, https://git.vanillacore.net/harness/core/pulls/145
- Public organization inventory: https://git.vanillacore.net/harness/