quick-fixes round: quiz-only teach-check reveal, ESC-abort confirm popup + question dismissal, confidence prompt diet

- requiresReveal gates on pending.tool === 'quiz' (owner: only graded quizzes
  arrive collapsed; ask/explain always open)
- ESC-abort chain opens AbortTurnDialog everywhere (owner ruling); from a
  pending question abort IS dismissal — dismissedAnswer rides the answer
  path, the teacher acknowledges the skip in its next turn; popup-Esc is
  claimed via the state gate (two window listeners fire per Esc)
- 25-confidence.md: stripped the slider/JSON/calibration mechanics — pure
  reading doctrine remains
- toolQuestionKind named once in core (dock parity), dock switched to it
This commit is contained in:
amosblomqvist
2026-10-03 01:03:29 +03:00
parent 60971ca6fe
commit 3e5b857c63
8 changed files with 271 additions and 10 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
## Confidence — reading the learner's self-assessment
Every quiz answer arrives as a compact JSON record; its `confidence` field is the learner's stated probability that his selected answer is correct, set on a snapped slider (0, 30, 50, 70, 90, 100). This is calibration data, not decoration. Read it against the verdict:
Every quiz answer carries `confidence`: the learner's stated probability, as a percentage, that the answer he picked is correct. Read it against the verdict — it changes what the outcome means:
- 50 is a genuine coin flip. 90–100 claims certainty; 0–30 claims real doubt.
- For an n-option question, chance is 1/n. Confidence at or below chance on a picked answer means he believes his pick is wrong — a forced choice he distrusts, often trap suspicion. Treat it as honesty, not error.
+6
View File
@@ -41,6 +41,12 @@ export interface PendingToolCall {
/** Tools that render in the question dock and gate the composer. */
export const QUESTION_TOOLS = ["quiz", "ask_user_question", "explain"] as const;
/** The tool→kind map (the dock's own fold, named once — the dismissal
* path and the dock must never disagree on what a tool is). */
export function toolQuestionKind(tool: string): "quiz" | "ask" | "explain" {
return tool === "quiz" ? "quiz" : tool === "explain" ? "explain" : "ask";
}
/** Tools fulfilled by background sub-agent jobs (W2/W6). */
export const JOB_TOOLS = ["research", "demo", "visual"] as const;
+17
View File
@@ -615,6 +615,23 @@ export function buildAnswer(qd: QuestionData, req: AnswerRequest): QuestionAnswe
* note. The full graded QuestionAnswerData rides `details` for the record
* and the UI; this is the JSON the agent actually reads.
*/
/** The dismissal record (ESC-abort from a pending question, owner
* 2026-10-02): the minimum honest answer. The model sees
* {kind, dismissed:true} via compactAnswerForModel; the answered card
* renders the question text + "skipped". Rides the same answer path —
* the teacher acknowledges the skip in its next turn, exactly like any
* answer. */
export function dismissedAnswer(kind: QuestionKind, questionText: string): QuestionAnswerData {
return {
kind,
questionText,
dontKnow: false,
answers: [],
dismissed: true,
answeredAt: new Date().toISOString(),
};
}
export function compactAnswerForModel(ad: QuestionAnswerData): Record<string, unknown> {
if (ad.dismissed) return { kind: ad.kind, dismissed: true };
const out: Record<string, unknown> = { kind: ad.kind };