feat(T2a): tool-toggle seam + phase-based question principles

Part 1 — tool availability (the settings seam, machinery generic):
- core/pending.ts: TOGGLEABLE_TOOLS (today: research, demo, visual —
  the model-facing tool names), EnabledTools = the JSON-native seam
  shape Partial<Record<string, boolean>> (absent key = enabled, so old
  records behave exactly as today), filterEnabledTools shared helper.
  quiz/ask_user_question are always-on FOR NOW (user-decided); the
  filtering is generic so future toggleables (question-tool variants,
  the read tool, …) need no factory change.
- createProberTools/createTeacherTools take enabled?: EnabledTools; the
  composition root (chat-handler, Platform's lane this wave) passes the
  resolved settings through. probe_done is re-added after the filter —
  always-on by construction (it ends the phase; a prober that can never
  conclude is a lockout).
- Prompts stay STATIC under toggles this wave (user-decided); standing
  principle recorded: tools ship self-contained usage guidance in their
  descriptions; system prompts reference only always-on tools.

Part 2 — phase-based question-writing principles (user-locked wording,
drafted with the user over three iterations):
- NEW teacher/21-question-principles.md (composes after 20-teaching):
  answerable-by-reasoning-from-the-session (the fairness rule that
  filters unanswerable questions without forbidding visible answers),
  never-just-reciting-your-words-back, PREFER problems that apply the
  knowledge (preference, not hard rule — honest recall is the fallback,
  not the habit), catch-confusion-early-and-nameable (wrong options as
  real misconceptions), answer-is-signal-not-verdict.
- prober/10-probe-doctrine.md: 'no rush' retired; quick-measurement
  directive in — every question moves an edge (bracket, narrow, or
  characterize); a bracketed strand is done.

Tests: TOGGLEABLE_TOOLS pin, prober/teacher filtering (absent = full,
false drops exactly that tool, probe_done survives), prompt pins (the
principles compose verbatim into the teacher prompt, the no-rush
framing is gone from the prober). 547/547, typecheck/lint/build green.
This commit is contained in:
amosblomqvist
2026-09-19 14:06:07 +03:00
parent 487f5d4cea
commit cab1632c74
6 changed files with 158 additions and 15 deletions
+23
View File
@@ -949,6 +949,29 @@ describe("prompt composition", () => {
}
});
it("the question principles compose into the teacher prompt; the probe's quick-measurement revision landed", () => {
const teacher = composeInstructions("teacher");
// the T2a doctrine fragment (user-locked wording 2026-09-15): verbatim presence
for (const pin of [
"Answerable by reasoning from the session",
"Never just reciting your words back",
"Prefer problems that apply the knowledge",
"Catch confusion early, and make it nameable",
]) {
assert.ok(teacher.includes(pin), `teacher prompt lost the principle: ${pin}`);
}
// it loads in sorted fragment order: right after 20-teaching, before 30-jobs
assert.ok(
teacher.indexOf("Question principles") > teacher.indexOf("# Teach — the loop"),
"principles sit after the teaching loop",
);
// the prober's doctrine: the "no rush" framing is gone, quick measurement in
const prober = composeInstructions("prober");
assert.ok(!prober.includes("There is no rush"), "the old no-rush framing is retired");
assert.ok(prober.includes("measurement should be efficient"), "the quick-measurement directive is in");
assert.ok(!prober.includes("Question principles"), "the teach-phase fragment never composes into the prober");
});
it("guards prompt size — no fragment can silently balloon (the README-leak class)", () => {
// The wave-1 leak: README.md (815 bytes / ~200 tokens) rode into every
// teacher turn until the numeric-prefix filter stopped it. This guard