practice polish: no phantom wake, Practice header tag, honest meta

- needsContinuation: a persisted problem-pose ack (terminate:true ended
  the turn deliberately) owes no continuation — reloads after a pose no
  longer wake a phantom turn; error results stay owed like any
  interrupted tool.
- Chat header: practice sessions swap the phase chip for a quiet
  Practice tag (no probe→teach arc applies) and hide the coverage ring
  (practice never extracts — an ever-filling ring would lie).
- ChatSummary gains pendingProblem (activeProblemOf at projection); the
  sidebar meta says 'Problem waiting' for a practice row with a posed
  problem. Kind threads through Chat/[id] page.
- Home picker chip gains Clear (picks were otherwise irremovable).
This commit is contained in:
amosblomqvist
2026-10-04 22:06:22 +03:00
parent e76e42f764
commit 9362baf36e
9 changed files with 101 additions and 7 deletions
+11 -2
View File
@@ -515,9 +515,18 @@ export function needsContinuation(messages: readonly LhMessage[]): boolean {
if (!owner || (owner as { role?: string }).role !== "assistant") return false;
const content = (owner as { content?: unknown }).content;
if (!Array.isArray(content)) return false;
const answered = new Set(
messages.slice(start).map((m) => (m as ToolResultMessage).toolCallId),
const results = messages.slice(start) as ToolResultMessage[];
// PRACTICE (owner design, 2026-10-04): a batch acking a problem POSE
// owes nothing — terminate:true ended that turn DELIBERATELY (the
// learner's next thinking submission is the next turn), and unlike a
// question's placeholder this ack PERSISTS (the model reads its own
// "Problem posed" result later). Without this fold a reload after a
// pose would wake a phantom continuation turn.
const posedProblem = results.some(
(m) => m.toolName === PRACTICE_PROBLEM_TOOL && !m.isError && m.details == null,
);
if (posedProblem) return false;
const answered = new Set(results.map((m) => m.toolCallId));
const calls = (content as Array<{ type?: string; id?: string }>).filter(
(b) => b.type === "toolCall" && typeof b.id === "string",
);