practice: target-validation pins + the id convention in the base prompt (round 9 slice 3)
Four pins on the mastery-trace contract: a name target normalizes to the canonical id (case-insensitive, whole-graph set), an id passes through untouched, an unknown target rejects one-line AND does not arm the gate (honest activation — the next valid pose goes through), and the legacy node-source-less wiring stays unvalidated. The base prompt now states the convention: target takes the proposition ids from the session context — ids are how problems are traced.
This commit is contained in:
@@ -23,7 +23,7 @@ He has to be able to trust you completely; one confidently-delivered hallucinati
|
||||
|
||||
Two tools, and everything else is conversation. The mechanics only — the method (when to pose, what to pose, how to respond) is the doctrine's.
|
||||
|
||||
**`problem`** — poses one problem. The statement field is what the learner sees (markdown and LaTeX render); the modelSolution is your private commitment, hidden from the learner until you conclude. The call ends the turn. The learner then works in a thinking field; each submission arrives back as a *Your thinking* message. Whether it claims an answer is IN THE TEXT, not declared anywhere — read it:
|
||||
**`problem`** — poses one problem. The statement field is what the learner sees (markdown and LaTeX render); the modelSolution is your private commitment, hidden from the learner until you conclude. The target field takes the proposition ids from the session context — ids are how every problem is traced to the propositions it exercises. The call ends the turn. The learner then works in a thinking field; each submission arrives back as a *Your thinking* message. Whether it claims an answer is IN THE TEXT, not declared anywhere — read it:
|
||||
|
||||
- Working trace (mid-work, stuck, or asking for a nudge): the hint ladder applies, one rung per request. A trace may span several submissions.
|
||||
- A claimed answer: extract the moves, deliver the verdict. A claim is not a progress signal.
|
||||
|
||||
@@ -135,6 +135,50 @@ describe("the practice tool gating law", () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ── target validation (the mastery-trace contract, owner round 9) ──────────
|
||||
|
||||
describe("target validation — names normalize to ids, unknowns reject", () => {
|
||||
const NODES = [
|
||||
{ id: "n_parity", name: "Parity of a sum", statement: "The sum of two odds is even." },
|
||||
{ id: "n_pairing", name: "Pairing", statement: "Pair elements to count parity." },
|
||||
] as const;
|
||||
const graphNodes = () => NODES;
|
||||
|
||||
it("a NAME target silently normalizes to the canonical id (the record carries ids)", async () => {
|
||||
const healed = problemTool(createPracticeGate(ctxOf([])), graphNodes).prepareArguments!({
|
||||
...PROBLEM_INPUT,
|
||||
target: ["Parity of a sum", "pairing"],
|
||||
}) as { target: string[] };
|
||||
assert.deepEqual(healed.target, ["n_parity", "n_pairing"], "case-insensitive name → id, whole-graph set");
|
||||
});
|
||||
|
||||
it("an id target passes through untouched (already canonical)", async () => {
|
||||
const healed = problemTool(createPracticeGate(ctxOf([])), graphNodes).prepareArguments!({
|
||||
...PROBLEM_INPUT,
|
||||
target: ["n_parity"],
|
||||
}) as { target: string[] };
|
||||
assert.deepEqual(healed.target, ["n_parity"]);
|
||||
});
|
||||
|
||||
it("an UNKNOWN target rejects with a one-line error and does NOT arm the gate", async () => {
|
||||
const gate = createPracticeGate(ctxOf([]));
|
||||
const tool = problemTool(gate, graphNodes);
|
||||
const rejected = await tool.execute("call_x", { ...PROBLEM_INPUT, target: ["ghost-prop"] } as never);
|
||||
assert.equal(rejected.isError, true);
|
||||
assert.match((rejected.details as { problems: string[] }).problems[0]!, /ghost-prop/);
|
||||
assert.match((rejected.details as { problems: string[] }).problems[0]!, /proposition ids from the session context/);
|
||||
// honest activation (the BKT law): a rejected pose frees the slot —
|
||||
// the next, valid pose goes through.
|
||||
const valid = await tool.execute("call_y", { ...PROBLEM_INPUT, target: ["n_parity"] } as never);
|
||||
assert.equal(valid.isError, undefined);
|
||||
});
|
||||
|
||||
it("NO node source (the legacy/test wiring) = no validation, free strings ack", async () => {
|
||||
const result = await problemTool(createPracticeGate(ctxOf([]))).execute("call_x", PROBLEM_INPUT as never);
|
||||
assert.equal(result.isError, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
// ── the registry + the agent ────────────────────────────────────────────────
|
||||
|
||||
describe("the practice agent", () => {
|
||||
|
||||
Reference in New Issue
Block a user