fix: reasoning always renders above its message; dock drops delivered subagents at delivery
TWO owner-reported bugs from the live red-circle session (chat_muhpyoll_27305e23), both investigated to root cause first: 1. THINKING BLOCKS UNDER THE MESSAGE. GLM emits trailing reasoning tokens, so the persisted content lands [text, thinking] (4 of the 6 thinking-bearing messages from [21] on in that session) - and the view canon presented arrival order verbatim, rendering the reasoning UNDER its own message. Fix: projectView stable-partitions each assistant message - reasoning parts first (their own order kept), then text/tool parts. Owner ruling 2026-09-26: the reasoning belongs above, always. Covers live partials and settled records alike (one projection canon). NOT addressed (owner ruling): the plan_tools "missing" claim in the same session - wire-verified 11 tools incl. plan_init; GLM confabulation, no mitigation wanted. 2. DOCK CARDS LINGERING AFTER DELIVERY. /api/activity lists jobs with status "running", and the status flipped only in settleSteers at the DELIVERING TURN'S onFinish - so a subagent whose card was already visible in the transcript (marker injected mid-run) stayed in the background dock until the teacher finished teaching. Fix: flip the job at the moment the result reaches the learner - noteSteerVisible (jobs.ts) called from BOTH visibility seams: the marker's message_end wire event (live steer injection, ui-stream) and the write-ahead persist (the wake's input appends, chat-handler). finalizeJob is idempotent; settleSteers/runner-200 re-run it as a no-op, and the registry's delivery promise still settles exactly as before. Tests: 1001/1001 - view-canon hoist pin (trailing [text, think, call] projects reasoning-first; think-first unchanged), noteSteerVisible unit pin (flip at the seam, idempotent, handoff no-op), and the mid-run steering e2e now holds the turn open past the injection and pins the job flipping done at the marker's message_end WHILE the turn still streams. Live-verified read-only on the owner's server: the original session reloads with every reasoning trigger above its message.
This commit is contained in:
+18
-1
@@ -50,8 +50,9 @@ import { handleChatRequest } from "../src/server/chat-handler.ts";
|
||||
import {
|
||||
abortJob,
|
||||
deliverJobResult,
|
||||
registerLiveAgent,
|
||||
noteSteerVisible,
|
||||
recoverStaleJobs,
|
||||
registerLiveAgent,
|
||||
setJobRespawn,
|
||||
setTurnRunner,
|
||||
settleSteers,
|
||||
@@ -991,6 +992,22 @@ describe("Wave C: live steering handoff + settlement reconciliation", () => {
|
||||
assert.equal(jobStore.getJob(jobId)!.status, "done");
|
||||
});
|
||||
|
||||
it("noteSteerVisible flips the job at the visibility seam — before any settle (owner ruling 2026-09-26)", async () => {
|
||||
// The dock fix: the marker reaching the learner's view (wire
|
||||
// message_end / write-ahead persist) flips the job NOW — the
|
||||
// delivering turn may keep teaching for minutes. Idempotent with
|
||||
// the settle-path finalize.
|
||||
const { chatId, jobId, steer } = setup();
|
||||
assert.equal(jobStore.getJob(jobId)!.status, "running");
|
||||
noteSteerVisible(steer);
|
||||
assert.equal(jobStore.getJob(jobId)!.status, "done", "flipped at the seam, no turn settled");
|
||||
noteSteerVisible(steer); // the settle-path flip re-runs — no-op
|
||||
assert.equal(jobStore.getJob(jobId)!.status, "done");
|
||||
// A probe-handoff (no jobId) is a harmless no-op.
|
||||
const handoff = buildProbeHandoffSteer(chatId);
|
||||
noteSteerVisible(handoff);
|
||||
});
|
||||
|
||||
it("the idle wake carries EVERY pending marker in ONE turn", async () => {
|
||||
const { chatId, steer } = setup();
|
||||
const job2 = jobStore.createJob({ kind: "research", chatId, toolCallId: "call-2" });
|
||||
|
||||
Reference in New Issue
Block a user