practice — discretized parameter axes: named levels, never a number (owner-approved 2026-09-29)
Contracts: LadderRung 0-4, CueDistance 0-2, Presentation theory|
application; PracticeParams carries the three named levels. Doc
comments hold the three definition tables verbatim (rung names +
definitions from the ladder doctrine; cue levels; presentation
levels).
Core: LADDER_RUNGS/CUE_LEVELS/PRESENTATIONS tables in src/core/
practice.ts (definitions verbatim) — ONE source spoken by the agents'
prompts, the dialog's stepped controls, and the exam header; core
barrel re-exports practice.ts (pure domain, browser-safe).
Agents: Part I's ladder intro now 'five rungs, numbered; the request
arrives as a rung' with bullets numbered 0-4 (definitions untouched);
Part III's parameters block replaced with the named-level doctrine
(rung = exact class, no continuum; cue counts lesson examples AND
previous problems' situations; presentation dresses, never changes);
partitioner's RESPECT THE PARAMETERS replaced (low rungs = many small
assignments, high = larger; far cue makes confusables-together MORE
important; presentation does not shape the partition). renderParams
outputs the exact named-level block. Anchor/0..1 language swept dead.
Service + store: strict union validation — floats between levels,
out-of-range, wrong strings, legacy dial shapes all 400 at the API
and fail isProblemSet (legacy sets skipped from lists; no migration —
data/practice is disposable scratch, owner wipes before testing).
UI: dialog = 5-detent ladder slider + 3-detent cue slider (each shows
the level NAME + one-line definition) + Theory|Application segmented
toggle; defaults rung 2 / cue 1 / theory; Create posts the new shape.
Exam header gains the levels line ('Direct exercise · Adjacent
context · Theory').
Tests: renderParams exact-block pin; strict-validation pins (new
tests/practice-service.test.ts — bad unions 400, legacy shape 400);
store pins (legacy floats + 2.5 rung fail); render pins (detents +
names + definitions, defaults, detent-swap, toggle active segment,
levels line); every params fixture moved off floats. 1138/1138, tsc
0, lint 0, build 0.
Docs: architecture.md — discretization paragraph (compact tables,
named-level invocation, defaults, one-table source), UI mentions;
README axes sentence.
This commit is contained in:
@@ -36,7 +36,7 @@ import type { PiEngine } from "../models.ts";
|
||||
import { runSubmitAgent } from "./submit-agent.ts";
|
||||
import { callUsage } from "./extraction.ts";
|
||||
import type { CallUsage, PracticeParams, ReasoningChain } from "../../core/contracts.ts";
|
||||
import { chainCoverage } from "../../core/practice.ts";
|
||||
import { chainCoverage, CUE_LEVELS, LADDER_RUNGS, PRESENTATIONS } from "../../core/practice.ts";
|
||||
|
||||
// ─── doctrines ───────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -109,13 +109,13 @@ Three operations modify a problem at any position on the axes.
|
||||
|
||||
### The ladder
|
||||
|
||||
Canonical points on the main axis, bottom to top:
|
||||
The main axis, bottom to top — five rungs, numbered; the request arrives as a rung:
|
||||
|
||||
- **Worked example** — the solution is shown; nothing is demanded. An installation object; nothing yet to recall.
|
||||
- **Completion** — the path is shown with a gap; the learner supplies one step. What to recall is nearly announced.
|
||||
- **Direct exercise** — one step, no hint, but the situation makes the applicable proposition unmistakable. Trains execution, not choosing.
|
||||
- **Composition** — several steps, chained; the path is narrow but must be held and run.
|
||||
- **Path-finding** — several steps, the path concealed, distractors present, the statement suggesting wrong methods. The competition shape.
|
||||
- **0 Worked example** — the solution is shown; nothing is demanded. An installation object; nothing yet to recall.
|
||||
- **1 Completion** — the path is shown with a gap; the learner supplies one step. What to recall is nearly announced.
|
||||
- **2 Direct exercise** — one step, no hint, but the situation makes the applicable proposition unmistakable. Trains execution, not choosing.
|
||||
- **3 Composition** — several steps, chained; the path is narrow but must be held and run.
|
||||
- **4 Path-finding** — several steps, the path concealed, distractors present, the statement suggesting wrong methods. The competition shape.
|
||||
|
||||
The ladder is the main axis only. Cue distance and the three operations apply at every point.
|
||||
|
||||
@@ -183,7 +183,11 @@ You are one generator in a two-stage pipeline: a partitioner has already divided
|
||||
INPUTS (in the user message):
|
||||
- YOUR TARGET: the chains this problem is built to strengthen — the partitioner's assignment.
|
||||
- THE FULL SET: the proposition legend and chain list of the ENTIRE selection, not just the target. A coherent problem often cannot be built from the target alone — you may draw on other propositions of the full set as supporting material when the problem needs it. DOCTRINE: minimal use of outside propositions, maximal focus on the target — the target is what the learner must recall; outside propositions are material the solution may pass through. The never-announce rule covers every proposition, target or not.
|
||||
- THE PARAMETERS: search-to-solution ratio, cue distance, presentation, each 0..1 (0 = flashcard end / lesson-close / pure theory; 1 = competition end / far transfer / real-world application). 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.
|
||||
- THE PARAMETERS: three settings, each a named level — never a number to interpolate.
|
||||
THE LADDER RUNG (the main axis): 0 Worked example · 1 Completion · 2 Direct exercise · 3 Composition · 4 Path-finding (Part I's ladder). The invocation names your rung; build exactly that CLASS of problem. There is no continuum between rungs — a rung-3 problem is composition, not "composition with a little path-finding."
|
||||
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.
|
||||
- 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:
|
||||
@@ -201,7 +205,7 @@ PARTITION DOCTRINE:
|
||||
- SMALLEST SOUND SET: prefer fewer, well-loaded problems. But never force unrelated material together: a problem's chains must share a real target relationship (confusable with each other, feeding the same conclusion, forming a path, or clustered around one idea). If the only thing two chains share is the graph, they belong in different problems.
|
||||
- GROUP CONFUSABLES TOGETHER: propositions that apply to almost the same situations must land in the same problem — discrimination (choosing between near-identical situations) is a problem-engineering operation, and it is only possible when both confusables are in the room.
|
||||
- READ THE MIX OFF THE SHAPE: many singletons and few multi-premise chains suggest recall + discrimination problems; long premise chains suggest composition and path-finding. Your partition is where that judgment lives.
|
||||
- RESPECT THE PARAMETERS: the request carries the three dials (search-to-solution ratio, cue distance, presentation). Higher ratios support — and favor — larger assignments (more material to conceal a path in); at the bottom of the axis, one-step problems dominate and single chains stand alone.
|
||||
- RESPECT THE PARAMETERS: the request names the ladder rung, the cue distance, and the presentation. Low rungs (0–2) mean many small assignments — single chains standing alone; high rungs (3–4) mean larger assignments — composition needs material to chain, path-finding needs distractor material to conceal the path among. Far cue distance makes grouping confusables together MORE important: the discrimination must travel into the far situation. Presentation does not shape your partition.
|
||||
- DO NOT DUPLICATE: previous problems the learner has already seen are listed with their chains. If an existing problem already covers a group of chains, re-partition so the new set differs in what it groups and how.
|
||||
|
||||
OUTPUT — your entire deliverable is ONE submit_partition call: "partitions", a list of { chains: [ids] }, one entry per problem, together containing every input chain exactly once. No empty entries; no unknown ids; no problem without a real target relationship.`;
|
||||
@@ -277,10 +281,14 @@ export function renderChains(chains: ReasoningChain[], propositions: Proposition
|
||||
}
|
||||
|
||||
export function renderParams(params: PracticeParams): string {
|
||||
const rung = LADDER_RUNGS[params.ladderRung];
|
||||
const cue = CUE_LEVELS[params.cueDistance];
|
||||
const pres = PRESENTATIONS[params.presentation];
|
||||
return [
|
||||
`search-to-solution ratio: ${params.searchSolutionRatio.toFixed(1)} (0 = announced recall, 1 = competition problem)`,
|
||||
`cue distance: ${params.cueDistance.toFixed(1)} (0 = lesson-close, 1 = far transfer)`,
|
||||
`presentation: ${params.presentation.toFixed(1)} (0 = pure theory, 1 = real-world application)`,
|
||||
"THE PARAMETERS",
|
||||
`Ladder rung ${params.ladderRung} — ${rung.name}: ${rung.definition}`,
|
||||
`Cue distance ${params.cueDistance} — ${cue.name}: ${cue.definition}`,
|
||||
`Presentation: ${pres.name} — ${pres.definition}`,
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -49,17 +49,57 @@ export interface ReasoningChain {
|
||||
conclusion: string;
|
||||
}
|
||||
|
||||
/** The three dials every problem set is configured with (owner, 2026-09-29)
|
||||
* — all 0..1, interpreted by the agents against the framework's axes. */
|
||||
/** The main axis, discretized (owner, 2026-09-29 — named levels, never
|
||||
* a number to interpolate; definitions verbatim from the ladder
|
||||
* doctrine): */
|
||||
export type LadderRung = 0 | 1 | 2 | 3 | 4;
|
||||
|
||||
/** Cue distance from every situation the learner has already met — the
|
||||
* lesson's own examples AND every previous problem's situation
|
||||
* (definitions owner-approved 2026-09-29): */
|
||||
export type CueDistance = 0 | 1 | 2;
|
||||
|
||||
/** How the situation is dressed — never changes the rung or the cue
|
||||
* distance: */
|
||||
export type Presentation = "theory" | "application";
|
||||
|
||||
/** The three settings every problem set is configured with (owner,
|
||||
* 2026-09-29) — each a named level, invoked as that level exactly:
|
||||
*
|
||||
* THE LADDER (the main axis, bottom to top):
|
||||
* 0 Worked example — the solution is shown; nothing is demanded. An
|
||||
* installation object; nothing yet to recall.
|
||||
* 1 Completion — the path is shown with a gap; the learner supplies
|
||||
* one step. What to recall is nearly announced.
|
||||
* 2 Direct exercise — one step, no hint, but the situation makes the
|
||||
* applicable proposition unmistakable. Trains execution, not choosing.
|
||||
* 3 Composition — several steps, chained; the path is narrow but must
|
||||
* be held and run.
|
||||
* 4 Path-finding — several steps, the path concealed, distractors
|
||||
* present, the statement suggesting wrong methods. The competition
|
||||
* shape.
|
||||
*
|
||||
* THE CUE DISTANCE:
|
||||
* 0 Lesson context — the textbook/toy-example shape in the teaching's
|
||||
* own framing.
|
||||
* 1 Adjacent context — a genuinely new situation built from the
|
||||
* teaching's own world — same domain, different setup/packaging.
|
||||
* 2 Far context — as far from the typical cue as recognition allows,
|
||||
* 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.
|
||||
* application — a concrete worldly affair — a case, episode, system,
|
||||
* or story — in which the proposition's structure appears in the wild. */
|
||||
export interface PracticeParams {
|
||||
/** Search-to-solution ratio — the main axis: flashcard recall (0) to
|
||||
* competition problem (1). */
|
||||
searchSolutionRatio: number;
|
||||
/** Cue distance from every situation already used (0 = near the
|
||||
* lesson, 1 = far transfer — still inside the recognizable band). */
|
||||
cueDistance: number;
|
||||
/** Presentation axis: pure theory (0) to real-world application (1). */
|
||||
presentation: number;
|
||||
/** Which class of problem to build — exactly that rung. */
|
||||
ladderRung: LadderRung;
|
||||
/** How far the situation sits from the cues already met. */
|
||||
cueDistance: CueDistance;
|
||||
/** How the situation is dressed. */
|
||||
presentation: Presentation;
|
||||
}
|
||||
|
||||
/** Where a set stands in the two-stage run. The set is PERSISTED at
|
||||
@@ -119,8 +159,8 @@ export interface Problem {
|
||||
export interface ProblemSet {
|
||||
/** "ps_" + ts36 + rand — store-assigned. */
|
||||
id: string;
|
||||
/** Display label — auto-generated (the session's title, or
|
||||
* date-based). */
|
||||
/** Display label — the picked cluster's label, or the dated
|
||||
* default. */
|
||||
title: string;
|
||||
createdAt: string;
|
||||
params: PracticeParams;
|
||||
|
||||
@@ -31,6 +31,7 @@ export * from "./phases.ts";
|
||||
export * from "./pending.ts";
|
||||
export * from "./prompt-segments.ts";
|
||||
export * from "./math-delims.ts";
|
||||
export * from "./practice.ts";
|
||||
// Wave B (owner-approved 2026-09-26): the native record (LhMessage), the
|
||||
// and the input-events contract — all
|
||||
// pure logic, browser-safe.
|
||||
|
||||
+24
-1
@@ -11,7 +11,30 @@
|
||||
* the union of the problems' chains must equal the set's chains; the
|
||||
* partitioner's submit is validated against it.
|
||||
*/
|
||||
import type { GraphEdge, GraphNode, Problem, ReasoningChain } from "./contracts.ts";
|
||||
import type { CueDistance, GraphEdge, GraphNode, LadderRung, Presentation, Problem, ReasoningChain } from "./contracts.ts";
|
||||
|
||||
/** The named levels (owner-approved 2026-09-29) — definitions verbatim
|
||||
* from the ladder doctrine / the contract's tables. The UI's stepped
|
||||
* controls, the exam header's levels line, and the agents' prompt
|
||||
* rendering all speak these names — never a bare number. */
|
||||
export const LADDER_RUNGS: Record<LadderRung, { name: string; definition: string }> = {
|
||||
0: { name: "Worked example", definition: "the solution is shown; nothing is demanded. An installation object; nothing yet to recall." },
|
||||
1: { name: "Completion", definition: "the path is shown with a gap; the learner supplies one step. What to recall is nearly announced." },
|
||||
2: { name: "Direct exercise", definition: "one step, no hint, but the situation makes the applicable proposition unmistakable. Trains execution, not choosing." },
|
||||
3: { name: "Composition", definition: "several steps, chained; the path is narrow but must be held and run." },
|
||||
4: { name: "Path-finding", definition: "several steps, the path concealed, distractors present, the statement suggesting wrong methods. The competition shape." },
|
||||
};
|
||||
|
||||
export const CUE_LEVELS: Record<CueDistance, { name: string; definition: string }> = {
|
||||
0: { name: "Lesson context", definition: "the textbook/toy-example shape in the teaching's own framing." },
|
||||
1: { name: "Adjacent context", definition: "a genuinely new situation built from the teaching's own world — same domain, different setup/packaging." },
|
||||
2: { name: "Far context", definition: "as far from the typical cue as recognition allows, the edge of the recognizable band — the proposition must still genuinely apply, and the learner must be able to find it by looking." },
|
||||
};
|
||||
|
||||
export const PRESENTATIONS: Record<Presentation, { name: string; definition: string }> = {
|
||||
theory: { name: "Theory", definition: "the subject's own terms — symbols, formal objects, native vocabulary." },
|
||||
application: { name: "Application", definition: "a concrete worldly affair — a case, episode, system, or story — in which the proposition's structure appears in the wild." },
|
||||
};
|
||||
|
||||
/** The selection's follows-edges as chains: every edge into a node joins
|
||||
* that node's (one) chain; a node with no in- AND no out-follows-edge in
|
||||
|
||||
@@ -41,10 +41,13 @@ export function getPracticeSet(id: string): ProblemSet | null {
|
||||
function parseParams(raw: unknown): PracticeParams | null {
|
||||
if (typeof raw !== "object" || raw === null) return null;
|
||||
const p = raw as Record<string, unknown>;
|
||||
const dial = (v: unknown): v is number => typeof v === "number" && Number.isFinite(v) && v >= 0 && v <= 1;
|
||||
if (!dial(p.searchSolutionRatio) || !dial(p.cueDistance) || !dial(p.presentation)) return null;
|
||||
// Named levels ONLY (owner, 2026-09-29): exact unions, nothing between —
|
||||
// a rung is a rung; there is no float to interpolate.
|
||||
if (p.ladderRung !== 0 && p.ladderRung !== 1 && p.ladderRung !== 2 && p.ladderRung !== 3 && p.ladderRung !== 4) return null;
|
||||
if (p.cueDistance !== 0 && p.cueDistance !== 1 && p.cueDistance !== 2) return null;
|
||||
if (p.presentation !== "theory" && p.presentation !== "application") return null;
|
||||
return {
|
||||
searchSolutionRatio: p.searchSolutionRatio,
|
||||
ladderRung: p.ladderRung,
|
||||
cueDistance: p.cueDistance,
|
||||
presentation: p.presentation,
|
||||
};
|
||||
@@ -88,7 +91,7 @@ export async function createPracticeSetResponse(body: unknown): Promise<Response
|
||||
const params = parseParams(b.params);
|
||||
if (!params) {
|
||||
return Response.json(
|
||||
{ error: "params must carry searchSolutionRatio, cueDistance, presentation as numbers in [0,1]" },
|
||||
{ error: "params must carry ladderRung (0-4), cueDistance (0-2), presentation (theory|application) as exact levels" },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
+10
-6
@@ -35,13 +35,17 @@ const isStringArray = (v: unknown): v is string[] =>
|
||||
const isParams = (v: unknown): boolean => {
|
||||
if (typeof v !== "object" || v === null) return false;
|
||||
const p = v as Record<string, unknown>;
|
||||
// Named levels ONLY (owner, 2026-09-29): legacy float-params sets fail
|
||||
// here and are skipped from lists — no migration (owner wipes
|
||||
// data/practice before testing).
|
||||
return (
|
||||
typeof p.searchSolutionRatio === "number" &&
|
||||
Number.isFinite(p.searchSolutionRatio) &&
|
||||
typeof p.cueDistance === "number" &&
|
||||
Number.isFinite(p.cueDistance) &&
|
||||
typeof p.presentation === "number" &&
|
||||
Number.isFinite(p.presentation)
|
||||
(p.ladderRung === 0 ||
|
||||
p.ladderRung === 1 ||
|
||||
p.ladderRung === 2 ||
|
||||
p.ladderRung === 3 ||
|
||||
p.ladderRung === 4) &&
|
||||
(p.cueDistance === 0 || p.cueDistance === 1 || p.cueDistance === 2) &&
|
||||
(p.presentation === "theory" || p.presentation === "application")
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user