@interleavelove/keating 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +274 -0
  2. package/bin/keating.js +31 -0
  3. package/dist/src/cli/main.js +165 -0
  4. package/dist/src/core/animation.js +372 -0
  5. package/dist/src/core/benchmark.js +238 -0
  6. package/dist/src/core/config.js +81 -0
  7. package/dist/src/core/evolution.js +224 -0
  8. package/dist/src/core/learner-state.js +88 -0
  9. package/dist/src/core/lesson-plan.js +155 -0
  10. package/dist/src/core/map.js +89 -0
  11. package/dist/src/core/paths.js +69 -0
  12. package/dist/src/core/pi-agent.js +58 -0
  13. package/dist/src/core/policy.js +53 -0
  14. package/dist/src/core/project.js +189 -0
  15. package/dist/src/core/prompt-evolution.js +337 -0
  16. package/dist/src/core/random.js +19 -0
  17. package/dist/src/core/self-improve.js +419 -0
  18. package/dist/src/core/topics.js +620 -0
  19. package/dist/src/core/types.js +1 -0
  20. package/dist/src/core/util.js +28 -0
  21. package/dist/src/core/verification.js +162 -0
  22. package/dist/src/pi/hyperteacher-extension.js +180 -0
  23. package/dist/src/runtime/pi.js +118 -0
  24. package/dist/test/animation.test.js +43 -0
  25. package/dist/test/config.test.js +36 -0
  26. package/dist/test/evolution.test.js +39 -0
  27. package/dist/test/fuzz.test.js +37 -0
  28. package/dist/test/hyperteacher-extension.test.js +122 -0
  29. package/dist/test/lesson-plan.test.js +35 -0
  30. package/dist/test/pipeline.test.js +57 -0
  31. package/dist/test/prompt-evolution.test.js +89 -0
  32. package/package.json +58 -0
  33. package/pi/prompts/bridge.md +14 -0
  34. package/pi/prompts/diagnose.md +15 -0
  35. package/pi/prompts/improve.md +39 -0
  36. package/pi/prompts/learn.md +21 -0
  37. package/pi/prompts/quiz.md +14 -0
  38. package/pi/skills/adaptive-teaching/SKILL.md +33 -0
  39. package/scripts/install/install.sh +307 -0
@@ -0,0 +1,122 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { mkdtemp } from "node:fs/promises";
4
+ import { join } from "node:path";
5
+ import { tmpdir } from "node:os";
6
+ import { ensureProjectScaffold } from "../src/core/project.js";
7
+ import hyperteacher from "../src/pi/hyperteacher-extension.js";
8
+ function createMockPi() {
9
+ const commands = new Map();
10
+ const events = new Map();
11
+ return {
12
+ commands,
13
+ events,
14
+ registerCommand(name, cmd) {
15
+ commands.set(name, cmd);
16
+ },
17
+ on(event, handler) {
18
+ events.set(event, handler);
19
+ }
20
+ };
21
+ }
22
+ function createMockCtx(cwd) {
23
+ const notifications = [];
24
+ let editorText = "";
25
+ return {
26
+ cwd,
27
+ notifications,
28
+ get editorText() {
29
+ return editorText;
30
+ },
31
+ ui: {
32
+ notify(message, level) {
33
+ notifications.push({ message, level });
34
+ },
35
+ setEditorText(text) {
36
+ editorText = text;
37
+ },
38
+ async select(_title, options) {
39
+ return options[0] ?? "";
40
+ }
41
+ }
42
+ };
43
+ }
44
+ async function setup() {
45
+ const cwd = await mkdtemp(join(tmpdir(), "keating-ext-"));
46
+ await ensureProjectScaffold(cwd);
47
+ const pi = createMockPi();
48
+ hyperteacher(pi);
49
+ const ctx = createMockCtx(cwd);
50
+ return { cwd, pi, ctx };
51
+ }
52
+ test("/plan with string arg produces notification", async () => {
53
+ const { pi, ctx } = await setup();
54
+ await pi.commands.get("plan").handler("derivative", ctx);
55
+ assert.ok(ctx.notifications.some((n) => n.message.includes("derivative")));
56
+ assert.ok(ctx.editorText.length > 0);
57
+ });
58
+ test("/plan with array arg joins words", async () => {
59
+ const { pi, ctx } = await setup();
60
+ await pi.commands.get("plan").handler(["bayes", "rule"], ctx);
61
+ assert.ok(ctx.notifications.some((n) => n.message.includes("bayes")));
62
+ });
63
+ test("/plan with empty string shows usage", async () => {
64
+ const { pi, ctx } = await setup();
65
+ await pi.commands.get("plan").handler("", ctx);
66
+ assert.ok(ctx.notifications[0].message.includes("Usage"));
67
+ });
68
+ test("/plan with empty array shows usage", async () => {
69
+ const { pi, ctx } = await setup();
70
+ await pi.commands.get("plan").handler([], ctx);
71
+ assert.ok(ctx.notifications[0].message.includes("Usage"));
72
+ });
73
+ test("/map with string arg produces map", async () => {
74
+ const { pi, ctx } = await setup();
75
+ await pi.commands.get("map").handler("derivative", ctx);
76
+ assert.ok(ctx.notifications.some((n) => n.message.includes("Generated")));
77
+ });
78
+ test("/animate with string arg produces animation", async () => {
79
+ const { pi, ctx } = await setup();
80
+ await pi.commands.get("animate").handler("entropy", ctx);
81
+ assert.ok(ctx.notifications.some((n) => n.message.includes("Generated")));
82
+ });
83
+ test("/verify with string arg produces checklist", async () => {
84
+ const { pi, ctx } = await setup();
85
+ await pi.commands.get("verify").handler("derivative", ctx);
86
+ assert.ok(ctx.notifications.some((n) => n.message.includes("checklist")));
87
+ });
88
+ test("/verify with empty arg shows usage", async () => {
89
+ const { pi, ctx } = await setup();
90
+ await pi.commands.get("verify").handler("", ctx);
91
+ assert.ok(ctx.notifications[0].message.includes("Usage"));
92
+ });
93
+ test("/feedback records signal", async () => {
94
+ const { pi, ctx } = await setup();
95
+ await pi.commands.get("feedback").handler("up derivative", ctx);
96
+ assert.ok(ctx.notifications.some((n) => n.message.includes("thumbs-up")));
97
+ });
98
+ test("/feedback with invalid signal shows usage", async () => {
99
+ const { pi, ctx } = await setup();
100
+ await pi.commands.get("feedback").handler("", ctx);
101
+ assert.ok(ctx.notifications[0].message.includes("Usage"));
102
+ });
103
+ test("/bench with empty args runs suite", async () => {
104
+ const { pi, ctx } = await setup();
105
+ await pi.commands.get("bench").handler("", ctx);
106
+ assert.ok(ctx.notifications.some((n) => n.message.includes("Benchmark score")));
107
+ });
108
+ test("/policy shows current policy", async () => {
109
+ const { pi, ctx } = await setup();
110
+ await pi.commands.get("policy").handler("", ctx);
111
+ assert.ok(ctx.editorText.includes("Policy:"));
112
+ });
113
+ test("/trace with empty args works", async () => {
114
+ const { pi, ctx } = await setup();
115
+ await pi.commands.get("trace").handler("", ctx);
116
+ assert.ok(ctx.notifications.length > 0);
117
+ });
118
+ test("session_start event fires notification", async () => {
119
+ const { pi, ctx } = await setup();
120
+ await pi.events.get("session_start")({}, ctx);
121
+ assert.ok(ctx.notifications.some((n) => n.message.includes("Keating loaded")));
122
+ });
@@ -0,0 +1,35 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { buildLessonPlan, lessonPlanToMarkdown } from "../src/core/lesson-plan.js";
4
+ import { DEFAULT_POLICY, clampPolicy } from "../src/core/policy.js";
5
+ import { Prng } from "../src/core/random.js";
6
+ test("lesson plans preserve phase order and non-empty bullets across randomized policies", () => {
7
+ const prng = new Prng(42);
8
+ const topics = ["derivative", "entropy", "bayes", "falsifiability", "stoicism", "complex systems"];
9
+ for (let index = 0; index < 100; index += 1) {
10
+ const policy = clampPolicy({
11
+ ...DEFAULT_POLICY,
12
+ name: `policy-${index}`,
13
+ analogyDensity: prng.next(),
14
+ socraticRatio: prng.next(),
15
+ formalism: prng.next(),
16
+ retrievalPractice: prng.next(),
17
+ exerciseCount: prng.int(1, 5),
18
+ diagramBias: prng.next(),
19
+ reflectionBias: prng.next(),
20
+ interdisciplinaryBias: prng.next(),
21
+ challengeRate: prng.next()
22
+ });
23
+ const plan = buildLessonPlan(prng.pick(topics), policy);
24
+ assert.equal(plan.phases[0]?.title, "Orientation");
25
+ assert.equal(plan.phases.at(-1)?.title, "Transfer and Reflection");
26
+ assert.ok(plan.phases.some((phase) => phase.title === "Guided Practice"));
27
+ for (const phase of plan.phases) {
28
+ assert.ok(phase.bullets.length > 0);
29
+ for (const bullet of phase.bullets) {
30
+ assert.ok(bullet.length > 5);
31
+ }
32
+ }
33
+ assert.ok(lessonPlanToMarkdown(plan).includes(`# Lesson Plan: ${plan.topic.title}`));
34
+ }
35
+ });
@@ -0,0 +1,57 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { access, mkdtemp, mkdir, readFile, writeFile } from "node:fs/promises";
4
+ import { join } from "node:path";
5
+ import { tmpdir } from "node:os";
6
+ import { configPath } from "../src/core/config.js";
7
+ import { animateTopicArtifact, benchPolicyArtifact, currentPolicySummary, ensureProjectScaffold, evolvePolicyArtifact, evolvePromptArtifact, listArtifacts, mapTopicArtifact, planTopicArtifact } from "../src/core/project.js";
8
+ test("acceptance pipeline creates plans, maps, animations, benchmark reports, and evolved policy state", async () => {
9
+ const workdir = await mkdtemp(join(tmpdir(), "keating-pipeline-"));
10
+ await ensureProjectScaffold(workdir);
11
+ await mkdir(join(workdir, "pi", "prompts"), { recursive: true });
12
+ await writeFile(join(workdir, "pi", "prompts", "learn.md"), `---
13
+ description: Teach a concept adaptively with a mastery-first lesson loop.
14
+ ---
15
+ Teach the learner the following topic: $@
16
+
17
+ Workflow:
18
+ 1. Start with a diagnostic question or assumption check.
19
+ 2. Give at least one worked example.
20
+ 3. Ask for retrieval or reconstruction, not just agreement.
21
+ `);
22
+ const plan = await planTopicArtifact(workdir, "derivative");
23
+ const map = await mapTopicArtifact(workdir, "derivative");
24
+ const animation = await animateTopicArtifact(workdir, "derivative");
25
+ const bench = await benchPolicyArtifact(workdir, "derivative");
26
+ const evolution = await evolvePolicyArtifact(workdir, "derivative");
27
+ const promptEvolution = await evolvePromptArtifact(workdir, "learn");
28
+ await access(plan.planPath);
29
+ await access(map.mmdPath);
30
+ await access(animation.playerPath);
31
+ await access(animation.scenePath);
32
+ await access(animation.storyboardPath);
33
+ await access(animation.manifestPath);
34
+ await access(bench.reportPath);
35
+ await access(bench.tracePath);
36
+ await access(evolution.reportPath);
37
+ await access(evolution.tracePath);
38
+ await access(evolution.policyPath);
39
+ await access(promptEvolution.reportPath);
40
+ await access(promptEvolution.evolvedPromptPath);
41
+ await access(configPath(workdir));
42
+ const summary = await currentPolicySummary(workdir);
43
+ const report = await readFile(bench.reportPath, "utf8");
44
+ const trace = await readFile(evolution.tracePath, "utf8");
45
+ const storyboard = await readFile(animation.storyboardPath, "utf8");
46
+ const manifest = await readFile(animation.manifestPath, "utf8");
47
+ const promptReport = await readFile(promptEvolution.reportPath, "utf8");
48
+ const artifacts = await listArtifacts(workdir);
49
+ assert.ok(summary.includes("Policy:"));
50
+ assert.ok(report.includes("# Benchmark Report"));
51
+ assert.ok(trace.includes("\"decision\""));
52
+ assert.ok(storyboard.includes("# Animation Storyboard: Derivative"));
53
+ assert.ok(manifest.includes("\"sceneKind\": \"function-graph\""));
54
+ assert.ok(promptReport.includes("# Prompt Evolution Report: learn"));
55
+ assert.ok(artifacts.some((artifact) => artifact.path.endsWith("animations/derivative/player.html")));
56
+ assert.ok(artifacts.some((artifact) => artifact.path.endsWith("prompt-evolution/learn.evolved.md")));
57
+ });
@@ -0,0 +1,89 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { mkdtemp, mkdir, writeFile } from "node:fs/promises";
4
+ import { join } from "node:path";
5
+ import { tmpdir } from "node:os";
6
+ import { prosperStyleWinner, evolvePrompt } from "../src/core/prompt-evolution.js";
7
+ import { ensureProjectScaffold } from "../src/core/project.js";
8
+ async function mockEvaluator(_cwd, promptPath, prompt) {
9
+ const isGood = prompt.includes("own voice");
10
+ return {
11
+ promptPath,
12
+ promptName: "learn",
13
+ score: isGood ? 90 : 50,
14
+ objectives: {
15
+ voice_divergence: isGood ? 1 : 0.2,
16
+ diagnosis: 0.5,
17
+ verification: 0.5,
18
+ retrieval: 0.5,
19
+ transfer: 0.5,
20
+ structure: 0.5
21
+ },
22
+ feedback: isGood ? [] : ["Add voice"]
23
+ };
24
+ }
25
+ async function mockGenerator(_cwd, basePrompt, _evaluation, _iteration) {
26
+ return basePrompt + "\nHelp the learner find their own voice.";
27
+ }
28
+ test("PROSPER-style winner prefers candidates that win across multiple objectives", () => {
29
+ const candidates = [
30
+ {
31
+ iteration: 1,
32
+ label: "narrow",
33
+ prompt: "narrow",
34
+ parentLabel: "learn",
35
+ accepted: false,
36
+ preferenceScore: 0,
37
+ evaluation: {
38
+ promptPath: "learn.md",
39
+ promptName: "learn",
40
+ score: 78,
41
+ objectives: {
42
+ voice_divergence: 1,
43
+ diagnosis: 0.2,
44
+ verification: 0.2,
45
+ retrieval: 0.2,
46
+ transfer: 0.2,
47
+ structure: 1
48
+ },
49
+ feedback: []
50
+ }
51
+ },
52
+ {
53
+ iteration: 2,
54
+ label: "balanced",
55
+ prompt: "balanced",
56
+ parentLabel: "learn",
57
+ accepted: false,
58
+ preferenceScore: 0,
59
+ evaluation: {
60
+ promptPath: "learn.md",
61
+ promptName: "learn",
62
+ score: 84,
63
+ objectives: {
64
+ voice_divergence: 0.8,
65
+ diagnosis: 0.8,
66
+ verification: 0.8,
67
+ retrieval: 0.8,
68
+ transfer: 0.8,
69
+ structure: 0.8
70
+ },
71
+ feedback: []
72
+ }
73
+ }
74
+ ];
75
+ const winner = prosperStyleWinner(candidates);
76
+ assert.equal(winner.label, "balanced");
77
+ assert.ok(winner.preferenceScore > candidates[0].preferenceScore);
78
+ });
79
+ test("prompt evolution works with mocked LLM calls", async () => {
80
+ const workdir = await mkdtemp(join(tmpdir(), "keating-prompt-evolution-mock-"));
81
+ await ensureProjectScaffold(workdir);
82
+ await mkdir(join(workdir, "pi", "prompts"), { recursive: true });
83
+ await writeFile(join(workdir, "pi", "prompts", "learn.md"), "Teach the topic.");
84
+ const run = await evolvePrompt(workdir, "learn", 2, mockEvaluator, mockGenerator);
85
+ assert.ok(run.baseline.score < run.best.evaluation.score);
86
+ assert.ok(run.best.prompt.includes("own voice"));
87
+ assert.equal(run.exploredCandidates.length, 2);
88
+ assert.ok(run.best.accepted);
89
+ });
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@interleavelove/keating",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "A Pi-powered hyperteacher package with self-improving teaching policies, lesson maps, and animated visual teaching artifacts.",
6
+ "keywords": [
7
+ "pi-package",
8
+ "hyperteacher",
9
+ "education",
10
+ "feynman-style",
11
+ "teaching-agent",
12
+ "ai-education",
13
+ "cognitive-empowerment"
14
+ ],
15
+ "author": "Diogenes of Toronto",
16
+ "license": "MIT",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/Diogenesoftoronto/keating.git"
20
+ },
21
+ "bugs": {
22
+ "url": "https://github.com/Diogenesoftoronto/keating/issues"
23
+ },
24
+ "homepage": "https://keating.help",
25
+ "bin": {
26
+ "keating": "./bin/keating.js"
27
+ },
28
+ "engines": {
29
+ "node": ">=20.19.0"
30
+ },
31
+ "files": [
32
+ "bin/",
33
+ "dist/",
34
+ "pi/",
35
+ "scripts/install/"
36
+ ],
37
+ "pi": {
38
+ "extensions": [
39
+ "./dist/src/pi"
40
+ ],
41
+ "skills": [
42
+ "./pi/skills"
43
+ ],
44
+ "prompts": [
45
+ "./pi/prompts"
46
+ ]
47
+ },
48
+ "scripts": {
49
+ "build": "bun x tsc -p tsconfig.json",
50
+ "test": "bun test ./test/*.test.ts",
51
+ "prepack": "bun x tsc -p tsconfig.json"
52
+ },
53
+ "dependencies": {
54
+ "@google/generative-ai": "^0.24.1",
55
+ "dotenv": "^17.4.0",
56
+ "manim-web": "^0.3.16"
57
+ }
58
+ }
@@ -0,0 +1,14 @@
1
+ ---
2
+ description: Bridge a concept across domains including science, math, philosophy, code, law, history, psychology, medicine, arts, and politics so the learner sees what transfers and what does not.
3
+ args: <topic>
4
+ section: Teaching Workflows
5
+ topLevelCli: true
6
+ ---
7
+ Bridge the following concept across domains: $@
8
+
9
+ Structure:
10
+
11
+ 1. Define the core invariant idea.
12
+ 2. Show how that idea appears in at least two domains.
13
+ 3. Point out where the analogy stops working.
14
+ 4. End with a transfer question for the learner.
@@ -0,0 +1,15 @@
1
+ ---
2
+ description: Diagnose what a learner already understands, where they are confused, and what to teach next.
3
+ args: <topic>
4
+ section: Teaching Workflows
5
+ topLevelCli: true
6
+ ---
7
+ Diagnose the learner's current state on: $@
8
+
9
+ Requirements:
10
+
11
+ 0. If `.keating/state/learner.json` exists, read the learner's identified misconceptions and mastery estimates before asking diagnostic questions. Use prior state to focus your questions.
12
+ 1. Ask at most three high-information questions before explaining.
13
+ 2. Infer likely misconceptions from the answers.
14
+ 3. Separate "missing prerequisite", "partial intuition", and "formal gap".
15
+ 4. End with a proposed next teaching step, not a full lecture.
@@ -0,0 +1,39 @@
1
+ ---
2
+ description: Run the self-improvement loop — diagnose benchmark weaknesses and propose code changes.
3
+ args: [history]
4
+ section: Meta-Evolution
5
+ topLevelCli: true
6
+ ---
7
+ Run the Keating self-improvement pipeline.
8
+
9
+ If the argument is "history", show the improvement attempt archive:
10
+ - Run `/improve history` and display the result.
11
+
12
+ Otherwise, generate a new improvement proposal:
13
+
14
+ 1. Run `/improve` to diagnose benchmark weaknesses and generate a proposal.
15
+ 2. Read the proposal file under `.keating/outputs/improvements/`.
16
+ 3. The proposal contains:
17
+ - A ranked list of diagnosed weaknesses (from benchmark traces).
18
+ - Specific target files that may be modified.
19
+ - Structured instructions for what to change and why.
20
+ - Safety rules: which files are mutable vs immutable.
21
+
22
+ 4. Execute the proposed changes:
23
+ - Only modify files listed in the proposal's `targets`.
24
+ - Never modify: `self-improve.ts`, `types.ts`, `config.ts`, `paths.ts`, `random.ts`.
25
+ - Before each edit, explain what you are changing and why.
26
+ - Keep changes minimal and focused on the diagnosed weakness.
27
+
28
+ 5. After making changes, run the benchmark to evaluate:
29
+ - `keating bench` to get the new score.
30
+ - Compare against the before-score in the proposal.
31
+ - If the score improved or held steady, the change is accepted.
32
+ - If the score dropped, revert your changes.
33
+
34
+ 6. Report the outcome: what changed, before/after scores, and whether the change was kept or rolled back.
35
+
36
+ Safety contract:
37
+ - The self-improvement loop modifies teaching logic (lesson plans, benchmark weights, animations, topic definitions, maps, policy defaults).
38
+ - It never modifies the improvement engine itself, type definitions, configuration, or the random seed system.
39
+ - Every attempt is archived with snapshots so changes can always be rolled back.
@@ -0,0 +1,21 @@
1
+ ---
2
+ description: Teach a concept adaptively with a mastery-first lesson loop.
3
+ args: <topic>
4
+ section: Teaching Workflows
5
+ topLevelCli: true
6
+ ---
7
+ Teach the learner the following topic: $@
8
+
9
+ Workflow:
10
+
11
+ 0. Before teaching, check `.keating/outputs/verifications/` for this topic. If no verification checklist exists, run `/verify <topic>` first. Do not present unverified factual claims as settled truth.
12
+ 0b. Check `.keating/state/learner.json` for prior coverage of this topic. If the learner has seen it before, skip orientation and go straight to retrieval practice or misconception repair.
13
+ 1. Start with a short diagnostic question or assumption check.
14
+ 2. Move through intuition before formal structure.
15
+ 3. Repair the likeliest misconception explicitly.
16
+ 4. Give at least one worked example.
17
+ 5. Ask for retrieval or reconstruction, not just agreement.
18
+ 6. End by bridging the topic to another domain or a practical consequence.
19
+
20
+ If a matching Keating artifact already exists under `.keating/outputs/plans/` or `.keating/outputs/maps/`, read it and use it.
21
+ If it does not exist and a structured lesson would help, tell the user to run `/plan <topic>` or `/map <topic>`.
@@ -0,0 +1,14 @@
1
+ ---
2
+ description: Quiz a learner on a topic with feedback that reveals misconceptions instead of just scoring answers.
3
+ args: <topic>
4
+ section: Teaching Workflows
5
+ topLevelCli: true
6
+ ---
7
+ Run a short mastery quiz on: $@
8
+
9
+ Rules:
10
+
11
+ 1. Ask one question at a time.
12
+ 2. Mix recall, transfer, and misconception-revealing prompts.
13
+ 3. After each answer, say what the answer shows about understanding.
14
+ 4. If the learner misses something, teach the minimum needed correction before continuing.
@@ -0,0 +1,33 @@
1
+ ---
2
+ name: adaptive-teaching
3
+ description: Use when the user wants to learn, be taught, be quizzed, or understand an idea across any domain — science, math, philosophy, code, law, politics, psychology, medicine, arts, or history — with active scaffolding.
4
+ ---
5
+
6
+ # Adaptive Teaching
7
+
8
+ Use this skill when the user asks to learn a concept rather than merely receive a summary.
9
+
10
+ ## Teaching Contract
11
+
12
+ 1. Diagnose first if the learner state is unknown. Check `.keating/state/learner.json` for prior coverage.
13
+ 2. Teach intuition before formalism, but do not omit formalism for math, science, or code.
14
+ 3. Surface one misconception early.
15
+ 4. Use retrieval and reconstruction prompts, not just exposition.
16
+ 5. End with transfer, reflection, or a next challenge.
17
+ 6. Verify factual claims before teaching. If `.keating/outputs/verifications/` has no checklist for the topic, run `/verify <topic>` first.
18
+
19
+ ## Keating Artifacts
20
+
21
+ - If a lesson plan would help, tell the user to run `/plan <topic>`.
22
+ - If a concept map would help, tell the user to run `/map <topic>`.
23
+ - If an animation would help, tell the user to run `/animate <topic>`.
24
+ - If they want to verify facts first, tell the user to run `/verify <topic>`.
25
+ - If they want the teacher to improve, tell the user to run `/bench [topic]` and `/evolve [topic]`.
26
+ - If they want to give feedback, tell the user to run `/feedback <up|down|confused> [topic]`.
27
+
28
+ ## Red Flags
29
+
30
+ - Do not drown the learner in formal language before a concrete hook exists.
31
+ - Do not act as though the synthetic benchmark proves pedagogical truth.
32
+ - Do not confuse a correct answer with stable understanding; probe transfer.
33
+ - Do not present unverified factual claims as settled truth. If verification artifacts exist, consult them. If claims are flagged as unconfirmed, hedge appropriately.