practice: the between-problems pause — conclude ends the turn, the learner's go-ahead paces the stream (round 16)
Owner ruling (2026-10-06): every concluded problem opens a pause. The
agent may speak its closing words about the learner's last thinking
after the conclude result, then ENDS ITS TURN — a system-prompt law
deliberately, per owner correction, NOT tool-level terminate. The next
problem is never posed until the learner explicitly moves on; the pause
is the session's teaching space (repairs, clarifications, follow-ups —
still surgical, never re-teaching).
The move-on signal is a first-class input: { type: "next" } appends a
practice-next marker (content = single-sourced NEXT_PROBLEM_TEXT — the
model's unambiguous go-ahead: pose per the selection rules or wrap if
the arc is complete). Honest 400s: practice sessions only, nothing
active (mid-problem input rides the dock), riding after any pending
dismissal synthesis exactly like text.
The fast path (owner approved: composer-band chip + Enter-on-empty):
- Composer.enterAction gains "next" — Enter on an EMPTY composer in
the pause fires the signal; typing anything makes Enter a send again;
busy (steer/stop) outranks the pause.
- The Next chip hovers at the composer band's top-right (pick-pill
chrome register) whenever the pause is live and a problem has ever
been posed; the placeholder becomes the hint ("Ask about this
problem — or press Enter for the next one…", exact);
- the transcript renders the signal as a compact centered NEXT PROBLEM
line (mono-caps chrome); session-core appends it optimistically.
CONCLUDE_ACK_TEXT rewritten to carry the law; doctrine §VIII gains the
pause law (owner-sanctioned edit #3); 00-base's conclude/dismissal
bullets rewritten + the between-problems section added.
This commit is contained in:
@@ -32,6 +32,7 @@ import {
|
||||
concludedByDismissalPair,
|
||||
danglingQuestionOf,
|
||||
newAnswerResult,
|
||||
newNextMessage,
|
||||
newProblemDismissedMarker,
|
||||
newThinkingMessage,
|
||||
newUserMessage,
|
||||
@@ -48,7 +49,12 @@ export type ChatInput =
|
||||
* thinking on the ACTIVE problem — one lane (owner 2026-10-05):
|
||||
* whether it claims an answer is read from the text. One
|
||||
* problem may carry many submissions; each is its own turn. */
|
||||
| { type: "thinking"; problemCallId: string; text: string };
|
||||
| { type: "thinking"; problemCallId: string; text: string }
|
||||
/** The practice move-on signal (owner round 16): the learner's
|
||||
* explicit go-ahead during the between-problems pause — the Next
|
||||
* chip or Enter on an empty composer. Practice sessions only, and
|
||||
* only with nothing active (mid-problem input rides the dock). */
|
||||
| { type: "next" };
|
||||
|
||||
/** A wake-pipeline steer delivery — same turn composition, server-origin.
|
||||
* Wave C (2026-09-26): the idle wake carries EVERY queued marker as its
|
||||
@@ -172,6 +178,8 @@ export function parseChatInput(input: unknown): ChatInput {
|
||||
throw new ChatInputError("thinking input requires non-empty text");
|
||||
}
|
||||
return { type: "thinking", problemCallId: i.problemCallId, text: i.text };
|
||||
case "next":
|
||||
return { type: "next" };
|
||||
default:
|
||||
throw new ChatInputError(`unknown input type: ${String(i.type)}`);
|
||||
}
|
||||
@@ -206,6 +214,19 @@ export function inputAppends(record: { messages: LhMessage[] }, input: ChatInput
|
||||
}
|
||||
return [...dismissalConclusion(record), newThinkingMessage(input.problemCallId, input.text)];
|
||||
}
|
||||
case "next": {
|
||||
// Honest 400s, the thinking gate's discipline: the signal is a
|
||||
// between-problems affordance — practice sessions only, nothing
|
||||
// active (mid-problem input rides the dock), and the client gates
|
||||
// make violations unreachable in practice anyway.
|
||||
if ((record as { kind?: string }).kind !== "practice") {
|
||||
throw new ChatInputError("next input is a practice-session affordance");
|
||||
}
|
||||
if (activeProblemOf(record.messages)) {
|
||||
throw new ChatInputError("next input requires no active problem");
|
||||
}
|
||||
return [...dismissalConclusion(record), newNextMessage()];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user