fix: verdict death — plan→teach seam inferred from first nodeId-tagged quiz

The Accept/Decline verdict gate is gone (owner ruling 2026-09-26):
presenting the plan ends the teacher's turn; the learner's next message
is a natural reply. Teaching begins at the first quiz call tagged with a
nodeId (probe quizzes carry none) — both folds (view phaseAt3, native
phaseAt3Lh) advance on the call itself, and the teach divider renders
before that whole assistant block. No backwards compat: verdict markers,
the verdict input event, optimistic appends, composer buttons, and
projection all deleted; prompts and docs rewritten to the natural flow.
This commit is contained in:
amosblomqvist
2026-09-27 00:48:46 +03:00
parent dcb839232c
commit 145b470752
17 changed files with 221 additions and 389 deletions
+3 -19
View File
@@ -13,13 +13,13 @@
* built client-side exactly as today; a dismissal is an answer
* carrying its flag). Server-side this becomes the call's
* native tool result — fork 1's normal agent history.
* verdict — the plan verdict (accept/decline); an optional note is the
* learner's own real user message, sent first, exactly the
* two-real-messages scheme.
* continue — run the next turn on the current record (mount
* continuations, job-settle re-syncs: the record ends at an
* answered question with no teacher reaction yet).
*
* (The plan verdict input died 2026-09-26 with the verdict itself — the
* learner's post-plan reply is a natural text message.)
*
* Server-originated delivery (the wake pipeline's steers) is NOT a client
* input — the handler accepts it on the same path (TurnInput) so every
* turn rides one composition.
@@ -31,7 +31,6 @@ import {
danglingQuestionOf,
newAnswerResult,
newUserMessage,
newVerdictMessage,
} from "./native-messages.ts";
import { QUESTION_TOOLS } from "./pending.ts";
@@ -39,7 +38,6 @@ import { QUESTION_TOOLS } from "./pending.ts";
export type ChatInput =
| { type: "text"; text: string }
| { type: "answer"; toolCallId: string; answer: QuestionAnswerData }
| { type: "verdict"; accepted: boolean; note?: string }
| { type: "continue" };
/** A wake-pipeline steer delivery — same turn composition, server-origin.
@@ -73,12 +71,6 @@ export function parseChatInput(input: unknown): ChatInput {
throw new ChatInputError("answer input requires the graded answer object");
}
return { type: "answer", toolCallId: i.toolCallId, answer: i.answer as QuestionAnswerData };
case "verdict":
if (typeof i.accepted !== "boolean") throw new ChatInputError("verdict input requires accepted");
if (i.note !== undefined && typeof i.note !== "string") {
throw new ChatInputError("verdict note must be a string");
}
return { type: "verdict", accepted: i.accepted, note: i.note as string | undefined };
case "continue":
return { type: "continue" };
default:
@@ -106,14 +98,6 @@ export function inputAppends(record: { messages: LhMessage[] }, input: ChatInput
}
return [newAnswerResult(input.toolCallId, pending.tool, input.answer)];
}
case "verdict": {
const appends: LhMessage[] = [];
if (input.note && input.note.trim().length > 0) {
appends.push(newUserMessage(input.note));
}
appends.push(newVerdictMessage(input.accepted, new Date().toISOString()));
return appends;
}
case "continue":
return [];
}