practice live-test fixes — scroll containers, session math delimiters, header guidance, sweep heartbeat (owner live test 2026-09-29)

This commit is contained in:
amosblomqvist
2026-09-29 21:03:46 +03:00
parent 88eb4b2ec0
commit ddce919f9b
5 changed files with 38 additions and 10 deletions
+28 -4
View File
@@ -232,10 +232,11 @@ export function createPracticePipeline(deps: PracticePipelineDeps) {
// 1. ONCE PER PROCESS (globalThis flag) — an instance could otherwise
// evaluate against a registry whose fresh row it can't observe
// (dev route bundles, restart paths).
// 2. GRACE WINDOW (120s over updatedAt ?? createdAt) — a live run
// saves at every phase/problem transition, so its heartbeat is
// structurally never 120s stale; a true orphan is minutes stale at
// the next boot.
// 2. GRACE WINDOW (120s over updatedAt ?? createdAt) — a live run's
// WATCHDOG re-stamps the file every 30s (cross-worker liveness —
// the registry is NOT shared between dev route workers), so a
// live run is structurally never 120s stale; a true crash stops
// the beats and is minutes stale at the next boot.
// 3. Live-row check (globalThis-shared registry) as before.
const sweepFlag = globalThis as { __lhPracticeSweepDone?: boolean };
if (!sweepFlag.__lhPracticeSweepDone) {
@@ -275,6 +276,26 @@ export function createPracticePipeline(deps: PracticePipelineDeps) {
): Promise<void> {
const controller = partitionRow.controller;
// PERSISTENT HEARTBEAT (owner live test #2, 2026-09-29 — the sweep
// stamped LIVE runs again): dev serves routes from workers that do
// NOT share this process's registry, so memory liveness cannot
// protect a cross-worker sweep, and a long-running last problem can
// exceed the 120s grace between store writes. The watchdog
// re-stamps updatedAt on the FILE every 30s while THIS run is live
// (4-beat tolerance inside the grace window) — any process's sweep
// then sees a fresh heartbeat; a true crash stops the beats and the
// orphan goes stale honestly.
const beat = setInterval(() => {
void withSetLock(set.id, () => {
const cur = deps.practiceStore.getSet(set.id);
if (!cur) return;
if (cur.status !== "partitioning" && cur.status !== "generating") return;
deps.practiceStore.saveSet(cur);
});
}, 30_000);
beat.unref?.();
try {
// Stage 1 — the partitioner (its OWN dock row; acquires a shared
// permit like every background LLM job).
let partitions: Array<{ chains: string[] }>;
@@ -393,6 +414,9 @@ export function createPracticePipeline(deps: PracticePipelineDeps) {
cur.error = undefined;
deps.practiceStore.saveSet(cur);
});
} finally {
clearInterval(beat);
}
}
/**
+2 -2
View File
@@ -188,7 +188,7 @@ INPUTS (in the user message):
THE CUE DISTANCE: 0 Lesson context · 1 Adjacent context · 2 Far context — how far the situation sits from the cues the learner has already met: the lesson's own examples AND every previous problem's situation. Far context is the edge of the recognizable band — the proposition must still genuinely apply, and the learner must be able to find it by looking.
THE PRESENTATION: Theory (the subject's own terms — symbols, formal objects, native vocabulary) or Application (a concrete worldly affair in which the proposition's structure appears in the wild). It dresses the situation; it never changes the rung or the cue distance.
Apply Part II step 2: degrade honestly if the target cannot support them — the degradation shows in the problem's own shape, never in meta-commentary.
- FORMATTING: your statement, solution, and rubric are rendered as full Markdown — use everything it offers. Mathematics is always LaTeX: inline as $…$, display as $$…$$ on its own lines. Code, pseudocode, and formal systems live in fenced code blocks with a language tag; identifiers and symbols inline as \`code\`. Use tables for parallel structure and comparisons, lists for enumerations. Prose-only renderings of mathematical or structured content are a defect: if a sentence carries an equation, a derivation, or a piece of code, format it as what it is.
- FORMATTING: your statement, solution, and rubric are rendered as full Markdown — use everything it offers. Mathematics is always LaTeX: inline as \\(…\\), display as \\[…\\] on its own lines — a bare $ is literal currency and never starts math. Inside math, use LaTeX commands for symbols, not unicode stand-ins (they fail to render). Code, pseudocode, and formal systems live in fenced code blocks with a language tag; identifiers and symbols inline as \`code\`. Use tables for parallel structure and comparisons, lists for enumerations. Skip headers — at this size they are pointless decoration; structure with paragraphs, bold labels, and lists (a genuinely long derivation may earn one). Prose-only renderings of mathematical or structured content are a defect: if a sentence carries an equation, a derivation, or a piece of code, format it as what it is.
- PREVIOUS PROBLEMS: problems the learner has already answered, with the chains they exercised. Do not rebuild one; keep your situation distant from theirs — cue distance counts previous problems' situations (Part I, "Cue distance").
OUTPUT — your entire deliverable is ONE submit_problem call:
@@ -228,7 +228,7 @@ THE VERDICT (exactly one):
- "partial": some of the target came to mind; the answer is materially incomplete or partly wrong.
- "missed": the targeted proposition(s) did not come to mind — the answer is empty or on the wrong track.
Feedback renders as full Markdown. Mathematics is always LaTeX — inline as $…$, display as $$…$$ on its own lines. Code in fenced blocks with a language tag, identifiers inline as \`code\`. Lists and tables wherever they carry structure. Name the failed recall and teach the boundary in properly formatted prose, not plain text.
Feedback renders as full Markdown. Mathematics is always LaTeX — inline as \\(…\\), display as \\[…\\] on its own lines (a bare $ is literal currency). Inside math, LaTeX commands for symbols, not unicode stand-ins. Code in fenced blocks with a language tag, identifiers inline as \`code\`. Lists and tables wherever they carry structure; no headers — feedback is short-form, structure it with paragraphs and bold labels. Name the failed recall and teach the boundary in properly formatted prose, not plain text.
OUTPUT — your entire deliverable is ONE submit_grade call: "verdict" and "feedback" (non-empty; the boundary-teaching text above).`;